diff --git a/BUILD.gn b/BUILD.gn
index a3ab7fe..112ac02 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -427,6 +427,10 @@
         }
       }
 
+      if (checkout_fuchsia) {
+        deps += [ "//build/fuchsia/starview:run_cuttlefish_test" ]
+      }
+
       if (enable_chrome_android_internal) {
         deps += [ "//clank" ]
       }
diff --git a/DEPS b/DEPS
index 07175f7..4fed2d40 100644
--- a/DEPS
+++ b/DEPS
@@ -44,6 +44,7 @@
   'checkout_android_prebuilts_build_tools',
   'checkout_clang_coverage_tools',
   'checkout_copybara',
+  'checkout_fuchsia',
   'checkout_glic_e2e_tests',
   'checkout_ios_webkit',
   'checkout_mutter',
@@ -347,7 +348,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
-  'boringssl_revision': 'e055b59aa175e41614664d04c8b57f243bbba78f',
+  'boringssl_revision': '922245af6eda11b3f101ab5f542093eb5e7d1a74',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
@@ -367,7 +368,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '656cb777798fa420a13faba3758779e9ed6c4798',
+  'freetype_revision': '9e9d3b73f31367dbb4261f93c727a277f6632c77',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
@@ -387,7 +388,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': '6423f30d7ced32b24acc63178459aa917778ebc8',
+  'crossbench_revision': '4b6b619a4bf65ef82bf3218b866d3cb0e997c6f3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
@@ -1710,7 +1711,7 @@
     'packages': [
       {
         'package': 'chromium/chrome/test/data/variations/cipd',
-        'version': 'lz0uU9YjQGJNs4jqDlDqD9pnIg3mICghgXE_XLtKg4kC',
+        'version': '3ksu6GHkUbpzkDsAaj431h_iUnzF8hv9XNfc3HpViEwC',
       },
     ],
     'condition': 'non_git_source',
@@ -1722,7 +1723,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '9924b3ae796f1ead39f335234c232c4226e25c0f',
+    '60af3ed4b83a012447f9a27fad47768a55e80fb3',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -2170,7 +2171,7 @@
     Var('chromium_git') + '/chromium/web-tests.git' + '@' + Var('crossbench_web_tests_revision'),
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'fa1fac8477c70532274e7244777a846537004750',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '33025ff4f2a57667af0b8302d1547b8c58922cf9',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -2448,6 +2449,17 @@
       'dep_type': 'cipd',
   },
 
+  'src/third_party/ktfmt/cipd': {
+      'packages': [
+          {
+              'package': 'chromium/third_party/ktfmt',
+              'version': 'version:2@v0.64.a441f23abe',
+          },
+      ],
+      'condition': '(checkout_android or checkout_linux) and non_git_source',
+      'dep_type': 'cipd',
+  },
+
   'src/third_party/leveldatabase/src':
     Var('chromium_git') + '/external/leveldb.git' + '@' + '7ee830d02b623e8ffe0b95d59a74db1e58da04c5',
 
@@ -3118,16 +3130,16 @@
       ],
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@909979df82b1ef3a50180031299f94d54b7d4e0b',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@e6f723a0c78be663f0b10b074c66c454973bb23f',
   'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@90afccfbd49dff0349d86a41762e9de24e1df811',
   'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3',
   'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@942fe4b988359a0750b79f0ae7ed735994d3147d',
-  'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@5b5a25231a3543559b4f2cb27fe725a08b76b5d0',
+  'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@388f5af1a6d4a4df4ab9bf69a1817255952e6ef1',
   'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@f9973cd97e6f3584707e7ef1c425e336f1b92a5b',
-  'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@e5715c8a3959f2a40a6bc13a3a88a3862843b105',
-  'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@d9c353983147b260354764f8d8cc452a8d8c2a36',
+  'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@fbfe4e7b3126dba48e4551fef5c6a19f15d6de12',
+  'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@572d10d787b74601ea09b696521c950c259ae815',
   'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@48b2a63b17eb79d612bd276d6e1adeb1f73c03e9',
-  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@4a33de058dae899fdec1718e9daf81a5eda3bc2d',
+  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@06171f9489fa80fe0cafcb25f9dfc1dcd3408f1a',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '82a9d47e4f9d91f0e32d2b6acd9714fcee1933a0',
@@ -4011,7 +4023,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '824a640140c461884db8ada2da1bb1a3ba8ef09d',
+        'c39c69a52e62b3a5a47c43b4bd884a7d289f509e',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/tools/android-webview-arm.orderfile.txt b/android_webview/tools/android-webview-arm.orderfile.txt
index d5706b6..df508c6 100644
--- a/android_webview/tools/android-webview-arm.orderfile.txt
+++ b/android_webview/tools/android-webview-arm.orderfile.txt
@@ -1 +1 @@
-DYqvxAsc7MqIUm4vQYcHuPq6GYLpU-Au_UeYowUFhh0C
+D1tDFZmPkZHRn7d4rkpq8EjtDfv5XRjSExzmR2l3A6AC
diff --git a/android_webview/tools/android-webview-arm64.orderfile.txt b/android_webview/tools/android-webview-arm64.orderfile.txt
index 36889ec..fa56866 100644
--- a/android_webview/tools/android-webview-arm64.orderfile.txt
+++ b/android_webview/tools/android-webview-arm64.orderfile.txt
@@ -1 +1 @@
-4u8_5fKgBLrN5M7TZwRw5UfCDTtFveXeKn776K7cth8C
+kPe-a5SciwMixPDM_J3nctS8WCEKTUBF9sZeCbY_jUgC
diff --git a/ash/fast_ink/fast_ink_host_unittest.cc b/ash/fast_ink/fast_ink_host_unittest.cc
index b335e507..c054a8a5 100644
--- a/ash/fast_ink/fast_ink_host_unittest.cc
+++ b/ash/fast_ink/fast_ink_host_unittest.cc
@@ -5,7 +5,7 @@
 #include "ash/fast_ink/fast_ink_host.h"
 
 #include <memory>
-#include <tuple>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -40,59 +40,51 @@
 namespace ash {
 namespace {
 
-class FastInkHostTest
-    : public FrameSinkHostTestBase<FastInkHost>,
-      public ::testing::WithParamInterface<
-          std::tuple<std::string, bool, gfx::Rect, gfx::Rect, gfx::Rect>> {
- public:
-  FastInkHostTest()
-      : first_display_specs_(std::get<0>(GetParam())),
-        auto_update_(std::get<1>(GetParam())),
-        content_rect_(std::get<2>(GetParam())),
-        expected_quad_rect_(std::get<3>(GetParam())),
-        expected_quad_layer_rect_(std::get<4>(GetParam())) {}
-
-  FastInkHostTest(const FastInkHostTest&) = delete;
-  FastInkHostTest& operator=(const FastInkHostTest&) = delete;
-
-  ~FastInkHostTest() override = default;
-
-  // AshTestBase:
-  void SetUp() override {
-    SetDisplaySpecs(first_display_specs_);
-    FrameSinkHostTestBase<FastInkHost>::SetUp();
-  }
-
- protected:
-  std::string first_display_specs_;
-  bool auto_update_ = false;
-  gfx::Rect content_rect_;
-  gfx::Rect expected_quad_rect_;
-  gfx::Rect expected_quad_layer_rect_;
+struct FastInkHostTestParams {
+  std::string first_display_specs;
+  bool auto_update = false;
+  gfx::Rect content_rect;
+  gfx::Rect expected_quad_rect;
+  gfx::Rect expected_quad_layer_rect;
 };
 
-TEST_P(FastInkHostTest, CorrectFrameSubmittedToLayerTreeFrameSink) {
+class FastInkHostFrameSubmissionTest
+    : public FrameSinkHostTestBase<FastInkHost>,
+      public ::testing::WithParamInterface<FastInkHostTestParams> {
+ public:
+  void SetUp() override {
+    SetDisplaySpecs(GetParam().first_display_specs);
+    FrameSinkHostTestBase<FastInkHost>::SetUp();
+  }
+};
+
+TEST_P(FastInkHostFrameSubmissionTest,
+       CorrectFrameSubmittedToLayerTreeFrameSink) {
   // Request the first frame.
   OnBeginFrame();
 
+  const auto& params = GetParam();
+
   SCOPED_TRACE(base::StringPrintf(
       "Test params: first_display_specs=%s | auto_update=%s | content_rect=%s "
       "| expected_quad_rect=%s | expected_quad_layer_rect=%s",
-      first_display_specs_.c_str(), auto_update_ ? "true" : "false",
-      content_rect_.ToString().c_str(), expected_quad_rect_.ToString().c_str(),
-      expected_quad_layer_rect_.ToString().c_str()));
+      params.first_display_specs.c_str(), params.auto_update ? "true" : "false",
+      params.content_rect.ToString().c_str(),
+      params.expected_quad_rect.ToString().c_str(),
+      params.expected_quad_layer_rect.ToString().c_str()));
 
   constexpr gfx::Rect kTestTotalDamageRectInDIP = gfx::Rect(0, 0, 50, 25);
 
-  if (auto_update_) {
-    frame_sink_host()->AutoUpdateSurface(content_rect_,
+  if (params.auto_update) {
+    frame_sink_host()->AutoUpdateSurface(params.content_rect,
                                          kTestTotalDamageRectInDIP);
 
     // When host auto updates, it only submits a frame when requested by
     // LayerTreeFrameSink via a BeginFrameSource.
     OnBeginFrame();
   } else {
-    frame_sink_host()->UpdateSurface(content_rect_, kTestTotalDamageRectInDIP,
+    frame_sink_host()->UpdateSurface(params.content_rect,
+                                     kTestTotalDamageRectInDIP,
                                      /*synchronous_draw=*/true);
   }
 
@@ -110,21 +102,84 @@
   viz::DrawQuad* quad = quad_list.back();
   EXPECT_EQ(quad->material, viz::DrawQuad::Material::kTextureContent);
 
-  EXPECT_EQ(quad->rect, expected_quad_rect_);
-  EXPECT_EQ(quad->visible_rect, expected_quad_rect_);
+  EXPECT_EQ(quad->rect, params.expected_quad_rect);
+  EXPECT_EQ(quad->visible_rect, params.expected_quad_rect);
 
   ASSERT_EQ(render_pass_list.front()->shared_quad_state_list.size(), 1u);
   auto* shared_quad_state =
       render_pass_list.front()->shared_quad_state_list.front();
 
-  EXPECT_EQ(shared_quad_state->quad_layer_rect, expected_quad_layer_rect_);
+  EXPECT_EQ(shared_quad_state->quad_layer_rect,
+            params.expected_quad_layer_rect);
   EXPECT_EQ(shared_quad_state->visible_quad_layer_rect,
-            expected_quad_layer_rect_);
+            params.expected_quad_layer_rect);
 
-  EXPECT_EQ(frame.resource_list.back().GetIsOverlayCandidate(), auto_update_);
+  EXPECT_EQ(frame.resource_list.back().GetIsOverlayCandidate(),
+            params.auto_update);
 }
 
-TEST_P(FastInkHostTest, RecreateGpuBufferOnLosingFrameSink) {
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    FastInkHostFrameSubmissionTest,
+    testing::Values(
+        // When auto updating surface, we update the full surface, ignoring the
+        // content_rect.
+        FastInkHostTestParams{
+            .first_display_specs = "1000x500",
+            .auto_update = true,
+            .content_rect = gfx::Rect(10, 10),
+            .expected_quad_rect = gfx::Rect(0, 0, 1000, 500),
+            .expected_quad_layer_rect = gfx::Rect(0, 0, 1000, 500)},
+        FastInkHostTestParams{
+            .first_display_specs = "1000x500*2",
+            .auto_update = true,
+            .content_rect = gfx::Rect(10, 10),
+            .expected_quad_rect = gfx::Rect(0, 0, 1000, 500),
+            .expected_quad_layer_rect = gfx::Rect(0, 0, 1000, 500)},
+        FastInkHostTestParams{
+            .first_display_specs = "1000x500*2/r",
+            .auto_update = true,
+            .content_rect = gfx::Rect(10, 10),
+            .expected_quad_rect = gfx::Rect(0, 0, 1000, 500),
+            .expected_quad_layer_rect = gfx::Rect(0, 0, 500, 1000)},
+        // When auto updating is off, we update the surface enclosed by
+        // content_rect.
+        FastInkHostTestParams{
+            .first_display_specs = "1000x500",
+            .auto_update = false,
+            .content_rect = gfx::Rect(10, 10),
+            .expected_quad_rect = gfx::Rect(0, 0, 10, 10),
+            .expected_quad_layer_rect = gfx::Rect(0, 0, 1000, 500)},
+        FastInkHostTestParams{
+            .first_display_specs = "1000x500*2",
+            .auto_update = false,
+            .content_rect = gfx::Rect(10, 10),
+            .expected_quad_rect = gfx::Rect(0, 0, 20, 20),
+            .expected_quad_layer_rect = gfx::Rect(0, 0, 1000, 500)},
+        FastInkHostTestParams{
+            .first_display_specs = "1000x500*2/l",
+            .auto_update = false,
+            .content_rect = gfx::Rect(10, 15),
+            .expected_quad_rect = gfx::Rect(0, 480, 30, 20),
+            .expected_quad_layer_rect = gfx::Rect(0, 0, 500, 1000)},
+        // If content rect is partially outside of the buffer, quad rect is
+        // clipped by buffer size.
+        FastInkHostTestParams{
+            .first_display_specs = "1000x500",
+            .auto_update = false,
+            .content_rect = gfx::Rect(995, 0, 10, 10),
+            .expected_quad_rect = gfx::Rect(995, 0, 5, 10),
+            .expected_quad_layer_rect = gfx::Rect(0, 0, 1000, 500)},
+        FastInkHostTestParams{
+            .first_display_specs = "3000x2000*2.252252",
+            .auto_update = false,
+            .content_rect = gfx::Rect(0, 0, 1332, 888),
+            .expected_quad_rect = gfx::Rect(0, 0, 3000, 2000),
+            .expected_quad_layer_rect = gfx::Rect(0, 0, 3000, 2000)}));
+
+using FastInkHostTest = FrameSinkHostTestBase<FastInkHost>;
+
+TEST_F(FastInkHostTest, RecreateGpuBufferOnLosingFrameSink) {
   FastInkHostTestApi fast_ink_host_test(frame_sink_host());
 
   // Buffer is not initialized when there is no begin frame received.
@@ -154,7 +209,7 @@
   EXPECT_TRUE(fast_ink_host_test.client_shared_image());
 }
 
-TEST_P(FastInkHostTest, DelayPaintingUntilReceivingFirstBeginFrame) {
+TEST_F(FastInkHostTest, DelayPaintingUntilReceivingFirstBeginFrame) {
   FastInkHostTestApi fast_ink_host_test(frame_sink_host());
 
   // Buffer is not initialized when there is no begin frame received.
@@ -190,64 +245,5 @@
             SK_ColorGREEN);
 }
 
-INSTANTIATE_TEST_SUITE_P(
-    /* no prefix */,
-    FastInkHostTest,
-    testing::Values(
-        // When auto updating surface, we update the full surface, ignoring the
-        // content_rect.
-        std::make_tuple(
-            /*first_display_specs=*/"1000x500",
-            /*auto_update=*/true,
-            /*content_rect=*/gfx::Rect(10, 10),
-            /*expected_quad_rect=*/gfx::Rect(0, 0, 1000, 500),
-            /*expected_quad_layer_rect=*/gfx::Rect(0, 0, 1000, 500)),
-        std::make_tuple(
-            /*first_display_specs=*/"1000x500*2",
-            /*auto_update=*/true,
-            /*content_rect=*/gfx::Rect(10, 10),
-            /*expected_quad_rect=*/gfx::Rect(0, 0, 1000, 500),
-            /*expected_quad_layer_rect=*/gfx::Rect(0, 0, 1000, 500)),
-        std::make_tuple(
-            /*first_display_specs=*/"1000x500*2/r",
-            /*auto_update=*/true,
-            /*content_rect=*/gfx::Rect(10, 10),
-            /*expected_quad_rect=*/gfx::Rect(0, 0, 1000, 500),
-            /*expected_quad_layer_rect=*/gfx::Rect(0, 0, 500, 1000)),
-        // When auto updating is off, we update the surface enclosed by
-        // content_rect.
-        std::make_tuple(
-            /*first_display_specs=*/"1000x500",
-            /*auto_update=*/false,
-            /*content_rect=*/gfx::Rect(10, 10),
-            /*expected_quad_rect=*/gfx::Rect(0, 0, 10, 10),
-            /*expected_quad_layer_rect=*/gfx::Rect(0, 0, 1000, 500)),
-        std::make_tuple(
-            /*first_display_specs=*/"1000x500*2",
-            /*auto_update=*/false,
-            /*content_rect=*/gfx::Rect(10, 10),
-            /*expected_quad_rect=*/gfx::Rect(0, 0, 20, 20),
-            /*expected_quad_layer_rect=*/gfx::Rect(0, 0, 1000, 500)),
-        std::make_tuple(
-            /*first_display_specs=*/"1000x500*2/l",
-            /*auto_update=*/false,
-            /*content_rect=*/gfx::Rect(10, 15),
-            /*expected_quad_rect=*/gfx::Rect(0, 480, 30, 20),
-            /*expected_quad_layer_rect=*/gfx::Rect(0, 0, 500, 1000)),
-        // If content rect is partially outside of the buffer, quad rect is
-        // clipped by buffer size.
-        std::make_tuple(
-            /*first_display_specs=*/"1000x500",
-            /*auto_update=*/false,
-            /*content_rect=*/gfx::Rect(995, 0, 10, 10),
-            /*expected_quad_rect=*/gfx::Rect(995, 0, 5, 10),
-            /*expected_quad_layer_rect=*/gfx::Rect(0, 0, 1000, 500)),
-        std::make_tuple(
-            /*first_display_specs=*/"3000x2000*2.252252",
-            /*auto_update=*/false,
-            /*content_rect=*/gfx::Rect(0, 0, 1332, 888),
-            /*expected_quad_rect=*/gfx::Rect(0, 0, 3000, 2000),
-            /*expected_quad_layer_rect=*/gfx::Rect(0, 0, 3000, 2000))));
-
 }  // namespace
 }  // namespace ash
diff --git a/base/BUILD.gn b/base/BUILD.gn
index f4a383b..c351539 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -664,8 +664,6 @@
     "sampling_heap_profiler/lock_free_bloom_filter.h",
     "sampling_heap_profiler/poisson_allocation_sampler.cc",
     "sampling_heap_profiler/poisson_allocation_sampler.h",
-    "sampling_heap_profiler/sampling_heap_churn_profiler.cc",
-    "sampling_heap_profiler/sampling_heap_churn_profiler.h",
     "sampling_heap_profiler/sampling_heap_profiler.cc",
     "sampling_heap_profiler/sampling_heap_profiler.h",
     "scoped_add_feature_flags.cc",
@@ -2373,8 +2371,6 @@
       sources -= [
         "sampling_heap_profiler/poisson_allocation_sampler.cc",
         "sampling_heap_profiler/poisson_allocation_sampler.h",
-        "sampling_heap_profiler/sampling_heap_churn_profiler.cc",
-        "sampling_heap_profiler/sampling_heap_churn_profiler.h",
         "sampling_heap_profiler/sampling_heap_profiler.cc",
         "sampling_heap_profiler/sampling_heap_profiler.h",
       ]
@@ -4654,7 +4650,6 @@
   if (use_allocator_shim) {
     sources += [
       "sampling_heap_profiler/poisson_allocation_sampler_unittest.cc",
-      "sampling_heap_profiler/sampling_heap_churn_profiler_unittest.cc",
       "sampling_heap_profiler/sampling_heap_profiler_unittest.cc",
 
       # TODO(crbug.com/40158212): Move partition_alloc tests into its
diff --git a/base/android/java/src/org/chromium/base/UnownedUserDataHost.java b/base/android/java/src/org/chromium/base/UnownedUserDataHost.java
index f0f3792..de8fbb3 100644
--- a/base/android/java/src/org/chromium/base/UnownedUserDataHost.java
+++ b/base/android/java/src/org/chromium/base/UnownedUserDataHost.java
@@ -8,6 +8,7 @@
 
 import android.os.Handler;
 import android.os.Looper;
+import android.util.ArrayMap;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -19,8 +20,8 @@
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * UnownedUserDataHost is a type-safe and heterogeneous container that does not own the objects that
@@ -163,9 +164,9 @@
     private @Nullable Handler mHandler;
 
     /** The core data structure within this host. */
-    // Default capacity (16) is oversized for typical 1-6 entries.
-    private @Nullable HashMap<UnownedUserDataKey<?>, WeakReference<Object>> mUnownedUserDataMap =
-            new HashMap<>(4);
+    // ArrayMap with capacity 4 avoids heap entry allocations and fits typical 1-6 entries per host.
+    private @Nullable Map<UnownedUserDataKey<?>, WeakReference<Object>> mUnownedUserDataMap =
+            new ArrayMap<>(4);
 
     public UnownedUserDataHost() {
         this(new Handler(retrieveNonNullLooperOrThrow()));
diff --git a/base/android/java/src/org/chromium/base/UserDataHost.java b/base/android/java/src/org/chromium/base/UserDataHost.java
index 33b70c0..d80a003 100644
--- a/base/android/java/src/org/chromium/base/UserDataHost.java
+++ b/base/android/java/src/org/chromium/base/UserDataHost.java
@@ -4,12 +4,13 @@
 
 package org.chromium.base;
 
+import android.util.ArrayMap;
+
 import org.chromium.base.ThreadUtils.ThreadChecker;
 import org.chromium.build.annotations.EnsuresNonNull;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 
@@ -57,8 +58,8 @@
 public final class UserDataHost {
     private final ThreadChecker mThreadChecker = new ThreadChecker();
 
-    // Default capacity (16) is oversized for typical 1-3 entries.
-    private @Nullable Map<Class<? extends UserData>, UserData> mUserDataMap = new HashMap<>(4);
+    // ArrayMap with capacity 4 avoids heap entry allocations and fits typical 1-3 entries per host.
+    private @Nullable Map<Class<? extends UserData>, UserData> mUserDataMap = new ArrayMap<>(4);
 
     @EnsuresNonNull("mUserDataMap")
     private void checkThreadAndState() {
diff --git a/base/android/java/src/org/chromium/base/supplier/ObservableSupplierImpl.java b/base/android/java/src/org/chromium/base/supplier/ObservableSupplierImpl.java
index 4547ae3..6f95c02f 100644
--- a/base/android/java/src/org/chromium/base/supplier/ObservableSupplierImpl.java
+++ b/base/android/java/src/org/chromium/base/supplier/ObservableSupplierImpl.java
@@ -38,8 +38,9 @@
                 SettableMonotonicObservableSupplier<T>,
                 SettableNonNullObservableSupplier<T> {
     protected final ThreadChecker mThreadChecker = new ThreadChecker();
-    protected @Nullable ObserverList<Callback<T>> mObservers = new ObserverList<>();
+    protected @Nullable ObserverList<Callback<T>> mObservers;
     protected T mObject;
+    private boolean mIsDestroyed;
 
     @Deprecated // Migrate to ObservableSuppliers.*
     public ObservableSupplierImpl() {
@@ -61,10 +62,13 @@
 
     @Override
     public T addObserver(Callback<T> obs, @NotifyBehavior int behavior) {
-        assert mObservers != null : "addObserver called on destroyed supplier";
-        if (mObservers == null) {
+        assert !mIsDestroyed : "addObserver called on destroyed supplier";
+        if (mIsDestroyed) {
             return null;
         }
+        if (mObservers == null) {
+            mObservers = new ObserverList<>();
+        }
         // ObserverList has its own ThreadChecker.
         mObservers.addObserver(obs);
 
@@ -77,7 +81,9 @@
                 ThreadUtils.assertOnUiThread();
                 ThreadUtils.postOnUiThread(
                         () -> {
-                            if (mObject == currentObject && mObservers.hasObserver(obs)) {
+                            if (mObject == currentObject
+                                    && mObservers != null
+                                    && mObservers.hasObserver(obs)) {
                                 obs.onResult(currentObject);
                             }
                         });
@@ -103,23 +109,30 @@
         // destroyed, so it's easier to ignore set() after destroy() than to have callers have to
         // track the state. It can also be hard to ensure queued callbacks that call set() are
         // cancelled, so again, just ignore after destroy().
-        if (mObservers != null) {
+        if (!mIsDestroyed) {
             mThreadChecker.assertOnValidThread();
             assert object != null || !Boolean.FALSE.equals(mAllowSetToNull)
                     : "set(null) called on a non-nullable supplier";
             T prevValue = mObject;
             mObject = object;
-            callObservers(prevValue);
+            if (mObservers != null) {
+                callObservers(prevValue);
+            }
         }
     }
 
     @Override
     @SuppressWarnings("NullAway")
     public void destroy() {
+        mIsDestroyed = true;
         mObservers = null;
         mObject = null;
     }
 
+    /* package */ boolean isDestroyed() {
+        return mIsDestroyed;
+    }
+
     @RequiresNonNull("mObservers")
     private void callObservers(T prevValue) {
         T value = mObject;
diff --git a/base/android/java/src/org/chromium/base/supplier/TransitiveObservableSupplier.java b/base/android/java/src/org/chromium/base/supplier/TransitiveObservableSupplier.java
index e622c1d..b513329e 100644
--- a/base/android/java/src/org/chromium/base/supplier/TransitiveObservableSupplier.java
+++ b/base/android/java/src/org/chromium/base/supplier/TransitiveObservableSupplier.java
@@ -88,7 +88,7 @@
     public ChildT get() {
         // If we have no observers, our copy of the value is not kept current.
         // Also - do not call any delegates after destroy(), since it might not be safe to do so.
-        if (mObservers != null && mObservers.isEmpty()) {
+        if (!isDestroyed() && !hasObservers()) {
             ChildT ret = null;
             ParentT parentValue = mParentSupplier.get();
             if (parentValue != null) {
diff --git a/base/sampling_heap_profiler/sampling_heap_churn_profiler.cc b/base/sampling_heap_profiler/sampling_heap_churn_profiler.cc
deleted file mode 100644
index a1920c5..0000000
--- a/base/sampling_heap_profiler/sampling_heap_churn_profiler.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2026 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/sampling_heap_profiler/sampling_heap_churn_profiler.h"
-
-#include <algorithm>
-#include <utility>
-#include <vector>
-
-#include "base/check_op.h"
-#include "base/rand_util.h"
-
-namespace base {
-
-SamplingHeapChurnProfiler::SamplingHeapChurnProfiler() = default;
-SamplingHeapChurnProfiler::~SamplingHeapChurnProfiler() = default;
-
-void SamplingHeapChurnProfiler::Start() {
-  // Relaxed memory ordering is sufficient since synchronization of data is
-  // handled under mutex_.
-  running_sessions_.fetch_add(1, std::memory_order_relaxed);
-}
-
-void SamplingHeapChurnProfiler::Stop() {
-  // `fetch_sub` returns previous value.
-  CHECK_GT(running_sessions_.fetch_sub(1, std::memory_order_relaxed), 0);
-}
-
-void SamplingHeapChurnProfiler::SetSubsamplingChance(double chance) {
-  subsampling_chance_ = chance;
-}
-
-bool SamplingHeapChurnProfiler::ShouldRecordAllocFree() const {
-  if (running_sessions_.load(std::memory_order_relaxed) == 0) {
-    return false;
-  }
-  return base::RandDouble() < subsampling_chance_;
-}
-
-std::vector<SamplingHeapProfiler::Sample>
-SamplingHeapChurnProfiler::TakeSamples() {
-  AutoLock lock(mutex_);
-  std::vector<SamplingHeapProfiler::Sample> samples = std::move(churn_samples_);
-  churn_samples_.clear();
-  return samples;
-}
-
-void SamplingHeapChurnProfiler::RecordAllocFree(
-    SamplingHeapProfiler::Sample sample) {
-  AutoLock lock(mutex_);
-  CHECK_GT(subsampling_chance_, 0.0);
-  // Scale total size by inverse subsampling chance to estimate total byte
-  // churn.
-  sample.total /= subsampling_chance_;
-  churn_samples_.push_back(std::move(sample));
-}
-
-}  // namespace base
diff --git a/base/sampling_heap_profiler/sampling_heap_churn_profiler.h b/base/sampling_heap_profiler/sampling_heap_churn_profiler.h
deleted file mode 100644
index c619a5d4..0000000
--- a/base/sampling_heap_profiler/sampling_heap_churn_profiler.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2026 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_SAMPLING_HEAP_PROFILER_SAMPLING_HEAP_CHURN_PROFILER_H_
-#define BASE_SAMPLING_HEAP_PROFILER_SAMPLING_HEAP_CHURN_PROFILER_H_
-
-#include <atomic>
-#include <vector>
-
-#include "base/base_export.h"
-#include "base/sampling_heap_profiler/sampling_heap_profiler.h"
-#include "base/synchronization/lock.h"
-#include "base/thread_annotations.h"
-
-namespace base {
-
-// The class implements sampling profiling of alloc-free memory churn.
-// It is called directly by SamplingHeapProfiler when a sampled allocation
-// is freed, and records a subsampled stream of alloc-free stack traces.
-class BASE_EXPORT SamplingHeapChurnProfiler {
- public:
-  SamplingHeapChurnProfiler(const SamplingHeapChurnProfiler&) = delete;
-  SamplingHeapChurnProfiler& operator=(const SamplingHeapChurnProfiler&) =
-      delete;
-
-  // Starts collecting churn samples.
-  void Start();
-  void Stop();
-
-  void SetSubsamplingChance(double chance);
-
-  // Returns true if an alloc-free event should be recorded based on the
-  // configured subsampling chance. Called by SamplingHeapProfiler before moving
-  // the sample.
-  bool ShouldRecordAllocFree() const;
-
-  // Returns and clears all accumulated churn samples.
-  std::vector<SamplingHeapProfiler::Sample> TakeSamples();
-
-  // Called directly by SamplingHeapProfiler when a live sample is removed.
-  void RecordAllocFree(SamplingHeapProfiler::Sample sample);
-
- private:
-  SamplingHeapChurnProfiler();
-  ~SamplingHeapChurnProfiler();
-
-  friend class SamplingHeapProfiler;
-  friend struct std::default_delete<SamplingHeapChurnProfiler>;
-
-  // Mutex to guard access to |churn_samples_|.
-  Lock mutex_;
-  std::vector<SamplingHeapProfiler::Sample> churn_samples_ GUARDED_BY(mutex_);
-  double subsampling_chance_ = 0.005;
-
-  // Number of running profiling sessions. Stored as an atomic to allow a fast,
-  // lock-free check on the hot path when any sampled allocation is freed.
-  // Relaxed memory ordering is sufficient because synchronization of
-  // |churn_samples_| is guarded by |mutex_|.
-  std::atomic<int> running_sessions_{0};
-};
-
-}  // namespace base
-
-#endif  // BASE_SAMPLING_HEAP_PROFILER_SAMPLING_HEAP_CHURN_PROFILER_H_
diff --git a/base/sampling_heap_profiler/sampling_heap_churn_profiler_unittest.cc b/base/sampling_heap_profiler/sampling_heap_churn_profiler_unittest.cc
deleted file mode 100644
index ded718a..0000000
--- a/base/sampling_heap_profiler/sampling_heap_churn_profiler_unittest.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2026 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/sampling_heap_profiler/sampling_heap_churn_profiler.h"
-
-#include <stdlib.h>
-
-#include "base/allocator/dispatcher/dispatcher.h"
-#include "base/allocator/dispatcher/notification_data.h"
-#include "base/allocator/dispatcher/subsystem.h"
-#include "base/sampling_heap_profiler/poisson_allocation_sampler.h"
-#include "base/sampling_heap_profiler/sampling_heap_profiler.h"
-#include "build/build_config.h"
-#include "partition_alloc/shim/allocator_shim.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-
-using ScopedSuppressRandomnessForTesting =
-    PoissonAllocationSampler::ScopedSuppressRandomnessForTesting;
-using base::allocator::dispatcher::AllocationNotificationData;
-using base::allocator::dispatcher::AllocationSubsystem;
-using base::allocator::dispatcher::FreeNotificationData;
-
-class SamplingHeapChurnProfilerTest : public ::testing::Test {
- public:
-  void SetUp() override {
-#if BUILDFLAG(IS_APPLE)
-    allocator_shim::InitializeAllocatorShim();
-#endif
-    SamplingHeapProfiler::Init();
-
-    ASSERT_FALSE(PoissonAllocationSampler::AreHookedSamplesMuted());
-    ASSERT_FALSE(PoissonAllocationSampler::ScopedMuteThreadSamples::IsMuted());
-    ASSERT_FALSE(ScopedSuppressRandomnessForTesting::IsSuppressed());
-
-    allocator::dispatcher::Dispatcher::GetInstance().InitializeForTesting(
-        PoissonAllocationSampler::Get());
-  }
-
-  void TearDown() override {
-    allocator::dispatcher::Dispatcher::GetInstance().ResetForTesting();
-  }
-};
-
-TEST_F(SamplingHeapChurnProfilerTest, StartStop) {
-  auto& churn_profiler = SamplingHeapProfiler::Get()->churn_profiler();
-  churn_profiler.Start();
-  churn_profiler.Start();
-  churn_profiler.Stop();
-  churn_profiler.Stop();
-}
-
-TEST_F(SamplingHeapChurnProfilerTest, RecordAllocFree) {
-  ScopedSuppressRandomnessForTesting suppress;
-  auto* sampler = PoissonAllocationSampler::Get();
-  sampler->SetSamplingInterval(1024);
-
-  auto* heap_profiler = SamplingHeapProfiler::Get();
-  auto& churn_profiler = heap_profiler->churn_profiler();
-
-  auto session = heap_profiler->Start(
-      base::ByteSize(1024), SamplingHeapProfiler::Priority::kBackground);
-  churn_profiler.Start();
-  churn_profiler.SetSubsamplingChance(1.0);  // 100% chance for testing.
-
-  void* const kAddress = reinterpret_cast<void*>(0x1234);
-  sampler->OnAllocation(AllocationNotificationData(
-      kAddress, 10000, nullptr, AllocationSubsystem::kManualForTesting));
-  sampler->OnFree(
-      FreeNotificationData(kAddress, AllocationSubsystem::kManualForTesting));
-
-  std::vector<SamplingHeapProfiler::Sample> samples =
-      churn_profiler.TakeSamples();
-  EXPECT_EQ(1u, samples.size());
-  if (!samples.empty()) {
-    EXPECT_EQ(10000u, samples[0].size);
-    EXPECT_EQ(AllocationSubsystem::kManualForTesting, samples[0].allocator);
-  }
-
-  // TakeSamples() should clear accumulated churn samples.
-  EXPECT_TRUE(churn_profiler.TakeSamples().empty());
-
-  heap_profiler->Stop(*session);
-  churn_profiler.Stop();
-}
-
-TEST_F(SamplingHeapChurnProfilerTest, TakeSamples) {
-  ScopedSuppressRandomnessForTesting suppress;
-  auto* sampler = PoissonAllocationSampler::Get();
-  sampler->SetSamplingInterval(1024);
-
-  auto* heap_profiler = SamplingHeapProfiler::Get();
-  auto& churn_profiler = heap_profiler->churn_profiler();
-
-  auto session = heap_profiler->Start(
-      base::ByteSize(1024), SamplingHeapProfiler::Priority::kBackground);
-  churn_profiler.Start();
-  churn_profiler.SetSubsamplingChance(1.0);
-
-  void* const kAddress1 = reinterpret_cast<void*>(0x1111);
-  sampler->OnAllocation(AllocationNotificationData(
-      kAddress1, 1024, nullptr, AllocationSubsystem::kManualForTesting));
-  sampler->OnFree(
-      FreeNotificationData(kAddress1, AllocationSubsystem::kManualForTesting));
-
-  std::vector<SamplingHeapProfiler::Sample> samples =
-      churn_profiler.TakeSamples();
-  ASSERT_EQ(1u, samples.size());
-  EXPECT_EQ(1024u, samples[0].size);
-
-  EXPECT_TRUE(churn_profiler.TakeSamples().empty());
-
-  heap_profiler->Stop(*session);
-  churn_profiler.Stop();
-}
-
-}  // namespace base
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.cc b/base/sampling_heap_profiler/sampling_heap_profiler.cc
index 49f11193..cb62329 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler.cc
+++ b/base/sampling_heap_profiler/sampling_heap_profiler.cc
@@ -15,12 +15,10 @@
 #include "base/debug/stack_trace.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
-#include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "base/sampling_heap_profiler/lock_free_address_hash_set.h"
 #include "base/sampling_heap_profiler/poisson_allocation_sampler.h"
-#include "base/sampling_heap_profiler/sampling_heap_churn_profiler.h"
 #include "base/threading/thread_local_storage.h"
 #include "base/trace_event/heap_profiler_allocation_context_tracker.h"  // no-presubmit-check
 #include "build/build_config.h"
@@ -144,9 +142,7 @@
 SamplingHeapProfiler::Sample& SamplingHeapProfiler::Sample::operator=(
     const Sample&) = default;
 
-SamplingHeapProfiler::SamplingHeapProfiler()
-    : churn_profiler_(base::WrapUnique(new SamplingHeapChurnProfiler())) {}
-
+SamplingHeapProfiler::SamplingHeapProfiler() = default;
 SamplingHeapProfiler::~SamplingHeapProfiler() {
   if (record_thread_names_.load(std::memory_order_acquire)) {
     base::ThreadIdNameManager::GetInstance()->RemoveObserver(this);
@@ -322,13 +318,7 @@
 void SamplingHeapProfiler::SampleRemoved(void* address) {
   DCHECK(base::PoissonAllocationSampler::ScopedMuteThreadSamples::IsMuted());
   base::AutoLock lock(mutex_);
-  auto it = samples_.find(address);
-  if (it != samples_.end()) {
-    if (churn_profiler_->ShouldRecordAllocFree()) {
-      churn_profiler_->RecordAllocFree(std::move(it->second.sample));
-    }
-    samples_.erase(it);
-  }
+  samples_.erase(address);
 }
 
 std::vector<SamplingHeapProfiler::Sample> SamplingHeapProfiler::GetSamples(
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.h b/base/sampling_heap_profiler/sampling_heap_profiler.h
index eb8dc86..37c3bda 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler.h
+++ b/base/sampling_heap_profiler/sampling_heap_profiler.h
@@ -25,8 +25,6 @@
 
 namespace base {
 
-class SamplingHeapChurnProfiler;
-
 // The class implements sampling profiling of native memory heap.
 // It uses PoissonAllocationSampler to aggregate the heap allocations and
 // record samples.
@@ -108,8 +106,6 @@
   // frame is at the front of the returned span.
   span<const void*> CaptureStackTrace(span<const void*> frames);
 
-  SamplingHeapChurnProfiler& churn_profiler() { return *churn_profiler_; }
-
   static void Init();
   static SamplingHeapProfiler* Get();
 
@@ -178,8 +174,6 @@
   // Which unwinder to use.
   std::atomic<StackUnwinder> unwinder_{StackUnwinder::kDefault};
 
-  std::unique_ptr<SamplingHeapChurnProfiler> churn_profiler_;
-
   friend class NoDestructor<SamplingHeapProfiler>;
   friend class SamplingHeapProfilerTest;
 };
diff --git a/build/android/pylib/base/environment_factory.py b/build/android/pylib/base/environment_factory.py
index 7c7d9119..32b3b068 100644
--- a/build/android/pylib/base/environment_factory.py
+++ b/build/android/pylib/base/environment_factory.py
@@ -2,9 +2,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import os
+
 from pylib import constants
 from pylib.local.device import local_device_environment
 from pylib.local.device import local_device_network_environment
+from pylib.local.emulator import fuchsia_cuttlefish_emulator_environment
 from pylib.local.machine import local_machine_environment
 
 try:
@@ -15,9 +18,7 @@
 except ImportError:
   local_emulator_environment = None
 
-
 def CreateEnvironment(args, output_manager, error_func):
-
   if args.environment == 'local':
     if args.command not in constants.LOCAL_MACHINE_TESTS:
       if args.avd_config:
@@ -27,6 +28,15 @@
         return local_emulator_environment.LocalEmulatorEnvironment(
             args, output_manager, error_func)
 
+      # TODO(crbug.com/517946352): Fuchsia/Starview tests should explicitly pass
+      # an emulator flag instead of inferring Cuttlefish from script existence
+      # and environment variables.
+      if (os.environ.get('ISOLATED_OUTDIR')
+          or os.environ.get('CHROME_HEADLESS')) and os.path.exists(
+              fuchsia_cuttlefish_emulator_environment.CUTTLEFISH_SCRIPT):
+        return (fuchsia_cuttlefish_emulator_environment.
+                FuchsiaCuttlefishEmulatorEnvironment)(args, output_manager,
+                                                      error_func)
       if args.connect_over_network:
         return local_device_network_environment.LocalDeviceNetworkEnvironment(
             args, output_manager, error_func)
diff --git a/build/android/pylib/local/emulator/fuchsia_cuttlefish_emulator_environment.py b/build/android/pylib/local/emulator/fuchsia_cuttlefish_emulator_environment.py
new file mode 100644
index 0000000..462133d
--- /dev/null
+++ b/build/android/pylib/local/emulator/fuchsia_cuttlefish_emulator_environment.py
@@ -0,0 +1,85 @@
+# Copyright 2026 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import subprocess
+import sys
+
+from devil.android import device_utils
+from pylib import constants
+from pylib.local.device import local_device_environment
+
+_DEFAULT_ADB_PORT = 6525
+
+CUTTLEFISH_SCRIPT = os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'fuchsia',
+                                 'starview', 'run_cuttlefish.py')
+
+
+class FuchsiaCuttlefishEmulatorEnvironment(
+    local_device_environment.LocalDeviceEnvironment):
+
+  def __init__(self, args, output_manager, error_func):
+    super().__init__(args, output_manager, error_func)
+    self._cuttlefish_script = CUTTLEFISH_SCRIPT
+    assert os.path.exists(self._cuttlefish_script), (
+        f'Cuttlefish script not found at {self._cuttlefish_script}')
+    self._cuttlefish_proc = None
+
+  #override
+  def SetUp(self):
+    adb_port = _DEFAULT_ADB_PORT
+    cmd = [
+        sys.executable, self._cuttlefish_script, '--adb-port',
+        str(adb_port), '--headless'
+    ]
+    self._cuttlefish_proc = subprocess.Popen(cmd,
+                                             stdout=subprocess.PIPE,
+                                             stderr=subprocess.STDOUT,
+                                             text=True)
+
+    for line in iter(self._cuttlefish_proc.stdout.readline, ''):
+      logging.info('[Cuttlefish] %s', line.strip())
+      if 'Press Ctrl+C to terminate the emulator.' in line:
+        break
+
+    self._device_serials = [f'127.0.0.1:{adb_port}']
+
+    orig_wait_until_fully_booted = device_utils.DeviceUtils.WaitUntilFullyBooted
+
+    def _starnix_wait_until_fully_booted(device_self, *args, **kwargs):
+      try:
+        # pylint: disable=protected-access
+        device_self._cache['external_storage'] = '/data/local/tmp'
+        device_self._cache['current_user'] = 0
+        device_self._cache['needs_su'] = False
+      except Exception:  # pylint: disable=broad-except
+        pass
+
+      try:
+        orig_wait_until_fully_booted(device_self, *args, **kwargs)
+      except Exception as e:  # pylint: disable=broad-except
+        if 'is_sd_card_ready' in str(e):
+          logging.warning(
+              'Ignoring is_sd_card_ready timeout on Starnix device: %s', e)
+        else:
+          raise
+
+    device_utils.DeviceUtils.WaitUntilFullyBooted = (
+        _starnix_wait_until_fully_booted)
+
+    super().SetUp()
+
+  #override
+  def TearDown(self):
+    try:
+      super().TearDown()
+    finally:
+      if self._cuttlefish_proc:
+        logging.info('Terminating Cuttlefish process...')
+        self._cuttlefish_proc.terminate()
+        try:
+          self._cuttlefish_proc.wait(timeout=10)
+        except subprocess.TimeoutExpired:
+          self._cuttlefish_proc.kill()
diff --git a/build/android/test_runner.pydeps b/build/android/test_runner.pydeps
index 3d8fb85..eb2363d 100644
--- a/build/android/test_runner.pydeps
+++ b/build/android/test_runner.pydeps
@@ -184,6 +184,7 @@
 pylib/local/device/local_device_test_run.py
 pylib/local/emulator/__init__.py
 pylib/local/emulator/avd.py
+pylib/local/emulator/fuchsia_cuttlefish_emulator_environment.py
 pylib/local/emulator/ini.py
 pylib/local/emulator/local_emulator_environment.py
 pylib/local/emulator/proto/__init__.py
diff --git a/build/fuchsia/starview/BUILD.gn b/build/fuchsia/starview/BUILD.gn
index 2a75f645..6e61732 100644
--- a/build/fuchsia/starview/BUILD.gn
+++ b/build/fuchsia/starview/BUILD.gn
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/fuchsia/generate_runner_scripts.gni")
 import("//build/config/python.gni")
 
 python_library("run_cuttlefish") {
@@ -12,16 +11,28 @@
     "avbtool.py",
     "cvd_avb_testkey_rsa4096.pem",
     "cvd_rsa4096.avbpubkey",
+    "simg2img.py",
     "README.run_cuttlefish.md",
+
     "//third_party/android_sdk/public/platform-tools/adb",
-    "//third_party/fuchsia-sdk/sdk/tools/${test_host_cpu}/",
+
+    # TODO(crbug.com/517946352): We use host_cpu directly here to avoid
+    # importing generate_runner_scripts.gni (which asserts is_fuchsia). However,
+    # this means we cannot run this on arm64 hosts without addressing the
+    # difference between test_host_cpu and host_cpu.
+    "//third_party/fuchsia-sdk/sdk/tools/${host_cpu}/",
   ]
 }
 
 python_library("run_cuttlefish_test") {
   testonly = true
   pydeps_file = "run_cuttlefish_test.pydeps"
-  deps = [ ":run_cuttlefish" ]
+  deps = [
+    ":run_cuttlefish",
+
+    # logdog_wrapper.py (used when running on Android builders) imports devil.
+    "//third_party/catapult/devil",
+  ]
   data = [
     "//.vpython3",
     "run_cuttlefish_test.py",
diff --git a/build/fuchsia/starview/README.run_cuttlefish.md b/build/fuchsia/starview/README.run_cuttlefish.md
index b008501..354954b 100644
--- a/build/fuchsia/starview/README.run_cuttlefish.md
+++ b/build/fuchsia/starview/README.run_cuttlefish.md
@@ -51,35 +51,16 @@
      /path/to/packages/uboot/
    ```
 
-4. **Install simg2img (if not already present in PATH)**:
-   The script needs the `simg2img` tool to unsparse the Android super image.
-   On gLinux/Debian/Ubuntu, you can install it via:
-   ```bash
-   sudo apt-get install android-sdk-libsparse-utils
-   ```
-   Alternatively, you can download/place a prebuilt `simg2img` binary under the packages directory:
-   `/path/to/packages/simg2img/bin/simg2img`
-
----
-
-## How to Run
-
-To start the emulator, execute the boot script from the Chromium `src/` directory root, pointing it to the directory containing your prebuilt packages:
-
-```bash
-python3 build/fuchsia/starview/run_cuttlefish.py --packages /path/to/packages/
-```
-
 ### Useful Command Line Arguments
 
 * `--packages <dir>`: Directory containing:
   * `cuttlefish/` subdirectory: Must contain the AOSP guest image ZIP file (e.g. `cf_x86_64_blazer_starnix-img-*.zip`).
   * `uboot/` subdirectory: Must contain the `u-boot.rom` bootloader.
   * Defaults to `../../` (which resolves to the `src/` checkout root when the script is run from `src/out/<build_dir>/` on Swarming).
-* `--simg2img-path <path>`: Optional path to the `simg2img` host utility. If not specified, the script looks under `--packages/simg2img/bin/simg2img` or searches in the host `PATH`.
 * `--adb-port <port>`: The host port to map the guest ADB daemon (port `5555`) to. Defaults to `6520`.
 * `--headless`: Runs QEMU without a graphical window. It defaults to headless if no GUI desktop environment is detected on the host.
 
+
 ---
 
 ## How It Works Under the Hood
diff --git a/build/fuchsia/starview/run_cuttlefish.py b/build/fuchsia/starview/run_cuttlefish.py
index 4db86174..9c2baf7 100755
--- a/build/fuchsia/starview/run_cuttlefish.py
+++ b/build/fuchsia/starview/run_cuttlefish.py
@@ -6,18 +6,20 @@
 
 import argparse
 import logging
+import os
+from pathlib import Path
 import re
+import shutil
 import socket
 import struct
-import threading
-import os
-import shutil
 import subprocess
 import sys
 import tempfile
+import threading
 import time
 import zipfile
 
+
 STARVIEW_DIR = os.path.dirname(os.path.abspath(__file__))
 
 # Setup import path for common testing utils
@@ -36,14 +38,13 @@
 
 sys.path.append(STARVIEW_DIR)
 import partition_creator
+import simg2img
 from hvc_mock import start_hvc_mock_responder
 
 
-IS_HEADLESS_BY_DEFAULT = not (
-    'DISPLAY' in os.environ or
-    'WAYLAND_DISPLAY' in os.environ or
-    'XDG_CURRENT_DESKTOP' in os.environ
-)
+IS_HEADLESS_BY_DEFAULT = not ('DISPLAY' in os.environ
+                              or 'WAYLAND_DISPLAY' in os.environ
+                              or 'XDG_CURRENT_DESKTOP' in os.environ)
 
 
 def sign_uboot_env_image(image_path, partition_size=73728):
@@ -85,13 +86,11 @@
   with zipfile.ZipFile(cuttlefish_zip, 'r') as zip_ref:
     zip_ref.extractall(temp_dir)
 
-  logging.info(f"Unsparsing super.img using {args.simg2img_path}...")
   super_img = os.path.join(temp_dir, 'super.img')
-  subprocess.run([args.simg2img_path, super_img, super_img + '.raw'], check=True)
-  os.replace(super_img + '.raw', super_img)
-
+  simg2img.unsparse_super_image(super_img)
   partition_creator.create_zero_image(os.path.join(temp_dir, 'metadata.img'), 16)
   partition_creator.create_zero_image(os.path.join(temp_dir, 'u-boot-vars.img'), 1)
+
   partition_creator.create_misc_image(os.path.join(temp_dir, 'misc.img'))
   partition_creator.create_zero_image(os.path.join(temp_dir, 'dummy.img'), 1)
 
@@ -106,9 +105,10 @@
 
   create_persistent_vbmeta_image(os.path.join(temp_dir, 'persistent_vbmeta.img'))
 
-  # Always create a fresh empty userdata.img of 2GB to avoid leaking states
-  logging.info("Creating a fresh empty 2GB userdata.img...")
-  partition_creator.create_zero_image(os.path.join(temp_dir, 'userdata.img'), 2048)
+  # Always create a fresh empty userdata.img of 16GB to avoid leaking states
+  logging.info("Creating a fresh empty 16GB userdata.img...")
+  partition_creator.create_zero_image(os.path.join(temp_dir, 'userdata.img'),
+                                      16384)
 
   partition_creator.create_gpt_and_vmdk([
       {'name': 'uboot_env', 'path': os.path.join(temp_dir, 'uboot_env.img')},
@@ -126,32 +126,46 @@
 
   qemu_cmd = [
       args.qemu_path,
-      '-m', '4096',
-      '-smp', '4',
-      '-machine', 'pc',
-      '-cpu', 'host',
+      '-m',
+      '28672',
+      '-smp',
+      '8',
+      '-machine',
+      'pc',
+      '-cpu',
+      'host',
       '-enable-kvm',
       # Load U-Boot ROM as pflash (read-only code)
-      '-drive', f'if=pflash,format=raw,readonly=on,file={bootloader}',
+      '-drive',
+      f'if=pflash,format=raw,readonly=on,file={bootloader}',
       # Load U-Boot vars flash (read-write environment)
-      '-drive', f'if=pflash,format=raw,file={os.path.join(temp_dir, "u-boot-vars.img")}',
+      '-drive',
+      f'if=pflash,format=raw,file={os.path.join(temp_dir, "u-boot-vars.img")}',
       # Shift our main system disk to virtio 1 (second slot) by mapping a dummy disk
       # to virtio 0, since Cuttlefish U-Boot and fstab expect Android on virtio 1.
-      '-drive', f'file={os.path.join(temp_dir, "dummy.img")},format=raw,if=none,id=drive-disk0',
-      '-device', 'virtio-blk-pci-non-transitional,drive=drive-disk0,id=virtio-disk0,bootindex=1',
+      '-drive',
+      f'file={os.path.join(temp_dir, "dummy.img")},format=raw,if=none,id=drive-disk0',
+      '-device',
+      'virtio-blk-pci-non-transitional,drive=drive-disk0,id=virtio-disk0,bootindex=1',
       # Map our partitioned VMDK disk as virtio 1
-      '-drive', f'file={os.path.join(temp_dir, "disk.vmdk")},format=vmdk,if=none,id=drive-disk1',
-      '-device', 'virtio-blk-pci-non-transitional,drive=drive-disk1,id=virtio-disk1',
+      '-drive',
+      f'file={os.path.join(temp_dir, "disk.vmdk")},format=vmdk,if=none,id=drive-disk1',
+      '-device',
+      'virtio-blk-pci-non-transitional,drive=drive-disk1,id=virtio-disk1',
       # User-mode networking with static hostfwd for ADB and SSH
-      '-netdev', f'user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::{args.adb_port}-:5555',
-      '-device', 'virtio-net-pci,netdev=net0,vectors=8,addr=1.2',
+      '-netdev',
+      f'user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::{args.adb_port}-:5555',
+      '-device',
+      'virtio-net-pci,netdev=net0,vectors=8,addr=1.2',
       # Map standard serial port (U-Boot & Kernel console) directly to stdio
-      '-serial', 'stdio',
+      '-serial',
+      'stdio',
       # Enable QEMU monitor on a unix socket
-      '-monitor', f'unix:{os.path.join(temp_dir, "monitor.sock")},server,nowait',
+      '-monitor',
+      f'unix:{os.path.join(temp_dir, "monitor.sock")},server,nowait',
   ]
-
   if args.headless:
+
     qemu_cmd.append('-nographic')
 
   hvc_args, hvc_stop_event = start_hvc_mock_responder(temp_dir)
@@ -165,12 +179,36 @@
   return qemu_proc, hvc_stop_event
 
 
+def _check_adb_health(adb_path, adb_port, is_root=False):
+  """Checks if guest ADB is connected and responsive via shell echo ready."""
+  role = " (root)" if is_root else ""
+  logging.info(f"Attempting to connect to ADB on port {adb_port}{role}...")
+  for _ in range(240):
+    subprocess.run([adb_path, 'connect', f'127.0.0.1:{adb_port}'],
+                   stdout=subprocess.DEVNULL,
+                   stderr=subprocess.DEVNULL)
+    res = subprocess.run([adb_path, 'devices'], capture_output=True, text=True)
+    if f"127.0.0.1:{adb_port}\tdevice" in res.stdout:
+      echo_res = subprocess.run(
+          [adb_path, '-s', f'127.0.0.1:{adb_port}', 'shell', 'echo', 'ready'],
+          capture_output=True,
+          text=True)
+      if echo_res.returncode == 0 and 'ready' in echo_res.stdout:
+        return True
+    time.sleep(2)
+  logging.error(f"Error: Timed out waiting for guest ADB connection{role}.")
+  return False
+
+
 def main():
-  logging.basicConfig(level=logging.INFO, format='%(levelname)s %(asctime)s %(message)s')
+  common.catch_sigterm()
   parser = argparse.ArgumentParser(description="Boot Cuttlefish in raw QEMU")
-  parser.add_argument('--packages', default='../../',
-                      help='Directory containing cuttlefish guest images, bootloader, '
-                           'and other tools. Defaults to "../../" (based on CWD on Swarming).')
+  parser.add_argument(
+      '--packages',
+      default=Path(STARVIEW_DIR) / '../../..',
+      type=Path,
+      help='Directory containing cuttlefish guest images, bootloader, '
+      'and other tools.')
   parser.add_argument('--qemu-path',
                       default=os.path.join(common.SDK_ROOT, 'tools', 'x64',
                                            'qemu_internal', 'bin',
@@ -183,14 +221,22 @@
                       help='Path to adb binary.')
   parser.add_argument('--adb-port', type=int, default=6520,
                       help='Host port to forward guest ADB (5555) to.')
-  parser.add_argument('--simg2img-path', default=None,
-                      help='Path to simg2img binary. If not provided, '
-                           'will look under "--packages/simg2img" or search in PATH.')
   parser.add_argument('--headless', action='store_true', default=IS_HEADLESS_BY_DEFAULT,
                       help='Run QEMU in headless mode (without GUI). '
                            'Defaults to True if no desktop environment is detected.')
   args = parser.parse_args()
 
+  isolated_outdir = os.environ.get('ISOLATED_OUTDIR')
+  handlers = [logging.StreamHandler()]
+  if isolated_outdir:
+    os.makedirs(isolated_outdir, exist_ok=True)
+    log_file = os.path.join(isolated_outdir, 'emulator.log')
+    handlers.append(logging.FileHandler(log_file))
+  logging.basicConfig(level=logging.INFO,
+                      format='%(levelname)s %(asctime)s %(message)s',
+                      handlers=handlers,
+                      force=True)
+
   logging.info(f"Running in {'headless' if args.headless else 'headfull'} mode.")
 
   if not os.path.exists(args.qemu_path) and not shutil.which(args.qemu_path):
@@ -201,27 +247,8 @@
     logging.error(f"Error: ADB not found at {args.adb_path}")
     return 1
 
-  packages_dir = os.path.abspath(args.packages)
-  if not os.path.exists(packages_dir):
-    logging.error(f"Error: Packages directory does not exist: {packages_dir}")
-    return 1
-
-  if not args.simg2img_path:
-    packages_simg2img = os.path.join(packages_dir, 'simg2img', 'bin', 'simg2img')
-    if os.path.exists(packages_simg2img):
-      args.simg2img_path = packages_simg2img
-    else:
-      args.simg2img_path = shutil.which('simg2img')
-
-  if not args.simg2img_path or not (os.path.exists(args.simg2img_path) or
-                                    shutil.which(args.simg2img_path)):
-    logging.error(
-        f"Error: simg2img not found. Please install "
-        f"android-sdk-libsparse-utils or provide --simg2img-path.")
-    return 1
-
-  cuttlefish_dir = os.path.join(packages_dir, 'cuttlefish')
-  if not os.path.exists(cuttlefish_dir):
+  cuttlefish_dir = args.packages / 'cuttlefish'
+  if not cuttlefish_dir.exists():
     logging.error(f"Error: Cuttlefish directory does not exist: {cuttlefish_dir}")
     return 1
 
@@ -235,8 +262,8 @@
     logging.error(f"Error: No Cuttlefish guest image ZIP found in {cuttlefish_dir}")
     return 1
 
-  bootloader = os.path.join(packages_dir, 'uboot', 'u-boot.rom')
-  if not os.path.exists(bootloader):
+  bootloader = args.packages / 'uboot' / 'u-boot.rom'
+  if not bootloader.exists():
     logging.error(f"Error: Bootloader not found at {bootloader}")
     return 1
 
@@ -250,32 +277,58 @@
       logging.info("Waiting 15 seconds for guest to boot...")
       time.sleep(15)
 
-      logging.info(f"Attempting to connect to ADB on port {args.adb_port}...")
-      for attempt in range(60):
-        logging.info(f"ADB connection attempt {attempt+1}/60...")
-        subprocess.run([args.adb_path, 'connect', f'127.0.0.1:{args.adb_port}'],
-                       stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
-        res = subprocess.run([args.adb_path, 'devices'], capture_output=True, text=True)
-        if f"127.0.0.1:{args.adb_port}\tdevice" in res.stdout:
-          logging.info("Successfully connected to guest ADB!")
-          break
-        time.sleep(5)
-      else:
-        logging.error("Error: Timed out waiting for guest ADB connection.")
+      if not _check_adb_health(args.adb_path, args.adb_port, is_root=False):
         return 1
 
+      logging.info("Restarting ADB daemon as root...")
+      subprocess.run(
+          [args.adb_path, '-s', f'127.0.0.1:{args.adb_port}', 'root'],
+          capture_output=True,
+          text=True)
+
+      if not _check_adb_health(args.adb_path, args.adb_port, is_root=True):
+        return 1
+      # Known Starnix / Android guest boot workarounds (b/539219514, b/540947081):
+      # 1. Pre-create missing dalvik-cache, tmp, and storage directories.
+      # 2. Ensure /data/local/tmp permissions allow ADB transfers.
+      # 3. Clear sys.init.updatable_crashing flag and restart zygote to
+      #    recover from cold-boot crash loops.
+      for setup_cmd in [
+          'mkdir -p /data/local/tmp',
+          'mkdir -p /data/misc/credstore',
+          'mkdir -p /data/dalvik-cache/x86_64',
+          'mkdir -p /data/dalvik-cache/x86',
+          'mkdir -p /data/dalvik-cache/arm64',
+          'mkdir -p "${EXTERNAL_STORAGE:-/sdcard}"',
+          'mkdir -p /sdcard',
+          'mkdir -p /storage/emulated/0',
+          'mkdir -p /mnt/sdcard',
+          'chmod 777 /data/local/tmp',
+          'setprop sys.init.updatable_crashing 0',
+          'setprop ctl.start zygote',
+          'setprop ctl.restart zygote',
+      ]:
+        res = subprocess.run([
+            args.adb_path, '-s', f'127.0.0.1:{args.adb_port}', 'shell',
+            setup_cmd
+        ],
+                             capture_output=True,
+                             text=True)
+        if res.returncode != 0:
+          logging.warning('ADB setup command %r failed (exit code %d): %s',
+                          setup_cmd, res.returncode, res.stderr.strip())
+        else:
+          logging.info('Executed ADB setup command: %s', setup_cmd)
+
+      logging.info("Successfully connected to guest ADB!")
+
       logging.info("Press Ctrl+C to terminate the emulator.")
-      while True:
-        time.sleep(1)
-
-    except KeyboardInterrupt:
-      logging.info("Terminating emulator...")
-      return 0
-
+      common.wait_for_sigterm()
     finally:
       if hvc_stop_event:
         logging.info("Stopping HVC mock responder...")
         hvc_stop_event.set()
+
       if qemu_proc and qemu_proc.poll() is None:
         logging.info("Terminating QEMU process...")
         qemu_proc.terminate()
diff --git a/build/fuchsia/starview/run_cuttlefish.pydeps b/build/fuchsia/starview/run_cuttlefish.pydeps
index 0c104cb..f5d2b0b 100644
--- a/build/fuchsia/starview/run_cuttlefish.pydeps
+++ b/build/fuchsia/starview/run_cuttlefish.pydeps
@@ -1,8 +1,9 @@
 # Generated by running:
-#   build/print_python_deps.py build/fuchsia/starview/run_cuttlefish.py
-build/fuchsia/starview/hvc_mock.py
-build/fuchsia/starview/partition_creator.py
-build/fuchsia/starview/run_cuttlefish.py
-build/fuchsia/test/common.py
-build/fuchsia/test/compatible_utils.py
-build/fuchsia/test/repeating_log.py
+#   build/print_python_deps.py --root build/fuchsia/starview --output build/fuchsia/starview/run_cuttlefish.pydeps build/fuchsia/starview/run_cuttlefish.py
+../test/common.py
+../test/compatible_utils.py
+../test/repeating_log.py
+hvc_mock.py
+partition_creator.py
+run_cuttlefish.py
+simg2img.py
diff --git a/build/fuchsia/starview/run_cuttlefish_test.py b/build/fuchsia/starview/run_cuttlefish_test.py
index e1568643..3f5fa8a 100755
--- a/build/fuchsia/starview/run_cuttlefish_test.py
+++ b/build/fuchsia/starview/run_cuttlefish_test.py
@@ -4,6 +4,7 @@
 # found in the LICENSE file.
 
 import argparse
+import json
 import os
 import subprocess
 import sys
@@ -11,13 +12,16 @@
 
 
 def main():
+  start_time = time.time()
   parser = argparse.ArgumentParser(description="Test run_cuttlefish.py script")
-  parser.add_argument(
-      '--packages',
-      default='.',
-      help='Directory containing the cuttlefish guest images and tools.')
+  parser.add_argument('--isolated-script-test-output',
+                      type=os.path.abspath,
+                      help='Path to write isolated script test output JSON.')
   args_parsed, extra_args = parser.parse_known_args()
-
+  # Filter out any other isolated-script arguments from extra_args before passing to run_cuttlefish.py
+  extra_args = [
+      arg for arg in extra_args if not arg.startswith('--isolated-script-test-')
+  ]
   starview_dir = os.path.dirname(os.path.abspath(__file__))
   run_script = os.path.join(starview_dir, 'run_cuttlefish.py')
   adb_path = os.path.join(
@@ -28,11 +32,8 @@
 
   # Launch emulator via run_cuttlefish.py on a test port
   adb_port = 6525
-  cmd = [
-      sys.executable, run_script, '--packages', args_parsed.packages,
-      '--adb-port',
-      str(adb_port), '--headless'
-  ] + extra_args
+  cmd = [sys.executable, run_script, '--adb-port',
+         str(adb_port), '--headless'] + extra_args
   print(f"Starting emulator under test: {' '.join(cmd)}")
   proc = subprocess.Popen(cmd,
                           stdout=subprocess.PIPE,
@@ -41,16 +42,13 @@
 
   success = False
   try:
-    start_time = time.time()
+
     for line in iter(proc.stdout.readline, ''):
       print(f"[Emulator Log] {line.strip()}")
       if "Successfully connected to guest ADB!" in line:
         print("Emulator reported successful ADB connection!")
         success = True
         break
-      if time.time() - start_time > 150:
-        print("Timeout waiting for emulator to boot.")
-        break
 
     if success:
       # Run adb shell command to verify the Android container is alive
@@ -83,6 +81,30 @@
       proc.wait()
     print("Emulator process terminated.")
 
+  if args_parsed.isolated_script_test_output:
+    failure_type = 'PASS' if success else 'FAIL'
+    results_json = {
+        'version': 3,
+        'interrupted': False,
+        'num_failures_by_type': {
+            failure_type: 1
+        },
+        'path_delimiter': '/',
+        'seconds_since_epoch': start_time,
+        'tests': {
+            'run_cuttlefish_test': {
+                'expected': 'PASS',
+                'actual': failure_type,
+                'time': time.time() - start_time,
+            },
+        },
+    }
+    output_dir = os.path.dirname(args_parsed.isolated_script_test_output)
+    if output_dir:
+      os.makedirs(output_dir, exist_ok=True)
+    with open(args_parsed.isolated_script_test_output, 'w') as f:
+      json.dump(results_json, f, indent=2)
+
   if success:
     print("TEST PASSED")
     sys.exit(0)
diff --git a/build/fuchsia/starview/run_cuttlefish_test.pydeps b/build/fuchsia/starview/run_cuttlefish_test.pydeps
index 12fd0ec..502e0a4 100644
--- a/build/fuchsia/starview/run_cuttlefish_test.pydeps
+++ b/build/fuchsia/starview/run_cuttlefish_test.pydeps
@@ -1,3 +1,3 @@
 # Generated by running:
-#   build/print_python_deps.py build/fuchsia/starview/run_cuttlefish_test.py
-build/fuchsia/starview/run_cuttlefish_test.py
+#   build/print_python_deps.py --root build/fuchsia/starview --output build/fuchsia/starview/run_cuttlefish_test.pydeps build/fuchsia/starview/run_cuttlefish_test.py
+run_cuttlefish_test.py
diff --git a/build/fuchsia/starview/simg2img.py b/build/fuchsia/starview/simg2img.py
new file mode 100644
index 0000000..a92007f
--- /dev/null
+++ b/build/fuchsia/starview/simg2img.py
@@ -0,0 +1,79 @@
+# Copyright 2026 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Pure Python implementation of Android Sparse Image decoder (simg2img).
+
+NOTE: This is a temporary solution before we can find a better way to include
+the simg2img C++ binary. It's slower but works without host binary dependencies.
+"""
+
+import logging
+import os
+import struct
+
+
+def is_sparse_image(file_path):
+  """Checks if a file is an Android Sparse Image by reading its magic bytes."""
+  if not os.path.exists(file_path):
+    return False
+  with open(file_path, 'rb') as f:
+    header = f.read(4)
+    return header == b'\x3a\xff\x26\xed'
+
+
+def unsparse_image(sparse_path, raw_path):
+  """Pure Python implementation of simg2img."""
+  logging.info(f"Unsparsing {sparse_path} using pure Python decoder...")
+  with open(sparse_path, 'rb') as in_f, open(raw_path, 'wb') as out_f:
+    header_data = in_f.read(28)
+    if len(header_data) < 28:
+      raise ValueError("Invalid sparse image: header too short")
+    magic, major, minor, file_hdr_sz, chunk_hdr_sz, blk_sz, total_blks, total_chunks, _ = struct.unpack(
+        '<IHHHHIIII', header_data)
+
+    if magic != 0xED26FF3A:
+      raise ValueError(f"Invalid sparse image magic: {hex(magic)}")
+
+    if file_hdr_sz > 28:
+      in_f.seek(file_hdr_sz)
+
+    for _ in range(total_chunks):
+      chunk_header = in_f.read(12)
+      if len(chunk_header) < 12:
+        break
+      chunk_type, _, chunk_sz, total_sz = struct.unpack('<HHII', chunk_header)
+      data_sz = chunk_sz * blk_sz
+
+      if chunk_type == 0xCAC1:  # RAW
+        remaining = data_sz
+        while remaining > 0:
+          chunk_bytes = in_f.read(min(remaining, 1024 * 1024))
+          out_f.write(chunk_bytes)
+          remaining -= len(chunk_bytes)
+      elif chunk_type == 0xCAC2:  # FILL
+        fill_val = in_f.read(4)
+        zero_buf = fill_val * (1024 * 256)
+        blocks = data_sz // len(zero_buf)
+        remainder = data_sz % len(zero_buf)
+        for _ in range(blocks):
+          out_f.write(zero_buf)
+        if remainder:
+          out_f.write(fill_val * (remainder // 4))
+      elif chunk_type == 0xCAC3:  # DONT CARE
+        out_f.seek(data_sz, os.SEEK_CUR)
+      elif chunk_type == 0xCAC4:  # CRC32
+        in_f.read(4)
+      else:
+        raise ValueError(f"Unknown sparse chunk type: {hex(chunk_type)}")
+
+
+def unsparse_super_image(super_img):
+  """Converts super_img in-place if it is an Android sparse image."""
+  if not is_sparse_image(super_img):
+    logging.info(f"{super_img} is already raw/unsparse. Skipping simg2img.")
+    return
+
+  logging.info(f"Unsparsing {super_img}...")
+  raw_path = super_img + '.raw'
+  unsparse_image(super_img, raw_path)
+  os.replace(raw_path, super_img)
diff --git a/cc/input/input_handler.cc b/cc/input/input_handler.cc
index dabc707..dfb3c326 100644
--- a/cc/input/input_handler.cc
+++ b/cc/input/input_handler.cc
@@ -2542,7 +2542,18 @@
     // Forget the scroll container that is currently
     // latched so that any scroll gesture that occurs during the snap
     // animation will be allowed to scroll the appropriate container.
-    ClearCurrentlyScrollingNode();
+    // Only do this when the gesture has ended: triggered from ScrollEnd, or
+    // the GSE already arrived and the scroll-end is deferred. Otherwise the
+    // gesture is still in progress and clearing would stall subsequent
+    // ScrollUpdates (https://crbug.com/504284803). This happens when a smooth
+    // scroll animation finishes before the gesture's scroll-end, e.g. a wheel
+    // tick's smooth animation completes while the user keeps scrolling.
+    bool gesture_ended =
+        reason == SnapReason::kGestureScrollEnd ||
+        deferred_scroll_ends_.contains(scroll_node->element_id);
+    if (gesture_ended) {
+      ClearCurrentlyScrollingNode();
+    }
 
     EnsureSnapAnimationData(scroll_node->element_id);
     // The updated snap target will be set when the animation is completed.
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 15c6dad8..d1db49a 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -15193,6 +15193,20 @@
     DrawFrame();
   }
 
+  void TickScrollAnimationsUntilIdle() {
+    // Tick snap animations to completion to avoid violating
+    // deferred_scroll_end_
+    // DCHECK in InputHandler::ScrollEnd.
+    AnimationHost* animation_host = GetImplAnimationHost();
+    int t = 1;
+    while (animation_host->HasImplOnlyScrollAnimatingElement()) {
+      animation_host->TickAnimations(
+          base::TimeTicks() + base::Milliseconds(t++ * 100),
+          host_impl_->GetScrollTree(), true, nullptr);
+      animation_host->UpdateAnimationState(true, nullptr);
+    }
+  }
+
   raw_ptr<LayerImpl> snapping_layer1_ = nullptr;
   raw_ptr<LayerImpl> snapping_layer2_ = nullptr;
   raw_ptr<ScrollNode> scroll_node1_ = nullptr;
@@ -15251,16 +15265,7 @@
   EXPECT_EQ(snap_state_map.at(container2_id_).animating_snap_target_ids_.y,
             ElementId(30));
 
-  // Tick snap animations to completion to avoid violating deferred_scroll_end_
-  // DCHECK in InputHandler::ScrollEnd.
-  AnimationHost* animation_host = GetImplAnimationHost();
-  int t = 1;
-  while (animation_host->HasImplOnlyScrollAnimatingElement()) {
-    animation_host->TickAnimations(
-        base::TimeTicks() + base::Milliseconds(t++ * 100),
-        host_impl_->GetScrollTree(), true, nullptr);
-    animation_host->UpdateAnimationState(true, nullptr);
-  }
+  TickScrollAnimationsUntilIdle();
 
   // Finish the snap animation for scroll_node1.
   handler.ScrollOffsetAnimationFinished(container1_id_);
@@ -15275,6 +15280,130 @@
   EXPECT_FALSE(snap_state_map.contains(container2_id_));
 }
 
+// A smooth scroll animation can finish while the gesture is still ongoing
+// (e.g. one wheel tick's smooth animation completes while the user keeps
+// scrolling). SnapAtScrollEnd triggered from ScrollOffsetAnimationFinished in
+// that situation must not clear the latched node, otherwise subsequent
+// ScrollUpdate calls early-return and scrolling stalls
+// (https://crbug.com/504284803)
+TEST_P(ConcurrentSnapAnimationsTest,
+       MidGestureScrollOffsetAnimationFinishedKeepsLatchedNode) {
+  auto& handler = GetInputHandler();
+
+  gfx::Point position(50, 50);
+  ui::ScrollInputType type = ui::ScrollInputType::kWheel;
+  base::flat_map<ElementId, InputHandler::SnapAnimationData>& snap_state_map =
+      handler.get_snap_animation_data_map_for_testing();
+
+  // Begin a wheel gesture on the snap container and perform an animated
+  // (kScrollByPixel) update, which creates an impl-only smooth scroll
+  // animation.
+  handler.ScrollBegin(BeginState(position, gfx::Vector2dF(0, 150), type).get(),
+                      type);
+  host_impl_->GetScrollTree().set_currently_scrolling_node(scroll_node1_->id);
+  ASSERT_TRUE(
+      handler
+          .ScrollUpdate(AnimatedUpdateState(position, gfx::Vector2dF(0, 150)))
+          .did_scroll);
+
+  // Let the smooth scroll animation finish. No ScrollEnd is issued: the
+  // gesture is still in progress, mimicking the user keeps scrolling.
+  TickScrollAnimationsUntilIdle();
+
+  // The smooth animation finished mid-gesture -> SnapAtScrollEnd starts a
+  // snap animation. The latched node must be preserved so further updates
+  // keep working; without the fix it was cleared here.
+  handler.ScrollOffsetAnimationFinished(container1_id_);
+  EXPECT_TRUE(snap_state_map.contains(container1_id_));
+  EXPECT_TRUE(host_impl_->GetScrollTree().CurrentlyScrollingNode());
+
+  // The user keeps scrolling: the update must still be consumed (it retargets
+  // the running snap animation). Without the fix this returned
+  // did_scroll=false.
+  EXPECT_TRUE(
+      handler
+          .ScrollUpdate(AnimatedUpdateState(position, gfx::Vector2dF(0, 100)))
+          .did_scroll);
+
+  // Clean teardown: abort the retargeted animation and end without snapping so
+  // no new snap animation is created.
+  GetImplAnimationHost()->ScrollAnimationAbort(container1_id_);
+  handler.ScrollEnd(/*should_snap=*/false, std::nullopt);
+  EXPECT_FALSE(host_impl_->GetScrollTree().CurrentlyScrollingNode());
+}
+
+// When the GSE arrives while a smooth animation is still running, the
+// scroll-end is deferred. Once that animation finishes, SnapAtScrollEnd must
+// still clear the latched node so a new gesture during the snap animation can
+// latch to a different container.
+TEST_P(ConcurrentSnapAnimationsTest,
+       DeferredGestureScrollOffsetAnimationFinishedClearsLatchedNode) {
+  auto& handler = GetInputHandler();
+
+  gfx::Point position(50, 50);
+  ui::ScrollInputType type = ui::ScrollInputType::kWheel;
+  base::flat_map<ElementId, InputHandler::SnapAnimationData>& snap_state_map =
+      handler.get_snap_animation_data_map_for_testing();
+
+  handler.ScrollBegin(BeginState(position, gfx::Vector2dF(0, 150), type).get(),
+                      type);
+  host_impl_->GetScrollTree().set_currently_scrolling_node(scroll_node1_->id);
+  ASSERT_TRUE(
+      handler
+          .ScrollUpdate(AnimatedUpdateState(position, gfx::Vector2dF(0, 150)))
+          .did_scroll);
+
+  // GSE arrives while the smooth animation is still running -> ScrollEnd
+  // defers and leaves the node latched.
+  handler.ScrollEnd(/*should_snap=*/true, std::nullopt);
+  EXPECT_TRUE(host_impl_->GetScrollTree().CurrentlyScrollingNode());
+
+  // The smooth animation finishes -> gesture has ended (scroll-end deferred),
+  // so SnapAtScrollEnd clears the latched node.
+  TickScrollAnimationsUntilIdle();
+  handler.ScrollOffsetAnimationFinished(container1_id_);
+  EXPECT_FALSE(host_impl_->GetScrollTree().CurrentlyScrollingNode());
+  EXPECT_TRUE(snap_state_map.contains(container1_id_));
+
+  // Flush the snap animation + deferred scroll-end for clean teardown.
+  TickScrollAnimationsUntilIdle();
+  handler.ScrollOffsetAnimationFinished(container1_id_);
+  EXPECT_FALSE(host_impl_->GetScrollTree().CurrentlyScrollingNode());
+  EXPECT_FALSE(snap_state_map.contains(container1_id_));
+}
+
+// The ScrollEnd path (should_snap with no running animation) must continue to
+// clear the latched node -- unchanged behavior.
+TEST_P(ConcurrentSnapAnimationsTest, GestureScrollEndSnapClearsLatchedNode) {
+  auto& handler = GetInputHandler();
+
+  gfx::Point position(50, 50);
+  ui::ScrollInputType type = ui::ScrollInputType::kWheel;
+  base::flat_map<ElementId, InputHandler::SnapAnimationData>& snap_state_map =
+      handler.get_snap_animation_data_map_for_testing();
+
+  handler.ScrollBegin(BeginState(position, gfx::Vector2dF(0, 150), type).get(),
+                      type);
+  host_impl_->GetScrollTree().set_currently_scrolling_node(scroll_node1_->id);
+  ASSERT_TRUE(
+      handler
+          .ScrollUpdate(AnimatedUpdateState(position, gfx::Vector2dF(0, 150)))
+          .did_scroll);
+
+  // Finish the smooth animation first so ScrollEnd takes the direct snap path
+  // rather than deferring.
+  TickScrollAnimationsUntilIdle();
+
+  handler.ScrollEnd(/*should_snap=*/true, std::nullopt);
+  EXPECT_FALSE(host_impl_->GetScrollTree().CurrentlyScrollingNode());
+  EXPECT_TRUE(snap_state_map.contains(container1_id_));
+
+  // Flush the snap animation + deferred scroll-end for clean teardown.
+  TickScrollAnimationsUntilIdle();
+  handler.ScrollOffsetAnimationFinished(container1_id_);
+  EXPECT_FALSE(snap_state_map.contains(container1_id_));
+}
+
 class ElasticOverscrollTest : public LayerTreeHostImplTest {
  public:
   LayerTreeSettings DefaultSettings() override {
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 743a0b9..5a610c92 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -4,7 +4,9 @@
 
 import("//build/config/android/config.gni")
 import("//build/config/cronet/config.gni")
+import("//build/config/gclient_args.gni")
 import("//build/toolchain/gcc_toolchain.gni")
+
 import("//chrome/android/chrome_common_shared_library.gni")
 import("//chrome/android/modules/chrome_feature_module_tmpl.gni")
 import("//components/supervised_user/buildflags.gni")
@@ -2586,7 +2588,10 @@
     apk_under_test = ":chrome_public_apk"
     android_test_apk = ":chrome_smoke_test_apk"
     additional_apks = [ "//net/android:net_test_support_apk" ]
-    extra_args = _common_smoke_test_args
+    if (checkout_fuchsia) {
+      data_deps = [ "//build/fuchsia/starview:run_cuttlefish" ]
+    }
+
     if (!is_java_debug) {
       proguard_mapping_path = "$root_build_dir/apks/ChromePublic.apk.mapping"
     }
@@ -2594,6 +2599,7 @@
 
   instrumentation_test_runner("chrome_public_bundle_smoke_test") {
     apk_under_test = "//chrome/android:chrome_public_bundle_apks"
+
     android_test_apk = ":chrome_smoke_test_apk"
     extra_args = _common_smoke_test_args
     additional_apks = [ "//net/android:net_test_support_apk" ]
diff --git a/chrome/android/features/tab_ui/java/res/layout/vertical_tab_layout.xml b/chrome/android/features/tab_ui/java/res/layout/vertical_tab_layout.xml
index 121f18e..d6b51393 100644
--- a/chrome/android/features/tab_ui/java/res/layout/vertical_tab_layout.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/vertical_tab_layout.xml
@@ -42,7 +42,6 @@
             android:contentDescription="@string/accessibility_collapse_vertical_tabs"
             android:scaleType="center"
             android:padding="@dimen/vertical_tabs_header_button_padding"
-            android:layout_marginEnd="@dimen/vertical_tabs_header_button_collapsed_margin_end"
             android:visibility="visible" />
         <View
             android:id="@+id/header_spacer"
@@ -50,25 +49,31 @@
             android:layout_height="match_parent"
             android:layout_weight="1"
             android:visibility="visible" />
-        <ImageButton
-            android:id="@+id/grid_button"
-            android:layout_width="@dimen/vertical_tabs_header_button_size"
-            android:layout_height="@dimen/vertical_tabs_header_button_size"
-            android:src="@drawable/ic_grid_view_16dp"
-            android:background="@drawable/vertical_tabs_left_rounded_button_background"
-            android:contentDescription="@string/accessibility_tab_groups"
-            android:scaleType="center"
-            android:padding="@dimen/vertical_tabs_header_button_padding"
-            android:layout_marginEnd="@dimen/vertical_tabs_header_button_gap"/>
-        <ImageButton
-            android:id="@+id/tab_search_button"
-            android:layout_width="@dimen/vertical_tabs_header_button_size"
-            android:layout_height="@dimen/vertical_tabs_header_button_size"
-            android:src="@drawable/ic_manage_search_16dp"
-            android:background="@drawable/vertical_tabs_right_rounded_button_background"
-            android:contentDescription="@string/accessibility_search_loupe_tooltip_text"
-            android:scaleType="center"
-            android:padding="@dimen/vertical_tabs_header_button_padding"/>
+        <LinearLayout
+            android:id="@+id/tab_action_buttons_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+            <ImageButton
+                android:id="@+id/grid_button"
+                android:layout_width="@dimen/vertical_tabs_header_button_size"
+                android:layout_height="@dimen/vertical_tabs_header_button_size"
+                android:src="@drawable/ic_grid_view_16dp"
+                android:background="@drawable/vertical_tabs_left_rounded_button_background"
+                android:contentDescription="@string/accessibility_tab_groups"
+                android:scaleType="center"
+                android:padding="@dimen/vertical_tabs_header_button_padding"
+                android:layout_marginEnd="@dimen/vertical_tabs_header_button_gap"/>
+            <ImageButton
+                android:id="@+id/tab_search_button"
+                android:layout_width="@dimen/vertical_tabs_header_button_size"
+                android:layout_height="@dimen/vertical_tabs_header_button_size"
+                android:src="@drawable/ic_manage_search_16dp"
+                android:background="@drawable/vertical_tabs_right_rounded_button_background"
+                android:contentDescription="@string/accessibility_search_loupe_tooltip_text"
+                android:scaleType="center"
+                android:padding="@dimen/vertical_tabs_header_button_padding"/>
+        </LinearLayout>
     </LinearLayout>
 
     <!-- Pinned Tabs Recycler View -->
diff --git a/chrome/android/features/tab_ui/java/res/layout/vertical_tab_pinned_item.xml b/chrome/android/features/tab_ui/java/res/layout/vertical_tab_pinned_item.xml
index c9d9dbc..4642b66 100644
--- a/chrome/android/features/tab_ui/java/res/layout/vertical_tab_pinned_item.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/vertical_tab_pinned_item.xml
@@ -11,7 +11,7 @@
     android:layout_width="@dimen/vertical_tab_pinned_item_width"
     android:layout_height="@dimen/vertical_tab_pinned_item_height"
     android:background="@drawable/vertical_tab_pinned_item_background"
-    android:layout_marginBottom="@dimen/vertical_tab_item_margin_bottom"
+    android:layout_marginBottom="@dimen/vertical_tab_pinned_item_margin_bottom"
     android:clickable="true"
     android:focusable="true">
 
@@ -47,4 +47,36 @@
         android:visibility="gone"
         app:indicatorSize="@dimen/vertical_tab_item_icon_size"
         app:trackThickness="@dimen/vertical_tab_item_loading_spinner_thickness" />
+
+    <!-- Actuation Indicator (Cursor Spark). -->
+    <ImageView
+        android:id="@+id/actuation_spark"
+        android:layout_width="@dimen/vertical_tab_item_actuation_icon_size"
+        android:layout_height="@dimen/vertical_tab_item_actuation_icon_size"
+        android:layout_gravity="center"
+        android:scaleType="center"
+        android:src="@drawable/ic_arrow_selector_spark_14dp"
+        android:visibility="gone"
+        android:importantForAccessibility="no"
+        app:tint="@macro/default_icon_color_accent1" />
+
+    <!-- Actuation Spinner (Ring Overlay). -->
+    <ImageView
+        android:id="@+id/actuation_spinner"
+        android:layout_width="@dimen/vertical_tab_item_actuation_icon_size"
+        android:layout_height="@dimen/vertical_tab_item_actuation_icon_size"
+        android:layout_gravity="center"
+        android:src="@drawable/tab_indicator_spinner"
+        android:visibility="gone"
+        android:importantForAccessibility="no"
+        app:tint="@macro/default_icon_color_accent1" />
+
+    <!-- Media Indicator (e.g., speaker icon when playing audio). -->
+    <ImageView
+        android:id="@+id/media_indicator_icon"
+        android:layout_width="@dimen/vertical_tab_item_icon_size"
+        android:layout_height="@dimen/vertical_tab_item_icon_size"
+        android:layout_gravity="center"
+        android:visibility="gone"
+        android:importantForAccessibility="no" />
 </FrameLayout>
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml
index 4a910645..a720888 100644
--- a/chrome/android/features/tab_ui/java/res/values/dimens.xml
+++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -229,6 +229,7 @@
     <dimen name="vertical_tab_pinned_item_width">42dp</dimen>
     <dimen name="vertical_tab_pinned_item_height">32dp</dimen>
     <dimen name="vertical_tab_pinned_item_ai_indicator_margin_start">2dp</dimen>
+    <dimen name="vertical_tab_pinned_item_margin_bottom">4dp</dimen>
 
     <!-- New Tab button dimens.-->
     <dimen name="vertical_tabs_new_tab_button_height">32dp</dimen>
@@ -243,7 +244,6 @@
     <dimen name="vertical_tabs_header_button_size">32dp</dimen>
     <dimen name="vertical_tabs_header_button_padding">4dp</dimen>
     <dimen name="vertical_tabs_header_button_gap">2dp</dimen>
-    <dimen name="vertical_tabs_header_button_collapsed_margin_end">8dp</dimen>
 
     <!-- Dimens for tab search -->
     <dimen name="tab_search_overlay_width">264dp</dimen>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java
index e8bcd81e..89ee336 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java
@@ -96,7 +96,6 @@
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Supplier;
@@ -260,14 +259,13 @@
                                             mPaneManagerSupplier.get().getDefaultPane();
                             assumeNonNull(tabSwitcherPaneBase);
                             Callback<Integer> requestOpenTabGroupDialog =
-                                    (rootId) -> {
-                                        hide(
-                                                ANIM_DURATION_MS,
-                                                () -> {
-                                                    tabSwitcherPaneBase.requestOpenTabGroupDialog(
-                                                            rootId);
-                                                });
-                                    };
+                                    (Integer rootId) ->
+                                            hide(
+                                                    ANIM_DURATION_MS,
+                                                    () ->
+                                                            tabSwitcherPaneBase
+                                                                    .requestOpenTabGroupDialog(
+                                                                            rootId));
                             // Archive status is reset through any tab group open action in
                             // LocalTabGroupMutationHelper#createNewTabGroup().
                             TabSwitcherUtils.openTabGroupDialog(
@@ -293,7 +291,7 @@
                             .getTabArchiver()
                             .unarchiveAndRestoreTabs(
                                     mRegularTabCreator,
-                                    Arrays.asList(tab),
+                                    Collections.singletonList(tab),
                                     /* updateTimestamp= */ true,
                                     /* areTabsBeingOpened= */ true);
 
@@ -330,12 +328,7 @@
             };
 
     private final TabArchiveSettings.Observer mTabArchiveSettingsObserver =
-            new TabArchiveSettings.Observer() {
-                @Override
-                public void onSettingChanged() {
-                    updateIphPropertyModel();
-                }
-            };
+            this::updateIphPropertyModel;
 
     private final TabGroupSyncService.Observer mTabGroupSyncObserver =
             new TabGroupSyncService.Observer() {
@@ -897,7 +890,7 @@
                         .withNegativeButton(R.string.cancel)
                         .withSupportStopShowing(false)
                         .build(),
-                (dismissHandler, buttonClickResult, stopShowing) -> {
+                (_, buttonClickResult, _) -> {
                     if (buttonClickResult == ButtonClickResult.POSITIVE) {
                         mArchivedTabModel
                                 .getTabRemover()
@@ -957,7 +950,7 @@
         if (mIphMessagePropertyModel == null) return;
         mIphMessagePropertyModel.set(
                 MessageCardViewProperties.DESCRIPTION_TEXT,
-                getIphDescription(mActivity, mTabArchiveSettings, (view) -> onIphReviewClicked()));
+                getIphDescription(mActivity, mTabArchiveSettings, (_) -> onIphReviewClicked()));
     }
 
     private void refreshArchivedTabList() {
@@ -1076,10 +1069,6 @@
         mTabListEditorCoordinator = tabListEditorCoordinator;
     }
 
-    ArchiveDelegate getArchiveDelegateForTesting() {
-        return mArchiveDelegate;
-    }
-
     TabListEditorCoordinator.LifecycleObserver getTabListEditorLifecycleObserver() {
         return mTabListEditorLifecycleObserver;
     }
@@ -1102,8 +1091,4 @@
     FrameLayout getCloseAllTabsButtonContainer() {
         return mDialogView.findViewById(R.id.close_all_tabs_button_container);
     }
-
-    DestroyChecker getDestroyCheckerForTesting() {
-        return mDestroyChecker;
-    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsIphMessageCardViewModel.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsIphMessageCardViewModel.java
index 48d2708..62a1e894 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsIphMessageCardViewModel.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsIphMessageCardViewModel.java
@@ -25,9 +25,11 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabSwitcherMessageManager.MessageType.ARCHIVED_TABS_IPH_MESSAGE;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 
 import androidx.appcompat.content.res.AppCompatResources;
 
+import org.chromium.base.Callback;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.browser.tasks.tab_management.MessageCardView.ActionProvider;
 import org.chromium.chrome.browser.tasks.tab_management.MessageCardView.ServiceDismissActionProvider;
@@ -58,11 +60,10 @@
                 .with(MESSAGE_IDENTIFIER, DEFAULT_MESSAGE_IDENTIFIER)
                 .with(
                         ICON_PROVIDER,
-                        (callback) -> {
-                            callback.onResult(
-                                    AppCompatResources.getDrawable(
-                                            context, R.drawable.archived_tab_icon));
-                        })
+                        (Callback<Drawable> callback) ->
+                                callback.onResult(
+                                        AppCompatResources.getDrawable(
+                                                context, R.drawable.archived_tab_icon)))
                 .with(MESSAGE_SERVICE_DISMISS_ACTION_PROVIDER, serviceDismissActionProvider)
                 .with(MESSAGE_SERVICE_ACTION_PROVIDER, actionProvider)
                 .with(DISMISS_BUTTON_CONTENT_DESCRIPTION, dismissButtonContextDescription)
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoReauthPromoViewModel.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoReauthPromoViewModel.java
index 7b3553f..9122d283 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoReauthPromoViewModel.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoReauthPromoViewModel.java
@@ -8,9 +8,11 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.CARD_TYPE;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 
 import androidx.appcompat.content.res.AppCompatResources;
 
+import org.chromium.base.Callback;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.browser.incognito.IncognitoUtils;
@@ -65,7 +67,7 @@
                 .with(MessageCardViewProperties.SECONDARY_ACTION_TEXT, dismissActionText)
                 .with(
                         MessageCardViewProperties.SECONDARY_ACTION_BUTTON_CLICK_HANDLER,
-                        view -> {
+                        _ -> {
                             data.getDismissActionProvider().action();
                             RecordHistogram.recordEnumeratedHistogram(
                                     "Android.IncognitoReauth.PromoAcceptedOrDismissed",
@@ -91,11 +93,11 @@
                 .with(MessageCardViewProperties.TITLE_TEXT, titleText)
                 .with(
                         MessageCardViewProperties.ICON_PROVIDER,
-                        (callback) -> {
-                            callback.onResult(
-                                    AppCompatResources.getDrawable(
-                                            context, R.drawable.ic_incognito_reauth_promo_icon));
-                        })
+                        (Callback<Drawable> callback) ->
+                                callback.onResult(
+                                        AppCompatResources.getDrawable(
+                                                context,
+                                                R.drawable.ic_incognito_reauth_promo_icon)))
                 .with(CARD_TYPE, TabListModel.CardProperties.ModelType.MESSAGE)
                 .with(CARD_ALPHA, 1f)
                 .build();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LargeMessageCardViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LargeMessageCardViewBinder.java
index 1862302..47c45f7 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LargeMessageCardViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LargeMessageCardViewBinder.java
@@ -25,9 +25,7 @@
         if (MessageCardViewProperties.ACTION_TEXT == propertyKey) {
             itemView.setActionText(model.get(MessageCardViewProperties.ACTION_TEXT));
             itemView.setActionButtonOnClickListener(
-                    v -> {
-                        LargeMessageCardViewBinder.handleReviewActionButton(model);
-                    });
+                    _ -> LargeMessageCardViewBinder.handleReviewActionButton(model));
         } else if (MessageCardViewProperties.TITLE_TEXT == propertyKey) {
             itemView.setTitleText(model.get(MessageCardViewProperties.TITLE_TEXT));
         } else if (MessageCardViewProperties.DESCRIPTION_TEXT == propertyKey) {
@@ -36,9 +34,7 @@
             itemView.setDismissButtonContentDescription(
                     model.get(MessageCardViewProperties.DISMISS_BUTTON_CONTENT_DESCRIPTION));
             itemView.setDismissButtonOnClickListener(
-                    v -> {
-                        LargeMessageCardViewBinder.handleDismissActionButton(model);
-                    });
+                    _ -> LargeMessageCardViewBinder.handleDismissActionButton(model));
         } else if (MessageCardViewProperties.SECONDARY_ACTION_TEXT == propertyKey) {
             itemView.setSecondaryActionText(
                     model.get(MessageCardViewProperties.SECONDARY_ACTION_TEXT));
@@ -70,10 +66,7 @@
         MessageCardView.IconProvider provider = model.get(MessageCardViewProperties.ICON_PROVIDER);
 
         if (provider != null) {
-            provider.fetchIconDrawable(
-                    (drawable) -> {
-                        itemView.setIconDrawable(drawable);
-                    });
+            provider.fetchIconDrawable(itemView::setIconDrawable);
         }
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinder.java
index 0c08a3be..b246b4b 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MessageCardViewBinder.java
@@ -32,7 +32,7 @@
             itemView.setDismissButtonContentDescription(
                     model.get(MessageCardViewProperties.DISMISS_BUTTON_CONTENT_DESCRIPTION));
             itemView.setDismissButtonOnClickListener(
-                    v -> {
+                    _ -> {
                         int type = model.get(MessageCardViewProperties.MESSAGE_TYPE);
                         MessageCardView.ActionProvider uiProvider =
                                 model.get(MessageCardViewProperties.UI_DISMISS_ACTION_PROVIDER);
@@ -75,15 +75,12 @@
         MessageCardView.IconProvider provider = model.get(MessageCardViewProperties.ICON_PROVIDER);
 
         if (provider != null) {
-            provider.fetchIconDrawable(
-                    (drawable) -> {
-                        itemView.setIcon(drawable);
-                    });
+            provider.fetchIconDrawable(itemView::setIcon);
         }
     }
 
     static OnClickListener getConfirmationOnClickListener(PropertyModel model) {
-        return v -> {
+        return _ -> {
             MessageCardView.ActionProvider uiProvider =
                     model.get(MessageCardViewProperties.UI_ACTION_PROVIDER);
             if (uiProvider != null) uiProvider.action();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabArchiveSettingsFragment.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabArchiveSettingsFragment.java
index f0c61b22c..a7dc5c3 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabArchiveSettingsFragment.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabArchiveSettingsFragment.java
@@ -9,6 +9,8 @@
 import android.content.Context;
 import android.os.Bundle;
 
+import androidx.preference.Preference;
+
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.MonotonicObservableSupplier;
 import org.chromium.base.supplier.ObservableSuppliers;
@@ -21,7 +23,6 @@
 import org.chromium.chrome.browser.settings.ChromeBaseSettingsFragment;
 import org.chromium.chrome.browser.settings.search.ChromeBaseSearchIndexProvider;
 import org.chromium.chrome.browser.tab.TabArchiveSettings;
-import org.chromium.chrome.browser.tab.TabArchiveSettings.Observer;
 import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.browser_ui.settings.search.SettingsIndexData;
@@ -37,13 +38,7 @@
     static final String PREF_TAB_ARCHIVE_INCLUDE_DUPLICATE_TABS =
             "tab_archive_include_duplicate_tabs";
 
-    private final TabArchiveSettings.Observer mTabArchiveSettingsObserver =
-            new Observer() {
-                @Override
-                public void onSettingChanged() {
-                    configureSettings();
-                }
-            };
+    private final TabArchiveSettings.Observer mTabArchiveSettingsObserver = this::configureSettings;
 
     private TabArchiveSettings mArchiveSettings;
 
@@ -77,12 +72,12 @@
     private void configureSettings() {
         // Archive time delta radio button.
         TabArchiveTimeDeltaPreference archiveTimeDeltaPreference =
-                (TabArchiveTimeDeltaPreference) findPreference(INACTIVE_TIMEDELTA_PREF);
+                findPreference(INACTIVE_TIMEDELTA_PREF);
         archiveTimeDeltaPreference.initialize(mArchiveSettings);
 
         // Auto delete switch.
         ChromeSwitchPreference enableAutoDeleteSwitch =
-                (ChromeSwitchPreference) findPreference(PREF_TAB_ARCHIVE_ALLOW_AUTODELETE);
+                findPreference(PREF_TAB_ARCHIVE_ALLOW_AUTODELETE);
         int autoDeleteTimeDeltaMonths = mArchiveSettings.getAutoDeleteTimeDeltaMonths();
         enableAutoDeleteSwitch.setSummary(
                 getResources()
@@ -95,7 +90,7 @@
         enableAutoDeleteSwitch.setEnabled(mArchiveSettings.getArchiveEnabled());
         enableAutoDeleteSwitch.setChecked(isAutoDeleteEnabled);
         enableAutoDeleteSwitch.setOnPreferenceChangeListener(
-                (preference, newValue) -> {
+                (Preference _, Object newValue) -> {
                     boolean enabled = (boolean) newValue;
                     mArchiveSettings.setAutoDeleteEnabled(enabled);
                     RecordHistogram.recordBooleanHistogram(
@@ -105,12 +100,12 @@
 
         // Duplicate tabs switch.
         ChromeSwitchPreference enableArchiveDuplicateTabsSwitch =
-                (ChromeSwitchPreference) findPreference(PREF_TAB_ARCHIVE_INCLUDE_DUPLICATE_TABS);
+                findPreference(PREF_TAB_ARCHIVE_INCLUDE_DUPLICATE_TABS);
         enableArchiveDuplicateTabsSwitch.setEnabled(mArchiveSettings.getArchiveEnabled());
         enableArchiveDuplicateTabsSwitch.setChecked(
                 mArchiveSettings.isArchiveDuplicateTabsEnabled());
         enableArchiveDuplicateTabsSwitch.setOnPreferenceChangeListener(
-                (preference, newValue) -> {
+                (Preference _, Object newValue) -> {
                     boolean enabled = (boolean) newValue;
                     mArchiveSettings.setArchiveDuplicateTabsEnabled(enabled);
                     RecordHistogram.recordBooleanHistogram(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
index a6d5b13..6186723 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
@@ -135,7 +135,7 @@
             Supplier<@Nullable TabBookmarker> tabBookmarkerSupplier,
             Supplier<@Nullable ShareDelegate> shareDelegateSupplier,
             Callback<@Nullable View> attachViewCallback) {
-        try (TraceEvent e = TraceEvent.scoped("TabGridDialogCoordinator.constructor")) {
+        try (TraceEvent _ = TraceEvent.scoped("TabGridDialogCoordinator.constructor")) {
             mActivity = activity;
             int componentId =
                     animationSourceViewProvider == null
@@ -209,10 +209,9 @@
             }
 
             Runnable showTabGroupColorPickerPopupRunnable =
-                    () -> {
-                        showTabGroupColorPickerPopup(
-                                mDialogView.findViewById(R.id.tab_group_color_icon));
-                    };
+                    () ->
+                            showTabGroupColorPickerPopup(
+                                    mDialogView.findViewById(R.id.tab_group_color_icon));
 
             mMediator =
                     new TabGridDialogMediator(
@@ -306,7 +305,7 @@
                                     toolbarView, recyclerView, mDialogView),
                             TabGridDialogViewBinder::bind);
             mBackPressChangedSupplier.set(isVisible());
-            mModel.addObserver((source, key) -> mBackPressChangedSupplier.set(isVisible()));
+            mModel.addObserver((_, _) -> mBackPressChangedSupplier.set(isVisible()));
 
             // This is always created post-native so calling these immediately is safe.
             // TODO(crbug.com/40894893): Consider inlining these behaviors in their respective
@@ -396,28 +395,25 @@
 
     private void showTabGroupColorPickerPopup(View anchorView) {
         PopupWindow.OnDismissListener onDismissListener =
-                new PopupWindow.OnDismissListener() {
-                    @Override
-                    public void onDismiss() {
-                        assumeNonNull(mTabGroupColorPickerCoordinator);
-                        mMediator.setSelectedTabGroupColor(
-                                assertNonNull(
-                                        mTabGroupColorPickerCoordinator
-                                                .getSelectedColorSupplier()
-                                                .get()));
+                () -> {
+                    assumeNonNull(mTabGroupColorPickerCoordinator);
+                    mMediator.setSelectedTabGroupColor(
+                            assertNonNull(
+                                    mTabGroupColorPickerCoordinator
+                                            .getSelectedColorSupplier()
+                                            .get()));
 
-                        // Only require a refresh of the tab list if accessed from the GTS,
-                        // skip if this is reached from the tab strip as the color will
-                        // refresh upon re-entering the tab switcher.
-                        if (mTabSwitcherResetHandler != null) {
-                            // Refresh the TabSwitcher's tab list to reflect the last
-                            // selected color in the color picker when it is dismissed. This
-                            // call will be invoked for both Grid and List modes on the GTS.
-                            TabModel tabModel = mCurrentTabModelSupplier.get();
-                            assumeNonNull(tabModel);
-                            mTabSwitcherResetHandler.resetWithListOfTabs(
-                                    tabModel.getRepresentativeTabList());
-                        }
+                    // Only require a refresh of the tab list if accessed from the GTS,
+                    // skip if this is reached from the tab strip as the color will
+                    // refresh upon re-entering the tab switcher.
+                    if (mTabSwitcherResetHandler != null) {
+                        // Refresh the TabSwitcher's tab list to reflect the last
+                        // selected color in the color picker when it is dismissed. This
+                        // call will be invoked for both Grid and List modes on the GTS.
+                        TabModel tabModel = mCurrentTabModelSupplier.get();
+                        assumeNonNull(tabModel);
+                        mTabSwitcherResetHandler.resetWithListOfTabs(
+                                tabModel.getRepresentativeTabList());
                     }
                 };
 
@@ -518,13 +514,6 @@
         mTabListCoordinator.waitForLayoutWithTab(tabId, r);
     }
 
-    Rect getGlobalLocationOfCurrentThumbnail() {
-        Rect thumbnail = mTabListCoordinator.getThumbnailLocationOfCurrentTab();
-        Rect recyclerViewLocation = mTabListCoordinator.getRecyclerViewLocation();
-        thumbnail.offset(recyclerViewLocation.left, recyclerViewLocation.top);
-        return thumbnail;
-    }
-
     TabGridDialogMediator.DialogController getDialogController() {
         return this;
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
index 31521f9..e227ef73 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -241,7 +241,6 @@
     private final Runnable mShowColorPickerPopupRunnable;
     private final Profile mOriginalProfile;
     private final @Nullable TabGroupSyncService mTabGroupSyncService;
-    private final @Nullable DataSharingService mDataSharingService;
     private final CollaborationService mCollaborationService;
     private final @Nullable TransitiveSharedGroupObserver mTransitiveSharedGroupObserver;
     private final @Nullable MessagingBackendService mMessagingBackendService;
@@ -252,7 +251,6 @@
     private final @Nullable DesktopWindowStateManager mDesktopWindowStateManager;
     private final BottomSheetObserver mBottomSheetObserver;
 
-    private @Nullable TabGroupListBottomSheetCoordinator mTabGroupListBottomSheetCoordinator;
     private @Nullable Token mCurrentTabGroupId;
     private TabGridDialogMenuCoordinator mTabGridDialogMenuCoordinator;
     private LazyOneshotSupplier<TabListEditorController> mTabListEditorControllerSupplier;
@@ -305,10 +303,11 @@
         mCollaborationService = CollaborationServiceFactory.getForProfile(mOriginalProfile);
         if (mTabGroupSyncService != null
                 && mCollaborationService.getServiceStatus().isAllowedToJoin()) {
-            mDataSharingService = DataSharingServiceFactory.getForProfile(mOriginalProfile);
+            @Nullable DataSharingService dataSharingService =
+                    DataSharingServiceFactory.getForProfile(mOriginalProfile);
             mTransitiveSharedGroupObserver =
                     new TransitiveSharedGroupObserver(
-                            mTabGroupSyncService, mDataSharingService, mCollaborationService);
+                            mTabGroupSyncService, dataSharingService, mCollaborationService);
             // This should be the first supplier set as the other suppliers depend on its value.
             mTransitiveSharedGroupObserver
                     .getCollaborationIdSupplier()
@@ -335,7 +334,6 @@
                     };
             mMessagingBackendService.addPersistentMessageObserver(mPersistentMessageObserver);
         } else {
-            mDataSharingService = null;
             mTransitiveSharedGroupObserver = null;
             mMessagingBackendService = null;
             mPersistentMessageObserver = null;
@@ -482,29 +480,27 @@
                         if (mSnackbarManager == null) return;
                         PostTask.postTask(
                                 TaskTraits.UI_DEFAULT,
-                                () -> {
-                                    mSnackbarManager.dismissSnackbars(
-                                            TabGridDialogMediator.this, tabs);
-                                });
+                                () ->
+                                        mSnackbarManager.dismissSnackbars(
+                                                TabGridDialogMediator.this, tabs));
                     }
 
                     private void dismissSingleTabSnackbar(int tabId) {
                         if (mSnackbarManager == null) return;
                         PostTask.postTask(
                                 TaskTraits.UI_DEFAULT,
-                                () -> {
-                                    mSnackbarManager.dismissSnackbars(
-                                            TabGridDialogMediator.this, tabId);
-                                });
+                                () ->
+                                        mSnackbarManager.dismissSnackbars(
+                                                TabGridDialogMediator.this, tabId));
                     }
 
                     private void dismissAllSnackbars() {
                         if (mSnackbarManager == null) return;
                         PostTask.postTask(
                                 TaskTraits.UI_DEFAULT,
-                                () -> {
-                                    mSnackbarManager.dismissSnackbars(TabGridDialogMediator.this);
-                                });
+                                () ->
+                                        mSnackbarManager.dismissSnackbars(
+                                                TabGridDialogMediator.this));
                     }
                 };
 
@@ -553,7 +549,7 @@
 
         TabModel tabModel = mCurrentTabModelSupplier.get();
         assumeNonNull(tabModel);
-        if (profile != null && modalDialogManager != null) {
+        if (modalDialogManager != null) {
             TabGroupCreationDialogManager tabGroupCreationDialogManager =
                     new TabGroupCreationDialogManager(activity, modalDialogManager, null);
             TabGroupCreationCallback tabGroupCreationCallback =
@@ -562,7 +558,7 @@
             // Dismiss the dialog if open. The dialog should be open when the bottom sheet is
             // visible.
             TabMovedCallback tabMovedCallback = () -> hideDialog(true);
-            mTabGroupListBottomSheetCoordinator =
+            @Nullable TabGroupListBottomSheetCoordinator tabGroupListBottomSheetCoordinator =
                     new TabGroupListBottomSheetCoordinator(
                             activity,
                             profile,
@@ -570,8 +566,9 @@
                             tabMovedCallback,
                             tabModel,
                             bottomSheetController,
-                            true,
-                            false);
+                            /* supportsShowNewGroup= */ true,
+                            /* destroyOnHide= */ false,
+                            /* windowAndroid= */ null);
 
             CollaborationService collaborationService =
                     CollaborationServiceFactory.getForProfile(profile);
@@ -590,7 +587,7 @@
                             tabBookmarkerSupplier,
                             profile,
                             tabModel,
-                            mTabGroupListBottomSheetCoordinator,
+                            tabGroupListBottomSheetCoordinator,
                             tabGroupCreationDialogManager,
                             shareDelegateSupplier,
                             mTabGroupSyncService,
@@ -979,7 +976,7 @@
         mModel.set(TabGridDialogProperties.TITLE_TEXT_WATCHER, textWatcher);
 
         View.OnFocusChangeListener onFocusChangeListener =
-                (v, hasFocus) -> {
+                (View _, boolean hasFocus) -> {
                     mIsUpdatingTitle = hasFocus;
                     mModel.set(TabGridDialogProperties.IS_KEYBOARD_VISIBLE, hasFocus);
                     mModel.set(TabGridDialogProperties.IS_TITLE_TEXT_FOCUSED, hasFocus);
@@ -989,14 +986,14 @@
     }
 
     private View.OnClickListener getCollapseButtonClickListener() {
-        return view -> {
+        return _ -> {
             hideDialog(true);
             RecordUserAction.record("TabGridDialog.Exit");
         };
     }
 
     private View.OnClickListener getAddButtonClickListener() {
-        return view -> {
+        return _ -> {
             // Get the current Tab first since hideDialog causes mCurrentTabGroupId to be null;
             List<Tab> tabsInGroup = getTabsInGroup(mCurrentTabGroupId);
             hideDialog(false);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewBinder.java
index b147455..ff75606 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewBinder.java
@@ -178,10 +178,11 @@
                 if (view.getWidth() == 0 || view.getHeight() == 0) {
                     // If layout hasn't happened post the scroll index change until layout happens.
                     view.post(
-                            () -> {
-                                setScrollIndex(
-                                        model.get(BROWSER_CONTROLS_STATE_PROVIDER), view, index);
-                            });
+                            () ->
+                                    setScrollIndex(
+                                            model.get(BROWSER_CONTROLS_STATE_PROVIDER),
+                                            view,
+                                            index));
                     return;
                 }
                 setScrollIndex(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
index fe331083df..e53075e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -335,12 +335,11 @@
             }
 
             TextResolver contentDescriptionResolver =
-                    (context) -> {
-                        return context.getString(
-                                R.string.accessibility_tab_price_card,
-                                priceDrop.previousPrice,
-                                priceDrop.price);
-                    };
+                    (Context context) ->
+                            context.getString(
+                                    R.string.accessibility_tab_price_card,
+                                    priceDrop.previousPrice,
+                                    priceDrop.price);
             PriceDropTextResolver priceDropResolver =
                     new PriceDropTextResolver(priceDrop.price, priceDrop.previousPrice);
             TabCardLabelData labelData =
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetCoordinator.java
index f72f1475..938dcfb 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetCoordinator.java
@@ -25,6 +25,8 @@
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason;
 import org.chromium.components.tab_group_sync.TabGroupSyncService;
+import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.base.WindowAndroid.ActivityStateObserver;
 import org.chromium.ui.modelutil.LayoutViewBuilder;
 import org.chromium.ui.modelutil.MVCListAdapter;
 import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
@@ -67,6 +69,8 @@
     private final SimpleRecyclerViewAdapter mSimpleRecyclerViewAdapter;
     private final TabListFaviconProvider mTabListFaviconProvider;
     private final TabGroupListBottomSheetMediator mMediator;
+    private final @Nullable WindowAndroid mWindowAndroid;
+    private final @Nullable ActivityStateObserver mActivityStateObserver;
 
     /**
      * @param context The {@link Context} to attach the bottom sheet to.
@@ -77,6 +81,7 @@
      * @param bottomSheetController Used to interact with the bottom sheet.
      * @param supportsShowNewGroup Whether the 'New Tab Group' row is supported.
      * @param destroyOnHide Whether this object should be destroyed on hiding the bottom sheet.
+     * @param windowAndroid Used to observe activity state changes.
      */
     public TabGroupListBottomSheetCoordinator(
             Context context,
@@ -86,7 +91,8 @@
             TabModel tabModel,
             BottomSheetController bottomSheetController,
             boolean supportsShowNewGroup,
-            boolean destroyOnHide) {
+            boolean destroyOnHide,
+            @Nullable WindowAndroid windowAndroid) {
         mView =
                 new TabGroupListBottomSheetView(
                         context, bottomSheetController, supportsShowNewGroup);
@@ -131,6 +137,7 @@
         @Nullable TabGroupSyncService tabGroupSyncService =
                 isProfileOffTheRecord ? null : TabGroupSyncServiceFactory.getForProfile(profile);
 
+        TabGroupListBottomSheetCoordinatorDelegate delegate = createDelegate(destroyOnHide);
         mMediator =
                 new TabGroupListBottomSheetMediator(
                         modelList,
@@ -140,8 +147,24 @@
                         faviconResolver,
                         tabGroupSyncService,
                         bottomSheetController,
-                        createDelegate(destroyOnHide),
+                        delegate,
                         supportsShowNewGroup);
+
+        mWindowAndroid = windowAndroid;
+        if (mWindowAndroid != null) {
+            mActivityStateObserver =
+                    new ActivityStateObserver() {
+                        @Override
+                        public void onActivityPaused() {
+                            if (mBottomSheetController.getCurrentSheetContent() == mView) {
+                                delegate.hide(StateChangeReason.NONE);
+                            }
+                        }
+                    };
+            mWindowAndroid.addActivityStateObserver(mActivityStateObserver);
+        } else {
+            mActivityStateObserver = null;
+        }
     }
 
     /** Creates the delegate. */
@@ -184,6 +207,9 @@
 
     /** Permanently cleans up this component. */
     public void destroy() {
+        if (mWindowAndroid != null && mActivityStateObserver != null) {
+            mWindowAndroid.removeActivityStateObserver(mActivityStateObserver);
+        }
         mMediator.destroy();
         mSimpleRecyclerViewAdapter.destroy();
         mTabListFaviconProvider.destroy();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetCoordinatorFactory.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetCoordinatorFactory.java
index 8ae7184..09c4499 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetCoordinatorFactory.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetCoordinatorFactory.java
@@ -13,6 +13,7 @@
 import org.chromium.chrome.browser.tabmodel.TabGroupUtils.TabMovedCallback;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.ui.base.WindowAndroid;
 
 /** Factory class for creating {@link TabGroupListBottomSheetCoordinator} instances. */
 @NullMarked
@@ -29,6 +30,7 @@
      * @param controller Used to interact with the bottom sheet.
      * @param supportsShowNewGroup Whether the 'New Tab Group' row is supported.
      * @param destroyOnHide Whether the coordinator should be destroyed on hide.
+     * @param windowAndroid Used to observe activity state.
      */
     TabGroupListBottomSheetCoordinator create(
             Context context,
@@ -38,5 +40,6 @@
             TabModel tabModel,
             BottomSheetController controller,
             boolean supportsShowNewGroup,
-            boolean destroyOnHide);
+            boolean destroyOnHide,
+            @Nullable WindowAndroid windowAndroid);
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetCoordinatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetCoordinatorUnitTest.java
index 53a21e9..03d175e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetCoordinatorUnitTest.java
@@ -21,6 +21,8 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -43,6 +45,8 @@
 import org.chromium.components.tab_group_sync.SavedTabGroup;
 import org.chromium.components.tab_group_sync.SavedTabGroupTab;
 import org.chromium.components.tab_group_sync.TabGroupSyncService;
+import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.base.WindowAndroid.ActivityStateObserver;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -62,6 +66,8 @@
     @Mock private DataSharingService mDataSharingService;
     @Mock private Profile mProfile;
     @Mock private Tab mTab;
+    @Mock private WindowAndroid mWindowAndroid;
+    @Captor private ArgumentCaptor<ActivityStateObserver> mActivityStateObserverCaptor;
     private final SavedTabGroup mSavedTabGroup = new SavedTabGroup();
     private final SavedTabGroupTab mSavedTabGroupTab = new SavedTabGroupTab();
     private Context mContext;
@@ -90,7 +96,8 @@
                         mTabModel,
                         mBottomSheetController,
                         /* supportsShowNewGroup= */ true,
-                        /* destroyOnHide= */ false);
+                        /* destroyOnHide= */ false,
+                        mWindowAndroid);
     }
 
     @Test
@@ -156,6 +163,48 @@
                 mTabModel,
                 mBottomSheetController,
                 /* supportsShowNewGroup= */ true,
-                /* destroyOnHide= */ false);
+                /* destroyOnHide= */ false,
+                /* windowAndroid= */ null);
+    }
+
+    @Test
+    public void testOnActivityPaused_closesSheetIfHostContent() {
+        verify(mWindowAndroid).addActivityStateObserver(mActivityStateObserverCaptor.capture());
+        ActivityStateObserver observer = mActivityStateObserverCaptor.getValue();
+
+        when(mBottomSheetController.getCurrentSheetContent())
+                .thenReturn(mock(TabGroupListBottomSheetView.class));
+        ArgumentCaptor<TabGroupListBottomSheetView> viewCaptor =
+                ArgumentCaptor.forClass(TabGroupListBottomSheetView.class);
+        mCoordinator.showBottomSheet(List.of(mTab));
+        verify(mBottomSheetController).requestShowContent(viewCaptor.capture(), eq(true));
+        TabGroupListBottomSheetView view = viewCaptor.getValue();
+
+        when(mBottomSheetController.getCurrentSheetContent()).thenReturn(view);
+        observer.onActivityPaused();
+
+        verify(mBottomSheetController).hideContent(eq(view), eq(true), eq(StateChangeReason.NONE));
+    }
+
+    @Test
+    public void testOnActivityPaused_doesNotCloseSheetIfNotHostContent() {
+        verify(mWindowAndroid).addActivityStateObserver(mActivityStateObserverCaptor.capture());
+        ActivityStateObserver observer = mActivityStateObserverCaptor.getValue();
+
+        when(mBottomSheetController.getCurrentSheetContent())
+                .thenReturn(mock(BottomSheetContent.class));
+        observer.onActivityPaused();
+
+        verify(mBottomSheetController, never())
+                .hideContent(any(), eq(true), eq(StateChangeReason.NONE));
+    }
+
+    @Test
+    public void testDestroy_removesObserver() {
+        verify(mWindowAndroid).addActivityStateObserver(mActivityStateObserverCaptor.capture());
+        ActivityStateObserver observer = mActivityStateObserverCaptor.getValue();
+
+        mCoordinator.destroy();
+        verify(mWindowAndroid).removeActivityStateObserver(eq(observer));
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java
index 9058beeb..5a69472 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java
@@ -114,13 +114,10 @@
 
         ViewBuilder<TabGroupRowView> innerBuilder = new LayoutViewBuilder<>(R.layout.tab_group_row);
         ViewBuilder<TabGroupRowView> tabGroupRowLayoutBuilder =
-                new ViewBuilder<>() {
-                    @Override
-                    public TabGroupRowView buildView(ViewGroup parent) {
-                        TabGroupRowView view = innerBuilder.buildView(parent);
-                        if (enableContainment()) view.setupForContainment();
-                        return view;
-                    }
+                (ViewGroup parent) -> {
+                    TabGroupRowView view = innerBuilder.buildView(parent);
+                    if (enableContainment()) view.setupForContainment();
+                    return view;
                 };
 
         mSimpleRecyclerViewAdapter.registerType(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediator.java
index 7022f22c..f398ecc 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediator.java
@@ -281,11 +281,10 @@
         List<SavedTabGroup> sortedTabGroups =
                 sortUtil.getSortedGroupList(
                         this::shouldShowGroupByState,
-                        (a, b) -> {
-                            return Long.compare(
-                                    TabUiUtils.getGroupLastUpdatedTimestamp(b),
-                                    TabUiUtils.getGroupLastUpdatedTimestamp(a));
-                        });
+                        (SavedTabGroup a, SavedTabGroup b) ->
+                                Long.compare(
+                                        TabUiUtils.getGroupLastUpdatedTimestamp(b),
+                                        TabUiUtils.getGroupLastUpdatedTimestamp(a)));
         for (SavedTabGroup savedTabGroup : sortedTabGroups) {
             TabGroupRowMediator rowMediator =
                     new TabGroupRowMediator(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupMenuActionHandler.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupMenuActionHandler.java
index 2c1294b..f3d8e734 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupMenuActionHandler.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupMenuActionHandler.java
@@ -107,8 +107,9 @@
                             /* tabMovedCallback= */ null,
                             mTabModel,
                             mBottomSheetController,
-                            true,
-                            true);
+                            /* supportsShowNewGroup= */ true,
+                            /* destroyOnHide= */ true,
+                            tab.getWindowAndroid());
             mTabGroupListBottomSheetCoordinatorSupplier.set(tabGroupListBottomSheetCoordinator);
             tabGroupListBottomSheetCoordinator.showBottomSheet(List.of(tab));
         }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupMenuActionHandlerUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupMenuActionHandlerUnitTest.java
index fd07a86..c0828cb 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupMenuActionHandlerUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupMenuActionHandlerUnitTest.java
@@ -92,7 +92,7 @@
         when(mTabModel.tabGroupExists(any())).thenReturn(true);
 
         TabGroupListBottomSheetCoordinatorFactory factory =
-                (a, b, callback, d, e, f, g, h) -> {
+                (a, b, callback, d, e, f, g, h, i) -> {
                     mTabGroupCreationCallback = callback;
                     return mTabGroupListBottomSheetCoordinator;
                 };
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
index 93f6957..287220dc 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
@@ -335,7 +335,7 @@
                     }
                 };
 
-        mCurrentTabModelObserver = (tabModel) -> resetTabStrip();
+        mCurrentTabModelObserver = _ -> resetTabStrip();
 
         mTabGroupObserver =
                 new TabGroupObserver() {
@@ -353,7 +353,7 @@
         tabModelSelector.getModel(false).addTabGroupObserver(mTabGroupObserver);
         tabModelSelector.getModel(true).addTabGroupObserver(mTabGroupObserver);
 
-        mOmniboxFocusObserver = isFocus -> resetTabStrip();
+        mOmniboxFocusObserver = _ -> resetTabStrip();
         mOmniboxFocusStateSupplier.addSyncObserverAndPostIfNonNull(mOmniboxFocusObserver);
 
         tabModelSelector.addObserverToAllModels(mTabModelObserver);
@@ -377,12 +377,11 @@
         mHandleBackPressChangedSupplier = handleBackPressChangedSupplier;
         if (mTabGridDialogControllerSupplier != null) {
             mTabGridDialogControllerSupplier.onAvailable(
-                    controller -> {
-                        controller
-                                .getHandleBackPressChangedSupplier()
-                                .addSyncObserverAndPostIfNonNull(
-                                        mHandleBackPressChangedSupplier::set);
-                    });
+                    controller ->
+                            controller
+                                    .getHandleBackPressChangedSupplier()
+                                    .addSyncObserverAndPostIfNonNull(
+                                            mHandleBackPressChangedSupplier::set));
         }
     }
 
@@ -409,7 +408,7 @@
 
     private void setupToolbarButtons() {
         View.OnClickListener showGroupDialogOnClickListener =
-                view -> {
+                _ -> {
                     // Don't handle taps until fully visible and done animating.
                     @Nullable DialogController controller = getTabGridDialogControllerIfExists();
                     if (controller != null && controller.getShowingOrAnimationSupplier().get()) {
@@ -428,12 +427,12 @@
         mModel.set(SHOW_GROUP_DIALOG_ON_CLICK_LISTENER, showGroupDialogOnClickListener);
 
         View.OnClickListener newTabButtonOnClickListener =
-                view -> {
+                _ -> {
                     Tab currentTab = mTabModelSelector.getCurrentTab();
                     assumeNonNull(currentTab);
                     List<Tab> relatedTabs = getTabsToShowForId(currentTab.getId());
 
-                    assert relatedTabs.size() > 0;
+                    assert !relatedTabs.isEmpty();
 
                     Profile currentTabProfile = currentTab.getProfile();
                     UrlConstantResolver urlConstantResolver =
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabHoverCardView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabHoverCardView.java
index 2f944bf3..cd88619fa 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabHoverCardView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabHoverCardView.java
@@ -146,9 +146,7 @@
         mTabModelSelector = tabModelSelector;
         mTabContentManager = tabContentManagerSupplier.get();
         mCurrentTabModelObserver =
-                (tabModel) -> {
-                    updateHoverCardColors(tabModel.isIncognitoBranded());
-                };
+                (TabModel tabModel) -> updateHoverCardColors(tabModel.isIncognitoBranded());
         mTabModelSelector
                 .getCurrentTabModelSupplier()
                 .addSyncObserverAndPostIfNonNull(mCurrentTabModelObserver);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java
index 29c4fbae..53be1f08 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java
@@ -278,9 +278,7 @@
                     model.get(IS_PINNED_TAB_STRIP_ANIMATING_SUPPLIER);
             if (supplier == null) return;
             supplier.addSyncObserverAndCallIfNonNull(
-                    _ -> {
-                        updateHairlineVisibility(model, hairline);
-                    });
+                    _ -> updateHairlineVisibility(model, hairline));
         }
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
index 0ca3f92..c338aa2 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -251,7 +251,7 @@
         if (mMode == TabListMode.GRID) {
             mAdapter.registerType(
                     UiType.TAB,
-                    parent -> {
+                    _ -> {
                         ViewGroup group =
                                 (ViewGroup)
                                         LayoutInflater.from(activity)
@@ -267,7 +267,7 @@
             // groups, an alternative view binder and model can be implemented.
             mAdapter.registerType(
                     UiType.TAB_GROUP,
-                    parent -> {
+                    _ -> {
                         ViewGroup group =
                                 (ViewGroup)
                                         LayoutInflater.from(activity)
@@ -299,11 +299,10 @@
         } else if (mMode == TabListMode.BOTTOM_STRIP) {
             mAdapter.registerType(
                     UiType.STRIP,
-                    parent -> {
-                        return (ViewGroup)
-                                LayoutInflater.from(activity)
-                                        .inflate(R.layout.tab_strip_item, parentView, false);
-                    },
+                    _ ->
+                            (ViewGroup)
+                                    LayoutInflater.from(activity)
+                                            .inflate(R.layout.tab_strip_item, parentView, false),
                     TabStripViewBinder::bind);
         } else {
             throw new IllegalArgumentException(
@@ -377,7 +376,7 @@
                         isSingleContextMode,
                         onDragStateChangedListener);
 
-        try (TraceEvent e = TraceEvent.scoped("TabListCoordinator.setupRecyclerView")) {
+        try (TraceEvent _ = TraceEvent.scoped("TabListCoordinator.setupRecyclerView")) {
             // Ignore attachToParent initially. In some activitys multiple TabListCoordinators are
             // created with the same parentView. Using attachToParent and subsequently trying to
             // locate the View with findViewById could then resolve to the wrong view. Instead use
@@ -436,8 +435,7 @@
 
         if (mMode == TabListMode.GRID) {
             mListLayoutListener =
-                    (view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
-                            updateGridCardLayout(right - left);
+                    (_, left, _, right, _, _, _, _, _) -> updateGridCardLayout(right - left);
         } else if (mMode == TabListMode.BOTTOM_STRIP) {
             assert onModelTokenChange != null;
             mTabStripSnapshotter =
@@ -562,17 +560,6 @@
         mTabListItemSizeChangedObserverList.removeObserver(observer);
     }
 
-    Rect getThumbnailLocationOfCurrentTab() {
-        // TODO(crbug.com/40627995): calculate the location before the real one is ready.
-        Rect rect =
-                mRecyclerView.getRectOfCurrentThumbnail(
-                        mModelList.indexFromTabId(mMediator.selectedTabId()),
-                        mMediator.selectedTabId());
-        if (rect == null) return new Rect();
-        rect.offset(0, 0);
-        return rect;
-    }
-
     /**
      * @param tabId The tab ID to get a rect for.
      * @return a {@link Rect} for the tab's thumbnail (may be an empty rect if the tab is not
@@ -633,7 +620,7 @@
     void initWithNative(Profile originalProfile) {
         if (mIsInitialized) return;
 
-        try (TraceEvent e = TraceEvent.scoped("TabListCoordinator.initWithNative")) {
+        try (TraceEvent _ = TraceEvent.scoped("TabListCoordinator.initWithNative")) {
             mIsInitialized = true;
 
             assert !originalProfile.isOffTheRecord() : "Expecting a non-incognito profile.";
@@ -719,7 +706,7 @@
 
         final Size oldDefaultSize = mMediator.getDefaultGridCardSize();
         final Size newDefaultSize = new Size(cardWidthPx, cardHeightPx);
-        if (oldDefaultSize != null && newDefaultSize.equals(oldDefaultSize)) return;
+        if (newDefaultSize.equals(oldDefaultSize)) return;
 
         mMediator.setDefaultGridCardSize(newDefaultSize);
         for (int i = 0; i < mModelList.size(); i++) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorAddToGroupAction.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorAddToGroupAction.java
index f77227d76..22833f9f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorAddToGroupAction.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorAddToGroupAction.java
@@ -186,6 +186,7 @@
 
     private void showBottomSheet(
             List<Tab> tabs, TabModel tabModel, Profile profile, BottomSheetController controller) {
+        assert !tabs.isEmpty();
         TabGroupCreationCallback groupCreationCallback =
                 tabGroupId -> mTabGroupCreationDialogManager.showDialog(tabGroupId, tabModel);
 
@@ -197,8 +198,9 @@
                         /* tabMovedCallback= */ null,
                         tabModel,
                         controller,
-                        true,
-                        true);
+                        /* supportsShowNewGroup= */ true,
+                        /* destroyOnHide= */ true,
+                        tabs.get(0).getWindowAndroid());
         mTabGroupListBottomSheetCoordinator.showBottomSheet(tabs);
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorMediator.java
index 18c5ec1..0a94731 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorMediator.java
@@ -49,6 +49,7 @@
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyListModel;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyObservable;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -95,7 +96,7 @@
     private boolean mHasSnackbarOverride;
 
     private final View.OnClickListener mNavigationClickListener =
-            v -> {
+            _ -> {
                 assumeNonNull(mNavigationProvider);
                 mNavigationProvider.goBack();
             };
@@ -197,15 +198,11 @@
                 };
 
         mSelectionObserver =
-                new SelectionDelegate.SelectionObserver<>() {
-                    @Override
-                    public void onSelectionStateChange(
-                            List<TabListEditorItemSelectionId> selectedItems) {
-                        // Synchronizes the visual properties of each tab model with the current
-                        // state of the selection delegate to update checkmarks.
-                        updateModelsFromSelection(selectedItems);
-                        updateItemPickerSelectionHandler();
-                    }
+                (List<TabListEditorItemSelectionId> selectedItems) -> {
+                    // Synchronizes the visual properties of each tab model with the current
+                    // state of the selection delegate to update checkmarks.
+                    updateModelsFromSelection(selectedItems);
+                    updateItemPickerSelectionHandler();
                 };
         mSelectionDelegate.addObserver(mSelectionObserver);
 
@@ -213,7 +210,7 @@
 
         mBackPressChangedSupplier.set(isEditorVisible());
         mModel.addObserver(
-                (source, key) -> {
+                (PropertyObservable<PropertyKey> _, PropertyKey key) -> {
                     if (key == TabListEditorProperties.IS_VISIBLE) {
                         mBackPressChangedSupplier.set(isEditorVisible());
                     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorMenuItem.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorMenuItem.java
index 7819807..ec8e87f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorMenuItem.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorMenuItem.java
@@ -211,15 +211,11 @@
         mOnClickRunnable = runnable;
         if (mActionView != null) {
             // For non-peripheral clicks.
-            mActionView.setOnClickListener(v -> onClick(/* triggeringMotion= */ null));
+            mActionView.setOnClickListener(_ -> onClick(/* triggeringMotion= */ null));
             // For peripheral clicks. This will intercept the event and prevent OnClickListener
             // from being called for peripheral events.
             mActionView.setOnTouchListener(
-                    new OnPeripheralClickListener(
-                            mActionView,
-                            (triggeringMotion) -> {
-                                onClick(triggeringMotion);
-                            }));
+                    new OnPeripheralClickListener(mActionView, this::onClick));
         }
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index 9a9ecd8..f4fdc311 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -21,6 +21,7 @@
 import android.animation.ObjectAnimator;
 import android.app.Activity;
 import android.content.ComponentCallbacks;
+import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -403,7 +404,6 @@
     private final @Nullable Supplier<@Nullable PriceWelcomeMessageController>
             mPriceWelcomeMessageControllerSupplier;
     private final @Nullable DataSharingTabManager mDataSharingTabManager;
-    private final @Nullable Runnable mOnTabGroupCreation;
     private final TabModelObserver mTabModelObserver;
     private final TabListLayoutDelegate mTabListLayoutDelegate;
     private final TabActionListener mTabClosedListener;
@@ -957,7 +957,6 @@
         mComponentId = componentId;
         mTabActionState = initialTabActionState;
         mDataSharingTabManager = dataSharingTabManager;
-        mOnTabGroupCreation = onTabGroupCreation;
         mUndoBarExplicitTrigger = undoBarExplicitTrigger;
         mSnackbarManager = snackbarManager;
         mAllowedSelectionCount = allowedSelectionCount;
@@ -1364,10 +1363,9 @@
                         if (shouldDisableItemAnimations) {
                             new Handler()
                                     .post(
-                                            () -> {
-                                                mRecyclerViewItemAnimationToggle
-                                                        .setDisableItemAnimations(false);
-                                            });
+                                            () ->
+                                                    mRecyclerViewItemAnimationToggle
+                                                            .setDisableItemAnimations(false));
                         }
                     }
 
@@ -1381,7 +1379,7 @@
 
         var tabGroupCreationDialogManager =
                 new TabGroupCreationDialogManager(
-                        activity, assumeNonNull(modalDialogManager), mOnTabGroupCreation);
+                        activity, assumeNonNull(modalDialogManager), onTabGroupCreation);
         mTabGridItemTouchHelperCallback =
                 new TabGridItemTouchHelperCallback(
                         activity,
@@ -2130,7 +2128,7 @@
             mTabListGroupMenuCoordinator =
                     new TabListGroupMenuCoordinator(
                             mOnMenuItemClickedCallback,
-                            () -> getCurrentTabModelChecked(),
+                            this::getCurrentTabModelChecked,
                             tabGroupSyncService,
                             collaborationService,
                             mActivity);
@@ -2668,10 +2666,10 @@
         }
 
         descriptionTextResolver =
-                (context) -> {
-                    return context.getString(
-                            R.string.accessibility_tabstrip_btn_close_tab, getTabTitleOrUrl(tab));
-                };
+                (Context context) ->
+                        context.getString(
+                                R.string.accessibility_tabstrip_btn_close_tab,
+                                getTabTitleOrUrl(tab));
         model.set(TabProperties.ACTION_BUTTON_DESCRIPTION_TEXT_RESOLVER, descriptionTextResolver);
     }
 
@@ -3249,9 +3247,7 @@
         assert mMode == TabListMode.BOTTOM_STRIP;
 
         Callback<PropertyModel> updateTabStripItemCallback =
-                (model) -> {
-                    model.set(TabProperties.HAS_NOTIFICATION_BUBBLE, hasUpdate);
-                };
+                (model) -> model.set(TabProperties.HAS_NOTIFICATION_BUBBLE, hasUpdate);
 
         forAllTabListItems(tabIdsToBeUpdated, updateTabStripItemCallback);
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabOverflowMenuCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabOverflowMenuCoordinator.java
index ddb9a20..9f02153b 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabOverflowMenuCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabOverflowMenuCoordinator.java
@@ -389,11 +389,7 @@
         }
         // Set up callbacks for submenu navigation.
         mHierarchicalMenuController.setupCallbacks(
-                /* headerModelList= */ null,
-                modelList,
-                () -> {
-                    dismiss();
-                });
+                /* headerModelList= */ null, modelList, this::dismiss);
     }
 
     public void configureMenuItemsForTesting(ModelList modelList, T id) {
@@ -500,7 +496,7 @@
                                             : R.string.menu_new_window)
                             .withStartIconRes(isIncognitoForced ? R.drawable.ic_domain : 0)
                             .withIsIncognito(isIncognito)
-                            .withClickListener(v -> moveToNewWindow(id))
+                            .withClickListener(_ -> moveToNewWindow(id))
                             .build());
         }
         for (InstanceInfo instanceInfo : activeInstances) {
@@ -512,7 +508,7 @@
                     new ListItemBuilder()
                             .withTitle(windowDisplayName)
                             .withIsIncognito(isIncognito)
-                            .withClickListener((v) -> moveToWindow(instanceInfo, id))
+                            .withClickListener((_) -> moveToWindow(instanceInfo, id))
                             .build());
         }
         return new ListItemBuilder()
@@ -621,7 +617,7 @@
                         mId,
                         mCollaborationId,
                         getMenuWidth(rectProvider.getRect().width()),
-                        (holder) -> dismissRunnable.run(),
+                        (_) -> dismissRunnable.run(),
                         mActivity,
                         /* isFlyout= */ true);
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSearchOverlayCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSearchOverlayCoordinator.java
index e901e06..6c7f24c 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSearchOverlayCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSearchOverlayCoordinator.java
@@ -49,6 +49,7 @@
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.omnibox.BackKeyBehaviorDelegate;
 import org.chromium.chrome.browser.omnibox.LocationBarEmbedder;
+import org.chromium.chrome.browser.omnibox.UrlBar;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxLoadUrlParams;
 import org.chromium.chrome.browser.omnibox.suggestions.action.OmniboxActionDelegateImpl;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -161,8 +162,8 @@
 
         mModel = TabSearchOverlayProperties.createDefaultModel();
         mModel.set(TabSearchOverlayProperties.VISIBLE, false);
-        mModel.set(TabSearchOverlayProperties.ON_SCRIM_CLICK, (v) -> hide());
-        mModel.set(TabSearchOverlayProperties.ON_CLOSE_CLICK, (v) -> hide());
+        mModel.set(TabSearchOverlayProperties.ON_SCRIM_CLICK, (_) -> hide());
+        mModel.set(TabSearchOverlayProperties.ON_CLOSE_CLICK, (_) -> hide());
         mModel.set(TabSearchOverlayProperties.ON_HIDE_FINISHED, this::onHideFinished);
 
         mSearchBoxDataProvider = new SearchBoxDataProvider();
@@ -210,10 +211,7 @@
                                         false);
         final LinearLayout panelContainer = mPanelContainer;
         View panelView = panelContainer.findViewById(R.id.tab_search_overlay_panel);
-        panelView.addOnLayoutChangeListener(
-                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                    updateExclusionRects();
-                });
+        panelView.addOnLayoutChangeListener((_, _, _, _, _, _, _, _, _) -> updateExclusionRects());
         View searchActivityView = panelContainer.findViewById(R.id.search_activity_container);
 
         setupWindowFocusListener(panelContainer);
@@ -360,6 +358,14 @@
         var searchUiCoordinator = assumeNonNull(mSearchUiCoordinator);
         searchUiCoordinator.setDefaultStatusIconOverrideResId(R.drawable.ic_suggestion_magnifier);
 
+        // Shrink the size of the UrlBar text from the default (16sp) to medium (14sp) so the hint
+        // text fits within the given viewport given the tab search overlay has a fixed small width.
+        var locationBarCoordinator = searchUiCoordinator.getLocationBarCoordinator();
+        var urlBar = (UrlBar) locationBarCoordinator.getContainerView().findViewById(R.id.url_bar);
+        if (urlBar != null) {
+            urlBar.setTextAppearance(R.style.TextAppearance_TextMedium);
+        }
+
         // If the profile supplier is null (rare), default to the non-incognito state as it is the
         // safest choice in terms of incognito agnostic wording.
         boolean isIncognito =
@@ -368,8 +374,7 @@
                 isIncognito
                         ? R.string.hub_search_empty_hint_incognito
                         : R.string.hub_search_empty_hint;
-        searchUiCoordinator
-                .getLocationBarCoordinator()
+        locationBarCoordinator
                 .getUrlBarCoordinator()
                 .setUrlBarHintText(mActivity.getResources().getString(hintTextRes));
     }
@@ -738,10 +743,6 @@
 
     // Testing methods.
 
-    @Nullable SearchUiCoordinator getSearchUiCoordinatorForTesting() {
-        return mSearchUiCoordinator;
-    }
-
     @Nullable LinearLayout getPanelContainerForTesting() {
         return mPanelContainer;
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java
index 970fdb2d..dd648af 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java
@@ -73,13 +73,12 @@
                         });
             } else {
                 button.setOnClickListener(
-                        v -> {
-                            model.get(TabProperties.TAB_CLICK_LISTENER)
-                                    .run(
-                                            v,
-                                            model.get(TabProperties.TAB_ID),
-                                            /* triggeringMotion= */ null);
-                        });
+                        (View v) ->
+                                model.get(TabProperties.TAB_CLICK_LISTENER)
+                                        .run(
+                                                v,
+                                                model.get(TabProperties.TAB_ID),
+                                                /* triggeringMotion= */ null));
             }
             setContentDescription(view, model);
         } else if (TabProperties.FAVICON_FETCHER == propertyKey) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPane.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPane.java
index 4cc0f02..60e462b 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPane.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPane.java
@@ -284,9 +284,8 @@
             OneshotSupplier<MonotonicObservableSupplier<Boolean>> wrappedSupplier =
                     newValue.getIsScrollingSupplier();
             wrappedSupplier.onAvailable(
-                    supplier -> {
-                        supplier.addSyncObserverAndPostIfNonNull(mScrollingObserver);
-                    });
+                    (MonotonicObservableSupplier<Boolean> supplier) ->
+                            supplier.addSyncObserverAndPostIfNonNull(mScrollingObserver));
         }
     }
 
@@ -302,7 +301,7 @@
             return;
         }
         mPriceAnnotationsPrefListener =
-                (sharedPrefs, key) -> {
+                (_, key) -> {
                     if (!PriceTrackingUtilities.TRACK_PRICES_ON_TABS.equals(key)
                             || !mIsVisibleSupplier.get()) {
                         return;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
index 93c00ee..01f547a 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
@@ -72,7 +72,6 @@
 import org.chromium.chrome.browser.user_education.UserEducationHelper;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.styles.ChromeColors;
-import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController.MenuOrKeyboardActionHandler;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.ui.base.DeviceFormFactor;
 
@@ -197,27 +196,24 @@
         mBottomBarHeight = BottomBarUtils.getBottomBarHeight(context);
 
         mMenuOrKeyboardActionHandler =
-                new MenuOrKeyboardActionHandler() {
-                    @Override
-                    public boolean handleMenuOrKeyboardAction(int id, boolean fromMenu) {
-                        if (id == R.id.menu_select_tabs) {
-                            @Nullable TabSwitcherPaneCoordinator coordinator =
-                                    mTabSwitcherPaneCoordinatorSupplier.get();
-                            if (coordinator == null) return false;
+                (int id, boolean _) -> {
+                    if (id == R.id.menu_select_tabs) {
+                        @Nullable TabSwitcherPaneCoordinator coordinator =
+                                mTabSwitcherPaneCoordinatorSupplier.get();
+                        if (coordinator == null) return false;
 
-                            coordinator.showTabListEditor();
-                            RecordUserAction.record("MobileMenuSelectTabs");
-                            return true;
-                        } else if (id == R.id.new_tab_group_menu_id) {
-                            mUiFlow.newTabGroupFlow();
-                            RecordUserAction.record("MobileMenuNewTabGroup");
-                            if (mTracker != null) {
-                                mTracker.notifyEvent("tab_switcher_add_to_group_clicked");
-                            }
-                            return true;
+                        coordinator.showTabListEditor();
+                        RecordUserAction.record("MobileMenuSelectTabs");
+                        return true;
+                    } else if (id == R.id.new_tab_group_menu_id) {
+                        mUiFlow.newTabGroupFlow();
+                        RecordUserAction.record("MobileMenuNewTabGroup");
+                        if (mTracker != null) {
+                            mTracker.notifyEvent("tab_switcher_add_to_group_clicked");
                         }
-                        return false;
+                        return true;
                     }
+                    return false;
                 };
 
         mManualSearchBoxAnimationSupplier =
@@ -480,8 +476,8 @@
                         viewportRect.bottom = windowViewportRect.bottom;
                     }
 
-                    int initialLeftOffset = 0;
-                    int finalLeftOffset = 0;
+                    int initialLeftOffset;
+                    int finalLeftOffset;
                     int initialTopOffset = 0;
                     int finalTopOffset = 0;
                     if (isShrink) {
@@ -722,8 +718,7 @@
         if (mWaitForTabStateInitializedStartTimeMs != null) {
             RecordHistogram.recordTimesHistogram(
                     "Android.GridTabSwitcher.TimeToTabStateInitializedFromShown",
-                    SystemClock.elapsedRealtime()
-                            - mWaitForTabStateInitializedStartTimeMs.longValue());
+                    SystemClock.elapsedRealtime() - mWaitForTabStateInitializedStartTimeMs);
             mWaitForTabStateInitializedStartTimeMs = null;
         }
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinator.java
index 8e54698d..7efaae7 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinator.java
@@ -199,8 +199,6 @@
                 }
             };
 
-    private final TabGridItemLongPressOrchestrator.OnLongPressTabItemEventListener
-            mLongPressItemEventListener = this::onLongPressOnTabCard;
     private final Activity mActivity;
     private final ProfileProvider mProfileProvider;
     private final Callback<Boolean> mOnVisibilityChanged = this::onVisibilityChanged;
@@ -243,7 +241,6 @@
             ObservableSuppliers.createNonNull(0.0f);
     private final @Nullable ImageView mPaneHairline;
     private final @Nullable PinnedTabStripCoordinator mPinnedTabsCoordinator;
-    private final @Nullable DirectionalScrollListener mSearchBoxVisibilityScrollListener;
     private @Nullable TabGridContextMenuCoordinator mContextMenuCoordinator;
     private @Nullable TabGroupListBottomSheetCoordinator mTabGroupListBottomSheetCoordinator;
 
@@ -316,7 +313,7 @@
             Callback<@Nullable View> setOverlayViewCallback,
             @Nullable TabSwitcherDragHandler tabSwitcherDragHandler,
             SettableNonNullObservableSupplier<Boolean> hubSearchBoxVisibilitySupplier) {
-        try (TraceEvent e = TraceEvent.scoped("TabSwitcherPaneCoordinator.constructor")) {
+        try (TraceEvent _ = TraceEvent.scoped("TabSwitcherPaneCoordinator.constructor")) {
             mProfileProvider = profileProvider;
             mIsVisibleSupplier = isVisibleSupplier;
             mIsAnimatingSupplier = isAnimatingSupplier;
@@ -342,9 +339,7 @@
                             .with(MODE, mode)
                             .with(FETCH_VIEW_BY_INDEX_CALLBACK, (f) -> mFetchViewByIndex = f)
                             .with(GET_VISIBLE_RANGE_CALLBACK, (f) -> mGetVisibleIndex = f)
-                            .with(
-                                    IS_SCROLLING_SUPPLIER_CALLBACK,
-                                    (f) -> mIsScrollingSupplier.set(f))
+                            .with(IS_SCROLLING_SUPPLIER_CALLBACK, mIsScrollingSupplier::set)
                             .with(
                                     PAGE_KEY_LISTENER,
                                     event ->
@@ -463,7 +458,9 @@
                             TabListEditorCoordinator.UNLIMITED_SELECTION,
                             false);
             mTabListCoordinator = tabListCoordinator;
-            tabListCoordinator.setOnLongPressTabItemEventListener(mLongPressItemEventListener);
+            TabGridItemLongPressOrchestrator.OnLongPressTabItemEventListener
+                    longPressItemEventListener = this::onLongPressOnTabCard;
+            tabListCoordinator.setOnLongPressTabItemEventListener(longPressItemEventListener);
 
             TabListRecyclerView recyclerView = tabListCoordinator.getContainerView();
             // Create a `FrameLayout` to hold both the pinned tab strip and the regular tab
@@ -521,7 +518,7 @@
             recyclerView.setBackgroundColor(Color.TRANSPARENT);
             recyclerView.addOnScrollListener(mTabListOnScrollListener);
 
-            mSearchBoxVisibilityScrollListener =
+            @Nullable DirectionalScrollListener searchBoxVisibilityScrollListener =
                     new DirectionalScrollListener(
                             () -> {
                                 // Swipe down -> Moving towards top of list.
@@ -550,7 +547,7 @@
                             }
                         }
                     };
-            recyclerView.addOnScrollListener(mSearchBoxVisibilityScrollListener);
+            recyclerView.addOnScrollListener(searchBoxVisibilityScrollListener);
             recyclerView.addOnScrollListener(scrollStateChangedListener);
 
             // TODO(agrieve): mTabModelSupplier can be changed to be NonNull assuming
@@ -772,7 +769,7 @@
 
     /** Post native initialization. */
     public void initWithNative() {
-        try (TraceEvent e = TraceEvent.scoped("TabSwitcherPaneCoordinator.initWithNative")) {
+        try (TraceEvent _ = TraceEvent.scoped("TabSwitcherPaneCoordinator.initWithNative")) {
             Profile originalProfile = mProfileProvider.getOriginalProfile();
             mTabListCoordinator.initWithNative(originalProfile);
             mMultiThumbnailCardProvider.initWithNative(originalProfile);
@@ -1147,10 +1144,6 @@
         return mPinnedTabsCoordinator;
     }
 
-    public @Nullable DirectionalScrollListener getDirectionalScrollListenerForTesting() {
-        return mSearchBoxVisibilityScrollListener;
-    }
-
     /* package */ @Nullable TabGridDialogCoordinator getTabGridDialogCoordinatorForTesting() {
         return mTabGridDialogCoordinator;
     }
@@ -1218,7 +1211,8 @@
                         tabModel,
                         mBottomSheetController,
                         /* supportsShowNewGroup= */ true,
-                        /* destroyOnHide= */ false);
+                        /* destroyOnHide= */ false,
+                        /* windowAndroid= */ null);
 
         ShowTabListEditor showTabListEditor =
                 tabId -> {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediator.java
index e73cd90..0ac603d 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneMediator.java
@@ -129,9 +129,7 @@
     private final Callback<Boolean> mOnAnimatingChanged = this::onAnimatingChanged;
     private final Callback<Boolean> mOnVisibilityChanged = this::onVisibilityChanged;
     private final Callback<Boolean> mNotifyBackPressedCallback =
-            ignored -> {
-                notifyBackPressStateChangedInternal();
-            };
+            _ -> notifyBackPressStateChangedInternal();
 
     /** Interface for getting scroll positions of tabs. */
     @FunctionalInterface
@@ -454,7 +452,7 @@
 
     public void openTabGroupDialog(int tabId) {
         List<Tab> relatedTabs = assumeNonNull(mTabModelSupplier.get()).getRelatedTabList(tabId);
-        if (relatedTabs.size() == 0) {
+        if (relatedTabs.isEmpty()) {
             relatedTabs = null;
         }
         assumeNonNull(mTabGridDialogControllerSupplier.get()).resetWithListOfTabs(relatedTabs);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarControllerUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarControllerUnitTest.java
index 95a244d9..2f5c278 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarControllerUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarControllerUnitTest.java
@@ -33,7 +33,6 @@
 import org.chromium.base.supplier.ObservableSuppliers;
 import org.chromium.base.supplier.SettableMonotonicObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.tab.Tab;
@@ -53,7 +52,6 @@
 
 /** Unit tests for {@link UndoGroupSnackbarController}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 @NullMarked
 public class UndoGroupSnackbarControllerUnitTest {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/archived_tabs_auto_delete_promo/ArchivedTabsAutoDeletePromoCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/archived_tabs_auto_delete_promo/ArchivedTabsAutoDeletePromoCoordinator.java
index e70dd30..39a9335 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/archived_tabs_auto_delete_promo/ArchivedTabsAutoDeletePromoCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/archived_tabs_auto_delete_promo/ArchivedTabsAutoDeletePromoCoordinator.java
@@ -83,15 +83,11 @@
 
         mModel.set(
                 ArchivedTabsAutoDeletePromoProperties.ON_YES_BUTTON_CLICK_LISTENER,
-                (v) -> {
-                    onPromoChoice(UserChoice.YES);
-                });
+                _ -> onPromoChoice(UserChoice.YES));
 
         mModel.set(
                 ArchivedTabsAutoDeletePromoProperties.ON_NO_BUTTON_CLICK_LISTENER,
-                (v) -> {
-                    onPromoChoice(UserChoice.NO);
-                });
+                _ -> onPromoChoice(UserChoice.NO));
     }
 
     /** Cleans up resources. */
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/color_picker/TabGroupColorPickerItemViewBinderUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/color_picker/TabGroupColorPickerItemViewBinderUnitTest.java
index 5ca6f9b..212d7d8 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/color_picker/TabGroupColorPickerItemViewBinderUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/color_picker/TabGroupColorPickerItemViewBinderUnitTest.java
@@ -70,9 +70,7 @@
                         TabGroupColorId.BLUE,
                         TabGroupColorPickerType.TAB_GROUP,
                         false,
-                        () -> {
-                            mModel.set(IS_SELECTED, !mModel.get(IS_SELECTED));
-                        },
+                        () -> mModel.set(IS_SELECTED, !mModel.get(IS_SELECTED)),
                         false,
                         /* itemIndex= */ 0);
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/color_picker/TabGroupColorPickerMediatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/color_picker/TabGroupColorPickerMediatorUnitTest.java
index 86edeea..8112d1eb 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/color_picker/TabGroupColorPickerMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/color_picker/TabGroupColorPickerMediatorUnitTest.java
@@ -52,9 +52,7 @@
                             /* color= */ color,
                             /* colorPickerType= */ TabGroupColorPickerType.TAB_GROUP,
                             /* isIncognito= */ false,
-                            /* onClickListener= */ () -> {
-                                mMediator.setSelectedColorItem(color);
-                            },
+                            /* onClickListener= */ () -> mMediator.setSelectedColorItem(color),
                             /* isSelected= */ false,
                             /* itemIndex= */ i);
             mColorItems.add(model);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/pinned_tabs_strip/PinnedTabStripItemView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/pinned_tabs_strip/PinnedTabStripItemView.java
index 45b4cf2..2390c36 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/pinned_tabs_strip/PinnedTabStripItemView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/pinned_tabs_strip/PinnedTabStripItemView.java
@@ -71,10 +71,7 @@
         }
 
         mFavicon.setVisibility(View.VISIBLE);
-        fetcher.fetch(
-                tabFavicon -> {
-                    setFavicon(tabFavicon, isSelected);
-                });
+        fetcher.fetch((TabFavicon tabFavicon) -> setFavicon(tabFavicon, isSelected));
     }
 
     /**
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/pinned_tabs_strip/PinnedTabStripItemViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/pinned_tabs_strip/PinnedTabStripItemViewBinder.java
index fb6bcf57..5e7eca3b 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/pinned_tabs_strip/PinnedTabStripItemViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/pinned_tabs_strip/PinnedTabStripItemViewBinder.java
@@ -39,10 +39,11 @@
             TabActionListener listener = model.get(TabProperties.TAB_CLICK_LISTENER);
             if (listener == null) return;
             view.setOnClickListener(
-                    v -> {
-                        listener.run(
-                                v, model.get(TabProperties.TAB_ID), /* triggeringMotion= */ null);
-                    });
+                    (View v) ->
+                            listener.run(
+                                    v,
+                                    model.get(TabProperties.TAB_ID),
+                                    /* triggeringMotion= */ null));
         } else if (TabProperties.TAB_CONTEXT_CLICK_LISTENER == propertyKey) {
             itemView.setNullableContextClickListener(
                     model.get(TabProperties.TAB_CONTEXT_CLICK_LISTENER),
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/pinned_tabs_strip/PinnedTabStripMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/pinned_tabs_strip/PinnedTabStripMediator.java
index 6e17f3d..fb862cc3 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/pinned_tabs_strip/PinnedTabStripMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/pinned_tabs_strip/PinnedTabStripMediator.java
@@ -458,7 +458,8 @@
                             newTabModel,
                             mBottomSheetController,
                             /* supportsShowNewGroup= */ true,
-                            /* destroyOnHide= */ false);
+                            /* destroyOnHide= */ false,
+                            /* windowAndroid= */ null);
             mContextMenuCoordinator =
                     PinnedTabStripItemContextMenuCoordinator.createContextMenuCoordinator(
                             mActivity,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/TabVerticalViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/TabVerticalViewBinder.java
index 70cd38e..9a19acd 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/TabVerticalViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/TabVerticalViewBinder.java
@@ -97,27 +97,11 @@
             updateIcons(model, view);
         } else if (TabProperties.TAB_GROUP_ID == propertyKey) {
             updateChildRowPadding(model, view);
-        } else if (TabProperties.CONTENT_DESCRIPTION_TEXT_RESOLVER == propertyKey) {
-            TabListViewBinderUtils.updateContentDescription(model, view);
-        } else if (TabProperties.ACCESSIBILITY_DELEGATE == propertyKey) {
-            view.setAccessibilityDelegate(model.get(TabProperties.ACCESSIBILITY_DELEGATE));
         } else if (TabProperties.ACTION_BUTTON_DESCRIPTION_TEXT_RESOLVER == propertyKey) {
             @Nullable View actionButton = view.findViewById(R.id.action_button);
             if (actionButton != null) {
                 TabListViewBinderUtils.updateActionButtonContentDescription(model, actionButton);
             }
-        } else if (TabProperties.MEDIA_INDICATOR == propertyKey) {
-            ImageView mediaIndicator = view.findViewById(R.id.media_indicator_icon);
-            if (mediaIndicator != null) {
-                @MediaState int mediaState = model.get(TabProperties.MEDIA_INDICATOR);
-                if (mediaState != MediaState.NONE) {
-                    mediaIndicator.setImageResource(TabUtils.getMediaIndicatorDrawable(mediaState));
-                }
-            }
-            updateIcons(model, view);
-        } else if (TabProperties.ACTOR_UI_STATE == propertyKey) {
-            TabListViewBinderUtils.setupActorIndicator(model, view);
-            updateIcons(model, view);
         } else if (TabProperties.IS_GLIC_ACTIVE == propertyKey) {
             boolean isGlicActive = TabListViewBinderUtils.setupGlicIndicator(model, view);
             updateGlicIndicatorBar(isGlicActive, view);
@@ -160,7 +144,9 @@
         }
 
         if (TabProperties.TITLE == propertyKey) {
-            view.setContentDescription(model.get(TabProperties.TITLE));
+            if (model.get(TabProperties.CONTENT_DESCRIPTION_TEXT_RESOLVER) == null) {
+                view.setContentDescription(model.get(TabProperties.TITLE));
+            }
         } else if (TabProperties.IS_SELECTED == propertyKey
                 || TabProperties.IS_INCOGNITO == propertyKey) {
             updatePinnedColors(model, view);
@@ -196,7 +182,6 @@
                 || TabProperties.IS_INCOGNITO == propertyKey) {
             updateGroupHeaderColors(model, view);
         } else if (TabProperties.CONTENT_DESCRIPTION_TEXT_RESOLVER == propertyKey) {
-            TabListViewBinderUtils.updateContentDescription(model, view);
             updateAccessibilityDelegate(model, view);
         } else if (TabProperties.IS_COLLAPSED == propertyKey) {
             updateChevronRotation(model, view);
@@ -240,6 +225,16 @@
         } else if (TabProperties.TAB_CONTEXT_CLICK_LISTENER == propertyKey) {
             TabListViewBinderUtils.setNullableContextClickListener(
                     model.get(TabProperties.TAB_CONTEXT_CLICK_LISTENER), view, model);
+        } else if (TabProperties.MEDIA_INDICATOR == propertyKey) {
+            updateMediaIndicator(model, view);
+            updateIcons(model, view);
+        } else if (TabProperties.ACTOR_UI_STATE == propertyKey) {
+            TabListViewBinderUtils.setupActorIndicator(model, view);
+            updateIcons(model, view);
+        } else if (TabProperties.CONTENT_DESCRIPTION_TEXT_RESOLVER == propertyKey) {
+            TabListViewBinderUtils.updateContentDescription(model, view);
+        } else if (TabProperties.ACCESSIBILITY_DELEGATE == propertyKey) {
+            view.setAccessibilityDelegate(model.get(TabProperties.ACCESSIBILITY_DELEGATE));
         }
     }
 
@@ -259,10 +254,11 @@
         updateIcons(model, view, view.isHovered());
     }
 
-    // TODO(crbug.com/527641177): Add icons for pinned tab and check priorities.
     private static void updateIcons(PropertyModel model, ViewGroup view, boolean isHovered) {
         boolean isRailCollapsed =
                 model.get(TabProperties.RAIL_COLLAPSE_STATE) == RailCollapseState.COLLAPSED;
+        boolean isPinned = TabProperties.isPinnedTab(model);
+        boolean isIconCompact = isRailCollapsed || isPinned;
         boolean isSelected = model.get(TabProperties.IS_SELECTED);
 
         View actionButton = view.findViewById(R.id.action_button);
@@ -279,7 +275,7 @@
         boolean actionWanted =
                 actionButton != null
                         && actionData != null
-                        && (isRailCollapsed
+                        && (isIconCompact
                                 ? (isSelected && (!DeviceInfo.isDesktop() || isHovered))
                                 : (!DeviceInfo.isDesktop() || isSelected || isHovered));
         @Nullable UiTabState actorState = model.get(TabProperties.ACTOR_UI_STATE);
@@ -299,7 +295,7 @@
         // 2. Apply priority rules for collapsed state.
         // Priority: Close > Recording/Sharing Media > AI Actuation > Standard Media >
         // Loading/Favicon.
-        if (isRailCollapsed) {
+        if (isIconCompact) {
             boolean isRecordingOrSharing =
                     mediaState == MediaState.RECORDING || mediaState == MediaState.SHARING;
             boolean recordingOrSharingWanted = mediaWanted && isRecordingOrSharing;
@@ -330,7 +326,7 @@
         if (actionButton != null) {
             updateViewConstraints(
                     actionButton,
-                    isRailCollapsed,
+                    isIconCompact,
                     UNSET,
                     UNSET,
                     PARENT_ID,
@@ -347,7 +343,7 @@
             updateActorAnimations(model, actuationSpark, actuationSpinner, actorActuationWanted);
             updateViewConstraints(
                     actuationSpark,
-                    isRailCollapsed,
+                    isIconCompact,
                     UNSET,
                     R.id.media_indicator_icon,
                     UNSET,
@@ -359,7 +355,7 @@
         if (mediaIndicator != null) {
             updateViewConstraints(
                     mediaIndicator,
-                    isRailCollapsed,
+                    isIconCompact,
                     UNSET,
                     R.id.action_button,
                     UNSET,
@@ -373,7 +369,7 @@
         if (faviconContainer != null && (loadingWanted || faviconWanted)) {
             updateViewConstraints(
                     faviconContainer,
-                    isRailCollapsed,
+                    isIconCompact,
                     PARENT_ID,
                     UNSET,
                     UNSET,
@@ -448,6 +444,28 @@
         glicIndicatorView.setVisibility(isGlicActive ? View.VISIBLE : View.GONE);
     }
 
+    /**
+     * Updates the media indicator icon drawable based on the current media state of the tab.
+     *
+     * @param model the model containing the tab properties.
+     * @param view the root ViewGroup representing the tab row item.
+     */
+    private static void updateMediaIndicator(PropertyModel model, ViewGroup view) {
+        ImageView mediaIndicator = view.findViewById(R.id.media_indicator_icon);
+        if (mediaIndicator != null) {
+            @MediaState int mediaState = model.get(TabProperties.MEDIA_INDICATOR);
+            if (mediaState != MediaState.NONE) {
+                mediaIndicator.setImageResource(TabUtils.getMediaIndicatorDrawable(mediaState));
+            }
+            boolean isIncognito = isIncognito(model);
+            boolean isSelected = model.get(TabProperties.IS_SELECTED);
+            ImageViewCompat.setImageTintList(
+                    mediaIndicator,
+                    TabCardThemeUtil.getMediaIndicatorColorStateList(
+                            mediaIndicator.getContext(), isIncognito, isSelected));
+        }
+    }
+
     private static void updateActorAnimations(
             PropertyModel model,
             View actuationSpark,
@@ -495,13 +513,13 @@
 
     private static void updateViewConstraints(
             View view,
-            boolean isRailCollapsed,
+            boolean isIconCompact,
             int startToStart,
             int endToStart,
             int endToEnd,
             int marginStartDimenId,
             int marginEndDimenId) {
-        if (isRailCollapsed) {
+        if (isIconCompact) {
             configureConstraints(
                     view,
                     /* startToStart= */ PARENT_ID,
@@ -586,6 +604,7 @@
                     actionButton, getActionButtonTintList(context, isSelected, isIncognito));
         }
         updateFaviconImage(model, view);
+        updateMediaIndicator(model, view);
         setupTabHoverListener(
                 model,
                 view,
@@ -627,6 +646,7 @@
             ViewCompat.setBackgroundTintList(view, tintList);
         }
         updateFaviconImage(model, view);
+        updateMediaIndicator(model, view);
         setupTabHoverListener(model, view, defaultBackgroundColor);
     }
 
@@ -775,8 +795,13 @@
                             .getDimensionPixelSize(R.dimen.vertical_tab_child_nesting_margin);
         }
 
+        boolean isPinned = TabProperties.isPinnedTab(model);
         int marginBottom =
-                view.getResources().getDimensionPixelSize(R.dimen.vertical_tab_item_margin_bottom);
+                view.getResources()
+                        .getDimensionPixelSize(
+                                isPinned
+                                        ? R.dimen.vertical_tab_pinned_item_margin_bottom
+                                        : R.dimen.vertical_tab_item_margin_bottom);
 
         if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams params) {
             if (params.getMarginStart() != marginStart || params.bottomMargin != marginBottom) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/TabVerticalViewBinderUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/TabVerticalViewBinderUnitTest.java
index 3af4d6eb1..f248c21e 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/TabVerticalViewBinderUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/TabVerticalViewBinderUnitTest.java
@@ -18,11 +18,13 @@
 
 import android.animation.ObjectAnimator;
 import android.app.Activity;
+import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
+import android.view.ContextThemeWrapper;
 import android.view.InputDevice;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -80,6 +82,7 @@
     private static final String TEST_TITLE = "Google Website";
     private static final String TEST_DESCRIPTION = "Normal Tab Description";
     private static final String TEST_ACCESSIBILITY_DESCRIPTION = "Accessibility Tab Description";
+    private static final int NON_TABLET_WIDTH_DP = 320;
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Mock private TabActionListener mCloseListener;
@@ -1410,58 +1413,38 @@
 
     @Test
     @SmallTest
-    public void testBindPinnedTab_RailCollapsed() {
-        ViewGroup pinnedView = inflatePinnedTabView();
-        pinnedView.setLayoutParams(
-                new ViewGroup.MarginLayoutParams(
-                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-
-        mModel.set(TabProperties.RAIL_COLLAPSE_STATE, RailCollapseState.COLLAPSED);
-
-        TabVerticalViewBinder.bindPinnedTab(mModel, pinnedView, TabProperties.RAIL_COLLAPSE_STATE);
-
-        int expectedSize =
-                pinnedView
-                        .getResources()
-                        .getDimensionPixelSize(R.dimen.vertical_tab_item_collapsed_size);
-        assertEquals(expectedSize, pinnedView.getLayoutParams().width);
-        assertEquals(expectedSize, pinnedView.getLayoutParams().height);
-
-        // Verify padding is collapsed margin
-        ViewGroup.MarginLayoutParams lp =
-                (ViewGroup.MarginLayoutParams) pinnedView.getLayoutParams();
-        int expectedMargin =
-                TabVerticalViewBinder.getCollapsedChildMarginStart(pinnedView.getContext());
-        assertEquals(expectedMargin, lp.getMarginStart());
+    public void testBindPinnedTab_RailCollapsed_Tablet() {
+        verifyBindPinnedTab_RailCollapsed(mActivity);
     }
 
     @Test
     @SmallTest
-    public void testBindPinnedTab_RailExpanded() {
-        ViewGroup pinnedView = inflatePinnedTabView();
-        pinnedView.setLayoutParams(
-                new ViewGroup.MarginLayoutParams(
-                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+    public void testBindPinnedTab_RailCollapsed_NonTablet() {
+        Configuration config = new Configuration(mActivity.getResources().getConfiguration());
+        config.smallestScreenWidthDp = NON_TABLET_WIDTH_DP;
+        Context nonTabletContext =
+                new ContextThemeWrapper(
+                        mActivity.createConfigurationContext(config),
+                        R.style.Theme_BrowserUI_DayNight);
+        verifyBindPinnedTab_RailCollapsed(nonTabletContext);
+    }
 
-        mModel.set(TabProperties.RAIL_COLLAPSE_STATE, RailCollapseState.EXPANDED);
+    @Test
+    @SmallTest
+    public void testBindPinnedTab_RailExpanded_Tablet() {
+        verifyBindPinnedTab_RailExpanded(mActivity);
+    }
 
-        TabVerticalViewBinder.bindPinnedTab(mModel, pinnedView, TabProperties.RAIL_COLLAPSE_STATE);
-
-        int expectedWidth =
-                pinnedView
-                        .getResources()
-                        .getDimensionPixelSize(R.dimen.vertical_tab_pinned_item_width);
-        int expectedHeight =
-                pinnedView
-                        .getResources()
-                        .getDimensionPixelSize(R.dimen.vertical_tab_pinned_item_height);
-        assertEquals(expectedWidth, pinnedView.getLayoutParams().width);
-        assertEquals(expectedHeight, pinnedView.getLayoutParams().height);
-
-        // Verify padding is 0
-        ViewGroup.MarginLayoutParams lp =
-                (ViewGroup.MarginLayoutParams) pinnedView.getLayoutParams();
-        assertEquals(0, lp.getMarginStart());
+    @Test
+    @SmallTest
+    public void testBindPinnedTab_RailExpanded_NonTablet() {
+        Configuration config = new Configuration(mActivity.getResources().getConfiguration());
+        config.smallestScreenWidthDp = NON_TABLET_WIDTH_DP;
+        Context nonTabletContext =
+                new ContextThemeWrapper(
+                        mActivity.createConfigurationContext(config),
+                        R.style.Theme_BrowserUI_DayNight);
+        verifyBindPinnedTab_RailExpanded(nonTabletContext);
     }
 
     @Test
@@ -1637,52 +1620,101 @@
 
     @Test
     @SmallTest
-    public void testTabItemDimensions_TabletVsNonTablet() {
+    public void testIconPriorities_PinnedTab() {
+        ViewGroup pinnedView = inflatePinnedTabView();
+        mModel.set(TabProperties.IS_PINNED, true);
+
+        // Setup favicon fetcher.
+        mModel.set(TabProperties.FAVICON_FETCHER, mFaviconFetcher);
+
+        // Setup all other icons to be active.
+        mModel.set(TabProperties.IS_LOADING, true);
+        mModel.set(TabProperties.MEDIA_INDICATOR, MediaState.RECORDING);
+        mModel.set(
+                TabProperties.ACTOR_UI_STATE,
+                new UiTabState(0, null, null, TabIndicatorStatus.DYNAMIC, false));
+        mModel.set(TabProperties.IS_SELECTED, true);
+
+        // Bind all properties.
+        TabVerticalViewBinder.bindPinnedTab(mModel, pinnedView, TabProperties.FAVICON_FETCHER);
+        TabVerticalViewBinder.bindPinnedTab(mModel, pinnedView, TabProperties.IS_LOADING);
+        TabVerticalViewBinder.bindPinnedTab(mModel, pinnedView, TabProperties.MEDIA_INDICATOR);
+        TabVerticalViewBinder.bindPinnedTab(mModel, pinnedView, TabProperties.ACTOR_UI_STATE);
+        TabVerticalViewBinder.bindPinnedTab(mModel, pinnedView, TabProperties.IS_SELECTED);
+
+        View spinner = pinnedView.findViewById(R.id.tab_loading_spinner);
+        View faviconView = pinnedView.findViewById(R.id.tab_favicon);
+        View mediaIndicatorView = pinnedView.findViewById(R.id.media_indicator_icon);
+        View actuationSparkView = pinnedView.findViewById(R.id.actuation_spark);
+
+        // --- Priority 1: Recording/Sharing Media Indicator ---
+        assertEquals(View.GONE, actuationSparkView.getVisibility());
+        assertEquals(View.VISIBLE, mediaIndicatorView.getVisibility());
+        assertEquals(View.GONE, spinner.getVisibility());
+        assertEquals(View.GONE, faviconView.getVisibility());
+
+        // --- Priority 2: AI Actuation Indicator ---
+        // Change Media to Standard (Audible) so AI actuation takes priority.
+        mModel.set(TabProperties.MEDIA_INDICATOR, MediaState.AUDIBLE);
+        TabVerticalViewBinder.bindPinnedTab(mModel, pinnedView, TabProperties.MEDIA_INDICATOR);
+
+        assertEquals(View.VISIBLE, actuationSparkView.getVisibility());
+        assertEquals(View.GONE, mediaIndicatorView.getVisibility());
+        assertEquals(View.GONE, spinner.getVisibility());
+        assertEquals(View.GONE, faviconView.getVisibility());
+
+        // --- Priority 3: Standard Media Indicator ---
+        // Disable AI actuation.
+        mModel.set(
+                TabProperties.ACTOR_UI_STATE,
+                new UiTabState(0, null, null, TabIndicatorStatus.NONE, false));
+        TabVerticalViewBinder.bindPinnedTab(mModel, pinnedView, TabProperties.ACTOR_UI_STATE);
+
+        assertEquals(View.GONE, actuationSparkView.getVisibility());
+        assertEquals(View.VISIBLE, mediaIndicatorView.getVisibility());
+        assertEquals(View.GONE, spinner.getVisibility());
+        assertEquals(View.GONE, faviconView.getVisibility());
+
+        // --- Priority 4: Loading Spinner ---
+        // Disable Media.
+        mModel.set(TabProperties.MEDIA_INDICATOR, MediaState.NONE);
+        TabVerticalViewBinder.bindPinnedTab(mModel, pinnedView, TabProperties.MEDIA_INDICATOR);
+
+        assertEquals(View.GONE, actuationSparkView.getVisibility());
+        assertEquals(View.GONE, mediaIndicatorView.getVisibility());
+        assertEquals(View.VISIBLE, spinner.getVisibility());
+        assertEquals(View.GONE, faviconView.getVisibility());
+
+        // --- Priority 5: Favicon ---
+        // Disable Loading.
+        mModel.set(TabProperties.IS_LOADING, false);
+        TabVerticalViewBinder.bindPinnedTab(mModel, pinnedView, TabProperties.IS_LOADING);
+
+        assertEquals(View.GONE, actuationSparkView.getVisibility());
+        assertEquals(View.GONE, mediaIndicatorView.getVisibility());
+        assertEquals(View.GONE, spinner.getVisibility());
+        assertEquals(View.VISIBLE, faviconView.getVisibility());
+    }
+
+    @Test
+    @SmallTest
+    public void testResourceQualifiers_TabletVsNonTablet() {
         // Touch tablet device (default in setUp, loads values-sw600dp)
-        int expectedTouchPadding =
-                mActivity
-                        .getResources()
-                        .getDimensionPixelSize(R.dimen.vertical_tab_item_padding_vertical);
-        int expectedTouchInset =
-                mActivity
-                        .getResources()
-                        .getDimensionPixelSize(R.dimen.vertical_tab_item_touch_target_inset);
         int expectedTouchHeight =
                 mActivity.getResources().getDimensionPixelSize(R.dimen.vertical_tab_item_height);
         int expectedTouchMarginBottom =
                 mActivity
                         .getResources()
                         .getDimensionPixelSize(R.dimen.vertical_tab_item_margin_bottom);
-        int expectedTouchPinnedHeight =
-                mActivity
-                        .getResources()
-                        .getDimensionPixelSize(R.dimen.vertical_tab_pinned_item_height);
-        int expectedTouchCollapsedSize =
-                mActivity
-                        .getResources()
-                        .getDimensionPixelSize(R.dimen.vertical_tab_item_collapsed_size);
 
-        assertEquals(2, expectedTouchPadding);
-        assertEquals(2, expectedTouchInset);
         assertEquals(40, expectedTouchHeight);
         assertEquals(0, expectedTouchMarginBottom);
-        assertEquals(36, expectedTouchPinnedHeight);
-        assertEquals(36, expectedTouchCollapsedSize);
 
         // Non-tablet device (loads values)
-        android.content.res.Configuration config =
-                new android.content.res.Configuration(mActivity.getResources().getConfiguration());
-        config.smallestScreenWidthDp = 320;
-        android.content.Context nonTabletContext = mActivity.createConfigurationContext(config);
+        Configuration config = new Configuration(mActivity.getResources().getConfiguration());
+        config.smallestScreenWidthDp = NON_TABLET_WIDTH_DP;
+        Context nonTabletContext = mActivity.createConfigurationContext(config);
 
-        int expectedDefaultPadding =
-                nonTabletContext
-                        .getResources()
-                        .getDimensionPixelSize(R.dimen.vertical_tab_item_padding_vertical);
-        int expectedDefaultInset =
-                nonTabletContext
-                        .getResources()
-                        .getDimensionPixelSize(R.dimen.vertical_tab_item_touch_target_inset);
         int expectedDefaultHeight =
                 nonTabletContext
                         .getResources()
@@ -1691,21 +1723,9 @@
                 nonTabletContext
                         .getResources()
                         .getDimensionPixelSize(R.dimen.vertical_tab_item_margin_bottom);
-        int expectedDefaultPinnedHeight =
-                nonTabletContext
-                        .getResources()
-                        .getDimensionPixelSize(R.dimen.vertical_tab_pinned_item_height);
-        int expectedDefaultCollapsedSize =
-                nonTabletContext
-                        .getResources()
-                        .getDimensionPixelSize(R.dimen.vertical_tab_item_collapsed_size);
 
-        assertEquals(0, expectedDefaultPadding);
-        assertEquals(0, expectedDefaultInset);
         assertEquals(32, expectedDefaultHeight);
         assertEquals(4, expectedDefaultMarginBottom);
-        assertEquals(32, expectedDefaultPinnedHeight);
-        assertEquals(32, expectedDefaultCollapsedSize);
     }
 
     @Test
@@ -1764,6 +1784,68 @@
         assertEquals(expectedTouchPinnedHeight, pinnedView.getLayoutParams().height);
     }
 
+    private void verifyBindPinnedTab_RailCollapsed(Context context) {
+        ViewGroup pinnedView =
+                (ViewGroup)
+                        LayoutInflater.from(context)
+                                .inflate(R.layout.vertical_tab_pinned_item, null, false);
+        pinnedView.setLayoutParams(
+                new ViewGroup.MarginLayoutParams(
+                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+        mModel.set(TabProperties.IS_PINNED, true);
+        mModel.set(TabProperties.RAIL_COLLAPSE_STATE, RailCollapseState.COLLAPSED);
+
+        TabVerticalViewBinder.bindPinnedTab(mModel, pinnedView, TabProperties.RAIL_COLLAPSE_STATE);
+
+        int expectedSize =
+                context.getResources()
+                        .getDimensionPixelSize(R.dimen.vertical_tab_item_collapsed_size);
+        assertEquals(expectedSize, pinnedView.getLayoutParams().width);
+        assertEquals(expectedSize, pinnedView.getLayoutParams().height);
+
+        ViewGroup.MarginLayoutParams lp =
+                (ViewGroup.MarginLayoutParams) pinnedView.getLayoutParams();
+        int expectedMarginStart = TabVerticalViewBinder.getCollapsedChildMarginStart(context);
+        assertEquals(expectedMarginStart, lp.getMarginStart());
+
+        int expectedMarginBottom =
+                context.getResources()
+                        .getDimensionPixelSize(R.dimen.vertical_tab_pinned_item_margin_bottom);
+        assertEquals(expectedMarginBottom, lp.bottomMargin);
+    }
+
+    private void verifyBindPinnedTab_RailExpanded(Context context) {
+        ViewGroup pinnedView =
+                (ViewGroup)
+                        LayoutInflater.from(context)
+                                .inflate(R.layout.vertical_tab_pinned_item, null, false);
+        pinnedView.setLayoutParams(
+                new ViewGroup.MarginLayoutParams(
+                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+        mModel.set(TabProperties.IS_PINNED, true);
+        mModel.set(TabProperties.RAIL_COLLAPSE_STATE, RailCollapseState.EXPANDED);
+
+        TabVerticalViewBinder.bindPinnedTab(mModel, pinnedView, TabProperties.RAIL_COLLAPSE_STATE);
+
+        int expectedWidth =
+                context.getResources()
+                        .getDimensionPixelSize(R.dimen.vertical_tab_pinned_item_width);
+        int expectedHeight =
+                context.getResources()
+                        .getDimensionPixelSize(R.dimen.vertical_tab_pinned_item_height);
+        assertEquals(expectedWidth, pinnedView.getLayoutParams().width);
+        assertEquals(expectedHeight, pinnedView.getLayoutParams().height);
+
+        ViewGroup.MarginLayoutParams lp =
+                (ViewGroup.MarginLayoutParams) pinnedView.getLayoutParams();
+        assertEquals(0, lp.getMarginStart());
+
+        int expectedMarginBottom =
+                context.getResources()
+                        .getDimensionPixelSize(R.dimen.vertical_tab_pinned_item_margin_bottom);
+        assertEquals(expectedMarginBottom, lp.bottomMargin);
+    }
+
     private ViewGroup inflatePinnedTabView() {
         return (ViewGroup)
                 LayoutInflater.from(mActivity)
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabListCoordinator.java
index 5beb9be..b41bdba 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabListCoordinator.java
@@ -1160,7 +1160,8 @@
 
         Resources res = mContainerView.getContext().getResources();
         int itemWidth = res.getDimensionPixelSize(R.dimen.vertical_tab_pinned_item_width);
-        int minHorizontalGap = res.getDimensionPixelSize(R.dimen.vertical_tab_item_margin_bottom);
+        int minHorizontalGap =
+                res.getDimensionPixelSize(R.dimen.vertical_tab_pinned_item_margin_bottom);
         if (itemWidth <= 0) return DEFAULT_GRID_SPAN_COUNT;
 
         int calculatedSpans =
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabListCoordinatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabListCoordinatorUnitTest.java
index 22e9eb9b..b314164 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabListCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabListCoordinatorUnitTest.java
@@ -1546,7 +1546,7 @@
         int itemMarginPx =
                 mActivity
                         .getResources()
-                        .getDimensionPixelSize(R.dimen.vertical_tab_item_margin_bottom);
+                        .getDimensionPixelSize(R.dimen.vertical_tab_pinned_item_margin_bottom);
         int testWidthPx =
                 itemWidthPx * 2
                         + itemMarginPx
@@ -1588,7 +1588,7 @@
         int itemMarginPx =
                 mActivity
                         .getResources()
-                        .getDimensionPixelSize(R.dimen.vertical_tab_item_margin_bottom);
+                        .getDimensionPixelSize(R.dimen.vertical_tab_pinned_item_margin_bottom);
         int testWidthPx =
                 itemWidthPx * 2
                         + itemMarginPx
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabListProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabListProperties.java
index dd9d4971..fcef2e3a 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabListProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabListProperties.java
@@ -23,6 +23,7 @@
 public class VerticalTabListProperties {
     /** State of the Vertical Tab Rail layout. */
     @IntDef({
+        RailCollapseState.UNKNOWN,
         RailCollapseState.EXPANDED,
         RailCollapseState.COLLAPSED,
         RailCollapseState.EXPANDED_FOR_HOVERING
@@ -30,6 +31,9 @@
     @Retention(RetentionPolicy.SOURCE)
     @Target({ElementType.TYPE_USE})
     public @interface RailCollapseState {
+        /** The rail collapse state is unknown. */
+        int UNKNOWN = -1;
+
         /** The rail is fully expanded, showing tab favicons and titles. */
         int EXPANDED = 0;
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabRailLayout.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabRailLayout.java
index a2ee98a..405795f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabRailLayout.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabRailLayout.java
@@ -16,6 +16,7 @@
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 
+import androidx.annotation.Px;
 import androidx.appcompat.widget.TooltipCompat;
 import androidx.constraintlayout.widget.ConstraintLayout;
 
@@ -35,17 +36,25 @@
 // click handlers) from VerticalTabListCoordinator to VerticalTabRailLayout.
 @NullMarked
 public class VerticalTabRailLayout extends ConstraintLayout {
+    private static final int HEADER_BUTTON_COUNT_SINGLE_ROW = 3;
     private @Nullable Callback<@RailCollapseState Integer> mExpandOrCollapseOnHoverListener;
 
     private VerticalTabListRecyclerView mRecyclerView;
     private TabListRecyclerView mPinnedTabsRecyclerView;
     private View mSpacerView;
     private LinearLayout mHeaderContainer;
+    private LinearLayout mTabActionButtonsContainer;
     private ImageButton mCollapseButton;
     private View mGridButton;
     private View mSearchButton;
     private View mHeaderSpacer;
     private View mNewTabButton;
+    private @Px int mMinSingleButtonRowWidthPx;
+    private @Px int mHeaderButtonSizePx;
+    private @Px int mHeaderButtonGapPx;
+    private @RailCollapseState int mCollapseState = RailCollapseState.EXPANDED;
+    private @RailCollapseState int mLastAppliedCollapseState = RailCollapseState.UNKNOWN;
+    private boolean mLastAppliedShowSingleRowHeader;
 
     public VerticalTabRailLayout(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
@@ -66,6 +75,9 @@
         mHeaderContainer = findViewById(R.id.vertical_tab_header_container);
         assert mHeaderContainer != null;
 
+        mTabActionButtonsContainer = findViewById(R.id.tab_action_buttons_container);
+        assert mTabActionButtonsContainer != null;
+
         mCollapseButton = findViewById(R.id.collapse_button);
         assert mCollapseButton != null;
 
@@ -93,6 +105,15 @@
 
     private void updateButtonSizes() {
         VerticalTabListViewBinder.updateButtonSizes(this);
+
+        Resources res = getContext().getResources();
+        mHeaderButtonSizePx = res.getDimensionPixelSize(R.dimen.vertical_tabs_header_button_size);
+        mHeaderButtonGapPx = res.getDimensionPixelSize(R.dimen.vertical_tabs_header_button_gap);
+        mMinSingleButtonRowWidthPx =
+                mHeaderButtonSizePx * HEADER_BUTTON_COUNT_SINGLE_ROW
+                        + mHeaderButtonGapPx * (HEADER_BUTTON_COUNT_SINGLE_ROW - 1)
+                        + getPaddingStart()
+                        + getPaddingEnd();
     }
 
     /** Returns the main tab list recycler view. */
@@ -110,6 +131,11 @@
         return mHeaderContainer;
     }
 
+    /** Returns the tab action buttons container view. */
+    public LinearLayout getTabActionButtonsContainer() {
+        return mTabActionButtonsContainer;
+    }
+
     /** Sets the visibility of the desktop window top spacer. */
     public void setDesktopWindowSpacerVisible(boolean visible) {
         mSpacerView.setVisibility(visible ? View.VISIBLE : View.GONE);
@@ -123,7 +149,17 @@
 
     /** Updates internal child view styling based on the current rail collapse state. */
     public void setCollapseState(@RailCollapseState int collapseState) {
-        updateCollapsedState(collapseState);
+        if (mCollapseState == collapseState) return;
+        mCollapseState = collapseState;
+        updateHeaderLayoutForWidth(getWidth());
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        if (w != oldw) {
+            updateHeaderLayoutForWidth(w);
+        }
     }
 
     @Override
@@ -190,30 +226,40 @@
         }
     }
 
-    private void updateCollapsedState(@RailCollapseState int railCollapseState) {
-        boolean isCollapsed = railCollapseState == RailCollapseState.COLLAPSED;
-        boolean isManuallyExpanded = railCollapseState == RailCollapseState.EXPANDED;
-        Resources res = getResources();
-        int buttonSize = res.getDimensionPixelSize(R.dimen.vertical_tabs_header_button_size);
-        int newTabHeight = res.getDimensionPixelSize(R.dimen.vertical_tabs_new_tab_button_height);
+    /**
+     * Updates header child view styling and layout parameters for the given rail width.
+     *
+     * @param width The measured width of the rail layout, or 0 if unmeasured.
+     */
+    private void updateHeaderLayoutForWidth(int width) {
+        boolean isCollapsed = mCollapseState == RailCollapseState.COLLAPSED;
+        // Default to single-row header when unmeasured (width <= 0) to match the initial XML state.
+        boolean canFitHeaderButtonsInSingleRow = width <= 0 || width >= mMinSingleButtonRowWidthPx;
+        boolean showSingleRowHeader = !isCollapsed && canFitHeaderButtonsInSingleRow;
+        if (mCollapseState == mLastAppliedCollapseState
+                && mLastAppliedShowSingleRowHeader == showSingleRowHeader) {
+            return;
+        }
+        mLastAppliedCollapseState = mCollapseState;
+        mLastAppliedShowSingleRowHeader = showSingleRowHeader;
 
+        Resources res = getResources();
+
+        // The whole header button container
         mHeaderContainer.setOrientation(
-                isCollapsed ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
+                showSingleRowHeader ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
         mHeaderContainer.setGravity(isCollapsed ? Gravity.CENTER_HORIZONTAL : Gravity.NO_GRAVITY);
 
+        // Collapse button
+        boolean isManuallyExpanded = mCollapseState == RailCollapseState.EXPANDED;
         var collapseParams = (ViewGroup.MarginLayoutParams) mCollapseButton.getLayoutParams();
-        collapseParams.width = buttonSize;
-        collapseParams.height = buttonSize;
-        collapseParams.setMarginEnd(
-                isCollapsed
-                        ? 0
-                        : res.getDimensionPixelSize(
-                                R.dimen.vertical_tabs_header_button_collapsed_margin_end));
+        collapseParams.width = mHeaderButtonSizePx;
+        collapseParams.height = mHeaderButtonSizePx;
         collapseParams.bottomMargin =
-                isCollapsed
-                        ? res.getDimensionPixelOffset(R.dimen.vertical_tabs_header_padding_vertical)
-                        : 0;
-        mCollapseButton.setLayoutParams(collapseParams);
+                showSingleRowHeader
+                        ? 0
+                        : res.getDimensionPixelOffset(
+                                R.dimen.vertical_tabs_header_padding_vertical);
         mCollapseButton.setImageResource(
                 isManuallyExpanded
                         ? R.drawable.vertical_tabs_menu_collapse
@@ -226,32 +272,61 @@
         mCollapseButton.setContentDescription(tooltipText);
         TooltipCompat.setTooltipText(mCollapseButton, tooltipText);
 
-        int gap = res.getDimensionPixelSize(R.dimen.vertical_tabs_header_button_gap);
-        var gridParams = (ViewGroup.MarginLayoutParams) mGridButton.getLayoutParams();
-        gridParams.width = buttonSize;
-        gridParams.height = buttonSize;
-        gridParams.setMarginEnd(isCollapsed ? 0 : gap);
-        gridParams.bottomMargin = isCollapsed ? gap : 0;
-        mGridButton.setLayoutParams(gridParams);
+        // Horizontal header spacer
+        mHeaderSpacer.setVisibility(showSingleRowHeader ? View.VISIBLE : View.GONE);
+
+        // Tab actions container (for grid and search buttons)
+        var tabActionParams = mTabActionButtonsContainer.getLayoutParams();
+        tabActionParams.width =
+                (!isCollapsed && !showSingleRowHeader)
+                        ? ViewGroup.LayoutParams.MATCH_PARENT
+                        : ViewGroup.LayoutParams.WRAP_CONTENT;
+
+        mTabActionButtonsContainer.setOrientation(
+                isCollapsed ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
+        mTabActionButtonsContainer.setGravity(
+                isCollapsed ? Gravity.CENTER_HORIZONTAL : Gravity.NO_GRAVITY);
+
+        // Grid button
+        boolean fillRowSpace = !isCollapsed && !showSingleRowHeader;
+
+        var gridParams = (LinearLayout.LayoutParams) mGridButton.getLayoutParams();
+        gridParams.width = fillRowSpace ? 0 : mHeaderButtonSizePx;
+        gridParams.height = mHeaderButtonSizePx;
+        gridParams.weight = fillRowSpace ? 1.0f : 0.0f;
+        gridParams.setMarginEnd(isCollapsed ? 0 : mHeaderButtonGapPx);
+        gridParams.bottomMargin = isCollapsed ? mHeaderButtonGapPx : 0;
         mGridButton.setBackgroundResource(
                 isCollapsed
                         ? R.drawable.vertical_tabs_top_rounded_button_background
                         : R.drawable.vertical_tabs_left_rounded_button_background);
 
-        var searchParams = (ViewGroup.MarginLayoutParams) mSearchButton.getLayoutParams();
-        searchParams.width = buttonSize;
-        searchParams.height = buttonSize;
-        mSearchButton.setLayoutParams(searchParams);
+        // Search button
+        var searchParams = (LinearLayout.LayoutParams) mSearchButton.getLayoutParams();
+        searchParams.width = fillRowSpace ? 0 : mHeaderButtonSizePx;
+        searchParams.height = mHeaderButtonSizePx;
+        searchParams.weight = fillRowSpace ? 1.0f : 0.0f;
         mSearchButton.setBackgroundResource(
                 isCollapsed
                         ? R.drawable.vertical_tabs_bottom_rounded_button_background
                         : R.drawable.vertical_tabs_right_rounded_button_background);
 
-        mHeaderSpacer.setVisibility(isCollapsed ? View.GONE : View.VISIBLE);
-
+        // New tab button
         var newTabParams = mNewTabButton.getLayoutParams();
-        newTabParams.width = isCollapsed ? buttonSize : ViewGroup.LayoutParams.MATCH_PARENT;
-        newTabParams.height = isCollapsed ? buttonSize : newTabHeight;
+        int newTabHeight = res.getDimensionPixelSize(R.dimen.vertical_tabs_new_tab_button_height);
+        newTabParams.width =
+                isCollapsed ? mHeaderButtonSizePx : ViewGroup.LayoutParams.MATCH_PARENT;
+        newTabParams.height = isCollapsed ? mHeaderButtonSizePx : newTabHeight;
+
+        mCollapseButton.setLayoutParams(collapseParams);
+        mTabActionButtonsContainer.setLayoutParams(tabActionParams);
+        mGridButton.setLayoutParams(gridParams);
+        mSearchButton.setLayoutParams(searchParams);
         mNewTabButton.setLayoutParams(newTabParams);
     }
+
+    @Px
+    int getMinSingleButtonRowWidthPxForTesting() {
+        return mMinSingleButtonRowWidthPx;
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabRailLayoutUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabRailLayoutUnitTest.java
index a115133..d1436d0 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabRailLayoutUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabRailLayoutUnitTest.java
@@ -23,6 +23,7 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 
@@ -114,6 +115,7 @@
         assertNotNull(mRailLayout.getRecyclerView());
         assertNotNull(mRailLayout.getPinnedTabsRecyclerView());
         assertNotNull(mRailLayout.getHeaderContainer());
+        assertNotNull(mRailLayout.getTabActionButtonsContainer());
     }
 
     @Test
@@ -142,16 +144,81 @@
 
     @Test
     @SmallTest
-    public void testSetCollapseState_ExpandedAndCollapsed() {
-        mRailLayout.setCollapseState(RailCollapseState.EXPANDED);
-        LinearLayout header = mRailLayout.getHeaderContainer();
-        assertEquals(LinearLayout.HORIZONTAL, header.getOrientation());
-        View spacer = mRailLayout.findViewById(R.id.header_spacer);
-        assertEquals(View.VISIBLE, spacer.getVisibility());
+    public void testSetCollapseState_ExpandedWideAndNarrowAndCollapsed() {
+        int minSingleRowRailWidthPx = mRailLayout.getMinSingleButtonRowWidthPxForTesting();
+        int twoRowButtonRailWidthPx = minSingleRowRailWidthPx - 1;
 
+        View gridButton = mRailLayout.findViewById(R.id.grid_button);
+        View searchButton = mRailLayout.findViewById(R.id.tab_search_button);
+        LinearLayout header = mRailLayout.getHeaderContainer();
+        View spacer = mRailLayout.findViewById(R.id.header_spacer);
+        LinearLayout tabActionContainer = mRailLayout.getTabActionButtonsContainer();
+
+        // 1. Expanded Wide (single row)
+        mRailLayout.setCollapseState(RailCollapseState.EXPANDED);
+        mRailLayout.layout(0, 0, minSingleRowRailWidthPx, 500);
+        assertEquals(LinearLayout.HORIZONTAL, header.getOrientation());
+        assertEquals(View.VISIBLE, spacer.getVisibility());
+        assertEquals(LinearLayout.HORIZONTAL, tabActionContainer.getOrientation());
+        assertEquals(
+                0.0f, ((LinearLayout.LayoutParams) gridButton.getLayoutParams()).weight, 0.01f);
+        assertEquals(
+                0.0f, ((LinearLayout.LayoutParams) searchButton.getLayoutParams()).weight, 0.01f);
+
+        // 2. Expanded Narrow (two rows, filling space)
+        mRailLayout.layout(0, 0, twoRowButtonRailWidthPx, 500);
+        assertEquals(LinearLayout.VERTICAL, header.getOrientation());
+        assertEquals(View.GONE, spacer.getVisibility());
+        assertEquals(LinearLayout.HORIZONTAL, tabActionContainer.getOrientation());
+        assertEquals(
+                1.0f, ((LinearLayout.LayoutParams) gridButton.getLayoutParams()).weight, 0.01f);
+        assertEquals(
+                1.0f, ((LinearLayout.LayoutParams) searchButton.getLayoutParams()).weight, 0.01f);
+        assertEquals(
+                ViewGroup.LayoutParams.MATCH_PARENT, tabActionContainer.getLayoutParams().width);
+
+        // 3. Explicit Collapsed State (single column / three rows)
         mRailLayout.setCollapseState(RailCollapseState.COLLAPSED);
         assertEquals(LinearLayout.VERTICAL, header.getOrientation());
         assertEquals(View.GONE, spacer.getVisibility());
+        assertEquals(LinearLayout.VERTICAL, tabActionContainer.getOrientation());
+        assertEquals(
+                0.0f, ((LinearLayout.LayoutParams) gridButton.getLayoutParams()).weight, 0.01f);
+    }
+
+    @Test
+    @SmallTest
+    public void testOnSizeChanged_SkipsUpdateWhenHeaderModeUnchanged() {
+        int minSingleRowRailWidthPx = mRailLayout.getMinSingleButtonRowWidthPxForTesting();
+        int narrowRailWidthPx = minSingleRowRailWidthPx - 1;
+        int buttonSize =
+                mActivity
+                        .getResources()
+                        .getDimensionPixelSize(R.dimen.vertical_tabs_header_button_size);
+
+        View gridButton = mRailLayout.findViewById(R.id.grid_button);
+
+        // 1. Initial size in single row mode.
+        mRailLayout.setCollapseState(RailCollapseState.EXPANDED);
+        mRailLayout.layout(0, 0, minSingleRowRailWidthPx + 100, 500);
+        assertEquals(buttonSize, gridButton.getLayoutParams().width);
+
+        // 2. Mutate a layout param to a custom value to verify it is not overwritten.
+        var params = gridButton.getLayoutParams();
+        params.width = 999;
+        gridButton.setLayoutParams(params);
+
+        // 3. Size change with a different wide width (still single row).
+        mRailLayout.layout(0, 0, minSingleRowRailWidthPx + 50, 500);
+        // Action is skipped because mode hasn't changed; width remains custom value 999.
+        assertEquals(999, gridButton.getLayoutParams().width);
+
+        // 4. Size change with narrow width (transitions to two-row mode).
+        mRailLayout.layout(0, 0, narrowRailWidthPx, 500);
+        // Action is performed; width is reset to 0 with weight 1.0.
+        assertEquals(0, gridButton.getLayoutParams().width);
+        assertEquals(
+                1.0f, ((LinearLayout.LayoutParams) gridButton.getLayoutParams()).weight, 0.01f);
     }
 
     @Test
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabsSideUiCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabsSideUiCoordinator.java
index f9dd2be..a62be73 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabsSideUiCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabsSideUiCoordinator.java
@@ -23,6 +23,7 @@
 import org.chromium.base.supplier.SettableNonNullObservableSupplier;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.browser_controls.BrowserStateBrowserControlsVisibilityDelegate;
 import org.chromium.chrome.browser.tasks.tab_management.vertical_tabs.VerticalTabListProperties.RailCollapseState;
 import org.chromium.chrome.browser.ui.side_ui.SideUiContainer;
 import org.chromium.chrome.browser.ui.side_ui.SideUiCoordinator;
@@ -35,6 +36,7 @@
 import org.chromium.chrome.browser.ui.side_ui.SideUiObserver;
 import org.chromium.chrome.browser.ui.vertical_tabs.VerticalTabUtils;
 import org.chromium.ui.base.ViewUtils;
+import org.chromium.ui.util.TokenHolder;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -50,6 +52,7 @@
     static final int COLLAPSED_WIDTH_DP = VerticalTabUtils.SIDE_UI_CONTAINER_COLLAPSED_WIDTH_DP;
 
     private final SideUiCoordinator mSideUiCoordinator;
+    private final BrowserStateBrowserControlsVisibilityDelegate mBrowserControlsVisibilityDelegate;
     private final FrameLayout mRootView;
     private final @AnchorSide int mAnchorSide;
     private final VerticalTabListCoordinator mTabListCoordinator;
@@ -63,15 +66,18 @@
     // Whether the vertical tab is set to visible via UI. Remains true even if it is temporarily
     // hidden by other conditions such as narrow window i.e. |mIsAutoHiddenSupplier.get()| is true.
     private boolean mManualVisible;
+    private int mBrowserControlsToken = TokenHolder.INVALID_TOKEN;
 
     public VerticalTabsSideUiCoordinator(
             Activity activity,
             SideUiCoordinator sideUiCoordinator,
+            BrowserStateBrowserControlsVisibilityDelegate browserControlsVisibilityDelegate,
             VerticalTabListCoordinator tabListCoordinator,
             SettableNonNullObservableSupplier<Boolean> isVerticalTabsActiveSupplier) {
         mAnchorSide = AnchorSide.LEFT;
 
         mSideUiCoordinator = sideUiCoordinator;
+        mBrowserControlsVisibilityDelegate = browserControlsVisibilityDelegate;
         mTabListCoordinator = tabListCoordinator;
         mCollapseController = mTabListCoordinator.getCollapseController();
         mIsVerticalTabsActiveSupplier = isVerticalTabsActiveSupplier;
@@ -108,6 +114,11 @@
         mManualVisible = show;
         if (!show) {
             updateAutoHiddenState(false);
+            releasePersistentShowingToken();
+        } else {
+            if (mBrowserControlsToken == TokenHolder.INVALID_TOKEN) {
+                mBrowserControlsToken = mBrowserControlsVisibilityDelegate.showControlsPersistent();
+            }
         }
         mSideUiCoordinator.updateUi(new UiUpdateRequest(getSideUiId(), suppressAnimations));
         // Fallback: If hiding VT when spec diff is empty (no hide animation scheduled),
@@ -120,12 +131,20 @@
 
     public void destroy() {
         updateAutoHiddenState(false);
+        releasePersistentShowingToken();
         mSideUiCoordinator.removeObserver(this);
         mCollapseController.setRailCollapseListener(null);
         mTabListCoordinator.destroy();
         mIsVerticalTabsActiveSupplier.set(false);
     }
 
+    private void releasePersistentShowingToken() {
+        if (mBrowserControlsToken != TokenHolder.INVALID_TOKEN) {
+            mBrowserControlsVisibilityDelegate.releasePersistentShowingToken(mBrowserControlsToken);
+            mBrowserControlsToken = TokenHolder.INVALID_TOKEN;
+        }
+    }
+
     // SideUiContainer implementation:
     @Override
     public View getView() {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabsSideUiCoordinatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabsSideUiCoordinatorUnitTest.java
index dcc17b8..4314106 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabsSideUiCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/vertical_tabs/VerticalTabsSideUiCoordinatorUnitTest.java
@@ -46,6 +46,8 @@
 import org.chromium.base.supplier.SettableNonNullObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.cc.input.BrowserControlsState;
+import org.chromium.chrome.browser.browser_controls.BrowserStateBrowserControlsVisibilityDelegate;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tasks.tab_management.vertical_tabs.VerticalTabListProperties.RailCollapseState;
 import org.chromium.chrome.browser.ui.side_ui.SideUiCoordinator;
@@ -64,6 +66,7 @@
     @Mock private VerticalTabListCoordinator mMockTabListCoordinator;
     @Mock private SideUiCoordinator mMockSideUiCoordinator;
 
+    private BrowserStateBrowserControlsVisibilityDelegate mBrowserControlsVisibilityDelegate;
     private VerticalTabRailCollapseController mCollapseController;
     private VerticalTabsSideUiCoordinator mCoordinator;
     private ActivityController<Activity> mActivityController;
@@ -95,11 +98,15 @@
                 new VerticalTabRailCollapseController(
                         mMockTabListCoordinator::setRailCollapseState);
         when(mMockTabListCoordinator.getCollapseController()).thenReturn(mCollapseController);
+        mBrowserControlsVisibilityDelegate =
+                new BrowserStateBrowserControlsVisibilityDelegate(
+                        ObservableSuppliers.alwaysFalse());
 
         mCoordinator =
                 new VerticalTabsSideUiCoordinator(
                         mActivity,
                         mMockSideUiCoordinator,
+                        mBrowserControlsVisibilityDelegate,
                         mMockTabListCoordinator,
                         mIsVerticalTabsActiveSupplier);
     }
@@ -170,10 +177,35 @@
                 HeightType.TOOLBAR);
         assertTrue(mIsVerticalTabsActiveSupplier.get());
 
+        assertEquals(BrowserControlsState.SHOWN, (int) mBrowserControlsVisibilityDelegate.get());
+
         mCoordinator.destroy();
         verify(mMockSideUiCoordinator).removeObserver(mCoordinator);
         verify(mMockTabListCoordinator).destroy();
         assertFalse(mIsVerticalTabsActiveSupplier.get());
+        assertEquals(BrowserControlsState.BOTH, (int) mBrowserControlsVisibilityDelegate.get());
+    }
+
+    @Test
+    @SmallTest
+    public void testBrowserControlsVisibility_whenVisibleAndDisabled() {
+        assertEquals(BrowserControlsState.BOTH, (int) mBrowserControlsVisibilityDelegate.get());
+
+        // Set visible -> browser controls should be locked to SHOWN.
+        mCoordinator.setVisible(/* show= */ true, /* suppressAnimations= */ false);
+        assertEquals(BrowserControlsState.SHOWN, (int) mBrowserControlsVisibilityDelegate.get());
+
+        // Calling setVisible(false) -> browser controls should return to BOTH.
+        mCoordinator.setVisible(/* show= */ false, /* suppressAnimations= */ false);
+        assertEquals(BrowserControlsState.BOTH, (int) mBrowserControlsVisibilityDelegate.get());
+    }
+
+    @Test
+    @SmallTest
+    public void testBrowserControlsVisibility_destroyWithoutBeingVisible() {
+        assertEquals(BrowserControlsState.BOTH, (int) mBrowserControlsVisibilityDelegate.get());
+        mCoordinator.destroy();
+        assertEquals(BrowserControlsState.BOTH, (int) mBrowserControlsVisibilityDelegate.get());
     }
 
     @Test
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/SelectableTabListEditorTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/SelectableTabListEditorTest.java
index cf99388..219d18e 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/SelectableTabListEditorTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/SelectableTabListEditorTest.java
@@ -1631,9 +1631,17 @@
                                     ButtonType.TEXT,
                                     IconPosition.START,
                                     AppCompatResources.getDrawable(cta, R.drawable.ic_widgets),
-                                    (a, b, c, d, e, ignored, f, g) ->
+                                    (a, b, c, d, e, ignored, f, g, h) ->
                                             new TabGroupListBottomSheetCoordinator(
-                                                    a, b, c, d, e, mBottomSheetController, f, g)));
+                                                    a,
+                                                    b,
+                                                    c,
+                                                    d,
+                                                    e,
+                                                    mBottomSheetController,
+                                                    f,
+                                                    g,
+                                                    h)));
                     showSelectionEditor(tabs, actions);
                 });
 
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java
index 3937c23..c46514d 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java
@@ -44,7 +44,6 @@
 import org.chromium.base.supplier.SettableNonNullObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.RobolectricUtil;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.chrome.browser.app.tabmodel.ArchivedTabModelOrchestrator;
 import org.chromium.chrome.browser.back_press.BackPressManager;
@@ -80,7 +79,6 @@
 import java.util.List;
 
 /** Tests for {@link ArchivedTabsDialogCoordinator}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class ArchivedTabsDialogCoordinatorUnitTest {
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/IphMessageServiceUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/IphMessageServiceUnitTest.java
index 432f639..14cc0c8 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/IphMessageServiceUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/IphMessageServiceUnitTest.java
@@ -22,7 +22,6 @@
 import org.mockito.junit.MockitoRule;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab_ui.TabSwitcherIphController;
@@ -34,7 +33,6 @@
 /** Unit tests for {@link IphMessageService}. */
 @SuppressWarnings({"ResultOfMethodCallIgnored", "ArraysAsListWithZeroOrOneArgument"})
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class IphMessageServiceUnitTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataTextInputLayoutTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataTextInputLayoutTest.java
index df6315ef..aeab2d2 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataTextInputLayoutTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupVisualDataTextInputLayoutTest.java
@@ -21,10 +21,8 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 
 /** Unit tests for {@link TabGroupVisualDataTextInputLayout}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class TabGroupVisualDataTextInputLayoutTest {
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorAddToGroupActionUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorAddToGroupActionUnitTest.java
index 1c9d9b4..25adc64 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorAddToGroupActionUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorAddToGroupActionUnitTest.java
@@ -104,7 +104,7 @@
                         TEXT,
                         START,
                         mDrawable,
-                        (a, b, c, d, e, f, g, h) -> mCoordinator);
+                        (a, b, c, d, e, f, g, h, i) -> mCoordinator);
         mAction.configure(mTabModelSupplier, mSelectionDelegate, mActionDelegate, false);
     }
 
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSearchOverlayCoordinatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSearchOverlayCoordinatorUnitTest.java
index 0cdcbd1..f9b3441 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSearchOverlayCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSearchOverlayCoordinatorUnitTest.java
@@ -64,6 +64,7 @@
 import org.chromium.chrome.browser.omnibox.LocationBarCoordinator;
 import org.chromium.chrome.browser.omnibox.OmniboxStub;
 import org.chromium.chrome.browser.omnibox.OverrideUrlLoadingDelegate;
+import org.chromium.chrome.browser.omnibox.UrlBar;
 import org.chromium.chrome.browser.omnibox.UrlBarCoordinator;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxLoadUrlParams;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -107,6 +108,8 @@
     @Mock private SearchUiCoordinator mSearchUiCoordinator;
     @Mock private LocationBarCoordinator mLocationBarCoordinator;
     @Mock private UrlBarCoordinator mUrlBarCoordinator;
+    @Mock private View mLocationBarContainerView;
+    @Mock private UrlBar mUrlBar;
     @Mock private OmniboxStub mOmniboxStub;
     @Mock private SearchActivityLocationBarLayout mSearchBox;
     @Mock private Profile mProfile;
@@ -160,6 +163,8 @@
         when(mSearchUiCoordinator.getLocationBarCoordinator()).thenReturn(mLocationBarCoordinator);
         when(mLocationBarCoordinator.getUrlBarCoordinator()).thenReturn(mUrlBarCoordinator);
         when(mLocationBarCoordinator.getOmniboxStub()).thenReturn(mOmniboxStub);
+        when(mLocationBarCoordinator.getContainerView()).thenReturn(mLocationBarContainerView);
+        when(mLocationBarContainerView.findViewById(R.id.url_bar)).thenReturn(mUrlBar);
         when(mOmniboxStub.isUrlBarFocused()).thenReturn(true);
         when(mSearchUiCoordinator.getSearchBox()).thenReturn(mSearchBox);
         when(mLocationBarCoordinator.getSuggestionsListNonEmptySupplier())
@@ -228,6 +233,7 @@
         verify(mUrlBarCoordinator)
                 .setUrlBarHintText(
                         mActivity.getResources().getString(R.string.hub_search_empty_hint));
+        verify(mUrlBar).setTextAppearance(R.style.TextAppearance_TextMedium);
     }
 
     @Test
diff --git a/chrome/android/java/res/layout/bookmark_desktop_navigation_item.xml b/chrome/android/java/res/layout/bookmark_desktop_navigation_item.xml
index 39f27065..8974d0a 100644
--- a/chrome/android/java/res/layout/bookmark_desktop_navigation_item.xml
+++ b/chrome/android/java/res/layout/bookmark_desktop_navigation_item.xml
@@ -8,13 +8,12 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
+    android:layout_width="208dp"
     android:layout_height="wrap_content"
     android:minHeight="56dp"
     android:orientation="horizontal"
     android:gravity="center_vertical"
-    android:paddingStart="24dp"
-    android:paddingEnd="16dp"
+    android:paddingHorizontal="24dp"
     android:paddingVertical="16dp"
     android:background="@drawable/bookmark_desktop_navigation_item_bg"
     android:clickable="true"
@@ -25,6 +24,7 @@
         android:id="@+id/icon"
         android:layout_width="24dp"
         android:layout_height="24dp"
+        app:tint="@color/tonal_button_icon_tint_list"
         android:importantForAccessibility="no"/>
 
     <TextView
@@ -35,6 +35,5 @@
         android:layout_marginStart="12dp"
         android:maxLines="1"
         android:ellipsize="end"
-        android:textAppearance=
-            "@style/TextAppearance.TextMedium.Primary"/>
+        android:textAppearance="@style/TextAppearance.BookmarkDesktopNavigationItem"/>
 </LinearLayout>
diff --git a/chrome/android/java/res/layout/bookmark_main_desktop.xml b/chrome/android/java/res/layout/bookmark_main_desktop.xml
index 359b363..bccecfc 100644
--- a/chrome/android/java/res/layout/bookmark_main_desktop.xml
+++ b/chrome/android/java/res/layout/bookmark_main_desktop.xml
@@ -20,6 +20,7 @@
         android:layout_width="@dimen/bookmark_desktop_navigation_pane_width"
         android:layout_height="match_parent"
         android:orientation="vertical"
+        android:paddingHorizontal="16dp"
         android:background="@macro/bookmark_desktop_navigation_pane_bg">
 
         <!-- Header -->
@@ -27,7 +28,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="@dimen/bookmark_desktop_navigation_header_top_margin"
-            android:layout_marginStart="@dimen/bookmark_desktop_navigation_header_start_margin"
+            android:layout_marginStart="16dp"
             android:layout_marginEnd="@dimen/bookmark_desktop_navigation_header_end_margin"
             android:layout_marginBottom="@dimen/bookmark_desktop_navigation_header_bottom_margin"
             android:gravity="center_vertical"
@@ -43,7 +44,6 @@
             android:layout_width="match_parent"
             android:layout_height="0dp"
             android:layout_weight="1"
-            android:paddingHorizontal="@dimen/bookmark_desktop_navigation_list_horizontal_padding"
             android:clipToPadding="false"
             android:overScrollMode="ifContentScrolls"/>
     </LinearLayout>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 48900a4..3b591c4c 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -220,11 +220,8 @@
     <dimen name="bookmark_desktop_navigation_pane_width">240dp</dimen>
     <dimen name="bookmark_desktop_navigation_header_top_margin">24dp</dimen>
     <dimen name="bookmark_desktop_navigation_header_bot_margin">8dp</dimen>
-    <dimen name="bookmark_desktop_navigation_header_start_margin">32dp</dimen>
     <dimen name="bookmark_desktop_navigation_header_end_margin">16dp</dimen>
     <dimen name="bookmark_desktop_navigation_header_bottom_margin">36dp</dimen>
-    <dimen
-        name="bookmark_desktop_navigation_list_horizontal_padding">24dp</dimen>
     <dimen name="bookmark_desktop_content_padding">24dp</dimen>
 
      <!-- Improved bookmark row properties. -->
diff --git a/chrome/android/java/res/values/ids.xml b/chrome/android/java/res/values/ids.xml
index 58bf561..2e59411 100644
--- a/chrome/android/java/res/values/ids.xml
+++ b/chrome/android/java/res/values/ids.xml
@@ -7,15 +7,17 @@
 -->
 
 <resources>
+    <!-- go/keep-sorted start -->
     <item name="background_drawable" type="id"/>
     <item name="bookmark_page" type="id"/>
+    <item name="custom_tab_bottom_bar_wrapper" type="id"/>
     <item name="foreground_drawable" type="id"/>
     <item name="fre_pager" type="id"/>
+    <item name="history_page_recycler_view" type="id"/>
     <item name="refine_view_id" type="id"/>
     <item name="tabswitcher_multiple_drawable" type="id"/>
     <item name="tabswitcher_single_drawable" type="id"/>
-    <item name="custom_tab_bottom_bar_wrapper" type="id"/>
-    <item name="history_page_recycler_view" type="id"/>
+    <!-- go/keep-sorted end -->
 
     <!-- Media playback notification -->
     <item type="id" name="media_playback_notification" />
@@ -27,243 +29,266 @@
     <item type="id" name="presentation_notification" />
 
     <!-- Sync UI constants -->
-    <item type="id" name="passphrase_type_list" />
+    <!-- go/keep-sorted start -->
     <item type="id" name="history_and_tabs_toggle"/>
+    <item type="id" name="passphrase_type_list" />
+    <!-- go/keep-sorted end -->
 
     <!-- Keyboard shortcuts -->
+    <!-- go/keep-sorted start -->
     <item type="id" name="close_window" />
-    <item type="id" name="print_id" />
-    <item type="id" name="focus_url_bar" />
-    <item type="id" name="focus_and_clear_url_bar" />
-    <item type="id" name="show_menu" />
-    <item type="id" name="open_recently_closed_tab" />
-    <item type="id" name="toggle_bookmark_bar" />
-    <item type="id" name="switch_keyboard_focus_row" />
-    <item type="id" name="select_next_tab" />
-    <item type="id" name="select_previous_tab" />
-    <item type="id" name="open_tab_strip_context_menu" />
-    <item type="id" name="focus_bookmarks" />
     <item type="id" name="dev_tools" />
-    <item type="id" name="task_manager" />
     <item type="id" name="esc_key" />
     <item type="id" name="feedback_form" />
-    <item type="id" name="view_source" />
+    <item type="id" name="focus_and_clear_url_bar" />
+    <item type="id" name="focus_bookmarks" />
+    <item type="id" name="focus_url_bar" />
+    <item type="id" name="open_recently_closed_tab" />
+    <item type="id" name="open_tab_strip_context_menu" />
+    <item type="id" name="print_id" />
+    <item type="id" name="select_next_tab" />
+    <item type="id" name="select_previous_tab" />
+    <item type="id" name="show_menu" />
+    <item type="id" name="switch_keyboard_focus_row" />
     <item type="id" name="tab_search" />
     <item type="id" name="tab_search_side_ui" />
+    <item type="id" name="task_manager" />
+    <item type="id" name="toggle_bookmark_bar" />
+    <item type="id" name="view_source" />
+    <!-- go/keep-sorted end -->
 
     <!-- Editor dialog -->
     <item type="id" name="editor_dialog_done_button" />
 
     <!-- Context Menu Ids -->
     <!-- Page Group -->
-    <item type="id" name="contextmenu_save_page" />
-    <item type="id" name="contextmenu_share_page" />
-    <item type="id" name="contextmenu_print_page" />
-    <item type="id" name="contextmenu_search_tab_with_google_lens" />
+    <!-- go/keep-sorted start -->
     <item type="id" name="contextmenu_back" />
-    <item type="id" name="contextmenu_forward" />
-    <item type="id" name="contextmenu_reload" />
-    <item type="id" name="contextmenu_open_in_reading_mode" />
-    <item type="id" name="contextmenu_send_tab_to_self" />
     <item type="id" name="contextmenu_create_qr_code" />
+    <item type="id" name="contextmenu_forward" />
     <item type="id" name="contextmenu_inspect_element" />
-    <item type="id" name="contextmenu_view_page_source" />
+    <item type="id" name="contextmenu_open_in_reading_mode" />
+    <item type="id" name="contextmenu_print_page" />
+    <item type="id" name="contextmenu_reload" />
+    <item type="id" name="contextmenu_save_page" />
+    <item type="id" name="contextmenu_search_tab_with_google_lens" />
+    <item type="id" name="contextmenu_send_tab_to_self" />
+    <item type="id" name="contextmenu_share_page" />
     <item type="id" name="contextmenu_translate" />
+    <item type="id" name="contextmenu_view_page_source" />
+    <!-- go/keep-sorted end -->
 
     <!-- Custom Tab Group -->
-    <item type="id" name="contextmenu_open_in_new_chrome_tab" />
+    <!-- go/keep-sorted start -->
     <item type="id" name="contextmenu_open_in_chrome_incognito_tab" />
+    <item type="id" name="contextmenu_open_in_new_chrome_tab" />
+    <!-- go/keep-sorted end -->
 
     <!-- Link Group -->
-    <item type="id" name="contextmenu_open_in_new_tab" />
-    <item type="id" name="contextmenu_open_in_new_tab_in_group" />
+    <!-- go/keep-sorted start -->
+    <item type="id" name="contextmenu_ask_gemini" />
+    <item type="id" name="contextmenu_direct_share_link" />
+    <item type="id" name="contextmenu_open_in_ephemeral_tab" />
     <item type="id" name="contextmenu_open_in_incognito_tab" />
     <item type="id" name="contextmenu_open_in_incognito_window" />
-    <item type="id" name="contextmenu_open_in_other_window" />
+    <item type="id" name="contextmenu_open_in_new_tab" />
+    <item type="id" name="contextmenu_open_in_new_tab_in_group" />
     <item type="id" name="contextmenu_open_in_new_window" />
-    <item type="id" name="contextmenu_show_interest_in_element" />
-    <item type="id" name="contextmenu_open_in_ephemeral_tab" />
+    <item type="id" name="contextmenu_open_in_other_window" />
+    <item type="id" name="contextmenu_read_later" />
     <item type="id" name="contextmenu_save_link_as" />
     <item type="id" name="contextmenu_share_link" />
-    <item type="id" name="contextmenu_direct_share_link" />
-    <item type="id" name="contextmenu_read_later" />
-    <item type="id" name="contextmenu_ask_gemini" />
+    <item type="id" name="contextmenu_show_interest_in_element" />
+    <!-- go/keep-sorted end -->
 
     <!--Image Group -->
-    <item type="id" name="contextmenu_load_original_image" />
-    <item type="id" name="contextmenu_save_image" />
-    <item type="id" name="contextmenu_open_image" />
-    <item type="id" name="contextmenu_open_image_in_new_tab" />
-    <item type="id" name="contextmenu_open_image_in_ephemeral_tab" />
+    <!-- go/keep-sorted start -->
     <item type="id" name="contextmenu_copy_image" />
+    <item type="id" name="contextmenu_direct_share_image" />
+    <item type="id" name="contextmenu_load_original_image" />
+    <item type="id" name="contextmenu_open_image" />
+    <item type="id" name="contextmenu_open_image_in_ephemeral_tab" />
+    <item type="id" name="contextmenu_open_image_in_new_tab" />
+    <item type="id" name="contextmenu_save_image" />
     <item type="id" name="contextmenu_search_by_image" />
     <item type="id" name="contextmenu_search_image_with_google_lens" />
-    <item type="id" name="contextmenu_share_image" />
-    <item type="id" name="contextmenu_direct_share_image" />
-    <item type="id" name="contextmenu_shop_similar_products" />
-    <item type="id" name="contextmenu_shop_image_with_google_lens" />
     <item type="id" name="contextmenu_search_similar_products" />
+    <item type="id" name="contextmenu_share_image" />
+    <item type="id" name="contextmenu_shop_image_with_google_lens" />
+    <item type="id" name="contextmenu_shop_similar_products" />
+    <!-- go/keep-sorted end -->
 
     <!-- Message Group -->
-    <item type="id" name="contextmenu_call" />
-    <item type="id" name="contextmenu_send_message" />
+    <!-- go/keep-sorted start -->
     <item type="id" name="contextmenu_add_to_contacts" />
+    <item type="id" name="contextmenu_call" />
     <item type="id" name="contextmenu_copy" />
+    <item type="id" name="contextmenu_send_message" />
+    <!-- go/keep-sorted end -->
 
     <!-- Shared Highlighting Group -->
-    <item type="id" name="contextmenu_share_highlight" />
-    <item type="id" name="contextmenu_remove_highlight" />
-    <item type="id" name="contextmenu_learn_more" />
+    <!-- go/keep-sorted start -->
     <item type="id" name="contextmenu_copy_link_to_highlight" />
+    <item type="id" name="contextmenu_learn_more" />
+    <item type="id" name="contextmenu_remove_highlight" />
+    <item type="id" name="contextmenu_share_highlight" />
+    <!-- go/keep-sorted end -->
 
     <!-- Video Group -->
-    <item type="id" name="contextmenu_save_video" />
+    <!-- go/keep-sorted start -->
     <item type="id" name="contextmenu_copy_video_frame" />
     <item type="id" name="contextmenu_download_video_frame" />
     <item type="id" name="contextmenu_picture_in_picture" />
+    <item type="id" name="contextmenu_save_video" />
+    <!-- go/keep-sorted end -->
 
     <!-- Menu item IDs for FullscreenActivities -->
     <item type="id" name="contextmenu_open_in_chrome" />
 
     <!-- Tags -->
-    <item type="id" name="highlight_color" />
+    <!-- go/keep-sorted start -->
     <item type="id" name="context_menu_delegate" />
-    <item type="id" name="ntp_view_layout_change_listener_tag" />
+    <item type="id" name="highlight_color" />
     <item type="id" name="ntp_search_box_text_watcher_tag" />
+    <item type="id" name="ntp_view_layout_change_listener_tag" />
+    <!-- go/keep-sorted end -->
 
     <!-- NTP-->
-    <item type="id" name="ntp_feed_header_menu_item_manage" />
+    <!-- go/keep-sorted start -->
     <item type="id" name="ntp_feed_header_menu_item_activity" />
     <item type="id" name="ntp_feed_header_menu_item_interest" />
-    <item type="id" name="ntp_feed_header_menu_item_reactions" />
     <item type="id" name="ntp_feed_header_menu_item_learn" />
+    <item type="id" name="ntp_feed_header_menu_item_manage" />
+    <item type="id" name="ntp_feed_header_menu_item_reactions" />
     <item type="id" name="ntp_feed_header_menu_item_toggle_switch" />
+    <!-- go/keep-sorted end -->
 
     <!-- App Menu -->
-    <item type="id" name="icon_row_menu_id" />
-    <item type="id" name="submenu_header_menu_id" />
-    <item type="id" name="back_menu_id" />
-    <item type="id" name="forward_menu_id" />
-    <item type="id" name="bookmark_this_page_id" />
-    <item type="id" name="offline_page_id" />
-    <item type="id" name="info_menu_id" />
-    <item type="id" name="reload_menu_id" />
-    <item type="id" name="update_menu_id" />
-    <item type="id" name="new_tab_menu_id" />
-    <item type="id" name="new_incognito_tab_menu_id" />
-    <item type="id" name="add_to_group_menu_id" />
+    <!-- go/keep-sorted start -->
+    <item type="id" name="about_chrome_menu_id" />
+    <item type="id" name="add_tab_to_reading_list_menu_id" />
     <item type="id" name="add_to_existing_group_menu_item_id" />
-    <item type="id" name="pin_tab_menu_id" />
-    <item type="id" name="unpin_tab_menu_id" />
-    <item type="id" name="mute_site_menu_id" />
-    <item type="id" name="unmute_site_menu_id" />
-    <item type="id" name="new_window_menu_id" />
-    <item type="id" name="new_incognito_window_menu_id" />
-    <item type="id" name="move_to_other_window_menu_id" />
-    <item type="id" name="add_to_new_group_sub_menu_id" />
-    <item type="id" name="add_to_group_sub_menu_id" />
     <item type="id" name="add_to_group_incognito_sub_menu_id" />
-    <item type="id" name="move_to_new_window_sub_menu_id" />
-    <item type="id" name="move_to_other_window_sub_menu_id" />
-    <item type="id" name="manage_all_windows_menu_id" />
-    <item type="id" name="divider_line_id" />
-    <item type="id" name="open_history_menu_id" />
-    <item type="id" name="passwords_and_autofill_parent_menu_id" />
-    <item type="id" name="google_password_manager_menu_id" />
-    <item type="id" name="payment_methods_menu_id" />
-    <item type="id" name="addresses_and_more_menu_id" />
-    <item type="id" name="copy_link_menu_id" />
-    <item type="id" name="send_to_devices_menu_id" />
-    <item type="id" name="qr_code_menu_id" />
-    <item type="id" name="name_window_menu_id" />
-    <item type="id" name="history_parent_menu_id" />
-    <item type="id" name="bookmark_menu_id" />
-    <item type="id" name="bookmark_folder_menu_id" />
-    <item type="id" name="quick_delete_menu_id" />
-    <item type="id" name="quick_delete_divider_line_id" />
-    <item type="id" name="page_info_divider_line_id" />
-    <item type="id" name="homepage_menu_id" />
-    <item type="id" name="downloads_menu_id" />
-    <item type="id" name="all_bookmarks_menu_id" />
-    <item type="id" name="toggle_bookmarks_bar_menu_id" />
-    <item type="id" name="bookmarks_parent_menu_id" />
-    <item type="id" name="bookmarks_header_menu_id" />
-    <item type="id" name="reading_list_parent_menu_id" />
-    <item type="id" name="tab_groups_parent_menu_id" />
-    <item type="id" name="tab_groups_header_menu_id" />
-    <item type="id" name="create_new_tab_group_menu_id" />
-    <item type="id" name="tab_group_menu_item_id" />
-    <item type="id" name="tab_group_tab_menu_item" />
+    <item type="id" name="add_to_group_menu_id" />
+    <item type="id" name="add_to_group_sub_menu_id" />
+    <item type="id" name="add_to_new_group_sub_menu_id" />
     <item type="id" name="add_to_reading_list_menu_id" />
-    <item type="id" name="show_reading_list_menu_id" />
+    <item type="id" name="addresses_and_more_menu_id" />
+    <item type="id" name="ai_pdf_menu_id" />
+    <item type="id" name="ai_web_menu_id" />
+    <item type="id" name="all_bookmarks_menu_id" />
+    <item type="id" name="auto_dark_web_contents_check_id" />
+    <item type="id" name="auto_dark_web_contents_id" />
+    <item type="id" name="back_menu_id" />
+    <item type="id" name="bookmark_folder_menu_id" />
+    <item type="id" name="bookmark_menu_id" />
+    <item type="id" name="bookmark_this_page_id" />
     <item type="id" name="bookmark_this_page_menu_id" />
-    <item type="id" name="recent_tabs_menu_id" />
-    <item type="id" name="recent_tabs_header_menu_id" />
-    <item type="id" name="your_devices_header_menu_id" />
+    <item type="id" name="bookmarks_header_menu_id" />
+    <item type="id" name="bookmarks_parent_menu_id" />
+    <item type="id" name="close_all_incognito_tabs_menu_id" />
+    <item type="id" name="close_all_tabs_menu_id" />
+    <item type="id" name="close_other_tabs_menu_id" />
+    <item type="id" name="close_tabs_to_the_right_menu_id" />
+    <item type="id" name="copy_link_menu_id" />
+    <item type="id" name="create_new_tab_group_menu_id" />
+    <item type="id" name="default_browser_promo_menu_id" />
+    <item type="id" name="direct_share_menu_id" />
+    <item type="id" name="disable_price_tracking_menu_id" />
+    <item type="id" name="divider_line_id" />
+    <item type="id" name="download_page_id" />
+    <item type="id" name="downloads_menu_id" />
+    <item type="id" name="duplicate_tab_menu_id" />
+    <item type="id" name="enable_price_tracking_menu_id" />
+    <item type="id" name="extensions_menu_menu_id" />
+    <item type="id" name="extensions_parent_menu_id" />
+    <item type="id" name="extensions_webstore_menu_id" />
+    <item type="id" name="find_in_page_id" />
+    <item type="id" name="forward_menu_id" />
+    <item type="id" name="get_image_descriptions_id" />
+    <item type="id" name="glic_menu_id" />
+    <item type="id" name="google_password_manager_menu_id" />
+    <item type="id" name="help_id" />
+    <item type="id" name="help_parent_menu_id" />
+    <item type="id" name="history_parent_menu_id" />
+    <item type="id" name="homepage_menu_id" />
+    <item type="id" name="icon_row_menu_id" />
+    <item type="id" name="info_menu_id" />
+    <item type="id" name="lens_overlay_menu_id" />
+    <item type="id" name="listen_to_feed_id" />
+    <item type="id" name="manage_all_windows_menu_id" />
+    <item type="id" name="manage_extensions_menu_id" />
+    <item type="id" name="managed_by_divider_line_id" />
+    <item type="id" name="managed_by_menu_id" />
+    <item type="id" name="menu_item_content_filter_divider_line_id" />
+    <item type="id" name="menu_item_content_filter_help_center_id" />
+    <item type="id" name="menu_select_tabs" />
+    <item type="id" name="more_tools_menu_id" />
+    <item type="id" name="move_to_new_window_sub_menu_id" />
+    <item type="id" name="move_to_other_window_menu_id" />
+    <item type="id" name="move_to_other_window_sub_menu_id" />
+    <item type="id" name="mute_site_menu_id" />
+    <item type="id" name="name_window_menu_id" />
+    <item type="id" name="new_incognito_tab_menu_id" />
+    <item type="id" name="new_incognito_window_menu_id" />
+    <item type="id" name="new_tab_group_menu_id" />
+    <item type="id" name="new_tab_menu_id" />
+    <item type="id" name="new_tab_to_the_right_menu_id" />
+    <item type="id" name="new_window_menu_id" />
+    <item type="id" name="ntp_customization_id" />
+    <item type="id" name="offline_page_id" />
+    <item type="id" name="open_history_menu_id" />
+    <item type="id" name="open_in_app_menu_id" />
+    <item type="id" name="open_webapk_id" />
+    <item type="id" name="open_with_id" />
+    <item type="id" name="page_info_divider_line_id" />
+    <item type="id" name="page_zoom_id" />
+    <item type="id" name="paint_preview_show_id" />
+    <item type="id" name="passwords_and_autofill_parent_menu_id" />
+    <item type="id" name="payment_methods_menu_id" />
+    <item type="id" name="pin_tab_menu_id" />
+    <item type="id" name="preferences_id" />
+    <item type="id" name="qr_code_menu_id" />
+    <item type="id" name="quick_delete_divider_line_id" />
+    <item type="id" name="quick_delete_menu_id" />
+    <item type="id" name="readaloud_menu_id" />
+    <item type="id" name="reader_mode_menu_id" />
+    <item type="id" name="reader_mode_prefs_id" />
+    <item type="id" name="reading_list_parent_menu_id" />
+    <item type="id" name="recent_entry_foreign_tab_menu_item" />
+    <item type="id" name="recent_entry_group_menu_item" />
     <item type="id" name="recent_entry_menu_item" />
     <item type="id" name="recent_entry_tab_menu_item" />
     <item type="id" name="recent_entry_window_menu_item" />
     <item type="id" name="recent_entry_window_tab_menu_item" />
-    <item type="id" name="recent_entry_group_menu_item" />
-    <item type="id" name="recent_entry_foreign_tab_menu_item" />
-    <item type="id" name="extensions_parent_menu_id" />
-    <item type="id" name="extensions_menu_menu_id" />
-    <item type="id" name="manage_extensions_menu_id" />
-    <item type="id" name="extensions_webstore_menu_id" />
-    <item type="id" name="page_zoom_id" />
-    <item type="id" name="share_menu_id" />
-    <item type="id" name="direct_share_menu_id" />
-    <item type="id" name="save_and_share_parent_menu_id" />
-    <item type="id" name="download_page_id" />
-    <item type="id" name="enable_price_tracking_menu_id" />
-    <item type="id" name="disable_price_tracking_menu_id" />
-    <item type="id" name="ai_web_menu_id" />
-    <item type="id" name="ai_pdf_menu_id" />
-    <item type="id" name="find_in_page_id" />
-    <item type="id" name="lens_overlay_menu_id" />
-    <item type="id" name="translate_id" />
-    <item type="id" name="readaloud_menu_id" />
-    <item type="id" name="more_tools_menu_id" />
-    <item type="id" name="reader_mode_menu_id" />
-    <item type="id" name="open_with_id" />
-    <item type="id" name="universal_install" />
-    <item type="id" name="open_webapk_id" />
-    <item type="id" name="request_desktop_site_id" />
+    <item type="id" name="recent_tabs_header_menu_id" />
+    <item type="id" name="recent_tabs_menu_id" />
+    <item type="id" name="reload_menu_id" />
     <item type="id" name="request_desktop_site_check_id" />
-    <item type="id" name="auto_dark_web_contents_id" />
-    <item type="id" name="auto_dark_web_contents_check_id" />
-    <item type="id" name="paint_preview_show_id" />
-    <item type="id" name="get_image_descriptions_id" />
-    <item type="id" name="reader_mode_prefs_id" />
-    <item type="id" name="preferences_id" />
-    <item type="id" name="ntp_customization_id" />
-    <item type="id" name="listen_to_feed_id" />
-    <item type="id" name="help_id" />
-    <item type="id" name="help_parent_menu_id" />
-    <item type="id" name="about_chrome_menu_id" />
-    <item type="id" name="default_browser_promo_menu_id" />
-    <item type="id" name="managed_by_divider_line_id" />
-    <item type="id" name="managed_by_menu_id" />
-    <item type="id" name="new_tab_group_menu_id" />
-    <item type="id" name="close_all_tabs_menu_id" />
-    <item type="id" name="close_all_incognito_tabs_menu_id" />
-    <item type="id" name="menu_select_tabs" />
-    <item type="id" name="menu_item_content_filter_divider_line_id" />
-    <item type="id" name="menu_item_content_filter_help_center_id" />
-    <item type="id" name="duplicate_tab_menu_id" />
-    <item type="id" name="glic_menu_id" />
-    <item type="id" name="open_in_app_menu_id" />
-    <item type="id" name="close_tabs_to_the_right_menu_id" />
-    <item type="id" name="close_other_tabs_menu_id" />
-    <item type="id" name="new_tab_to_the_right_menu_id" />
-    <item type="id" name="add_tab_to_reading_list_menu_id" />
-    <item type="id" name="send_to_your_device_menu_id" />
-    <item type="id" name="toggle_tab_layout_menu_id" />
+    <item type="id" name="request_desktop_site_id" />
+    <item type="id" name="save_and_share_parent_menu_id" />
     <item type="id" name="send_feedback_about_tab_strip_menu_id" />
+    <item type="id" name="send_to_devices_menu_id" />
+    <item type="id" name="send_to_your_device_menu_id" />
+    <item type="id" name="share_menu_id" />
+    <item type="id" name="show_reading_list_menu_id" />
+    <item type="id" name="submenu_header_menu_id" />
+    <item type="id" name="tab_group_menu_item_id" />
+    <item type="id" name="tab_group_tab_menu_item" />
+    <item type="id" name="tab_groups_header_menu_id" />
+    <item type="id" name="tab_groups_parent_menu_id" />
+    <item type="id" name="toggle_bookmarks_bar_menu_id" />
+    <item type="id" name="toggle_tab_layout_menu_id" />
+    <item type="id" name="translate_id" />
+    <item type="id" name="universal_install" />
+    <item type="id" name="unmute_site_menu_id" />
+    <item type="id" name="unpin_tab_menu_id" />
+    <item type="id" name="update_menu_id" />
+    <item type="id" name="your_devices_header_menu_id" />
+    <!-- go/keep-sorted end -->
 
     <!-- Custom Tabs -->
-    <item type="id" name="view_id_tag_key" />
-    <item type="id" name="custom_tabs_toolbar_tintable" />
+    <!-- go/keep-sorted start -->
     <item type="id" name="custom_tabs_app_menu_item_id_0" />
     <item type="id" name="custom_tabs_app_menu_item_id_1" />
     <item type="id" name="custom_tabs_app_menu_item_id_2" />
@@ -271,7 +296,10 @@
     <item type="id" name="custom_tabs_app_menu_item_id_4" />
     <item type="id" name="custom_tabs_app_menu_item_id_5" />
     <item type="id" name="custom_tabs_app_menu_item_id_6" />
+    <item type="id" name="custom_tabs_toolbar_tintable" />
     <item type="id" name="open_in_browser_id" />
     <item type="id" name="price_insights_menu_id" />
     <item type="id" name="toggle_caret_browsing" />
+    <item type="id" name="view_id_tag_key" />
+    <!-- go/keep-sorted end -->
 </resources>
diff --git a/chrome/android/java/res/values/styles.xml b/chrome/android/java/res/values/styles.xml
index 9c7728c..6e6870c 100644
--- a/chrome/android/java/res/values/styles.xml
+++ b/chrome/android/java/res/values/styles.xml
@@ -585,6 +585,9 @@
         <!-- The same as the regular style, but using dp instead of sp. -->
         <item name="android:textSize">16dp</item>
     </style>
+    <style name="TextAppearance.BookmarkDesktopNavigationItem" parent="TextAppearance.TextMedium.Primary">
+        <item name="android:textColor">@color/tonal_button_icon_tint_list</item>
+    </style>
 
      <!-- Magic Stack styles -->
     <style name="TextAppearance.ShoppingPriceDropSmallText" parent="TextAppearance.TextSmall.Secondary">
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index d76bb9db5..7d3bc66 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -3087,7 +3087,11 @@
                             menuItemData.getString(
                                     AppMenuPropertiesDelegateImpl.BOOKMARK_ID_BUNDLE_KEY));
             BookmarkOpener opener =
-                    new BookmarkOpenerImpl(mBookmarkModelSupplier, this, getComponentName());
+                    new BookmarkOpenerImpl(
+                            mBookmarkModelSupplier,
+                            this,
+                            getComponentName(),
+                            /* multiInstanceManager= */ null);
             opener.openBookmarkInCurrentTab(bookmarkId, currentTab.isIncognito());
             RecordUserAction.record("MobileMenuOpenBookmark");
             return true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkActivity.java
index 2d0c621dd..61e4a29f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkActivity.java
@@ -79,7 +79,8 @@
                 new BookmarkOpenerImpl(
                         () -> BookmarkModel.getForProfile(profile),
                         /* context= */ this,
-                        /* componentName= */ parentComponent);
+                        /* componentName= */ parentComponent,
+                        /* multiInstanceManager= */ null);
 
         ScrimManager scrimManager =
                 new ScrimManager(this, getContentView(), ScrimClient.BOOKMARK_ACTIVITY);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkOpenerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkOpenerImpl.java
index e70dcbf0..4b84505 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkOpenerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkOpenerImpl.java
@@ -26,6 +26,7 @@
 import org.chromium.chrome.browser.ActivityUtils;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
+import org.chromium.chrome.browser.multiwindow.MultiInstanceManager;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.components.bookmarks.BookmarkId;
@@ -43,20 +44,24 @@
     private final Supplier<@Nullable BookmarkModel> mBookmarkModelSupplier;
     private final Context mContext;
     private final @Nullable ComponentName mComponentName;
+    private final @Nullable MultiInstanceManager mMultiInstanceManager;
 
     /**
      * @param bookmarkModelSupplier Supplies the bookmark model, used to query for bookmark urls and
      *     type.
      * @param context The android activity context, used to build the intent to open bookmarks.
      * @param componentName The name of the parent component, can be null on tablets.
+     * @param multiInstanceManager The multi instance manager.
      */
     public BookmarkOpenerImpl(
             Supplier<@Nullable BookmarkModel> bookmarkModelSupplier,
             Context context,
-            @Nullable ComponentName componentName) {
+            @Nullable ComponentName componentName,
+            @Nullable MultiInstanceManager multiInstanceManager) {
         mBookmarkModelSupplier = bookmarkModelSupplier;
         mContext = context;
         mComponentName = componentName;
+        mMultiInstanceManager = multiInstanceManager;
     }
 
     @Override
@@ -68,7 +73,7 @@
         recordMetricsForOpenBookmarkInCurrentTab(item);
 
         Intent intent = createBasicOpenIntent(item, incognito, /* opensNewTabByDefault= */ false);
-        IntentHandler.startActivityForTrustedIntent(mContext, intent);
+        launchIntent(intent);
 
         return true;
     }
@@ -116,11 +121,24 @@
         if (extras != null) {
             intent.putExtras(extras);
         }
-        IntentHandler.startActivityForTrustedIntent(mContext, intent);
+        launchIntent(intent);
 
         return true;
     }
 
+    private void launchIntent(Intent intent) {
+        if (mMultiInstanceManager != null) {
+            int windowId = mMultiInstanceManager.getCurrentInstanceId();
+            if (windowId != MultiInstanceManager.INVALID_WINDOW_ID) {
+                intent.putExtra(IntentHandler.EXTRA_WINDOW_ID, windowId);
+                if (MultiWindowUtils.launchIntentInInstance(intent, windowId)) {
+                    return;
+                }
+            }
+        }
+        IntentHandler.startActivityForTrustedIntent(mContext, intent);
+    }
+
     @Override
     public boolean openBookmarksInNewWindow(
             List<BookmarkId> bookmarkIds, boolean incognito, @Nullable Bundle extras) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
index e74e72b..eeb7cc20 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -2638,7 +2638,8 @@
                                 mModel,
                                 mBottomSheetController,
                                 /* supportsShowNewGroup= */ true,
-                                /* destroyOnHide= */ false);
+                                /* destroyOnHide= */ false,
+                                mWindowAndroid);
             }
             mTabContextMenuCoordinator =
                     TabContextMenuCoordinator.createContextMenuCoordinator(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
index b0bb1318..31d573cf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
@@ -423,8 +423,6 @@
         ContactsPickerDelegateProvider.initialize();
 
         SearchActivityPreferencesManager.onNativeLibraryReady();
-        SearchWidgetProvider.initialize();
-        QuickActionSearchWidgetProvider.initialize();
 
         PrivacyPreferencesManagerImpl.getInstance().onNativeInitialized();
 
@@ -712,6 +710,11 @@
         // Asynchronously query system accessibility state so it is ready for clients.
         tasks.add(AccessibilityState::initializeOnStartup);
         tasks.add(TabPersistentStoreImpl::onDeferredStartup);
+        tasks.add(
+                () -> {
+                    SearchWidgetProvider.initialize();
+                    QuickActionSearchWidgetProvider.initialize();
+                });
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/MultiColumnTitleUpdaterTest.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/MultiColumnTitleUpdaterTest.java
index 2457f77..b3955d3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/MultiColumnTitleUpdaterTest.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/MultiColumnTitleUpdaterTest.java
@@ -38,7 +38,6 @@
 import org.chromium.base.supplier.ObservableSuppliers;
 import org.chromium.base.supplier.SettableMonotonicObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.build.annotations.Nullable;
@@ -55,7 +54,6 @@
 /** Unit tests for {@link MultiColumnTitleUpdater}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(qualifiers = "sw600dp")
-@Batch(Batch.UNIT_TESTS)
 public class MultiColumnTitleUpdaterTest {
 
     /** Fake PreferenceFragment for testing. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsMenuHelperUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsMenuHelperUnitTest.java
index d54bcad..1ef35af 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsMenuHelperUnitTest.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsMenuHelperUnitTest.java
@@ -41,7 +41,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncher;
@@ -51,7 +50,6 @@
 
 /** Unit tests for {@link SettingsMenuHelper}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class SettingsMenuHelperUnitTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsPageFragmentDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsPageFragmentDelegateImpl.java
index fa95a37b..ed1e012 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsPageFragmentDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsPageFragmentDelegateImpl.java
@@ -442,11 +442,36 @@
 
     private void createSearchCoordinator(
             MultiColumnSettings multiColumnSettings, @Nullable Bundle savedInstanceState) {
-        assert mSearchCoordinator == null;
+        // mSearchCoordinator may already be non-null if initialized during initSettings() before
+        // TitleUpdaterLifecycleCallbacks fired, or during activity recreation / theme change.
+        if (mSearchCoordinator != null) return;
+
         assert mToolbar != null;
         assert mSettingsHostFragment != null;
+
+        // containmentHelper can be null if mSettingsHostFragment is detached or not yet attached
+        // during activity recreation / theme changes.
         SettingsContainmentHelper containmentHelper = mSettingsHostFragment.getContainmentHelper();
-        assert containmentHelper != null;
+        if (containmentHelper == null) {
+            // SearchCoordinator requires containment item decorations to properly style search
+            // result highlights with rounded corners. Delay creation until SettingsHostFragment is
+            // attached and containmentHelper is available.
+            FragmentManager fragmentManager =
+                    ((FragmentActivity) mActivity).getSupportFragmentManager();
+            fragmentManager.registerFragmentLifecycleCallbacks(
+                    new FragmentManager.FragmentLifecycleCallbacks() {
+                        @Override
+                        public void onFragmentAttached(
+                                FragmentManager fm, Fragment f, Context context) {
+                            if (f == mSettingsHostFragment) {
+                                fm.unregisterFragmentLifecycleCallbacks(this);
+                                createSearchCoordinator(multiColumnSettings, savedInstanceState);
+                            }
+                        }
+                    },
+                    /* recursive= */ false);
+            return;
+        }
 
         mSearchCoordinator =
                 new SettingsSearchCoordinator(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
index 2209fab..de463b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
@@ -9,6 +9,7 @@
 import android.annotation.SuppressLint;
 import android.os.Handler;
 import android.os.Message;
+import android.util.ArraySet;
 
 import androidx.annotation.IntDef;
 
@@ -34,7 +35,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.HashSet;
 import java.util.Set;
 
 /**
@@ -85,7 +85,8 @@
     private boolean mIsFullscreenWaitingForLoad;
     private boolean mIsFocusedNodeEditable;
 
-    private final Set<Long> mOutstandingNavigations = new HashSet<>();
+    // There shouldn't be very many outstanding navigations at once so an ArraySet should be fine.
+    private final Set<Long> mOutstandingNavigations = new ArraySet<>();
 
     /**
      * Basic constructor.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabModelObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabModelObserver.java
index 4856843b..ffb41b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabModelObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabModelObserver.java
@@ -7,6 +7,7 @@
 import static org.chromium.build.NullUtil.assumeNonNull;
 
 import android.text.TextUtils;
+import android.util.ArrayMap;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -26,8 +27,8 @@
 import org.chromium.components.tab_groups.TabGroupColorId;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.function.Supplier;
 
@@ -90,7 +91,7 @@
     }
 
     private void buildGroupsAndCreateClosure(List<Tab> tabs) {
-        HashMap<Token, HistoricalEntry> tabGroupIdToGroup = new HashMap<>();
+        Map<Token, HistoricalEntry> tabGroupIdToGroup = new ArrayMap<>();
         List<HistoricalEntry> entries = new ArrayList<>();
 
         Profile profile = assumeNonNull(mTabModel.getProfile());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
index b6155c4..d4fa77a06 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -2391,6 +2391,7 @@
                     new VerticalTabsSideUiCoordinator(
                             mActivity,
                             mSideUiCoordinator,
+                            mBrowserControlsManager.getBrowserVisibilityDelegate(),
                             new VerticalTabListCoordinator(
                                     mActivity,
                                     assumeNonNull(mTabModelSelectorSupplier.get()),
@@ -2841,7 +2842,10 @@
         if (mBookmarkOpener == null) {
             mBookmarkOpener =
                     new BookmarkOpenerImpl(
-                            mBookmarkModelSupplier, mActivity, mActivity.getComponentName());
+                            mBookmarkModelSupplier,
+                            mActivity,
+                            mActivity.getComponentName(),
+                            mMultiInstanceManager);
         }
 
         if (mBookmarkBarCoordinator == null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
index 2b26dc0..d6dbca2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
@@ -376,7 +376,8 @@
             // Check if the tab is being created asynchronously.
             int assignedTabId = IntentHandler.getTabId(intent);
             boolean isReparenting = isReparenting(assignedTabId);
-            AsyncTabParams asyncParams = mAsyncTabParamsManager.remove(assignedTabId);
+            AsyncTabParams asyncParams =
+                    mAsyncTabParamsManager.getAsyncTabParams().get(assignedTabId);
             if ((type == TabLaunchType.FROM_REPARENTING
                             || type == TabLaunchType.FROM_REPARENTING_BACKGROUND)
                     && asyncParams == null) {
@@ -394,6 +395,7 @@
             @TabCreationState int creationState = TabCreationState.LIVE_IN_FOREGROUND;
             if (isReparenting) {
                 TabReparentingParams params = (TabReparentingParams) asyncParams;
+                assert params != null;
                 tab = params.getTabToReparent();
 
                 assert intent != null;
@@ -527,6 +529,7 @@
                 creationState = TabCreationState.LIVE_IN_BACKGROUND;
             }
             mTabModel.addTab(tab, position, type, creationState);
+            mAsyncTabParamsManager.remove(assignedTabId);
             return tab;
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/PendingTabClosureManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/PendingTabClosureManager.java
index 8d37eb8..f0a8c11 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/PendingTabClosureManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/PendingTabClosureManager.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.tabmodel;
 
+import android.util.ArraySet;
+
 import org.chromium.base.ThreadUtils.ThreadChecker;
 import org.chromium.base.TimeUtils;
 import org.chromium.build.annotations.NullMarked;
@@ -11,10 +13,10 @@
 import org.chromium.chrome.browser.tab.Tab;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
+import java.util.Set;
 
 /**
  * Manages the logic pertaining to tracking pending tab closures for a {@link TabModelImpl}. This
@@ -64,7 +66,7 @@
     /** Represents a set of tabs closed together. */
     static class TabClosureEvent {
         private final List<Tab> mClosingTabs;
-        private final HashSet<Tab> mUnhandledTabs;
+        private final Set<Tab> mUnhandledTabs;
         private final @Nullable Runnable mUndoRunnable;
         private final long mTimestamp;
 
@@ -74,7 +76,7 @@
          */
         public TabClosureEvent(List<Tab> tabs, @Nullable Runnable undoRunnable) {
             mClosingTabs = new ArrayList<>(tabs);
-            mUnhandledTabs = new HashSet<>(mClosingTabs);
+            mUnhandledTabs = new ArraySet<>(mClosingTabs);
             mUndoRunnable = undoRunnable;
             mTimestamp = TimeUtils.currentTimeMillis();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java
index b794bfc4..efc84140 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java
@@ -14,6 +14,8 @@
 
 import android.app.Activity;
 import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.SparseArray;
 
 import androidx.annotation.VisibleForTesting;
@@ -70,8 +72,6 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -312,7 +312,7 @@
     private final SettableNonNullObservableSupplier<Integer> mTabCountSupplier =
             ObservableSuppliers.createNonNull(0);
     private final Set<Integer> mMultiSelectedTabs = new LinkedHashSet<>();
-    private final Set<Token> mHidingTabGroups = new HashSet<>();
+    private final Set<Token> mHidingTabGroups = new ArraySet<>();
 
     // Efficient lookup of tabs by id rather than index (stored in C++). Also ensures the Java Tab
     // objects are not GC'd as the C++ TabAndroid objects only hold weak references to their Java
@@ -1416,7 +1416,7 @@
     public Set<Token> getAllTabGroupIds() {
         assertOnUiThread();
         if (mNativeTabCollectionTabModelImplPtr == 0) return Collections.emptySet();
-        return new HashSet<>(
+        return new ArraySet<>(
                 TabCollectionTabModelImplJni.get()
                         .getAllTabGroupIds(mNativeTabCollectionTabModelImplPtr));
     }
@@ -1459,7 +1459,7 @@
         // would likely perform better as the tabs could be iterated over only a single time.
         Supplier<Set<Token>> supplier =
                 () -> {
-                    Set<Token> tabGroupIds = new HashSet<>();
+                    Set<Token> tabGroupIds = new ArraySet<>();
                     TabList tabList = includePendingClosures ? getComprehensiveModel() : this;
                     for (Tab tab : tabList) {
                         if (tabsToExclude.contains(tab)) continue;
@@ -1842,7 +1842,7 @@
         } else {
             tabsToClose = new ArrayList<>(assumeNonNull(params.tabs));
             if (canHideTabGroups) {
-                Set<Tab> closingTabIds = new HashSet<>(tabsToClose);
+                Set<Tab> closingTabIds = new ArraySet<>(tabsToClose);
                 for (Token tabGroupId : getAllTabGroupIds()) {
                     if (closingTabIds.containsAll(getTabsInGroup(tabGroupId))) {
                         mHidingTabGroups.add(tabGroupId);
@@ -1887,7 +1887,7 @@
             }
         }
 
-        Set<Integer> tabsToCloseIds = new HashSet<>();
+        Set<Integer> tabsToCloseIds = new ArraySet<>(tabsToClose.size());
         boolean didCloseAlone = params.tabCloseType == TabCloseType.SINGLE;
         for (Tab tab : tabsToClose) {
             tabsToCloseIds.add(tab.getId());
@@ -2144,7 +2144,7 @@
             mCurrentTabSupplier.willSet(nearbyTab);
         }
 
-        Map<Token, @Nullable Tab> tabGroupShownTabs = new HashMap<>();
+        Map<Token, @Nullable Tab> tabGroupShownTabs = new ArrayMap<>();
         for (Tab tab : tabsToRemove) {
             assert mTabIdToTabs.indexOfKey(tab.getId()) >= 0 : "Tab not found in tab model.";
             if (pauseMedia) TabUtils.pauseMedia(tab);
@@ -2629,7 +2629,7 @@
     }
 
     private List<Token> getCandidateTabGroupIdsForMerge(List<Tab> tabsToMerge) {
-        HashSet<Token> processedTabGroups = new HashSet<>();
+        Set<Token> processedTabGroups = new ArraySet<>();
         List<Token> candidateTabGroupIds = new ArrayList<>();
         for (Tab tab : tabsToMerge) {
             Token tabGroupId = tab.getTabGroupId();
@@ -2723,7 +2723,7 @@
     private List<Token> maybeSendCloseTabGroupEvent(List<Tab> tabs, boolean committing) {
         LazyOneshotSupplier<Set<Token>> tabGroupIdsInComprehensiveModel =
                 getLazyAllTabGroupIds(tabs, /* includePendingClosures= */ committing);
-        Set<Token> processedTabGroups = new HashSet<>();
+        Set<Token> processedTabGroups = new ArraySet<>();
         List<Token> closingTabGroupIds = new ArrayList<>();
         for (Tab tab : tabs) {
             @Nullable Token tabGroupId = tab.getTabGroupId();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelObserverJniBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelObserverJniBridge.java
index eeb93482..7383155 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelObserverJniBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelObserverJniBridge.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.tabmodel;
 
+import android.util.ArrayMap;
+
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
@@ -17,7 +19,6 @@
 import org.chromium.chrome.browser.tab.TabSelectionType;
 
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -146,7 +147,7 @@
         int[] indices = new int[tabs.size()];
         Arrays.fill(indices, TabList.INVALID_TAB_INDEX);
 
-        Map<Tab, Integer> tabToTargetIndex = new HashMap<>();
+        Map<Tab, Integer> tabToTargetIndex = new ArrayMap<>(tabs.size());
         for (int i = 0; i < tabs.size(); i++) {
             tabToTargetIndex.put(tabs.get(i), i);
         }
diff --git a/chrome/android/javatests/BUILD.gn b/chrome/android/javatests/BUILD.gn
index cd21985..06ffdabe 100644
--- a/chrome/android/javatests/BUILD.gn
+++ b/chrome/android/javatests/BUILD.gn
@@ -1040,6 +1040,7 @@
     "src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsRenderTest.java",
     "src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxPedalsTest.java",
     "src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesTest.java",
+    "src/org/chromium/chrome/browser/omnibox/voice/VoiceSearchPTTest.java",
   ]
 
   deps = [
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceSearchPTTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceSearchPTTest.java
new file mode 100644
index 0000000..e0c64bce
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceSearchPTTest.java
@@ -0,0 +1,137 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.omnibox.voice;
+
+import static androidx.test.espresso.intent.Intents.intending;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
+
+import static org.chromium.base.test.transit.TransitAsserts.assertFinalDestination;
+
+import android.app.Activity;
+import android.app.Instrumentation.ActivityResult;
+import android.content.Intent;
+import android.os.Bundle;
+import android.speech.RecognizerIntent;
+
+import androidx.test.espresso.intent.Intents;
+import androidx.test.filters.LargeTest;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.base.test.util.Restriction;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarButtonVariant;
+import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarStatePredictor;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.transit.AutoResetCtaTransitTestRule;
+import org.chromium.chrome.test.transit.ChromeTransitTestRules;
+import org.chromium.chrome.test.transit.ntp.RegularNewTabPageStation;
+import org.chromium.chrome.test.transit.omnibox.OmniboxFacility;
+import org.chromium.chrome.test.transit.page.WebPageStation;
+import org.chromium.chrome.test.transit.testhtmls.NavigatePageStations;
+import org.chromium.ui.base.DeviceFormFactor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Public Transit tests for Voice Search. */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@Batch(Batch.PER_CLASS)
+@EnableFeatures(ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR_CUSTOMIZATION_V2)
+public final class VoiceSearchPTTest {
+    private static final String VOICE_QUERY = "query";
+
+    @Rule
+    public final AutoResetCtaTransitTestRule mCtaTestRule =
+            ChromeTransitTestRules.fastAutoResetCtaActivityRule();
+
+    @BeforeClass
+    public static void setUpBeforeActivityLaunched() {
+        AdaptiveToolbarStatePredictor.setToolbarStateForTesting(AdaptiveToolbarButtonVariant.VOICE);
+        VoiceRecognitionUtil.setIsVoiceSearchEnabledForTesting(true);
+    }
+
+    @AfterClass
+    public static void tearDownAfterActivityDestroyed() {
+        AdaptiveToolbarStatePredictor.setToolbarStateForTesting(null);
+        VoiceRecognitionUtil.setIsVoiceSearchEnabledForTesting(null);
+    }
+
+    @Before
+    public void setUp() {
+        Intents.init();
+    }
+
+    @After
+    public void tearDown() {
+        Intents.release();
+    }
+
+    @LargeTest
+    @Test
+    @Restriction(DeviceFormFactor.PHONE_OR_TABLET)
+    public void testNtpVoiceSearch_highConfidence_initiatesSearch() {
+        RegularNewTabPageStation ntp = mCtaTestRule.startOnNtp();
+
+        stubSpeechRecognitionResult(VOICE_QUERY, /* isHighConfidence= */ true);
+        WebPageStation searchResultsPage = ntp.clickNtpMicToSearchPage(VOICE_QUERY);
+
+        assertFinalDestination(searchResultsPage);
+    }
+
+    @LargeTest
+    @Test
+    @Restriction(DeviceFormFactor.PHONE_OR_TABLET)
+    public void testToolbarVoiceSearch_highConfidence_initiatesSearch() {
+        String testPageUrl = mCtaTestRule.getTestServer().getURL(NavigatePageStations.PATH_SIMPLE);
+        WebPageStation initialPage = mCtaTestRule.startOnWebPage(testPageUrl);
+
+        stubSpeechRecognitionResult(VOICE_QUERY, /* isHighConfidence= */ true);
+        WebPageStation searchResultsPage = initialPage.clickToolbarMicToSearchPage(VOICE_QUERY);
+
+        assertFinalDestination(searchResultsPage);
+    }
+
+    @LargeTest
+    @Test
+    @Restriction(DeviceFormFactor.PHONE_OR_TABLET)
+    public void testOmniboxVoiceSearch_highConfidence_initiatesSearch() {
+        RegularNewTabPageStation ntp = mCtaTestRule.startOnNtp();
+        OmniboxFacility omnibox = ntp.openOmnibox();
+
+        stubSpeechRecognitionResult(VOICE_QUERY, /* isHighConfidence= */ true);
+        WebPageStation searchResultsPage = omnibox.clickOmniboxMicToSearchPage(VOICE_QUERY);
+
+        assertFinalDestination(searchResultsPage);
+    }
+
+    private static void stubSpeechRecognitionResult(String query, boolean isHighConfidence) {
+        Bundle bundle = new Bundle();
+        bundle.putStringArrayList(RecognizerIntent.EXTRA_RESULTS, new ArrayList<>(List.of(query)));
+        float confidence =
+                isHighConfidence
+                        ? 1.0f
+                        : VoiceRecognitionHandler.VOICE_SEARCH_CONFIDENCE_NAVIGATE_THRESHOLD
+                                - 0.01f;
+        bundle.putFloatArray(RecognizerIntent.EXTRA_CONFIDENCE_SCORES, new float[] {confidence});
+
+        Intent resultIntent = new Intent();
+        resultIntent.putExtras(bundle);
+        ActivityResult result = new ActivityResult(Activity.RESULT_OK, resultIntent);
+
+        intending(hasAction(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)).respondWith(result);
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/UkmTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/UkmTest.java
index 6883dbd0..f1ba93b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/UkmTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/UkmTest.java
@@ -15,10 +15,16 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Features.DisableFeatures;
+import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.metrics.UmaSessionStats;
+import org.chromium.chrome.browser.preferences.Pref;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.content_public.browser.test.util.JavaScriptUtils;
 import org.chromium.ui.base.PageTransition;
 
@@ -108,6 +114,99 @@
 
     @Test
     @SmallTest
+    @EnableFeatures("RestructureMetricsConsentSettings")
+    public void testConsentMigrationOnStartup_SignedOut() throws Exception {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    Profile profile = ProfileManager.getLastUsedRegularProfile();
+                    boolean migrationDone =
+                            UserPrefs.get(profile)
+                                    .getBoolean(Pref.ADVANCED_REPORTING_PROFILE_MIGRATION_DONE);
+                    Assert.assertTrue("Migration done:", migrationDone);
+
+                    // By default in fresh profile, MSBB is disabled.
+                    // So kAdvancedReportingEnabled should be migrated to false.
+                    boolean advancedReportingEnabled =
+                            UserPrefs.get(profile).getBoolean(Pref.ADVANCED_REPORTING_ENABLED);
+                    Assert.assertFalse("Advanced reporting enabled:", advancedReportingEnabled);
+                });
+
+        Tab normalTab = mSyncTestRule.getActivityTab();
+        enableUkmUi(normalTab);
+        Assert.assertFalse("UKM Enabled:", isUkmEnabled(normalTab));
+    }
+
+    @Test
+    @SmallTest
+    @DisableFeatures("RestructureMetricsConsentSettings")
+    public void testConsentMigrationRollback() throws Exception {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> UmaSessionStats.updateMetricsAndCrashReportingForTesting(true));
+
+        // Sign in and set up sync and MSBB.
+        mSyncTestRule.setUpAccountAndEnableHistorySync();
+        Tab normalTab = mSyncTestRule.getActivityTab();
+        enableUkmUi(normalTab);
+        Assert.assertTrue("UKM Enabled:", isUkmEnabled(normalTab));
+
+        // Simulate a rollback scenario: the migration had run in a previous session
+        // (when the flag was enabled) and wrote kAdvancedReportingEnabled to false.
+        // Now, we are starting a session with the flag disabled (rolled back).
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    Profile profile = ProfileManager.getLastUsedRegularProfile();
+                    UserPrefs.get(profile)
+                            .setBoolean(Pref.ADVANCED_REPORTING_PROFILE_MIGRATION_DONE, true);
+                    UserPrefs.get(profile).setBoolean(Pref.ADVANCED_REPORTING_ENABLED, false);
+                });
+
+        // Since the feature flag is DISABLED, the UKM recorder should ignore
+        // advanced_reporting_enabled (which is false) and remain ENABLED (reverting to MSBB logic).
+        Assert.assertTrue("UKM Enabled after rollback:", isUkmEnabled(normalTab));
+    }
+
+    @Test
+    @SmallTest
+    @DisableFeatures("RestructureMetricsConsentSettings")
+    public void testConsentMigrationRollback_MsbbDisabled() throws Exception {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> UmaSessionStats.updateMetricsAndCrashReportingForTesting(true));
+
+        // Sign in and set up sync.
+        mSyncTestRule.setUpAccountAndEnableHistorySync();
+        Tab normalTab = mSyncTestRule.getActivityTab();
+        enableUkmUi(normalTab);
+        Assert.assertTrue("UKM Enabled:", isUkmEnabled(normalTab));
+
+        // Disable MSBB.
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    Profile profile = ProfileManager.getLastUsedRegularProfile();
+                    UserPrefs.get(profile)
+                            .setBoolean(Pref.URL_KEYED_ANONYMIZED_DATA_COLLECTION_ENABLED, false);
+                });
+        Assert.assertFalse("UKM Disabled after MSBB disabled:", isUkmEnabled(normalTab));
+
+        // Simulate a rollback scenario: the migration had run in a previous session
+        // (when the flag was enabled) and wrote kAdvancedReportingEnabled to true.
+        // Now, we are starting a session with the flag disabled (rolled back).
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    Profile profile = ProfileManager.getLastUsedRegularProfile();
+                    UserPrefs.get(profile)
+                            .setBoolean(Pref.ADVANCED_REPORTING_PROFILE_MIGRATION_DONE, true);
+                    UserPrefs.get(profile).setBoolean(Pref.ADVANCED_REPORTING_ENABLED, true);
+                });
+
+        // Since the feature flag is DISABLED, the UKM recorder should ignore
+        // advanced_reporting_enabled (which is true) and remain DISABLED (reverting to MSBB logic).
+        Assert.assertFalse(
+                "UKM Disabled after rollback even if advanced reporting is true:",
+                isUkmEnabled(normalTab));
+    }
+
+    @Test
+    @SmallTest
     // TODO(crbug.com/40117796): Enable the corrersponding C++ test and delete this
     // test.
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/LocationBarModelTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/LocationBarModelTest.java
index 808b3328..09906745 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/LocationBarModelTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/LocationBarModelTest.java
@@ -30,11 +30,11 @@
 import org.chromium.base.test.params.ParameterProvider;
 import org.chromium.base.test.params.ParameterSet;
 import org.chromium.base.test.params.ParameterizedRunner;
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.UrlUtils;
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider.ControlsPosition;
 import org.chromium.chrome.browser.dom_distiller.DomDistillerTabUtils;
@@ -50,8 +50,8 @@
 import org.chromium.chrome.browser.toolbar.top.ToolbarLayout;
 import org.chromium.chrome.browser.url_constants.UrlConstantResolver;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
+import org.chromium.chrome.test.transit.AutoResetCtaTransitTestRule;
 import org.chromium.chrome.test.transit.ChromeTransitTestRules;
-import org.chromium.chrome.test.transit.FreshCtaTransitTestRule;
 import org.chromium.chrome.test.transit.page.WebPageStation;
 import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.url.GURL;
@@ -66,12 +66,15 @@
 @RunWith(ParameterizedRunner.class)
 @ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@Batch(Batch.PER_CLASS)
 public class LocationBarModelTest {
     @Rule
-    public FreshCtaTransitTestRule mActivityTestRule =
-            ChromeTransitTestRules.freshChromeTabbedActivityRule();
+    public AutoResetCtaTransitTestRule mActivityTestRule =
+            ChromeTransitTestRules.autoResetCtaActivityRule();
 
     private WebPageStation mPage;
+    private ToolbarDataProvider.Observer mToolbarObserver;
+    private LocationBarDataProvider.Observer mLocationBarObserver;
 
     @Before
     public void setUp() throws InterruptedException {
@@ -80,7 +83,24 @@
 
     @After
     public void tearDown() {
-        mActivityTestRule.skipWindowAndTabStateCleanup();
+        if (mToolbarObserver != null || mLocationBarObserver != null) {
+            ThreadUtils.runOnUiThreadBlocking(
+                    () -> {
+                        LocationBarModel model =
+                                mActivityTestRule
+                                        .getActivity()
+                                        .getToolbarManager()
+                                        .getLocationBarModelForTesting();
+                        if (mToolbarObserver != null) {
+                            model.removeToolbarDataProviderObserver(mToolbarObserver);
+                            mToolbarObserver = null;
+                        }
+                        if (mLocationBarObserver != null) {
+                            model.removeObserver(mLocationBarObserver);
+                            mLocationBarObserver = null;
+                        }
+                    });
+        }
     }
 
     /**
@@ -177,7 +197,7 @@
         ChromeTabbedActivity activity = mActivityTestRule.getActivity();
         LocationBarModel locationBarModel =
                 activity.getToolbarManager().getLocationBarModelForTesting();
-        ToolbarDataProvider.Observer observer =
+        mToolbarObserver =
                 new ToolbarDataProvider.Observer() {
                     @Override
                     public void onIncognitoStateChanged() {
@@ -192,7 +212,7 @@
                             .getActivity()
                             .getTabModelSelector()
                             .selectModel(fromIncognito);
-                    locationBarModel.addToolbarDataProviderObserver(observer);
+                    locationBarModel.addToolbarDataProviderObserver(mToolbarObserver);
 
                     // Switch to an existing tab.
                     mActivityTestRule
@@ -229,13 +249,13 @@
         ChromeTabbedActivity activity = mActivityTestRule.getActivity();
         LocationBarModel locationBarModel =
                 activity.getToolbarManager().getLocationBarModelForTesting();
-        LocationBarDataProvider.Observer observer = mock(LocationBarDataProvider.Observer.class);
+        mLocationBarObserver = mock(LocationBarDataProvider.Observer.class);
         doAnswer(
                         (invocation) -> {
                             assertEquals(toIncognito, locationBarModel.isIncognito());
                             return null;
                         })
-                .when(observer)
+                .when(mLocationBarObserver)
                 .onIncognitoStateChanged();
 
         ThreadUtils.runOnUiThreadBlocking(
@@ -244,7 +264,7 @@
                             .getActivity()
                             .getTabModelSelector()
                             .selectModel(fromIncognito);
-                    locationBarModel.addObserver(observer);
+                    locationBarModel.addObserver(mLocationBarObserver);
 
                     // Switch to an existing tab.
                     mActivityTestRule
@@ -260,9 +280,9 @@
 
         assertEquals(toIncognito, locationBarModel.isIncognito());
         if (fromIncognito != toIncognito) {
-            verify(observer).onIncognitoStateChanged();
+            verify(mLocationBarObserver).onIncognitoStateChanged();
         } else {
-            verify(observer, times(0)).onIncognitoStateChanged();
+            verify(mLocationBarObserver, times(0)).onIncognitoStateChanged();
         }
     }
 
@@ -281,13 +301,13 @@
         ChromeTabbedActivity activity = mActivityTestRule.getActivity();
         LocationBarModel locationBarModel =
                 activity.getToolbarManager().getLocationBarModelForTesting();
-        LocationBarDataProvider.Observer observer = mock(LocationBarDataProvider.Observer.class);
+        mLocationBarObserver = mock(LocationBarDataProvider.Observer.class);
         doAnswer(
                         (invocation) -> {
                             assertEquals(toIncognito, locationBarModel.isIncognito());
                             return null;
                         })
-                .when(observer)
+                .when(mLocationBarObserver)
                 .onIncognitoStateChanged();
 
         ThreadUtils.runOnUiThreadBlocking(
@@ -296,7 +316,7 @@
                             .getActivity()
                             .getTabModelSelector()
                             .selectModel(fromIncognito);
-                    locationBarModel.addObserver(observer);
+                    locationBarModel.addObserver(mLocationBarObserver);
                 });
 
         // Switch to a new tab.
@@ -304,9 +324,9 @@
 
         assertEquals(toIncognito, locationBarModel.isIncognito());
         if (fromIncognito != toIncognito) {
-            verify(observer).onIncognitoStateChanged();
+            verify(mLocationBarObserver).onIncognitoStateChanged();
         } else {
-            verify(observer, times(0)).onIncognitoStateChanged();
+            verify(mLocationBarObserver, times(0)).onIncognitoStateChanged();
         }
     }
 
@@ -315,15 +335,15 @@
     public void testOnSecurityStateChanged() {
         LocationBarModel locationBarModel =
                 mActivityTestRule.getActivity().getToolbarManager().getLocationBarModelForTesting();
-        LocationBarDataProvider.Observer observer = mock(LocationBarDataProvider.Observer.class);
+        mLocationBarObserver = mock(LocationBarDataProvider.Observer.class);
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
-                    locationBarModel.addObserver(observer);
+                    locationBarModel.addObserver(mLocationBarObserver);
                 });
 
         mActivityTestRule.loadUrl(UrlUtils.encodeHtmlDataUri("test content"));
 
-        verify(observer, atLeast(1)).onSecurityStateChanged();
+        verify(mLocationBarObserver, atLeast(1)).onSecurityStateChanged();
     }
 
     private void assertDisplayAndEditText(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/OmahaServiceStartDelayerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/OmahaServiceStartDelayerTest.java
index cfcbca08..6eabf9b8 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/OmahaServiceStartDelayerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/OmahaServiceStartDelayerTest.java
@@ -32,13 +32,11 @@
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.RobolectricUtil;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 
 /** Tests for the {@link OmahaServiceStartDelayer}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 public class OmahaServiceStartDelayerTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Mock private Activity mActivity;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillDialogControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillDialogControllerTest.java
index 616624598..60a833c9 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillDialogControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillDialogControllerTest.java
@@ -27,7 +27,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.R;
 import org.chromium.components.browser_ui.widget.ActionConfirmationDialog;
 import org.chromium.components.browser_ui.widget.ActionConfirmationDialog.ConfirmationDialogHandler;
@@ -43,7 +42,6 @@
 /** Unit tests for {@link AutofillDialogController}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 public class AutofillDialogControllerTest {
     private static final long NATIVE_AUTOFILL_DIALOG_VIEW = 1234L;
     private static final String TEST_TITLE = "Test Title";
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkOpenerImplUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkOpenerImplUnitTest.java
index ebacece7..d63d2edd 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkOpenerImplUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkOpenerImplUnitTest.java
@@ -28,6 +28,7 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.IntentHandler;
+import org.chromium.chrome.browser.multiwindow.MultiInstanceManager;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkItem;
@@ -44,6 +45,7 @@
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Mock private BookmarkModel mBookmarkModel;
+    @Mock private MultiInstanceManager mMultiInstanceManager;
 
     private Activity mActivity;
     private BookmarkOpenerImpl mOpener;
@@ -134,7 +136,8 @@
                 new BookmarkOpenerImpl(
                         () -> mBookmarkModel,
                         mActivity,
-                        new ComponentName(mActivity, "TestActivity"));
+                        new ComponentName(mActivity, "TestActivity"),
+                        mMultiInstanceManager);
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
index a795a1e9..c9ff788 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
@@ -2876,7 +2876,8 @@
                         eq(mModel),
                         eq(mBottomSheetController),
                         eq(true),
-                        eq(false));
+                        eq(false),
+                        eq(mWindowAndroid));
     }
 
     /** Sets up tabModel and menu coordinator. */
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/contextmenu/ContextMenuMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/contextmenu/ContextMenuMediatorTest.java
index 61c0379..2f8b454 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/contextmenu/ContextMenuMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/contextmenu/ContextMenuMediatorTest.java
@@ -14,7 +14,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import static org.chromium.base.test.util.Batch.UNIT_TESTS;
 import static org.chromium.ui.listmenu.ListItemType.MENU_ITEM;
 import static org.chromium.ui.listmenu.ListItemType.MENU_ITEM_WITH_SUBMENU;
 import static org.chromium.ui.listmenu.ListItemType.SUBMENU_HEADER;
@@ -39,7 +38,6 @@
 import org.chromium.base.Callback;
 import org.chromium.base.FeatureList;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.chrome.browser.contextmenu.ContextMenuCoordinator.ContextMenuItemType;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -58,7 +56,6 @@
 
 /** Unit tests for the context menu mediator. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(UNIT_TESTS)
 @DisableFeatures({ChromeFeatureList.ENABLE_DOWNLOAD_SAVE_AS_CONTEXT_MENU})
 public class ContextMenuMediatorTest {
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/AuthTabColorProviderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/AuthTabColorProviderUnitTest.java
index 6220e78..628e350 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/AuthTabColorProviderUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/AuthTabColorProviderUnitTest.java
@@ -27,12 +27,10 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.ui.base.TestActivity;
 
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class AuthTabColorProviderUnitTest {
     @Rule
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/AuthTabIntentDataProviderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/AuthTabIntentDataProviderUnitTest.java
index 8fbb94aa..74f6330 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/AuthTabIntentDataProviderUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/AuthTabIntentDataProviderUnitTest.java
@@ -32,7 +32,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.R;
@@ -45,7 +44,6 @@
 import org.chromium.ui.base.TestActivity;
 
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class AuthTabIntentDataProviderUnitTest {
     @Rule
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinatorUnitTest.java
index c411cab80..764d54e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinatorUnitTest.java
@@ -44,7 +44,6 @@
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.base.supplier.SettableMonotonicObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
@@ -109,7 +108,6 @@
 
 /** JUnit tests for BaseCustomTabRootUiCoordinator. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 @EnableFeatures(ChromeFeatureList.CROSS_DEVICE_TASK_HANDOFF)
 public final class BaseCustomTabRootUiCoordinatorUnitTest {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
index 72e95e3a..e97a9c3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
@@ -41,7 +41,6 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.browserservices.PostMessageHandler;
 import org.chromium.chrome.browser.browserservices.intents.SessionHolder;
 import org.chromium.chrome.browser.browserservices.verification.ChromeOriginVerifier;
@@ -58,7 +57,6 @@
 
 /** Tests for ClientManager. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(
         manifest = Config.NONE,
         shadows = {ShadowPackageManager.class})
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigatorTest.java
index f78336b6..574b99e0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigatorTest.java
@@ -30,7 +30,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRule;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.browserservices.intents.WebappExtras;
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController.FinishReason;
@@ -53,7 +52,6 @@
 
 /** Tests for {@link CloseButtonNavigator}. */
 @RunWith(ParameterizedRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class CloseButtonNavigatorTest {
     @Parameters
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTrackerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTrackerUnitTest.java
index a61e585..8b806b7 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTrackerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTrackerUnitTest.java
@@ -24,7 +24,6 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.shared_preferences.SharedPreferencesManager;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityLifecycleUmaTracker.ClientIdentifierType;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
@@ -34,7 +33,6 @@
 
 /** Unit test for {@link CustomTabActivityLifecycleUmaTracker}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(shadows = {ShadowSystemClock.class})
 public class CustomTabActivityLifecycleUmaTrackerUnitTest {
     private static final String PACKAGE_A = "com.example.test.package";
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabAdaptiveToolbarBehaviorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabAdaptiveToolbarBehaviorUnitTest.java
index d1742a9..ac4b624 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabAdaptiveToolbarBehaviorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabAdaptiveToolbarBehaviorUnitTest.java
@@ -39,7 +39,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.ActivityTabProvider;
@@ -57,7 +56,6 @@
 
 /** Tests for {@link CustomTabAdaptiveToolbarBehavior}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 @EnableFeatures(ChromeFeatureList.CCT_ADAPTIVE_BUTTON)
 public class CustomTabAdaptiveToolbarBehaviorUnitTest {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegateUnitTest.java
index b7a6faa..f257da3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegateUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarDelegateUnitTest.java
@@ -38,7 +38,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsSizer;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
@@ -52,7 +51,6 @@
 
 /** Unit test for {@link CustomTabBottomBarDelegate}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class CustomTabBottomBarDelegateUnitTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java
index 5197d301..7dc8a23f 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java
@@ -71,7 +71,6 @@
 import org.chromium.base.DeviceInfo;
 import org.chromium.base.IntentUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.blink.mojom.DisplayMode;
@@ -100,7 +99,6 @@
 
 /** Tests for {@link CustomTabIntentDataProvider}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 @DisableFeatures({ChromeFeatureList.CCT_ADAPTIVE_BUTTON})
 public class CustomTabIntentDataProviderTest {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationBarControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationBarControllerTest.java
index 472e108..c798878 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationBarControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationBarControllerTest.java
@@ -26,7 +26,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browserservices.intents.ColorProvider;
@@ -36,7 +35,6 @@
 
 /** Tests for {@link CustomTabNavigationBarController}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class CustomTabNavigationBarControllerTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserverUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserverUnitTest.java
index f7a709b..b89fb49 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserverUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNavigationEventObserverUnitTest.java
@@ -21,7 +21,6 @@
 import org.mockito.junit.MockitoRule;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
@@ -31,7 +30,6 @@
 
 /** Tests for some parts of {@link CustomTabNavigationEventObserver}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class CustomTabNavigationEventObserverUnitTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Mock private CustomTabsConnection mConnection;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateControllerTest.java
index 23adfa1..bd57f07 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateControllerTest.java
@@ -33,14 +33,12 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
 import org.chromium.chrome.browser.night_mode.SystemNightModeMonitor;
 
 /** Tests for {@link CustomTabNightModeStateController}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class CustomTabNightModeStateControllerTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabObserverUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabObserverUnitTest.java
index f60d203..00f5c4c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabObserverUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabObserverUnitTest.java
@@ -27,7 +27,6 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.FeatureOverrides;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.browserservices.intents.SessionHolder;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
@@ -39,7 +38,6 @@
 
 /** Tests for screenshot capture inside {@link CustomTabObserver}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @SuppressWarnings("unchecked")
 public class CustomTabObserverUnitTest {
     private static final String TEST_URL = "https://example.com/test";
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProviderTest.java
index 94a795b..88ba63a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProviderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProviderTest.java
@@ -23,14 +23,12 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.browserservices.intents.ColorProvider;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.ui.system.StatusBarColorController;
 
 /** Tests for {@link CustomTabStatusBarColorProvider}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class CustomTabStatusBarColorProviderTest {
     private static final int USER_PROVIDED_COLOR = 0x99aabbcc;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionUnitTest.java
index eb39946e..748fc80a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsConnectionUnitTest.java
@@ -66,7 +66,6 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.browserservices.PostMessageHandler;
@@ -86,7 +85,6 @@
 
 /** Tests for some parts of {@link CustomTabsConnection}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class CustomTabsConnectionUnitTest {
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsFilterTouchUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsFilterTouchUnitTest.java
index 40876c5..c0e76195 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsFilterTouchUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsFilterTouchUnitTest.java
@@ -20,7 +20,6 @@
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -28,7 +27,6 @@
 
 /** Tests {@link CustomTabActivity} filters touch events from overlay activity. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @CommandLineFlags.Add({
     ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
     ChromeSwitches.DISABLE_NATIVE_INITIALIZATION
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorderTest.java
index 4b2b27b..ed76561e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorderTest.java
@@ -26,7 +26,6 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.customtabs.CustomTabsOpenTimeRecorder.CloseCause;
@@ -37,7 +36,6 @@
 
 /** Tests for some parts of {@link CustomTabsOpenTimeRecorder}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class CustomTabsOpenTimeRecorderTest {
     private static final String CHROME_PACKAGE_NAME = "chrome.package.name";
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabSnapshotControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabSnapshotControllerTest.java
index 5af7086..ecc034c0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabSnapshotControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabSnapshotControllerTest.java
@@ -27,7 +27,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -36,7 +35,6 @@
 
 /** Robolectric tests for {@link IncognitoCustomTabSnapshotController}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class IncognitoCustomTabSnapshotControllerTest {
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java
index 1884612..8deeb49f 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java
@@ -23,7 +23,6 @@
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.RobolectricUtil;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.tab.Tab;
 
@@ -31,7 +30,6 @@
 
 /** Tests for {@link NavigationInfoCaptureTrigger}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class NavigationInfoCaptureTriggerTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/RequestThrottlerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/RequestThrottlerTest.java
index 3224934..23c68ba0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/RequestThrottlerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/RequestThrottlerTest.java
@@ -17,11 +17,9 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 
 /** Tests for RequestThrottler. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class RequestThrottlerTest {
     private static final int UID = 1234;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/TrustedCdnTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/TrustedCdnTest.java
index b1405ee..874967c7d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/TrustedCdnTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/TrustedCdnTest.java
@@ -23,7 +23,6 @@
 import org.chromium.base.UnownedUserDataHost;
 import org.chromium.base.UserDataHost;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TrustedCdn;
@@ -45,7 +44,6 @@
  * functionality.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class TrustedCdnTest {
     private static final GURL PUBLISHER_URL = new GURL("https://www.publisher.com/");
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandlerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandlerTest.java
index 7dac6f0..f642387 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandlerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandlerTest.java
@@ -52,7 +52,6 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Promise;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.ShortcutHelper;
 import org.chromium.chrome.browser.browserservices.intents.SessionHolder;
 import org.chromium.chrome.browser.browserservices.ui.controller.CurrentPageVerifier;
@@ -67,7 +66,6 @@
 
 /** Tests for {@link WebAppLaunchHandler}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class WebAppLaunchHandlerTest {
     static final int WRONG_CLIENT_MODE = 65;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunFilterTouchUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunFilterTouchUnitTest.java
index 5587d015..c45e6849 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunFilterTouchUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunFilterTouchUnitTest.java
@@ -23,7 +23,6 @@
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.FeatureOverrides;
 import org.chromium.base.test.BaseRobolectricTestRule;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -38,7 +37,6 @@
  * MAKE_IDENTITY_MANAGER_SOURCE_OF_ACCOUNT_PART2 launch.
  */
 @RunWith(ParameterizedRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @CommandLineFlags.Add({
     ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
     ChromeSwitches.DISABLE_NATIVE_INITIALIZATION,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunUtilsUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunUtilsUnitTest.java
index 31fa2cc..04bb540 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunUtilsUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/FirstRunUtilsUnitTest.java
@@ -16,7 +16,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.firstrun.FirstRunUtils.SafetyFrePromoArm;
@@ -24,7 +23,6 @@
 
 /** Unit tests for {@link FirstRunUtils}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class FirstRunUtilsUnitTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/ExponentialBackoffSchedulerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/ExponentialBackoffSchedulerTest.java
index ea3845b..3627794 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/ExponentialBackoffSchedulerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/ExponentialBackoffSchedulerTest.java
@@ -12,12 +12,10 @@
 
 import org.chromium.base.FakeTimeTestRule;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 
 /** Tests the ExponentialBackoffScheduler. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class ExponentialBackoffSchedulerTest {
     private static final String PREFERENCE_NAME = "scheduler";
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
index 03a3266..d7cf00e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
@@ -20,7 +20,6 @@
 import org.chromium.base.FakeTimeTestRule;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.omaha.MockRequestGenerator.DeviceType;
 
@@ -39,13 +38,11 @@
 import java.util.List;
 
 /**
- * Tests for the {@link OmahaClient}.
- * Tests override the original OmahaClient's functions with the MockOmahaClient, which
- * provides a way to hook into functions to return values that would normally be provided by the
- * system, such as whether Chrome was installed through the system image.
+ * Tests for the {@link OmahaClient}. Tests override the original OmahaClient's functions with the
+ * MockOmahaClient, which provides a way to hook into functions to return values that would normally
+ * be provided by the system, such as whether Chrome was installed through the system image.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 @SuppressWarnings("UnusedMethod")
 public class OmahaBaseTest {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java
index be7d507..a8aabf79 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java
@@ -15,7 +15,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.omaha.MockRequestGenerator.DeviceType;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
@@ -27,7 +26,6 @@
 
 /** Unit tests for the RequestGenerator class. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class RequestGeneratorTest {
     private static final String INSTALL_SOURCE = "install_source";
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java
index e81b224..692d8711 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/ResponseParserTest.java
@@ -13,7 +13,6 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 
 import java.io.IOException;
@@ -21,7 +20,6 @@
 
 /** Unit tests for the Omaha ResponseParser. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class ResponseParserTest {
     // Note that the Omaha server appends "/" to the end of the URL codebase.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/StringSanitizerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/StringSanitizerTest.java
index 98fdfca..570d052b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/StringSanitizerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/StringSanitizerTest.java
@@ -9,12 +9,10 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 
 /** Tests the Omaha StringSanitizer. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class StringSanitizerTest {
     @Test
     @Feature({"Omaha"})
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/VersionNumberTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/VersionNumberTest.java
index c75564d..f2c9122 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omaha/VersionNumberTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omaha/VersionNumberTest.java
@@ -9,12 +9,10 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.BlockJUnit4ClassRunner;
 
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 
 /** Unit tests for VersionNumber. */
 @RunWith(BlockJUnit4ClassRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class VersionNumberTest {
     private static final String DEVELOPER_BUILD = "0.1 Developer Build";
     private static final String BUILD_BOGUS_1_2_3_4 = "Bogus-1.2.3.4";
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncherTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncherTest.java
index 4a815e7..28426f01 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncherTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncherTest.java
@@ -32,7 +32,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.safety_check.SafetyCheckSettingsFragment;
 import org.chromium.chrome.browser.safety_hub.SafetyHubFragment;
@@ -54,7 +53,6 @@
 /** Tests for password manager helper methods. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class PasswordCheckupLauncherTest {
     private static final AccountInfo TEST_ACCOUNT = TestAccounts.ACCOUNT1;
     private static final String TEST_NO_EMAIL_ADDRESS = null;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/payments/PaymentErrorStringsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/payments/PaymentErrorStringsTest.java
index e4e78ee..16650d1d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/payments/PaymentErrorStringsTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/payments/PaymentErrorStringsTest.java
@@ -11,14 +11,12 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.payments.ErrorStrings;
 
 import java.lang.reflect.Field;
 
 /** Tests for generated {@link ErrorStrings.java}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class PaymentErrorStringsTest {
     // Tests that error strings are generated successfully and have non-empty values.
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/settings/SettingsPageFragmentDelegateImplTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/settings/SettingsPageFragmentDelegateImplTest.java
index bbb417b..f1755f5d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/settings/SettingsPageFragmentDelegateImplTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/settings/SettingsPageFragmentDelegateImplTest.java
@@ -10,6 +10,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -740,4 +741,50 @@
         ShadowLooper.idleMainLooper();
         assertTrue(mDelegate.getHandleBackPressChangedSupplier().get());
     }
+
+    /** Regression test for crash due to activity recreation. http://crbug.com/542023392 */
+    @Test
+    public void testInitSettings_withNullContainmentHelper_delaysSearchCoordinatorCreation() {
+        SettingsContainmentHelper mockContainmentHelper = mock(SettingsContainmentHelper.class);
+        when(mMockSettingsHostFragment.getContainmentHelper()).thenReturn(null);
+
+        mDelegate.initSettings(mContainerView, "");
+
+        ArgumentCaptor<FragmentManager.FragmentLifecycleCallbacks> callbackCaptor =
+                ArgumentCaptor.forClass(FragmentManager.FragmentLifecycleCallbacks.class);
+        verify(mFragmentManager, atLeastOnce())
+                .registerFragmentLifecycleCallbacks(callbackCaptor.capture(), anyBoolean());
+
+        when(mFragmentView.findViewById(R.id.settings_title_in_detailed_pane))
+                .thenReturn(mTitleContainer);
+
+        for (FragmentManager.FragmentLifecycleCallbacks callback :
+                new ArrayList<>(callbackCaptor.getAllValues())) {
+            callback.onFragmentViewCreated(
+                    mFragmentManager, mMultiColumnSettings, mFragmentView, null);
+        }
+
+        // Search coordinator creation is delayed because containmentHelper is null.
+        assertNull(mDelegate.getSearchCoordinator());
+
+        // Now mock containmentHelper becoming available upon fragment attachment.
+        when(mMockSettingsHostFragment.getContainmentHelper()).thenReturn(mockContainmentHelper);
+
+        // Capture newly registered callbacks (the non-recursive attached listener).
+        verify(mFragmentManager, atLeastOnce())
+                .registerFragmentLifecycleCallbacks(callbackCaptor.capture(), eq(false));
+
+        for (FragmentManager.FragmentLifecycleCallbacks callback :
+                new ArrayList<>(callbackCaptor.getAllValues())) {
+            callback.onFragmentAttached(
+                    mFragmentManager,
+                    mMockSettingsHostFragment,
+                    ApplicationProvider.getApplicationContext());
+        }
+
+        SettingsSearchCoordinator searchCoordinator = mDelegate.getSearchCoordinator();
+        assertNotNull(searchCoordinator);
+        verify(mMultiColumnSettings).addObserver(searchCoordinator);
+        verify(mockContainmentHelper).getItemDecorations();
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/share/LensUtilsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/share/LensUtilsTest.java
index 38fad5d..0f9515c7 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/share/LensUtilsTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/share/LensUtilsTest.java
@@ -16,13 +16,11 @@
 import org.robolectric.shadows.ShadowPackageManager;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.IntentHandler;
 
 /** Tests of {@link LensUtils}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 public class LensUtilsTest {
     @Before
     public void setUp() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninAndHistorySyncActivityLauncherImplTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninAndHistorySyncActivityLauncherImplTest.java
index edccc53..3e52a978 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninAndHistorySyncActivityLauncherImplTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninAndHistorySyncActivityLauncherImplTest.java
@@ -33,7 +33,6 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.R;
@@ -63,7 +62,6 @@
  * <p>TODO(crbug.com/354912290): Update this test when the error UI will be implemented.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.PER_CLASS)
 @DisableFeatures({SigninFeatures.FORCE_HISTORY_OPT_IN_SCREEN})
 @Config(
         manifest = Config.NONE,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabAttributesTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabAttributesTest.java
index 8c6f0969..abe3340 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabAttributesTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabAttributesTest.java
@@ -4,11 +4,14 @@
 
 package org.chromium.chrome.browser.tab;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.when;
 
 import androidx.test.filters.SmallTest;
 
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -21,6 +24,9 @@
 import org.chromium.base.UserDataHost;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
 /** Tests for {@link TabAttributes}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
@@ -44,18 +50,18 @@
     @SmallTest
     public void testBasicGetAndSetOperation() {
         // |get| for an uninitialized attribute returns null.
-        Assert.assertNull(TabAttributes.from(mTab).get(ATTR1));
+        assertNull(TabAttributes.from(mTab).get(ATTR1));
 
         // |get| with a default value returns the given default.
-        Assert.assertFalse(TabAttributes.from(mTab).get(ATTR1, false));
+        assertFalse(TabAttributes.from(mTab).get(ATTR1, false));
 
         // |get| returns the stored attribute.
         TabAttributes.from(mTab).set(ATTR1, true);
-        Assert.assertTrue(TabAttributes.from(mTab).get(ATTR1));
+        assertTrue(TabAttributes.from(mTab).get(ATTR1));
 
         // |get| returns null after cleared.
         TabAttributes.from(mTab).clear(ATTR1);
-        Assert.assertNull(TabAttributes.from(mTab).get(ATTR1));
+        assertNull(TabAttributes.from(mTab).get(ATTR1));
     }
 
     @Test
@@ -64,11 +70,28 @@
         TestObject defaultValue = new TestObject();
 
         // The attribute is not set by default, therefore default value is returned.
-        Assert.assertEquals(defaultValue, TabAttributes.from(mTab).get(ATTR1, defaultValue));
+        assertEquals(defaultValue, TabAttributes.from(mTab).get(ATTR1, defaultValue));
 
         // Explicitly set the attribute to null. Now |get| should return null,
         // disregarding the default value.
         TabAttributes.from(mTab).set(ATTR1, null);
-        Assert.assertNull(TabAttributes.from(mTab).get(ATTR1, defaultValue));
+        assertNull(TabAttributes.from(mTab).get(ATTR1, defaultValue));
+    }
+
+    @Test
+    @SmallTest
+    public void testNumEntriesMatchesKeys() {
+        int keyCount = 0;
+        for (Field field : TabAttributeKeys.class.getDeclaredFields()) {
+            if (field.getType().equals(String.class)
+                    && Modifier.isStatic(field.getModifiers())
+                    && Modifier.isFinal(field.getModifiers())) {
+                keyCount++;
+            }
+        }
+        assertEquals(
+                "TabAttributeKeys.NUM_ENTRIES does not match the number of String keys",
+                TabAttributeKeys.NUM_ENTRIES,
+                keyCount);
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreatorJUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreatorJUnitTest.java
index bbce3fcb..26a4f91 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreatorJUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreatorJUnitTest.java
@@ -4,28 +4,47 @@
 
 package org.chromium.chrome.browser.tabmodel;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.Activity;
+import android.content.Context;
 import android.content.Intent;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
 
+import org.chromium.base.IntentUtils;
+import org.chromium.base.UserDataHost;
+import org.chromium.base.supplier.ObservableSuppliers;
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.app.tabmodel.AsyncTabParamsManagerSingleton;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileProvider;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabDelegateFactory;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.base.WindowAndroid;
+import org.chromium.url.GURL;
 
+import java.lang.ref.WeakReference;
 import java.util.List;
 
 /** Unit tests for {@link ChromeTabCreator}. */
@@ -35,17 +54,15 @@
 
     @Before
     public void setUp() {
+        IntentUtils.setForceIsTrustedIntentForTesting(/* isTrusted= */ true);
+        AsyncTabParamsManagerSingleton.getInstance().getAsyncTabParams().clear();
         Activity activity = Robolectric.buildActivity(Activity.class).setup().get();
-        mTabCreator =
-                new ChromeTabCreator(
-                        activity,
-                        mock(WindowAndroid.class),
-                        () -> null,
-                        new OneshotSupplierImpl<ProfileProvider>(),
-                        /* incognito= */ false,
-                        AsyncTabParamsManagerSingleton.getInstance(),
-                        () -> null,
-                        () -> null);
+        mTabCreator = createTabCreator(activity, /* incognito= */ false);
+    }
+
+    @After
+    public void tearDown() {
+        AsyncTabParamsManagerSingleton.getInstance().getAsyncTabParams().clear();
     }
 
     @Test
@@ -80,7 +97,6 @@
                         /* parent= */ null,
                         intent));
     }
-
     @Test
     public void testCreateNewTabs_NullTabModel() {
         // mTabModel is null by default.
@@ -106,4 +122,90 @@
                         true,
                         null));
     }
+
+    @Test
+    @DisableFeatures(ChromeFeatureList.PRECONNECT_ON_TAB_CREATION)
+    public void testCreateNewTab_AsyncParamsRemovedAfterAddTab_Incognito() {
+        runTestAsyncParamsRemovedAfterAddTab(/* incognito= */ true);
+    }
+
+    @Test
+    @DisableFeatures(ChromeFeatureList.PRECONNECT_ON_TAB_CREATION)
+    public void testCreateNewTab_AsyncParamsRemovedAfterAddTab_Standard() {
+        runTestAsyncParamsRemovedAfterAddTab(/* incognito= */ false);
+    }
+
+    private ChromeTabCreator createTabCreator(Activity activity, boolean incognito) {
+        Profile profile = mock(Profile.class);
+        when(profile.isOffTheRecord()).thenReturn(incognito);
+
+        ProfileProvider profileProvider = mock(ProfileProvider.class);
+        when(profileProvider.getOriginalProfile()).thenReturn(profile);
+        when(profileProvider.getOrCreateOffTheRecordProfile()).thenReturn(profile);
+        when(profileProvider.getOffTheRecordProfile(anyBoolean())).thenReturn(profile);
+
+        OneshotSupplierImpl<ProfileProvider> profileProviderSupplier = new OneshotSupplierImpl<>();
+        profileProviderSupplier.set(profileProvider);
+
+        WindowAndroid windowAndroid = mock(WindowAndroid.class);
+        when(windowAndroid.getOcclusionSupplier()).thenReturn(ObservableSuppliers.alwaysFalse());
+        when(windowAndroid.getContext()).thenReturn(new WeakReference<Context>(activity));
+
+        TabDelegateFactory tabDelegateFactory = mock(TabDelegateFactory.class);
+
+        return new ChromeTabCreator(
+                activity,
+                windowAndroid,
+                () -> tabDelegateFactory,
+                profileProviderSupplier,
+                incognito,
+                AsyncTabParamsManagerSingleton.getInstance(),
+                () -> null,
+                () -> null);
+    }
+
+    private void runTestAsyncParamsRemovedAfterAddTab(boolean incognito) {
+        Activity activity = Robolectric.buildActivity(Activity.class).setup().get();
+        ChromeTabCreator tabCreator = createTabCreator(activity, incognito);
+        TabModel tabModel = mock(TabModel.class);
+        TabModelOrderController orderController = mock(TabModelOrderController.class);
+        tabCreator.setTabModel(tabModel);
+        tabCreator.setTabModelOrderController(orderController);
+
+        int tabId = 789;
+        Tab mockTab = mock(Tab.class);
+        when(mockTab.getId()).thenReturn(tabId);
+        when(mockTab.isIncognito()).thenReturn(incognito);
+        when(mockTab.getUrl()).thenReturn(GURL.emptyGURL());
+        when(mockTab.getUserDataHost()).thenReturn(new UserDataHost());
+
+        TabReparentingParams params = new TabReparentingParams(mockTab, null);
+        AsyncTabParamsManagerSingleton.getInstance().add(tabId, params);
+
+        Intent intent = new Intent();
+        IntentHandler.setTabId(intent, tabId);
+
+        doAnswer(
+                        invocation -> {
+                            assertTrue(
+                                    "AsyncTabParams must remain in manager during addTab",
+                                    AsyncTabParamsManagerSingleton.getInstance()
+                                            .hasParamsForTabId(tabId));
+                            return null;
+                        })
+                .when(tabModel)
+                .addTab(any(Tab.class), anyInt(), anyInt(), anyInt());
+
+        tabCreator.createNewTab(
+                new LoadUrlParams("about:blank"),
+                TabLaunchType.FROM_REPARENTING,
+                /* parent= */ null,
+                intent);
+
+        assertFalse(
+                "AsyncTabParams must be removed after createNewTab",
+                AsyncTabParamsManagerSingleton.getInstance().hasParamsForTabId(tabId));
+        verify(tabModel)
+                .addTab(eq(mockTab), anyInt(), eq(TabLaunchType.FROM_REPARENTING), anyInt());
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java
index d5386b76..2d742b0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java
@@ -47,7 +47,6 @@
 import org.chromium.base.task.TaskRunner;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.RobolectricUtil;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.app.tabmodel.AsyncTabParamsManagerSingleton;
@@ -77,7 +76,6 @@
 
 /** Unit tests for the tab persistent store logic. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class TabPersistentStoreUnitTest {
     private static final @TabId int RESTORE_TAB_ID_1 = 31;
     private static final @TabId int RESTORE_TAB_ID_2 = 32;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/ToolbarIphControllerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/ToolbarIphControllerUnitTest.java
index 91898dcb..052a412 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/ToolbarIphControllerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/toolbar/ToolbarIphControllerUnitTest.java
@@ -28,7 +28,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.user_education.IphCommand;
 import org.chromium.chrome.browser.user_education.UserEducationHelper;
@@ -36,7 +35,6 @@
 
 /** Unit tests for {@link ToolbarIphController}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class ToolbarIphControllerUnitTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt
index 115ae51..9d6abc8 100644
--- a/chrome/android/profiles/arm.newest.txt
+++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-153.0.7994.0_pre1674732_rc-r1-merged.afdo.bz2
+chromeos-chrome-arm-153.0.7996.0_pre1675522_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 6efc68e..f1a722c 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -11105,6 +11105,15 @@
       <message name="IDS_MEMORY_SAVER_MODE_PROMO_TEXT" desc="The text shown for the high efficiency mode in-product promo bubble">
         Memory Saver frees up memory from inactive tabs so it can be used by active tabs and other apps
       </message>
+      <message name="IDS_PDF_TRANSLATE_PROMO_TITLE" desc="The title shown for the pdf reading mode translate in-product promo bubble">
+        Translate PDFs
+      </message>
+      <message name="IDS_PDF_TRANSLATE_PROMO_TEXT" desc="The text shown for the pdf reading mode translate in-product promo bubble">
+        PDF translations appear in the reading mode side panel
+      </message>
+      <message name="IDS_PDF_TRANSLATE_PROMO_ACCESSIBLE_TEXT" desc="Screen reader text for the pdf reading mode translate in-product promo bubble">
+        In-Product Help: PDF translations appear in the reading mode side panel
+      </message>
       <if expr="use_titlecase">
         <message name="IDS_MEMORY_SAVER_MODE_PROMO_ACTION_TEXT" desc="In Title Case: The custom action button text for the high efficiency mode in-product promo bubble">
           Turn On
diff --git a/chrome/app/generated_resources_grd/IDS_PDF_TRANSLATE_PROMO_ACCESSIBLE_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_PDF_TRANSLATE_PROMO_ACCESSIBLE_TEXT.png.sha1
new file mode 100644
index 0000000..b1171ad
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_PDF_TRANSLATE_PROMO_ACCESSIBLE_TEXT.png.sha1
@@ -0,0 +1 @@
+cd76067d9ee2b85034b5fbc4cd3fef20b68097f9
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_PDF_TRANSLATE_PROMO_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_PDF_TRANSLATE_PROMO_TEXT.png.sha1
new file mode 100644
index 0000000..b1171ad
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_PDF_TRANSLATE_PROMO_TEXT.png.sha1
@@ -0,0 +1 @@
+cd76067d9ee2b85034b5fbc4cd3fef20b68097f9
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_PDF_TRANSLATE_PROMO_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_PDF_TRANSLATE_PROMO_TITLE.png.sha1
new file mode 100644
index 0000000..b1171ad
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_PDF_TRANSLATE_PROMO_TITLE.png.sha1
@@ -0,0 +1 @@
+cd76067d9ee2b85034b5fbc4cd3fef20b68097f9
\ No newline at end of file
diff --git a/chrome/app/glic_strings.grdp b/chrome/app/glic_strings.grdp
index 475313a..0c1d3f5 100644
--- a/chrome/app/glic_strings.grdp
+++ b/chrome/app/glic_strings.grdp
@@ -216,6 +216,12 @@
   <message name="IDS_SETTINGS_GLIC_KEEP_SIDEPANEL_OPEN_ON_NEW_TABS" desc="Name of the setting to keep side panel open on new tabs.">
     Keep browsing with Gemini when you open new tabs
   </message>
+  <message name="IDS_SETTINGS_GLIC_SHAKE_TRIGGER_TOGGLE" desc="Name of the shake trigger setting.">
+    Shake cursor to select from screen
+  </message>
+  <message name="IDS_SETTINGS_GLIC_SHAKE_TRIGGER_TOGGLE_SUBLABEL" desc="Sublabel describing that shaking the cursor captures a region.">
+    Quickly shake the mouse cursor back and forth to select from the screen
+  </message>
   <message name="IDS_SETTINGS_GLIC_MEDIA_UNDERSTANDING" desc="Name of the media understanding setting.">
     Media Understanding
   </message>
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_SHAKE_TRIGGER_TOGGLE.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_SHAKE_TRIGGER_TOGGLE.png.sha1
new file mode 100644
index 0000000..8d82f43
--- /dev/null
+++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_SHAKE_TRIGGER_TOGGLE.png.sha1
@@ -0,0 +1 @@
+95debec8dc8db199f133e2c687f093489a944130
\ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_SHAKE_TRIGGER_TOGGLE_SUBLABEL.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_SHAKE_TRIGGER_TOGGLE_SUBLABEL.png.sha1
new file mode 100644
index 0000000..8d82f43
--- /dev/null
+++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_SHAKE_TRIGGER_TOGGLE_SUBLABEL.png.sha1
@@ -0,0 +1 @@
+95debec8dc8db199f133e2c687f093489a944130
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 2ce1fe68..1d8a91e 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2025,13 +2025,11 @@
     ":resources_util",
     ":unexpire_flags",
     "//base",
-    "//base:i18n",
     "//cc",
     "//chrome:extra_resources",
     "//chrome:resources",
     "//chrome:strings",
     "//chrome/app/resources:platform_locale_settings",
-    "//chrome/app/theme:theme_resources",
     "//chrome/app/vector_icons",
     "//chrome/browser/accessibility:impl",
     "//chrome/browser/accessibility_annotator",
@@ -2042,7 +2040,6 @@
     "//chrome/browser/ai:impl",
     "//chrome/browser/app_mode",
     "//chrome/browser/autocomplete",
-    "//chrome/browser/autocomplete:aim_eligibility_service",
     "//chrome/browser/autocomplete:impl",
     "//chrome/browser/autofill",
     "//chrome/browser/background",
@@ -2098,7 +2095,6 @@
     "//chrome/browser/data_saver",
     "//chrome/browser/data_sharing",
     "//chrome/browser/data_sharing:impl",
-    "//chrome/browser/desktop_to_mobile_promos:utils",
     "//chrome/browser/device_reauth",
     "//chrome/browser/devtools",
     "//chrome/browser/diagnostics",
@@ -2110,12 +2106,9 @@
     "//chrome/browser/domain_reliability",
     "//chrome/browser/domain_reliability:impl",
     "//chrome/browser/download:impl",
-    "//chrome/browser/download:status_text_builder_utils",
     "//chrome/browser/engagement",
     "//chrome/browser/enterprise",
     "//chrome/browser/enterprise:impl",
-    "//chrome/browser/enterprise/browser_management:management_identity_impl",
-    "//chrome/browser/enterprise/platform_auth:features",
     "//chrome/browser/external_protocol:impl",
     "//chrome/browser/favicon:impl",
     "//chrome/browser/feature_engagement",
@@ -2159,7 +2152,6 @@
     "//chrome/browser/lens/sapisid",
     "//chrome/browser/lifetime",
     "//chrome/browser/lifetime:impl",
-    "//chrome/browser/lifetime:termination_notification",
     "//chrome/browser/local_network_access",
     "//chrome/browser/login_detection",
     "//chrome/browser/lookalikes",
@@ -2168,12 +2160,9 @@
     "//chrome/browser/manta:impl",
     "//chrome/browser/media",
     "//chrome/browser/media:access_handler_impl",
-    "//chrome/browser/media:audio_process_ml_model_forwarder",
     "//chrome/browser/media:impl",
     "//chrome/browser/media/prefs",
     "//chrome/browser/media/router",
-    "//chrome/browser/media/router:media_router_feature",
-    "//chrome/browser/media/router/discovery/access_code:access_code_cast_feature",
     "//chrome/browser/media/webrtc",
     "//chrome/browser/memory:impl",
     "//chrome/browser/metrics",
@@ -2196,7 +2185,6 @@
     "//chrome/browser/optimization_guide:impl",
     "//chrome/browser/origin_trials",
     "//chrome/browser/page_content_annotations",
-    "//chrome/browser/page_content_annotations:extraction_service",
     "//chrome/browser/page_content_annotations:fetcher",
     "//chrome/browser/page_content_annotations:screenshot_service",
     "//chrome/browser/page_image_service",
@@ -3043,17 +3031,12 @@
     deps += [
       "//chrome/app/theme:chrome_unscaled_resources_grit",
       "//chrome/app/vector_icons",
-      "//chrome/browser/accessibility/tree_fixing:prefs",
-      "//chrome/browser/accessibility/tree_fixing:service",
       "//chrome/browser/accessibility_annotator/content_annotator",
       "//chrome/browser/actor/ui",
-      "//chrome/browser/actor/ui:actor_overlay",
       "//chrome/browser/actor/ui:window_controller_impl",
       "//chrome/browser/actor/ui/task_list_bubble:controller_impl",
-      "//chrome/browser/actor/ui/task_list_bubble:ui",
       "//chrome/browser/apps/app_service",
       "//chrome/browser/apps/app_service/publishers",
-      "//chrome/browser/apps/intent_helper:intent_chip_display_prefs",
       "//chrome/browser/apps/link_capturing",
       "//chrome/browser/badging",
       "//chrome/browser/badging:impl",
@@ -3066,9 +3049,7 @@
       "//chrome/browser/error_reporting",
       "//chrome/browser/glic/media",
       "//chrome/browser/glic/selection",
-      "//chrome/browser/headless:headless_extra_parts",
       "//chrome/browser/icon_transcoder",
-      "//chrome/browser/image_editor:image_editor_component_util",
       "//chrome/browser/importer",
       "//chrome/browser/importer:impl",
       "//chrome/browser/indigo",
@@ -3077,7 +3058,6 @@
       "//chrome/browser/lens/region_search",
       "//chrome/browser/media/router:mojo_impl",
       "//chrome/browser/media/router:providers_wired_display_impl",
-      "//chrome/browser/media/router/discovery/access_code:access_code_sink_service",
       "//chrome/browser/metrics/critical_user_journeys",
       "//chrome/browser/metrics/critical_user_journeys:impl",
       "//chrome/browser/metrics/desktop_session_duration",
@@ -4025,7 +4005,6 @@
 
   if (enterprise_watermark) {
     deps += [
-      "//chrome/browser/enterprise/watermark:watermark_lib",
       "//chrome/browser/ui/webui/watermark",
       "//components/enterprise/watermarking",
       "//components/enterprise/watermarking/content",
@@ -4052,7 +4031,6 @@
 
   if (is_linux || is_win || is_mac || is_chromeos) {
     deps += [
-      "//chrome/browser/enterprise/connectors/device_trust:features",
       "//chrome/browser/enterprise/connectors/device_trust/common",
       "//chrome/browser/enterprise/connectors/device_trust/signals",
       "//chrome/browser/enterprise/connectors/device_trust/signals/decorators/common",
@@ -4287,7 +4265,6 @@
     public_deps += [
       "//chrome/browser/extensions:chrome_extensions_browser_client",
       "//chrome/browser/extensions/api",
-      "//chrome/browser/extensions/api:api_registration",
       "//chrome/browser/extensions/keyed_services:keyed_service_factories",
       "//extensions/browser",
     ]
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 99f125b..1ddb878f 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3566,8 +3566,6 @@
     "enable-extension-ai-data-collection";
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
-constexpr char kGemma4ForBuiltInAIInternalName[] = "gemma4-for-built-in-ai";
-
 const FeatureEntry::FeatureParam kDiscountOnShoppyPage[] = {
     {commerce::kDiscountOnShoppyPageParam, "true"}};
 
@@ -10481,8 +10479,7 @@
      flag_descriptions::kClassifierAPIDescription, kOsDesktop,
      MULTI_VALUE_TYPE(kAIClassifierChoices), flag_descriptions::kAIAPIsLinks},
 
-    {kGemma4ForBuiltInAIInternalName,
-     flag_descriptions::kGemma4ForBuiltInAIName,
+    {"gemma4-for-built-in-ai", flag_descriptions::kGemma4ForBuiltInAIName,
      flag_descriptions::kGemma4ForBuiltInAIDescription, kOsDesktop,
      MULTI_VALUE_TYPE(kGemma4Choices), flag_descriptions::kAIAPIsLinks},
 
@@ -10869,7 +10866,7 @@
 #endif  // !BUILDFLAG(IS_ANDROID)
 
     {"glic", flag_descriptions::kGlicName, flag_descriptions::kGlicDescription,
-     kOsDesktop, FEATURE_VALUE_TYPE(features::kGlic)},
+     kOsDesktop | kOsAndroid, FEATURE_VALUE_TYPE(features::kGlic)},
     {"glic-hotkey-local-scope", flag_descriptions::kGlicHotkeyLocalScopeName,
      flag_descriptions::kGlicHotkeyLocalScopeDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kGlicHotkeyLocalScope)},
@@ -10885,7 +10882,7 @@
      flag_descriptions::kGlicZOrderChangesDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kGlicZOrderChanges)},
     {"glic-actor", flag_descriptions::kGlicActorName,
-     flag_descriptions::kGlicActorDescription, kOsDesktop,
+     flag_descriptions::kGlicActorDescription, kOsDesktop | kOsAndroid,
      ENABLE_DISABLE_VALUE_TYPE_AND_VALUE(switches::kEnableFeatures,
                                          "GlicActor,GlicActorUi",
                                          switches::kDisableFeatures,
@@ -10920,10 +10917,6 @@
      flag_descriptions::kGlicSparkSettingsAccessibleLabelsDescription,
      kOsDesktop,
      FEATURE_VALUE_TYPE(features::kGlicSparkSettingsAccessibleLabels)},
-    {"glic-opt-in-dialog-link-a11y-fix",
-     flag_descriptions::kGlicOptInDialogLinkA11yFixName,
-     flag_descriptions::kGlicOptInDialogLinkA11yFixDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(features::kGlicOptInDialogLinkA11yFix)},
     {"glic-actor-autofill", flag_descriptions::kGlicActorAutofillName,
      flag_descriptions::kGlicActorAutofillDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(autofill::features::kGlicActorAutofill)},
@@ -10994,7 +10987,7 @@
     {"glic-default-to-last-active-conversation",
      flag_descriptions::kGlicDefaultToLastActiveConversationName,
      flag_descriptions::kGlicDefaultToLastActiveConversationDescription,
-     kOsDesktop,
+     kOsDesktop | kOsAndroid,
      FEATURE_VALUE_TYPE(features::kGlicDefaultToLastActiveConversation)},
     {"glic-bind-pinned-unbound-tab",
      flag_descriptions::kGlicBindPinnedUnboundTabName,
@@ -11044,6 +11037,10 @@
                                     kGlicGuestUrlPresetTypes,
                                     "GlicGuestUrlPresets")},
 
+    {"glic-caa-guest-error", flag_descriptions::kGlicCaaGuestErrorName,
+     flag_descriptions::kGlicCaaGuestErrorDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(features::kGlicCaaGuestError)},
+
     // Glic enterprise settings
     {"glic-gemini-enterprise-settings-override",
      flag_descriptions::kGlicGeminiEnterpriseSettingsOverrideName,
@@ -13978,15 +13975,6 @@
   }
 #endif  // BUILDFLAG(IS_ANDROID)
 
-  // Only show the Gemma4 flag on Canary/Dev/Unknown and unofficial builds.
-  if (std::string_view(kGemma4ForBuiltInAIInternalName) ==
-      entry.internal_name) {
-    return channel != version_info::Channel::CANARY &&
-           channel != version_info::Channel::DEV &&
-           channel != version_info::Channel::UNKNOWN &&
-           version_info::IsOfficialBuild();
-  }
-
   if (flags::IsFlagExpired(storage, entry.internal_name)) {
     return true;
   }
diff --git a/chrome/browser/actor/execution_engine_origin_gating_browsertest.cc b/chrome/browser/actor/execution_engine_origin_gating_browsertest.cc
index 4807372..331a08ba 100644
--- a/chrome/browser/actor/execution_engine_origin_gating_browsertest.cc
+++ b/chrome/browser/actor/execution_engine_origin_gating_browsertest.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/optimization_guide/browser_test_util.h"
 #include "components/actor/core/actor_features.h"
 #include "components/actor/core/actor_switches.h"
+#include "components/actor/core/aggregated_journal.h"
 #include "components/actor/core/safety_list_manager.h"
 #include "components/actor/public/mojom/actor_types.mojom.h"
 #include "components/optimization_guide/core/filters/hints_component_util.h"
@@ -97,8 +98,27 @@
           // Respond to any pending checks
           if (window.signalRequestIsPending) {
             const temp = window.signalRequestIsPending;
-            temp(request);
             window.signalRequestIsPending = null;
+            temp(request);
+          }
+        }
+      );
+  })();
+)js";
+
+constexpr char kSetUpDelayedUserConfirmationDialogRequestHandler[] =
+    R"js(
+  (() => {
+    client.browser
+      .selectUserConfirmationDialogRequestHandler()
+      .subscribe(
+        request => {
+          window.pendingRequest = request;
+          // Respond to any pending checks
+          if (window.signalRequestIsPending) {
+            const temp = window.signalRequestIsPending;
+            window.signalRequestIsPending = null;
+            temp(request);
           }
         }
       );
@@ -209,7 +229,7 @@
   }
 
   [[nodiscard]] InteractiveTestApi::MultiStep
-  WaitUntilPendingNavigationConfirmationRequest(
+  WaitUntilPendingConfirmationRequest(
       const base::DictValue& expected_request,
       const base::Location& location = FROM_HERE) {
     static constexpr char kGetNavigationConfirmationRequestData[] =
@@ -245,11 +265,16 @@
                         window.signalRequestIsPending = resolve;
                         request = await promise;
                       }
-                      request.onConfirmationDecision({
+                      const arg = {
                         response: {
                           permissionGranted: $1,
                         },
-                      });
+                      };
+                      if (request.onConfirmationDecision) {
+                        request.onConfirmationDecision(arg);
+                      } else if (request.onDialogClosed) {
+                        request.onDialogClosed(arg);
+                      }
                       window.pendingRequest = null;
                     })();
                   )js",
@@ -2122,7 +2147,7 @@
 
   // Verify the background navigation confirmation request was sent to the
   // client.
-  RunTestSequence(WaitUntilPendingNavigationConfirmationRequest(
+  RunTestSequence(WaitUntilPendingConfirmationRequest(
       base::test::ParseJsonDict(content::JsReplace(
           R"({"navigationOrigin": $1, "taskId": $2})",
           url::Origin::Create(second_url), actor_task().id().value()))));
@@ -2239,4 +2264,187 @@
       /*expected_bucket_count=*/1);
 }
 
+enum class UiPromptType {
+  kNone,
+  kNavConfirmation,
+  kUserConfirmationDialog,
+};
+
+struct OutOfTurnTestParam {
+  std::string_view test_name;
+  std::string_view target_host;
+  UiPromptType ui_prompt_type = UiPromptType::kNone;
+  bool expects_permission_granted = false;
+};
+
+class OutOfTurnNavigationBrowserTest
+    : public ExecutionEngineOriginGatingBrowserTestBase,
+      public testing::WithParamInterface<OutOfTurnTestParam> {
+ public:
+  void SetUpOnMainThread() override {
+    ExecutionEngineOriginGatingBrowserTestBase::SetUpOnMainThread();
+    ParseSafetyListsForTesting(SafetyListManager::GetInstance(), R"json(
+      {
+        "navigation_blocked": [
+          { "from": "*", "to": "bar.com" }
+        ]
+      }
+    )json");
+  }
+
+  // Sets up mock web client handlers for confirmation requests. Out-of-turn
+  // navigations are treated like any other navigation and may trigger a user
+  // confirmation dialog or a navigation confirmation request depending on
+  // the target origin, requiring handlers to resolve pending requests.
+  void SetUpUiPrompt(UiPromptType ui_prompt_type) {
+    if (ui_prompt_type == UiPromptType::kNavConfirmation) {
+      RunTestSequence(CreateMockWebClientRequest(
+          kSetUpDelayedNavigationConfirmationRequestHandler));
+    } else if (ui_prompt_type == UiPromptType::kUserConfirmationDialog) {
+      RunTestSequence(CreateMockWebClientRequest(
+          kSetUpDelayedUserConfirmationDialogRequestHandler));
+    }
+  }
+
+  void ResolveUiPromptIfNeeded(UiPromptType ui_prompt_type,
+                               const GURL& target_url,
+                               bool expects_permission_granted) {
+    if (ui_prompt_type == UiPromptType::kUserConfirmationDialog) {
+      RunTestSequence(
+          WaitUntilPendingConfirmationRequest(
+              base::test::ParseJsonDict(content::JsReplace(
+                  R"({"navigationOrigin": $1, "forBlocklistedOrigin": true})",
+                  url::Origin::Create(target_url)))),
+          RespondToPendingRequest(expects_permission_granted));
+    } else if (ui_prompt_type == UiPromptType::kNavConfirmation) {
+      RunTestSequence(
+          WaitUntilPendingConfirmationRequest(
+              base::test::ParseJsonDict(content::JsReplace(
+                  R"({"navigationOrigin": $1, "taskId": $2})",
+                  url::Origin::Create(target_url), actor_task().id().value()))),
+          RespondToPendingRequest(expects_permission_granted));
+    }
+  }
+};
+
+IN_PROC_BROWSER_TEST_P(OutOfTurnNavigationBrowserTest,
+                       IdleEngine_OutOfTurnNavigation) {
+  const OutOfTurnTestParam& param = GetParam();
+  const GURL start_url =
+      embedded_https_test_server().GetURL("example.com", "/actor/link.html");
+  const GURL target_url = embedded_https_test_server().GetURL(
+      param.target_host, "/actor/blank.html");
+
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), start_url));
+  OpenGlicAndCreateTask();
+  actor_task().AddTab(active_tab()->GetHandle(), /*stop_task_on_detach=*/true,
+                      base::DoNothing());
+  CHECK(actor_task().HasTab(active_tab()->GetHandle()));
+
+  SetUpUiPrompt(param.ui_prompt_type);
+
+  content::TestNavigationObserver nav_observer(web_contents());
+
+  // Trigger out-of-turn navigation via JavaScript.
+  EXPECT_TRUE(content::ExecJs(
+      web_contents(),
+      content::JsReplace("window.location.href = $1;", target_url)));
+
+  ResolveUiPromptIfNeeded(param.ui_prompt_type, target_url,
+                          param.expects_permission_granted);
+
+  nav_observer.Wait();
+
+  if (param.expects_permission_granted) {
+    EXPECT_TRUE(nav_observer.last_navigation_succeeded());
+    EXPECT_EQ(web_contents()->GetLastCommittedURL(), target_url);
+  } else {
+    EXPECT_FALSE(nav_observer.last_navigation_succeeded());
+    EXPECT_EQ(web_contents()->GetLastCommittedURL(), start_url);
+  }
+}
+
+IN_PROC_BROWSER_TEST_P(OutOfTurnNavigationBrowserTest,
+                       InterleavedAction_OutOfTurnNavigation) {
+  const OutOfTurnTestParam& param = GetParam();
+  const GURL start_url =
+      embedded_https_test_server().GetURL("example.com", "/actor/link.html");
+  const GURL target_url = embedded_https_test_server().GetURL(
+      param.target_host, "/actor/blank.html");
+
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), start_url));
+  OpenGlicAndCreateTask();
+  actor_task().AddTab(active_tab()->GetHandle(), /*stop_task_on_detach=*/true,
+                      base::DoNothing());
+  CHECK(actor_task().HasTab(active_tab()->GetHandle()));
+
+  SetUpUiPrompt(param.ui_prompt_type);
+
+  content::TestNavigationObserver nav_observer(web_contents());
+
+  int dom_node_id = content::GetDOMNodeId(*main_frame(), "body").value();
+  std::unique_ptr<ToolRequest> click_on_page =
+      MakeClickRequest(*main_frame(), dom_node_id);
+
+  // 1. Trigger out-of-turn navigation via JS.
+  EXPECT_TRUE(content::ExecJs(
+      web_contents(),
+      content::JsReplace("window.location.href = $1;", target_url)));
+
+  // 2. While navigation is deferred/pending, execute a new action via Act().
+  ActResultFuture result;
+  actor_task().Act(ToRequestList(click_on_page), result.GetCallback());
+
+  // 3. Resolve the UI prompt for the out-of-turn navigation if required.
+  ResolveUiPromptIfNeeded(param.ui_prompt_type, target_url,
+                          param.expects_permission_granted);
+
+  // 4. Verify new action completes and navigation finishes according to
+  // expectation.
+  nav_observer.Wait();
+
+  if (param.expects_permission_granted) {
+    ExpectErrorResult(result, mojom::ActionResultCode::kFrameWentAway);
+    EXPECT_TRUE(nav_observer.last_navigation_succeeded());
+    EXPECT_EQ(web_contents()->GetLastCommittedURL(), target_url);
+  } else {
+    ExpectOkResult(result);
+    EXPECT_FALSE(nav_observer.last_navigation_succeeded());
+    EXPECT_EQ(web_contents()->GetLastCommittedURL(), start_url);
+  }
+}
+
+constexpr OutOfTurnTestParam kOutOfTurnTestParams[] = {
+    {.test_name = "SameOriginAllowed",
+     .target_host = "example.com",
+     .ui_prompt_type = UiPromptType::kNone,
+     .expects_permission_granted = true},
+    {.test_name = "NavConfirmationAllowed",
+     .target_host = "foo.com",
+     .ui_prompt_type = UiPromptType::kNavConfirmation,
+     .expects_permission_granted = true},
+    {.test_name = "NavConfirmationDenied",
+     .target_host = "foo.com",
+     .ui_prompt_type = UiPromptType::kNavConfirmation,
+     .expects_permission_granted = false},
+    {.test_name = "UserDialogAllowed",
+     .target_host = "blocked.example.com",
+     .ui_prompt_type = UiPromptType::kUserConfirmationDialog,
+     .expects_permission_granted = true},
+    {.test_name = "UserDialogDenied",
+     .target_host = "blocked.example.com",
+     .ui_prompt_type = UiPromptType::kUserConfirmationDialog,
+     .expects_permission_granted = false},
+    {.test_name = "Blocklisted",
+     .target_host = "bar.com",
+     .ui_prompt_type = UiPromptType::kNone,
+     .expects_permission_granted = false},
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         OutOfTurnNavigationBrowserTest,
+                         testing::ValuesIn(kOutOfTurnTestParams),
+                         [](const auto& info) {
+                           return std::string(info.param.test_name);
+                         });
 }  // namespace actor
diff --git a/chrome/browser/ai/ai_language_model_unittest.cc b/chrome/browser/ai/ai_language_model_unittest.cc
index 22e2b8700..0b1f705 100644
--- a/chrome/browser/ai/ai_language_model_unittest.cc
+++ b/chrome/browser/ai/ai_language_model_unittest.cc
@@ -27,14 +27,11 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/types/expected.h"
-#include "base/version_info/channel.h"
-#include "base/version_info/version_info.h"
 #include "build/build_config.h"
 #include "chrome/browser/ai/ai_test_utils.h"
 #include "chrome/browser/ai/features.h"
 #include "chrome/browser/component_updater/optimization_guide_on_device_model_installer.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
-#include "chrome/common/channel_info.h"
 #include "components/on_device_ai/ai_utils.h"
 #include "components/optimization_guide/core/model_execution/manifest_broker/test/fake_manifest_broker.h"
 #include "components/optimization_guide/core/model_execution/manifest_broker/test/scenario_builder.h"
@@ -624,6 +621,45 @@
   // Test most-creative
   test_sampling_mode(blink::mojom::AILanguageModelSamplingMode::kMostCreative,
                      ElementsAre("UfooEM", IsPromptWithParams(100, 1.2)));
+
+  // Test custom presets configured in metadata.
+  optimization_guide::proto::OnDeviceModelExecutionFeatureConfig config =
+      CreateConfig();
+  optimization_guide::proto::PromptApiMetadata metadata;
+
+  // Fully overridden preset: predictable
+  auto* preset_predictable = metadata.add_sampling_presets();
+  preset_predictable->set_name("predictable");
+  preset_predictable->set_top_k(20);
+  preset_predictable->set_temperature(0.1f);
+
+  // Partial preset override: slightly-creative (overrides temperature only,
+  // top_k falls back to mode-specific default 72).
+  auto* preset_partial = metadata.add_sampling_presets();
+  preset_partial->set_name("slightly-creative");
+  preset_partial->set_temperature(0.5f);
+
+  *config.mutable_feature_metadata() =
+      optimization_guide::AnyWrapProto(metadata);
+
+  optimization_guide::FakeAdaptationAsset::Content content{.config = config};
+  auto custom_asset = std::make_unique<optimization_guide::FakeAdaptationAsset>(
+      std::move(content));
+  fake_broker_->UpdateModelAdaptation(*custom_asset);
+
+  // Test predictable (custom preset overrides both top_k and temperature)
+  test_sampling_mode(blink::mojom::AILanguageModelSamplingMode::kPredictable,
+                     ElementsAre("UfooEM", IsPromptWithParams(20, 0.1f)));
+
+  // Test slightly-creative (partial preset: custom temperature, default top_k
+  // fallback of 72)
+  test_sampling_mode(
+      blink::mojom::AILanguageModelSamplingMode::kSlightlyCreative,
+      ElementsAre("UfooEM", IsPromptWithParams(72, 0.5f)));
+
+  // Test balanced (unconfigured preset falls back to default C++ values)
+  test_sampling_mode(blink::mojom::AILanguageModelSamplingMode::kBalanced,
+                     ElementsAre("UfooEM", IsPromptWithParams(64, 1.0f)));
 }
 
 TEST_F(AILanguageModelTest, SamplingModeDefault) {
@@ -2541,15 +2577,6 @@
 };
 
 TEST_F(AILanguageModelManifestTest, CanCreateAndCreateWithManifestGemma4) {
-  version_info::Channel channel = chrome::GetChannel();
-  if (channel != version_info::Channel::CANARY &&
-      channel != version_info::Channel::DEV &&
-      channel != version_info::Channel::UNKNOWN &&
-      version_info::IsOfficialBuild()) {
-    GTEST_SKIP() << "Experimental use case support is limited to "
-                    "Canary/Dev/Unknown channels and unofficial builds.";
-  }
-
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       kAIApiFoundationalModel, {{"model_version", "v4"}});
@@ -2576,15 +2603,6 @@
 }
 
 TEST_F(AILanguageModelManifestTest, CanCreateBeforeDownloadGemma4) {
-  version_info::Channel channel = chrome::GetChannel();
-  if (channel != version_info::Channel::CANARY &&
-      channel != version_info::Channel::DEV &&
-      channel != version_info::Channel::UNKNOWN &&
-      version_info::IsOfficialBuild()) {
-    GTEST_SKIP() << "Experimental use case support is limited to "
-                    "Canary/Dev/Unknown channels and unofficial builds.";
-  }
-
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       kAIApiFoundationalModel, {{"model_version", "v4"}});
diff --git a/chrome/browser/ai/ai_manager.cc b/chrome/browser/ai/ai_manager.cc
index c372d83b..792f4b1f 100644
--- a/chrome/browser/ai/ai_manager.cc
+++ b/chrome/browser/ai/ai_manager.cc
@@ -25,8 +25,6 @@
 #include "base/types/expected.h"
 #include "base/types/optional_ref.h"
 #include "base/types/pass_key.h"
-#include "base/version_info/channel.h"
-#include "base/version_info/version_info.h"
 #include "chrome/browser/ai/ai_context_bound_object.h"
 #include "chrome/browser/ai/ai_context_bound_object_set.h"
 #include "chrome/browser/ai/ai_language_model.h"
@@ -43,7 +41,6 @@
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/channel_info.h"
 #include "components/language/core/common/locale_util.h"
 #include "components/on_device_ai/ai_utils.h"
 #include "components/optimization_guide/core/delivery/model_util.h"
@@ -57,6 +54,7 @@
 #include "components/optimization_guide/core/optimization_guide_switches.h"
 #include "components/optimization_guide/proto/common_types.pb.h"
 #include "components/optimization_guide/proto/feature_configs.pb.h"
+#include "components/optimization_guide/proto/features/prompt_api.pb.h"
 #include "components/optimization_guide/public/mojom/model_broker.mojom-shared.h"
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/prefs/pref_service.h"
@@ -330,15 +328,6 @@
 template <typename FeatureConfigProto>
 std::optional<std::string> GetExperimentalUseCaseByModelVersion(
     const FeatureConfigProto& feature_config) {
-  // Support experimental use cases on Canary/Dev/Unknown and unofficial builds.
-  version_info::Channel channel = chrome::GetChannel();
-  if (channel != version_info::Channel::CANARY &&
-      channel != version_info::Channel::DEV &&
-      channel != version_info::Channel::UNKNOWN &&
-      version_info::IsOfficialBuild()) {
-    return std::nullopt;
-  }
-
   if (base::FeatureList::IsEnabled(kAIApiFoundationalModel)) {
     std::string model_version = base::GetFieldTrialParamValueByFeature(
         kAIApiFoundationalModel, kModelVersionParam);
@@ -702,6 +691,27 @@
   return blink::mojom::kWritingAssistanceMaxInputTokenSize;
 }
 
+std::string_view AILanguageModelSamplingModeToString(
+    blink::mojom::AILanguageModelSamplingMode sampling_mode) {
+  switch (sampling_mode) {
+    case blink::mojom::AILanguageModelSamplingMode::kMostPredictable:
+      return "most-predictable";
+    case blink::mojom::AILanguageModelSamplingMode::kPredictable:
+      return "predictable";
+    case blink::mojom::AILanguageModelSamplingMode::kSlightlyPredictable:
+      return "slightly-predictable";
+    case blink::mojom::AILanguageModelSamplingMode::kBalanced:
+      return "balanced";
+    case blink::mojom::AILanguageModelSamplingMode::kSlightlyCreative:
+      return "slightly-creative";
+    case blink::mojom::AILanguageModelSamplingMode::kCreative:
+      return "creative";
+    case blink::mojom::AILanguageModelSamplingMode::kMostCreative:
+      return "most-creative";
+  }
+  NOTREACHED();
+}
+
 }  // namespace
 
 // Feature flag for enabling foundational models in the AI API, requires the
@@ -905,7 +915,7 @@
       std::move(options->sampling_params);
   auto params = on_device_model::mojom::SessionParams::New();
 
-  // TODO(crbug.com/502214118): Get values from model-specific configs.
+  // Get sampling mode params values from model metadata, or use fallbacks.
   if (options->sampling_mode.has_value()) {
     switch (options->sampling_mode.value()) {
       case blink::mojom::AILanguageModelSamplingMode::kMostPredictable:
@@ -916,10 +926,18 @@
         params->temperature = kPredictableTemperature;
         params->top_k = kPredictableTopK;
         break;
+      case blink::mojom::AILanguageModelSamplingMode::kSlightlyPredictable:
+        params->temperature = kSlightlyPredictableTemperature;
+        params->top_k = kSlightlyPredictableTopK;
+        break;
       case blink::mojom::AILanguageModelSamplingMode::kBalanced:
         params->temperature = kBalancedTemperature;
         params->top_k = kBalancedTopK;
         break;
+      case blink::mojom::AILanguageModelSamplingMode::kSlightlyCreative:
+        params->temperature = kSlightlyCreativeTemperature;
+        params->top_k = kSlightlyCreativeTopK;
+        break;
       case blink::mojom::AILanguageModelSamplingMode::kCreative:
         params->temperature = kCreativeTemperature;
         params->top_k = kCreativeTopK;
@@ -928,14 +946,40 @@
         params->temperature = kMostCreativeTemperature;
         params->top_k = kMostCreativeTopK;
         break;
-      case blink::mojom::AILanguageModelSamplingMode::kSlightlyPredictable:
-        params->temperature = kSlightlyPredictableTemperature;
-        params->top_k = kSlightlyPredictableTopK;
-        break;
-      case blink::mojom::AILanguageModelSamplingMode::kSlightlyCreative:
-        params->temperature = kSlightlyCreativeTemperature;
-        params->top_k = kSlightlyCreativeTopK;
-        break;
+    }
+
+    std::string_view mode_str =
+        AILanguageModelSamplingModeToString(options->sampling_mode.value());
+    auto metadata = model_client->GetFeatureMetadata();
+    if (!metadata.has_value()) {
+      VLOG(1)
+          << "Manifest metadata missing when resolving sampling preset for: "
+          << mode_str;
+    } else {
+      auto parsed_metadata = AILanguageModel::ParseMetadata(metadata.value());
+      bool preset_found = false;
+      for (const auto& preset : parsed_metadata.sampling_presets()) {
+        if (preset.name() == mode_str) {
+          preset_found = true;
+          if (preset.has_temperature()) {
+            params->temperature = preset.temperature();
+          } else {
+            VLOG(1) << "Sampling preset '" << mode_str
+                    << "' missing temperature in manifest metadata.";
+          }
+          if (preset.has_top_k()) {
+            params->top_k = preset.top_k();
+          } else {
+            VLOG(1) << "Sampling preset '" << mode_str
+                    << "' missing top_k in manifest metadata.";
+          }
+          break;
+        }
+      }
+      if (!preset_found) {
+        VLOG(1) << "Manifest metadata missing sampling preset for: "
+                << mode_str;
+      }
     }
   } else if (sampling_params) {
     params->temperature = sampling_params->temperature;
diff --git a/chrome/browser/ai/ai_rewriter_unittest.cc b/chrome/browser/ai/ai_rewriter_unittest.cc
index 213aab7..d58298a 100644
--- a/chrome/browser/ai/ai_rewriter_unittest.cc
+++ b/chrome/browser/ai/ai_rewriter_unittest.cc
@@ -17,12 +17,9 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/types/expected.h"
-#include "base/version_info/channel.h"
-#include "base/version_info/version_info.h"
 #include "chrome/browser/ai/ai_test_utils.h"
 #include "chrome/browser/ai/features.h"
 #include "chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h"
-#include "chrome/common/channel_info.h"
 #include "components/optimization_guide/core/model_execution/manifest_broker/test/fake_manifest_broker.h"
 #include "components/optimization_guide/core/model_execution/manifest_broker/test/scenario_builder.h"
 #include "components/optimization_guide/core/model_execution/test/mock_on_device_capability.h"
@@ -867,15 +864,6 @@
 };
 
 TEST_F(AIRewriterManifestTest, CanCreateAndCreateWithManifestGemma4) {
-  version_info::Channel channel = chrome::GetChannel();
-  if (channel != version_info::Channel::CANARY &&
-      channel != version_info::Channel::DEV &&
-      channel != version_info::Channel::UNKNOWN &&
-      version_info::IsOfficialBuild()) {
-    GTEST_SKIP() << "Experimental use case support is limited to "
-                    "Canary/Dev/Unknown channels and unofficial builds.";
-  }
-
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       kAIApiFoundationalModel, {{"model_version", "v4"}});
@@ -899,15 +887,6 @@
 }
 
 TEST_F(AIRewriterManifestTest, CanCreateBeforeDownloadGemma4) {
-  version_info::Channel channel = chrome::GetChannel();
-  if (channel != version_info::Channel::CANARY &&
-      channel != version_info::Channel::DEV &&
-      channel != version_info::Channel::UNKNOWN &&
-      version_info::IsOfficialBuild()) {
-    GTEST_SKIP() << "Experimental use case support is limited to "
-                    "Canary/Dev/Unknown channels and unofficial builds.";
-  }
-
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       kAIApiFoundationalModel, {{"model_version", "v4"}});
diff --git a/chrome/browser/ai/ai_summarizer_unittest.cc b/chrome/browser/ai/ai_summarizer_unittest.cc
index aef5b77..2b8e6c2 100644
--- a/chrome/browser/ai/ai_summarizer_unittest.cc
+++ b/chrome/browser/ai/ai_summarizer_unittest.cc
@@ -15,13 +15,10 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/types/expected.h"
-#include "base/version_info/channel.h"
-#include "base/version_info/version_info.h"
 #include "chrome/browser/ai/ai_test_utils.h"
 #include "chrome/browser/ai/features.h"
 #include "chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
-#include "chrome/common/channel_info.h"
 #include "components/optimization_guide/core/model_execution/manifest_broker/test/fake_manifest_broker.h"
 #include "components/optimization_guide/core/model_execution/manifest_broker/test/scenario_builder.h"
 #include "components/optimization_guide/core/model_execution/test/mock_on_device_capability.h"
@@ -1307,15 +1304,6 @@
 }
 
 TEST_F(AISummarizerManifestTest, CanCreateAndCreateWithManifestGemma4) {
-  version_info::Channel channel = chrome::GetChannel();
-  if (channel != version_info::Channel::CANARY &&
-      channel != version_info::Channel::DEV &&
-      channel != version_info::Channel::UNKNOWN &&
-      version_info::IsOfficialBuild()) {
-    GTEST_SKIP() << "Experimental use case support is limited to "
-                    "Canary/Dev/Unknown channels and unofficial builds.";
-  }
-
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       kAIApiFoundationalModel, {{"model_version", "v4"}});
@@ -1339,15 +1327,6 @@
 }
 
 TEST_F(AISummarizerManifestTest, CanCreateBeforeDownloadGemma4) {
-  version_info::Channel channel = chrome::GetChannel();
-  if (channel != version_info::Channel::CANARY &&
-      channel != version_info::Channel::DEV &&
-      channel != version_info::Channel::UNKNOWN &&
-      version_info::IsOfficialBuild()) {
-    GTEST_SKIP() << "Experimental use case support is limited to "
-                    "Canary/Dev/Unknown channels and unofficial builds.";
-  }
-
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       kAIApiFoundationalModel, {{"model_version", "v4"}});
diff --git a/chrome/browser/ai/ai_writer_unittest.cc b/chrome/browser/ai/ai_writer_unittest.cc
index 7f170ac..90091d5 100644
--- a/chrome/browser/ai/ai_writer_unittest.cc
+++ b/chrome/browser/ai/ai_writer_unittest.cc
@@ -17,12 +17,9 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/types/expected.h"
-#include "base/version_info/channel.h"
-#include "base/version_info/version_info.h"
 #include "chrome/browser/ai/ai_test_utils.h"
 #include "chrome/browser/ai/features.h"
 #include "chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h"
-#include "chrome/common/channel_info.h"
 #include "components/optimization_guide/core/model_execution/manifest_broker/test/scenario_builder.h"
 #include "components/optimization_guide/core/model_execution/test/mock_on_device_capability.h"
 #include "components/optimization_guide/core/model_execution/test/substitution_builder.h"
@@ -851,15 +848,6 @@
 };
 
 TEST_F(AIWriterManifestTest, CanCreateAndCreateWithManifestGemma4) {
-  version_info::Channel channel = chrome::GetChannel();
-  if (channel != version_info::Channel::CANARY &&
-      channel != version_info::Channel::DEV &&
-      channel != version_info::Channel::UNKNOWN &&
-      version_info::IsOfficialBuild()) {
-    GTEST_SKIP() << "Experimental use case support is limited to "
-                    "Canary/Dev/Unknown channels and unofficial builds.";
-  }
-
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       kAIApiFoundationalModel, {{"model_version", "v4"}});
@@ -884,15 +872,6 @@
 }
 
 TEST_F(AIWriterManifestTest, CanCreateBeforeDownloadGemma4) {
-  version_info::Channel channel = chrome::GetChannel();
-  if (channel != version_info::Channel::CANARY &&
-      channel != version_info::Channel::DEV &&
-      channel != version_info::Channel::UNKNOWN &&
-      version_info::IsOfficialBuild()) {
-    GTEST_SKIP() << "Experimental use case support is limited to "
-                    "Canary/Dev/Unknown channels and unofficial builds.";
-  }
-
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       kAIApiFoundationalModel, {{"model_version", "v4"}});
diff --git a/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifierJunitTest.java b/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifierJunitTest.java
index 4c18c310..7cb587b4 100644
--- a/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifierJunitTest.java
+++ b/chrome/browser/android/browserservices/verification/java/src/org/chromium/chrome/browser/browserservices/verification/ChromeOriginVerifierJunitTest.java
@@ -27,7 +27,6 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.components.content_relationship_verification.OriginVerifier;
@@ -41,7 +40,6 @@
 
 /** Robolectric tests for ChromeOriginVerifier. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(ChromeOriginVerifierJunitTest.TEST_BATCH_NAME)
 public class ChromeOriginVerifierJunitTest {
     public static final String TEST_BATCH_NAME = "chrome_origin_verifier";
 
diff --git a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/AutofillThirdPartyModeContentProviderTest.java b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/AutofillThirdPartyModeContentProviderTest.java
index f2357b8e..d23d927 100644
--- a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/AutofillThirdPartyModeContentProviderTest.java
+++ b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/AutofillThirdPartyModeContentProviderTest.java
@@ -30,11 +30,9 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.shared_preferences.SharedPreferencesManager;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 
 /** Unit tests for {@link AutofillThirdPartyModeContentProvider}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.PER_CLASS)
 public class AutofillThirdPartyModeContentProviderTest {
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorModuleTest.java b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorModuleTest.java
index cb97ec7..d7eb1730 100644
--- a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorModuleTest.java
+++ b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorModuleTest.java
@@ -61,7 +61,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
@@ -108,7 +107,6 @@
 
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 @EnableFeatures(ChromeFeatureList.AUTOFILL_AI_WITH_DATA_SCHEMA)
 public class EntityEditorModuleTest {
     private static final String USER_EMAIL = "example@gmail.com";
diff --git a/chrome/browser/background/glic/BUILD.gn b/chrome/browser/background/glic/BUILD.gn
index 47279ae..8e57fa8 100644
--- a/chrome/browser/background/glic/BUILD.gn
+++ b/chrome/browser/background/glic/BUILD.gn
@@ -121,6 +121,27 @@
 }
 
 if (!is_android) {
+  source_set("browser_tests") {
+    testonly = true
+    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+    sources = [ "glic_background_mode_manager_browsertest.cc" ]
+    deps = [
+      ":glic",
+      ":impl",
+      "//base/test:test_support",
+      "//chrome/browser:browser_process",
+      "//chrome/browser/glic",
+      "//chrome/browser/glic/test_support",
+      "//chrome/browser/profiles:profile",
+      "//chrome/browser/profiles/keep_alive",
+      "//chrome/test:test_support",
+      "//components/keep_alive_registry",
+      "//components/prefs",
+      "//content/test:test_support",
+      "//testing/gtest",
+    ]
+  }
+
   source_set("interactive_ui_tests") {
     testonly = true
     defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
diff --git a/chrome/browser/background/glic/glic_background_mode_manager.cc b/chrome/browser/background/glic/glic_background_mode_manager.cc
index 4cf4d5b7..f1c6900 100644
--- a/chrome/browser/background/glic/glic_background_mode_manager.cc
+++ b/chrome/browser/background/glic/glic_background_mode_manager.cc
@@ -21,6 +21,8 @@
 #include "chrome/browser/glic/public/glic_enabling.h"
 #include "chrome/browser/glic/public/glic_keyed_service_factory.h"
 #include "chrome/browser/global_features.h"
+#include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
+#include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
 #include "chrome/browser/profiles/nuke_profile_directory_utils.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "components/keep_alive_registry/keep_alive_registry.h"
@@ -220,6 +222,10 @@
   profile_enabled_subscriptions_.erase(profile);
   profile_consent_subscriptions_.erase(profile);
 
+  if (profile_keep_alive_ && profile_keep_alive_->profile() == profile) {
+    profile_keep_alive_.reset();
+  }
+
   // If a profile is removed while in background mode, check if it must now be
   // exited.
   if (keep_alive_) {
@@ -241,6 +247,14 @@
         KeepAliveOrigin::GLIC_LAUNCHER, KeepAliveRestartOption::ENABLED);
   }
 
+  Profile* profile = GlicProfileManager::GetInstance()->GetProfileForLaunch();
+  if (!profile_keep_alive_ || profile_keep_alive_->profile() != profile) {
+    profile_keep_alive_ = profile
+                              ? std::make_unique<ScopedProfileKeepAlive>(
+                                    profile, ProfileKeepAliveOrigin::kGlicView)
+                              : nullptr;
+  }
+
   if (show_status_icon) {
     if (!status_icon_) {
       status_icon_ = GlicStatusIcon::Create(this, status_tray_);
@@ -254,6 +268,7 @@
 void GlicBackgroundModeManager::ExitBackgroundMode() {
   status_icon_.reset();
   keep_alive_.reset();
+  profile_keep_alive_.reset();
 }
 
 void GlicBackgroundModeManager::RegisterHotkeys(
diff --git a/chrome/browser/background/glic/glic_background_mode_manager.h b/chrome/browser/background/glic/glic_background_mode_manager.h
index 8e0917b..8a8eb4e 100644
--- a/chrome/browser/background/glic/glic_background_mode_manager.h
+++ b/chrome/browser/background/glic/glic_background_mode_manager.h
@@ -22,6 +22,7 @@
 #endif
 
 class ScopedKeepAlive;
+class ScopedProfileKeepAlive;
 class StatusTray;
 
 namespace ui {
@@ -114,6 +115,7 @@
   std::unique_ptr<GlicLauncherConfiguration> configuration_;
 
   std::unique_ptr<ScopedKeepAlive> keep_alive_;
+  std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive_;
 
   // TODO(https://crbug.com/378139555): Figure out how to not dangle this
   // pointer (and other instances of StatusTray).
diff --git a/chrome/browser/background/glic/glic_background_mode_manager_browsertest.cc b/chrome/browser/background/glic/glic_background_mode_manager_browsertest.cc
new file mode 100644
index 0000000..d3f973a
--- /dev/null
+++ b/chrome/browser/background/glic/glic_background_mode_manager_browsertest.cc
@@ -0,0 +1,113 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/background/glic/glic_background_mode_manager.h"
+
+#include "base/test/run_until.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/glic/glic_pref_names.h"
+#include "chrome/browser/glic/glic_profile_manager.h"
+#include "chrome/browser/glic/test_support/glic_browser_test.h"
+#include "chrome/browser/global_features.h"
+#include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_test_util.h"
+#include "chrome/test/base/platform_browser_test.h"
+#include "components/keep_alive_registry/keep_alive_registry.h"
+#include "components/keep_alive_registry/keep_alive_types.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/test/browser_test.h"
+
+namespace glic {
+
+class GlicBackgroundModeManagerBrowserTest : public GlicBrowserTest {
+ public:
+  void TearDownOnMainThread() override {
+    GlicProfileManager::ForceProfileForLaunchForTesting(std::nullopt);
+    g_browser_process->local_state()->SetBoolean(prefs::kGlicLauncherEnabled,
+                                                 false);
+    GlicBrowserTest::TearDownOnMainThread();
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(GlicBackgroundModeManagerBrowserTest,
+                       KeepAliveStateFollowsLauncherPref) {
+  auto* keep_alive_registry = KeepAliveRegistry::GetInstance();
+  auto* profile_manager = g_browser_process->profile_manager();
+  Profile* profile = GetProfile();
+
+  // Ensure global hotkey scope is disabled for this test so state is tied
+  // directly to the launcher pref.
+  g_browser_process->local_state()->SetBoolean(
+      prefs::kGlicHotkeyGlobalScopeEnabled, false);
+  g_browser_process->local_state()->SetBoolean(prefs::kGlicLauncherEnabled,
+                                               false);
+
+  ASSERT_FALSE(
+      keep_alive_registry->IsOriginRegistered(KeepAliveOrigin::GLIC_LAUNCHER));
+  EXPECT_FALSE(profile_manager->HasKeepAliveForTesting(
+      profile, ProfileKeepAliveOrigin::kGlicView));
+
+  g_browser_process->local_state()->SetBoolean(prefs::kGlicLauncherEnabled,
+                                               true);
+  EXPECT_TRUE(
+      keep_alive_registry->IsOriginRegistered(KeepAliveOrigin::GLIC_LAUNCHER));
+  EXPECT_TRUE(profile_manager->HasKeepAliveForTesting(
+      profile, ProfileKeepAliveOrigin::kGlicView));
+
+  g_browser_process->local_state()->SetBoolean(prefs::kGlicLauncherEnabled,
+                                               false);
+  EXPECT_FALSE(
+      keep_alive_registry->IsOriginRegistered(KeepAliveOrigin::GLIC_LAUNCHER));
+  EXPECT_TRUE(base::test::RunUntil([&]() {
+    return !profile_manager->HasKeepAliveForTesting(
+        profile, ProfileKeepAliveOrigin::kGlicView);
+  }));
+}
+
+// ChromeOS does not support creating multiple profiles without user session
+// setup.
+#if !BUILDFLAG(IS_CHROMEOS)
+IN_PROC_BROWSER_TEST_F(GlicBackgroundModeManagerBrowserTest,
+                       KeepAliveTransfersWhenLaunchProfileChanges) {
+  auto* profile_manager = g_browser_process->profile_manager();
+  Profile* profile0 = GetProfile();
+
+  // Create a second profile.
+  base::FilePath path1 = profile_manager->GenerateNextProfileDirectoryPath();
+  Profile& profile1 =
+      profiles::testing::CreateProfileSync(profile_manager, path1);
+
+  g_browser_process->local_state()->SetBoolean(
+      prefs::kGlicHotkeyGlobalScopeEnabled, false);
+
+  // Force launch profile to profile0.
+  GlicProfileManager::ForceProfileForLaunchForTesting(profile0);
+  g_browser_process->local_state()->SetBoolean(prefs::kGlicLauncherEnabled,
+                                               true);
+
+  EXPECT_TRUE(profile_manager->HasKeepAliveForTesting(
+      profile0, ProfileKeepAliveOrigin::kGlicView));
+  EXPECT_FALSE(profile_manager->HasKeepAliveForTesting(
+      &profile1, ProfileKeepAliveOrigin::kGlicView));
+
+  // Change launch profile to profile1 and re-enter background mode.
+  GlicProfileManager::ForceProfileForLaunchForTesting(&profile1);
+  auto* background_manager =
+      g_browser_process->GetFeatures()->glic_background_mode_manager();
+  ASSERT_TRUE(background_manager);
+  background_manager->EnterBackgroundMode(/*show_status_icon=*/false);
+
+  // Keepalive should have transferred to profile1.
+  EXPECT_TRUE(profile_manager->HasKeepAliveForTesting(
+      &profile1, ProfileKeepAliveOrigin::kGlicView));
+  EXPECT_TRUE(base::test::RunUntil([&]() {
+    return !profile_manager->HasKeepAliveForTesting(
+        profile0, ProfileKeepAliveOrigin::kGlicView);
+  }));
+}
+#endif  // !BUILDFLAG(IS_CHROMEOS)
+
+}  // namespace glic
diff --git a/chrome/browser/bookmarks/android/java/res/values/dimens.xml b/chrome/browser/bookmarks/android/java/res/values/dimens.xml
index deec627..1a4ef489 100644
--- a/chrome/browser/bookmarks/android/java/res/values/dimens.xml
+++ b/chrome/browser/bookmarks/android/java/res/values/dimens.xml
@@ -26,4 +26,5 @@
   <dimen name="bookmark_popup_folder_view_padding">12dp</dimen>
   <dimen name="bookmark_popup_folder_icon_container_size">32dp</dimen>
   <dimen name="bookmark_popup_input_padding">16dp</dimen>
+  <dimen name="bookmark_desktop_toolbar_title_margin_start">16dp</dimen>
 </resources>
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java
index a62e08a..6e98c7d 100644
--- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java
+++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java
@@ -64,7 +64,8 @@
                 new BookmarkOpenerImpl(
                         () -> BookmarkModel.getForProfile(profile),
                         /* context= */ host.getContext(),
-                        componentName);
+                        componentName,
+                        /* multiInstanceManager= */ null);
 
         mBookmarkUiPrefs = new BookmarkUiPrefs(ChromeSharedPreferences.getInstance());
         // Provide the BackPressManager to the coordinator so it can manage itself.
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPane.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPane.java
index 7845c46f..5d598e25 100644
--- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPane.java
+++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPane.java
@@ -95,7 +95,8 @@
                     new BookmarkOpenerImpl(
                             () -> BookmarkModel.getForProfile(originalProfile),
                             mContext,
-                            componentName);
+                            componentName,
+                            /* multiInstanceManager= */ null);
             mBookmarkUiPrefs = new BookmarkUiPrefs(ChromeSharedPreferences.getInstance());
             mBookmarkManager =
                     new BookmarkManagerCoordinator(
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarCoordinator.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarCoordinator.java
index fdb6fd2..66b52c1 100644
--- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarCoordinator.java
+++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarCoordinator.java
@@ -64,6 +64,13 @@
                                 null,
                                 isDialogUi);
         mToolbar.setNormalBackgroundColor(Color.TRANSPARENT);
+        if (BookmarkUtils.isDesktopBookmarksLayoutEnabled()) {
+            mToolbar.setTitleMarginStart(
+                    context.getResources()
+                            .getDimensionPixelSize(
+                                    R.dimen.bookmark_desktop_toolbar_title_margin_start));
+        }
+
         updateToolbarPadding(context);
 
         mToolbar.initializeSearchView(
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BasicBookmarkQueryHandlerUnitTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BasicBookmarkQueryHandlerUnitTest.java
index 1f68a9eb..5e39f1e 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BasicBookmarkQueryHandlerUnitTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BasicBookmarkQueryHandlerUnitTest.java
@@ -18,7 +18,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.bookmarks.BookmarkListEntry.ViewType;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.url.GURL;
@@ -26,7 +25,6 @@
 import java.util.List;
 
 /** Unit tests for {@link BasicBookmarkQueryHandler}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class BasicBookmarkQueryHandlerUnitTest {
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkAddNewFolderCoordinatorTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkAddNewFolderCoordinatorTest.java
index 6ad874cd..5d9d5c0 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkAddNewFolderCoordinatorTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkAddNewFolderCoordinatorTest.java
@@ -28,7 +28,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.R;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.ui.base.TestActivity;
@@ -38,7 +37,6 @@
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** Unit tests for {@link BookmarkAddNewFolderCoordinator}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class BookmarkAddNewFolderCoordinatorTest {
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderPickerMediatorUnitTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderPickerMediatorUnitTest.java
index 5086a39..5064613 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderPickerMediatorUnitTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkFolderPickerMediatorUnitTest.java
@@ -36,7 +36,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.bookmarks.BookmarkListEntry.ViewType;
 import org.chromium.chrome.browser.bookmarks.BookmarkUiPrefs.BookmarkRowDisplayPref;
@@ -59,7 +58,6 @@
 import java.util.Arrays;
 
 /** Unit tests for {@link BookmarkFolderPickerMediator}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 public class BookmarkFolderPickerMediatorUnitTest {
     @Rule
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkImageFetcherTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkImageFetcherTest.java
index 21ba570..911ff16 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkImageFetcherTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkImageFetcherTest.java
@@ -37,7 +37,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.page_image_service.ImageServiceBridge;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.ui.favicon.FaviconHelper;
@@ -58,7 +57,6 @@
 import java.util.Arrays;
 
 /** Unit tests for {@link BookmarkImageFetcher}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class BookmarkImageFetcherTest {
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerDragHelperTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerDragHelperTest.java
index 54f992a..1cdf6cf 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerDragHelperTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerDragHelperTest.java
@@ -32,7 +32,6 @@
 import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkType;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
@@ -41,7 +40,6 @@
 import java.util.concurrent.TimeUnit;
 
 /** Unit tests for {@link BookmarkManagerDragHelper}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class BookmarkManagerDragHelperTest {
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerOpenerTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerOpenerTest.java
index 325f3d5..e10314e 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerOpenerTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerOpenerTest.java
@@ -21,7 +21,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.UserActionTester;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileResolver;
@@ -30,7 +29,6 @@
 import org.chromium.ui.base.TestActivity;
 
 /** Unit tests for {@link BookmarkManagerOpener}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class BookmarkManagerOpenerTest {
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerViewBinderTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerViewBinderTest.java
index d0303c4..21086c43 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerViewBinderTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerViewBinderTest.java
@@ -28,7 +28,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.signin.PersonalizedSigninPromoView;
 import org.chromium.components.bookmarks.BookmarkId;
@@ -37,7 +36,6 @@
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /** Unit tests for {@link BookmarkManagerViewBinder}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class BookmarkManagerViewBinderTest {
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediatorUnitTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediatorUnitTest.java
index 2b48cf6..6cc1662 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediatorUnitTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediatorUnitTest.java
@@ -22,7 +22,6 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.commerce.PriceTrackingUtils;
 import org.chromium.chrome.browser.commerce.PriceTrackingUtilsJni;
@@ -46,7 +45,6 @@
 import org.chromium.url.GURL;
 
 /** Unit tests for {@link BookmarkSaveFlowMediator}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(
         manifest = Config.NONE,
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayoutTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayoutTest.java
index 811c4ddd..2f8351b 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayoutTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayoutTest.java
@@ -24,12 +24,10 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.R;
 import org.chromium.ui.base.TestActivity;
 
 /** Unit tests for {@link BookmarkTextInputLayout}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class BookmarkTextInputLayoutTest {
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediatorTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediatorTest.java
index 7a7ff9711..0bee2b2e 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediatorTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediatorTest.java
@@ -43,7 +43,6 @@
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.RobolectricUtil;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.R;
@@ -76,7 +75,6 @@
 import java.util.function.BooleanSupplier;
 
 /** Unit tests for {@link BookmarkToolbarMediator}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 @DisableFeatures({ChromeFeatureList.ANDROID_DESKTOP_BOOKMARK_LAYOUT})
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinderTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinderTest.java
index b9ed780e..fe77f67 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinderTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinderTest.java
@@ -17,12 +17,10 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /** Unit tests for {@link BookmarkToolbarViewBinder}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class BookmarkToolbarViewBinderTest {
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkUiPrefsUnitTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkUiPrefsUnitTest.java
index bcc16cf5..81a6988 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkUiPrefsUnitTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkUiPrefsUnitTest.java
@@ -24,14 +24,12 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.shared_preferences.SharedPreferencesManager;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.bookmarks.BookmarkUiPrefs.BookmarkRowDisplayPref;
 import org.chromium.chrome.browser.bookmarks.BookmarkUiPrefs.BookmarkRowSortOrder;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
 
 /** Unit tests for {@link BookmarkUiPrefs}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class BookmarkUiPrefsUnitTest {
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkUndoControllerTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkUndoControllerTest.java
index 4330bab..1d0694c1 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkUndoControllerTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkUndoControllerTest.java
@@ -26,7 +26,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
@@ -36,7 +35,6 @@
 
 /** Unit tests for {@link BookmarkUndoController}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class BookmarkUndoControllerTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkUtilsTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkUtilsTest.java
index 2b3a5f04..0329ca86 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkUtilsTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkUtilsTest.java
@@ -38,7 +38,6 @@
 import org.chromium.base.Callback;
 import org.chromium.base.DeviceInfo;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
@@ -76,7 +75,6 @@
 import java.util.List;
 
 /** Unit tests for {@link BookmarkUtils}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 @DisableFeatures(ChromeFeatureList.ANDROID_DESKTOP_BOOKMARK_POPUP)
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkQueryHandlerTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkQueryHandlerTest.java
index 3a5f6bf..b0223f3 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkQueryHandlerTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkQueryHandlerTest.java
@@ -41,7 +41,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.bookmarks.BookmarkUiPrefs.BookmarkRowSortOrder;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.components.bookmarks.BookmarkId;
@@ -59,7 +58,6 @@
 import java.util.List;
 
 /** Unit tests for {@link ImprovedBookmarkQueryHandler}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class ImprovedBookmarkQueryHandlerTest {
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkRowCoordinatorTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkRowCoordinatorTest.java
index 6d7fec7..0a7ffdb0 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkRowCoordinatorTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkRowCoordinatorTest.java
@@ -39,7 +39,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.bookmarks.BookmarkUiPrefs.BookmarkRowDisplayPref;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkItem;
@@ -59,7 +58,6 @@
 import org.chromium.url.JUnitTestGURLs;
 
 /** Unit tests for {@link ImprovedBookmarkRowCoordinator}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class ImprovedBookmarkRowCoordinatorTest {
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/PendingRunnableTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/PendingRunnableTest.java
index ad92d2af..ca353df 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/PendingRunnableTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/PendingRunnableTest.java
@@ -19,10 +19,8 @@
 import org.chromium.base.task.TaskTraits;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.RobolectricUtil;
-import org.chromium.base.test.util.Batch;
 
 /** Unit tests for {@link PendingRunnable}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class PendingRunnableTest {
diff --git a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/ShoppingAccessoryCoordinatorTest.java b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/ShoppingAccessoryCoordinatorTest.java
index 6ed29f4..fe826d75 100644
--- a/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/ShoppingAccessoryCoordinatorTest.java
+++ b/chrome/browser/bookmarks/android/junit/src/org/chromium/chrome/browser/bookmarks/ShoppingAccessoryCoordinatorTest.java
@@ -22,7 +22,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.bookmarks.ShoppingAccessoryViewProperties.PriceInfo;
 import org.chromium.components.commerce.core.ShoppingService;
 import org.chromium.components.payments.ui.CurrencyFormatter;
@@ -33,7 +32,6 @@
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** Unit tests for {@link ShoppingAccessoryCoordinator}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class ShoppingAccessoryCoordinatorTest {
diff --git a/chrome/browser/browsing_data/browsing_data_model_browsertest.cc b/chrome/browser/browsing_data/browsing_data_model_browsertest.cc
index af3166e..274e133 100644
--- a/chrome/browser/browsing_data/browsing_data_model_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_model_browsertest.cc
@@ -431,8 +431,6 @@
   }
 
   void SetUpOnMainThread() override {
-    PrivacySandboxSettingsFactory::GetForProfile(browser()->GetProfile())
-        ->SetAllPrivacySandboxAllowedForTesting();
     // Mark all Privacy Sandbox APIs as attested since the test cases are
     // testing behaviors not related to attestations.
     privacy_sandbox::PrivacySandboxAttestations::GetInstance()
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index 43c2bd6..d8fdc90e 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -69,7 +69,6 @@
 #include "chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h"
 #include "chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.h"
 #include "chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_factory.h"
-#include "chrome/browser/privacy_sandbox/privacy_sandbox_settings_factory.h"
 #include "chrome/browser/private_verification_tokens/private_verification_tokens_service.h"
 #include "chrome/browser/private_verification_tokens/private_verification_tokens_service_factory.h"
 #include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
@@ -131,7 +130,6 @@
 #include "components/permissions/permission_actions_history.h"
 #include "components/permissions/permission_decision_auto_blocker.h"
 #include "components/prefs/pref_service.h"
-#include "components/privacy_sandbox/privacy_sandbox_settings.h"
 #include "components/reading_list/core/reading_list_model.h"
 #include "components/safe_browsing/core/browser/verdict_cache_manager.h"
 #include "components/search_engines/search_engine_choice/search_engine_choice_service.h"
@@ -653,12 +651,6 @@
     }
 
     if (filter_builder->MatchesMostOriginsAndDomains()) {
-      auto* privacy_sandbox_settings =
-          PrivacySandboxSettingsFactory::GetForProfile(profile_);
-      if (privacy_sandbox_settings) {
-        privacy_sandbox_settings->OnCookiesCleared();
-      }
-
 #if BUILDFLAG(IS_ANDROID)
       Java_PackageHash_onCookiesDeleted(base::android::AttachCurrentThread(),
                                         profile_->GetJavaObject());
@@ -784,13 +776,6 @@
         ContentSettingsType::PERMISSION_ACTIONS_HISTORY, delete_begin,
         delete_end, website_settings_filter);
 
-    if (auto* privacy_sandbox_settings =
-            PrivacySandboxSettingsFactory::GetForProfile(profile_)) {
-      privacy_sandbox_settings->ClearFledgeJoiningAllowedSettings(delete_begin_,
-                                                                  delete_end_);
-      privacy_sandbox_settings->ClearTopicSettings(delete_begin_, delete_end_);
-    }
-
     // Cleared for both DATA_TYPE_HISTORY and DATA_TYPE_CONTENT_SETTINGS.
     host_content_settings_map_->ClearSettingsForOneTypeWithPredicate(
         ContentSettingsType::COOKIE_CONTROLS_METADATA, delete_begin, delete_end,
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 12ca5ad..365decb 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
@@ -4298,55 +4298,6 @@
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
-// When most cookies are cleared, PrivacySandboxSettings should call the
-// OnTopicsDataAccessibleSinceUpdated() method of its observers.
-TEST_F(ChromeBrowsingDataRemoverDelegateTest,
-       Call_OnTopicsDataAccessibleSinceUpdated_WhenClearingMostCookies) {
-  privacy_sandbox::PrivacySandboxSettings* settings =
-      PrivacySandboxSettingsFactory::GetForProfile(GetProfile());
-  privacy_sandbox_test_util::MockPrivacySandboxObserver observer;
-  base::ScopedObservation<privacy_sandbox::PrivacySandboxSettings,
-                          privacy_sandbox::PrivacySandboxSettings::Observer>
-      obs(&observer);
-  obs.Observe(settings);
-
-  EXPECT_CALL(observer, OnTopicsDataAccessibleSinceUpdated()).Times(1);
-
-  std::unique_ptr<BrowsingDataFilterBuilder> filter_builder =
-      BrowsingDataFilterBuilder::Create(
-          BrowsingDataFilterBuilder::Mode::kPreserve);
-  filter_builder->AddRegisterableDomain("example.test");
-  ASSERT_TRUE(filter_builder->MatchesMostOriginsAndDomains());
-  BlockUntilOriginDataRemoved(base::Time::Min(), base::Time::Max(),
-                              content::BrowsingDataRemover::DATA_TYPE_COOKIES,
-                              std::move(filter_builder));
-}
-
-// If only some cookies are cleared, PrivacySandboxSettings should NOT call the
-// OnTopicsDataAccessibleSinceUpdated() method of its observers.
-TEST_F(
-    ChromeBrowsingDataRemoverDelegateTest,
-    DontCall_OnTopicsDataAccessibleSinceUpdated_WhenOnlyClearingPartitionedCookies) {
-  privacy_sandbox::PrivacySandboxSettings* settings =
-      PrivacySandboxSettingsFactory::GetForProfile(GetProfile());
-  privacy_sandbox_test_util::MockPrivacySandboxObserver observer;
-  base::ScopedObservation<privacy_sandbox::PrivacySandboxSettings,
-                          privacy_sandbox::PrivacySandboxSettings::Observer>
-      obs(&observer);
-  obs.Observe(settings);
-
-  EXPECT_CALL(observer, OnTopicsDataAccessibleSinceUpdated()).Times(0);
-
-  // Create a filter builder that deletes only partitioned cookies.
-  std::unique_ptr<BrowsingDataFilterBuilder> filter_builder =
-      BrowsingDataFilterBuilder::Create(
-          BrowsingDataFilterBuilder::Mode::kPreserve);
-  filter_builder->SetPartitionedCookiesOnly(true);
-  ASSERT_FALSE(filter_builder->MatchesMostOriginsAndDomains());
-  BlockUntilOriginDataRemoved(base::Time::Min(), base::Time::Max(),
-                              content::BrowsingDataRemover::DATA_TYPE_COOKIES,
-                              std::move(filter_builder));
-}
 
 #if !BUILDFLAG(IS_ANDROID)
 // Ensures New Tab page local storage is clear when Microsoft auth service
diff --git a/chrome/browser/commerce/coupons/android/junit/src/org/chromium/chrome/browser/commerce/coupons/DiscountsBottomSheetContentMediatorTest.java b/chrome/browser/commerce/coupons/android/junit/src/org/chromium/chrome/browser/commerce/coupons/DiscountsBottomSheetContentMediatorTest.java
index cfb6036..04462b3 100644
--- a/chrome/browser/commerce/coupons/android/junit/src/org/chromium/chrome/browser/commerce/coupons/DiscountsBottomSheetContentMediatorTest.java
+++ b/chrome/browser/commerce/coupons/android/junit/src/org/chromium/chrome/browser/commerce/coupons/DiscountsBottomSheetContentMediatorTest.java
@@ -34,7 +34,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.commerce.ShoppingServiceFactory;
@@ -52,7 +51,6 @@
 import java.util.List;
 
 /** Unit Tests for {@link DiscountsBottomSheetContentMediator}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 public class DiscountsBottomSheetContentMediatorTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/browser/commerce/price_insights/android/javatests/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetMediatorTest.java b/chrome/browser/commerce/price_insights/android/javatests/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetMediatorTest.java
index a42aab3..b483c508 100644
--- a/chrome/browser/commerce/price_insights/android/javatests/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetMediatorTest.java
+++ b/chrome/browser/commerce/price_insights/android/javatests/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetMediatorTest.java
@@ -51,7 +51,6 @@
 import org.chromium.base.supplier.ObservableSuppliers;
 import org.chromium.base.supplier.SettableNonNullObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.commerce.ShoppingServiceFactory;
@@ -75,7 +74,6 @@
 import java.util.Arrays;
 
 /** Tests for {@link PriceInsightsBottomSheetMediator}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @EnableFeatures({NotificationFeatureMap.CACHE_NOTIIFICATIONS_ENABLED})
 @Config(
diff --git a/chrome/browser/context_hub/BUILD.gn b/chrome/browser/context_hub/BUILD.gn
index 4ae5c1a..8ea050585 100644
--- a/chrome/browser/context_hub/BUILD.gn
+++ b/chrome/browser/context_hub/BUILD.gn
@@ -48,6 +48,7 @@
     "//chrome/browser/tab_group_sync",
     "//chrome/browser/tab_group_sync:factories",
     "//components/saved_tab_groups/public",
+    "//components/sessions",
     "//sql",
   ]
 }
diff --git a/chrome/browser/context_hub/context_hub_service.cc b/chrome/browser/context_hub/context_hub_service.cc
index c419942..cc67ffd 100644
--- a/chrome/browser/context_hub/context_hub_service.cc
+++ b/chrome/browser/context_hub/context_hub_service.cc
@@ -18,6 +18,7 @@
 #include "base/functional/callback_helpers.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chrome/browser/context_hub/auto_todos/auto_todo_entry.h"
 #include "chrome/browser/context_hub/auto_todos/auto_todos_store.h"
@@ -39,6 +40,7 @@
 #include "components/saved_tab_groups/public/saved_tab_group.h"
 #include "components/saved_tab_groups/public/tab_group_sync_service.h"
 #include "components/saved_tab_groups/public/types.h"
+#include "components/sessions/content/session_tab_helper.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/page.h"
 #include "content/public/browser/web_contents.h"
@@ -77,12 +79,32 @@
 
 void OnPageContentExtracted(
     TabContextBarrierCallback barrier_callback,
-    base::WeakPtr<content::WebContents> /*web_contents*/,
+    base::WeakPtr<content::WebContents> web_contents,
+    base::WeakPtr<content::Page> page,
     std::optional<page_content_annotations::ExtractedPageContentResult>
-    /*extracted_result*/) {
-  // TODO(crbug.com/543605762): Extract page content and add to barrier
-  // callback to queue up for MES.
-  barrier_callback.Run(std::make_pair(TabData(), std::nullopt));
+        extracted_result) {
+  // Protect against navigations during the async extraction.
+  if (!web_contents || !page || !page->IsPrimary()) {
+    barrier_callback.Run(std::make_pair(TabData(), std::nullopt));
+    return;
+  }
+
+  // Populate the tab data to be used in the barrier callback.
+  TabData tab;
+  SessionID session_id =
+      sessions::SessionTabHelper::IdForTab(web_contents.get());
+  tab.id = session_id.is_valid() ? session_id.id() : -1;
+  tab.title = base::UTF16ToUTF8(web_contents->GetTitle());
+  tab.url = web_contents->GetLastCommittedURL();
+  tab.last_active_time = web_contents->GetLastActiveTime();
+
+  std::optional<optimization_guide::proto::PageContext> page_context;
+  if (extracted_result && extracted_result->page_content) {
+    page_context.emplace();
+    *page_context->mutable_annotated_page_content() =
+        extracted_result->page_content->data;
+  }
+  barrier_callback.Run(std::make_pair(std::move(tab), std::move(page_context)));
 }
 
 }  // namespace
@@ -190,7 +212,9 @@
 
   if (eligible_tabs.empty()) {
     if (callback) {
-      std::move(callback).Run(false);
+      // Return early if there are no eligible tabs to process. Return true to
+      // indicate that the operation was successful, just with no results.
+      std::move(callback).Run(true);
     }
     return;
   }
@@ -215,11 +239,12 @@
 
     // Get the page content from the primary page.
     content::Page& primary_page = tab->GetPrimaryPage();
+    base::WeakPtr<content::Page> page_weak_ptr = primary_page.GetWeakPtr();
     page_content_extraction_service_
         ->GetExtractedPageContentAndEligibilityForPageAsync(
             primary_page,
             base::BindOnce(&OnPageContentExtracted, barrier_callback,
-                           std::move(tab)),
+                           std::move(tab), std::move(page_weak_ptr)),
             /*trigger_if_not_cached=*/true);
   }
 }
diff --git a/chrome/browser/context_hub/context_hub_service_unittest.cc b/chrome/browser/context_hub/context_hub_service_unittest.cc
index 262b4a1..7ff3b6f 100644
--- a/chrome/browser/context_hub/context_hub_service_unittest.cc
+++ b/chrome/browser/context_hub/context_hub_service_unittest.cc
@@ -7,6 +7,7 @@
 #include <optional>
 
 #include "base/functional/callback_helpers.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/scoped_observation.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/gmock_callback_support.h"
@@ -24,6 +25,7 @@
 #include "components/optimization_guide/core/model_execution/test/mock_remote_model_executor.h"
 #include "components/optimization_guide/proto/features/context_hub.pb.h"
 #include "components/page_content_annotations/content/mock_page_content_services.h"
+#include "components/page_content_annotations/core/page_content_extraction_types.h"
 #include "components/personal_context/core/context_memory_error.h"
 #include "components/personal_context/core/mock_personal_context_service.h"
 #include "components/personal_context/proto/features/auto_todos.pb.h"
@@ -214,7 +216,7 @@
   service_.GenerateTabBasedTodos({web_contents->GetWeakPtr()},
                                  future.GetCallback());
 
-  EXPECT_FALSE(future.Get());
+  EXPECT_TRUE(future.Get());
 }
 
 TEST_F(ContextHubServiceTest, GenerateTabBasedTodos_VisibleTabNotEligible) {
@@ -233,7 +235,7 @@
   service_.GenerateTabBasedTodos({web_contents->GetWeakPtr()},
                                  future.GetCallback());
 
-  EXPECT_FALSE(future.Get());
+  EXPECT_TRUE(future.Get());
 }
 
 TEST_F(ContextHubServiceTest, GenerateTabBasedTodos_EligibleTabFetchesAPC) {
@@ -244,10 +246,19 @@
       ->SetLastActiveTime(base::Time::Now() - base::Hours(3));
   web_contents->WasHidden();
 
+  scoped_refptr<page_content_annotations::RefCountedAnnotatedPageContent> apc =
+      base::MakeRefCounted<
+          page_content_annotations::RefCountedAnnotatedPageContent>();
+
+  page_content_annotations::ExtractedPageContentResult extracted_result(
+      std::move(apc), base::Time::Now(),
+      /*is_eligible_for_server_upload=*/true,
+      /*screenshot_data=*/{});
+
   EXPECT_CALL(mock_page_content_extraction_service_,
               GetExtractedPageContentAndEligibilityForPageAsync(
                   testing::Ref(web_contents->GetPrimaryPage()), _, true))
-      .WillOnce(RunOnceCallback<1>(std::nullopt));
+      .WillOnce(RunOnceCallback<1>(std::move(extracted_result)));
 
   base::test::TestFuture<bool> future;
   service_.GenerateTabBasedTodos({web_contents->GetWeakPtr()},
@@ -277,10 +288,26 @@
       ->SetLastActiveTime(base::Time::Now() - base::Hours(5));
   web_contents3->WasHidden();
 
+  scoped_refptr<page_content_annotations::RefCountedAnnotatedPageContent> apc1 =
+      base::MakeRefCounted<
+          page_content_annotations::RefCountedAnnotatedPageContent>();
+  page_content_annotations::ExtractedPageContentResult extracted_result1(
+      std::move(apc1), base::Time::Now(),
+      /*is_eligible_for_server_upload=*/true,
+      /*screenshot_data=*/{});
+
+  scoped_refptr<page_content_annotations::RefCountedAnnotatedPageContent> apc3 =
+      base::MakeRefCounted<
+          page_content_annotations::RefCountedAnnotatedPageContent>();
+  page_content_annotations::ExtractedPageContentResult extracted_result3(
+      std::move(apc3), base::Time::Now(),
+      /*is_eligible_for_server_upload=*/true,
+      /*screenshot_data=*/{});
+
   EXPECT_CALL(mock_page_content_extraction_service_,
               GetExtractedPageContentAndEligibilityForPageAsync(
                   testing::Ref(web_contents1->GetPrimaryPage()), _, true))
-      .WillOnce(RunOnceCallback<1>(std::nullopt));
+      .WillOnce(RunOnceCallback<1>(std::move(extracted_result1)));
   EXPECT_CALL(mock_page_content_extraction_service_,
               GetExtractedPageContentAndEligibilityForPageAsync(
                   testing::Ref(web_contents2->GetPrimaryPage()), _, _))
@@ -288,7 +315,7 @@
   EXPECT_CALL(mock_page_content_extraction_service_,
               GetExtractedPageContentAndEligibilityForPageAsync(
                   testing::Ref(web_contents3->GetPrimaryPage()), _, true))
-      .WillOnce(RunOnceCallback<1>(std::nullopt));
+      .WillOnce(RunOnceCallback<1>(std::move(extracted_result3)));
 
   base::test::TestFuture<bool> future;
   service_.GenerateTabBasedTodos(
@@ -325,17 +352,63 @@
                                  future2.GetCallback());
   EXPECT_FALSE(future2.Get());
 
-  std::move(saved_callback).Run(std::nullopt);
+  scoped_refptr<page_content_annotations::RefCountedAnnotatedPageContent> apc =
+      base::MakeRefCounted<
+          page_content_annotations::RefCountedAnnotatedPageContent>();
+  page_content_annotations::ExtractedPageContentResult extracted_result(
+      std::move(apc), base::Time::Now(),
+      /*is_eligible_for_server_upload=*/true,
+      /*screenshot_data=*/{});
+  std::move(saved_callback).Run(std::move(extracted_result));
   EXPECT_TRUE(future1.Get());
 }
 
+TEST_F(ContextHubServiceTest,
+       GenerateTabBasedTodos_NavigatedPageDuringExtraction) {
+  std::unique_ptr<content::WebContents> web_contents =
+      content::WebContentsTester::CreateTestWebContents(&profile_, nullptr);
+  content::WebContentsTester::For(web_contents.get())
+      ->SetLastActiveTime(base::Time::Now() - base::Hours(3));
+  web_contents->WasHidden();
+
+  page_content_annotations::PageContentExtractionService::
+      GetExtractedPageContentAndEligibilityCallback saved_callback;
+  EXPECT_CALL(mock_page_content_extraction_service_,
+              GetExtractedPageContentAndEligibilityForPageAsync(
+                  testing::Ref(web_contents->GetPrimaryPage()), _, true))
+      .WillOnce([&](content::Page&, auto cb, bool) {
+        saved_callback = std::move(cb);
+      });
+
+  base::test::TestFuture<bool> future;
+  service_.GenerateTabBasedTodos({web_contents->GetWeakPtr()},
+                                 future.GetCallback());
+
+  // Navigate to a new page, making the old page non-primary.
+  content::WebContentsTester::For(web_contents.get())
+      ->NavigateAndCommit(GURL("https://example.com/new_page"));
+
+  scoped_refptr<page_content_annotations::RefCountedAnnotatedPageContent> apc =
+      base::MakeRefCounted<
+          page_content_annotations::RefCountedAnnotatedPageContent>();
+  page_content_annotations::ExtractedPageContentResult extracted_result(
+      std::move(apc), base::Time::Now(),
+      /*is_eligible_for_server_upload=*/true,
+      /*screenshot_data=*/{});
+
+  // Old extraction callback returns with extracted page content for old page,
+  // but page is no longer primary.
+  std::move(saved_callback).Run(std::move(extracted_result));
+  EXPECT_TRUE(future.Get());
+}
+
 TEST_F(ContextHubServiceTest, GenerateTabBasedTodos_NullWebContents) {
   // Tab is null weak pointer.
   base::test::TestFuture<bool> future;
   service_.GenerateTabBasedTodos({base::WeakPtr<content::WebContents>()},
                                  future.GetCallback());
 
-  EXPECT_FALSE(future.Get());
+  EXPECT_TRUE(future.Get());
 }
 
 TEST_F(ContextHubServiceTest, SaveTab) {
diff --git a/chrome/browser/context_hub/tab_group_store/tab_group_entry.h b/chrome/browser/context_hub/tab_group_store/tab_group_entry.h
index 79370bd..ba9d2b5 100644
--- a/chrome/browser/context_hub/tab_group_store/tab_group_entry.h
+++ b/chrome/browser/context_hub/tab_group_store/tab_group_entry.h
@@ -18,6 +18,7 @@
   int64_t id;
   std::string title;
   GURL url;
+  base::Time last_active_time;
 };
 
 // Represents unconfirmed (Auto Group Suggestions from the Context Hub Auto
diff --git a/chrome/browser/context_sharing/tab_bottom_sheet/android/BUILD.gn b/chrome/browser/context_sharing/tab_bottom_sheet/android/BUILD.gn
index e5aa527..56d55bb 100644
--- a/chrome/browser/context_sharing/tab_bottom_sheet/android/BUILD.gn
+++ b/chrome/browser/context_sharing/tab_bottom_sheet/android/BUILD.gn
@@ -138,6 +138,7 @@
     "java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetCoordinatorUnitTest.java",
     "java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetMediatorUnitTest.java",
     "java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetPeekViewBinderTest.java",
+    "java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetSuppressionControllerUnitTest.java",
     "java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetWebUiUnitTest.java",
     "java/src/org/chromium/chrome/browser/tab_bottom_sheet/WebViewResizingHelperUnitTest.java",
   ]
@@ -155,6 +156,8 @@
     "//chrome/browser/context_sharing/tab_bottom_sheet/public/android:java",
     "//chrome/browser/contextual_tasks/fusebox/android:java",
     "//chrome/browser/glic:metrics_java",
+    "//chrome/browser/keyboard_accessory/android/java",
+    "//chrome/browser/ui/android/layouts:java",
     "//chrome/browser/ui/side_panel_container/public:java",
     "//components/browser_ui/bottomsheet/android:java",
     "//components/browser_ui/widget/android:java",
@@ -192,6 +195,7 @@
     "//chrome/browser/context_sharing/tab_bottom_sheet/public/android:java",
     "//chrome/browser/contextual_tasks/fusebox/android:java",
     "//chrome/browser/flags:java",
+    "//chrome/browser/keyboard_accessory/android/java",
     "//chrome/browser/profiles/android:java",
     "//chrome/browser/tab:java",
     "//chrome/browser/ui/android/layouts:java",
diff --git a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetSuppressionControllerUnitTest.java b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetSuppressionControllerUnitTest.java
new file mode 100644
index 0000000..9a14b3d8
--- /dev/null
+++ b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetSuppressionControllerUnitTest.java
@@ -0,0 +1,231 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tab_bottom_sheet;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.shadows.ShadowLooper;
+
+import org.chromium.base.supplier.ObservableSuppliers;
+import org.chromium.base.supplier.OneshotSupplierImpl;
+import org.chromium.base.supplier.SettableMonotonicObservableSupplier;
+import org.chromium.base.supplier.SettableNonNullObservableSupplier;
+import org.chromium.base.supplier.SettableNullableObservableSupplier;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.keyboard_accessory.ManualFillingComponent;
+import org.chromium.chrome.browser.layouts.LayoutStateProvider;
+import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver;
+import org.chromium.chrome.browser.layouts.LayoutType;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabObserver;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorSupplier;
+import org.chromium.components.embedder_support.util.UrlConstants;
+import org.chromium.content_public.browser.NavigationHandle;
+import org.chromium.ui.base.WindowAndroid;
+import org.chromium.url.GURL;
+
+/** Unit tests for {@link TabBottomSheetSuppressionController}. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class TabBottomSheetSuppressionControllerUnitTest {
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock private WindowAndroid mWindowAndroid;
+    @Mock private LayoutStateProvider mLayoutStateProvider;
+    @Mock private TabBottomSheetSuppressionController.Delegate mDelegate;
+    @Mock private TabModelSelector mTabModelSelector;
+    @Mock private TabModel mTabModel;
+    @Mock private Tab mTab;
+    @Mock private ManualFillingComponent mManualFillingComponent;
+    @Mock private NavigationHandle mNavigationHandle;
+
+    @Captor private ArgumentCaptor<LayoutStateObserver> mLayoutStateObserverCaptor;
+    @Captor private ArgumentCaptor<TabObserver> mTabObserverCaptor;
+
+    private final OneshotSupplierImpl<LayoutStateProvider> mLayoutStateProviderSupplier =
+            new OneshotSupplierImpl<>();
+    private final SettableNonNullObservableSupplier<Boolean> mOmniboxFocusStateSupplier =
+            ObservableSuppliers.createNonNull(false);
+    private final SettableMonotonicObservableSupplier<TabModel> mTabModelSupplier =
+            ObservableSuppliers.createMonotonic();
+    private final SettableNullableObservableSupplier<Tab> mCurrentTabSupplier =
+            ObservableSuppliers.createNullable();
+    private final SettableMonotonicObservableSupplier<ManualFillingComponent>
+            mManualFillingComponentSupplier = ObservableSuppliers.createMonotonic();
+    private final SettableNonNullObservableSupplier<Boolean> mIsAccessoryRequestedSupplier =
+            ObservableSuppliers.createNonNull(false);
+    private final SettableNullableObservableSupplier<Tab> mActivePlaybackTabSupplier =
+            ObservableSuppliers.createNullable();
+
+    private TabBottomSheetSuppressionController mController;
+
+    @Before
+    public void setUp() {
+        TabModelSelectorSupplier.setInstanceForTesting(mTabModelSelector);
+
+        when(mTabModelSelector.getCurrentTabModelSupplier()).thenReturn(mTabModelSupplier);
+        when(mTabModelSelector.getCurrentTabSupplier()).thenReturn(mCurrentTabSupplier);
+        when(mTab.isIncognito()).thenReturn(false);
+        when(mTabModel.isIncognito()).thenReturn(false);
+
+        when(mManualFillingComponent.getIsAccessoryRequestedSupplier())
+                .thenReturn(mIsAccessoryRequestedSupplier);
+
+        mLayoutStateProviderSupplier.set(mLayoutStateProvider);
+
+        mController =
+                new TabBottomSheetSuppressionController(
+                        mWindowAndroid,
+                        mLayoutStateProviderSupplier,
+                        mOmniboxFocusStateSupplier,
+                        mDelegate);
+
+        mController.setManualFillingComponentSupplierForTesting(mManualFillingComponentSupplier);
+        mController.initReadAloudIntegrationForTesting(mActivePlaybackTabSupplier, () -> {});
+
+        ShadowLooper.idleMainLooper();
+        verify(mLayoutStateProvider).addObserver(mLayoutStateObserverCaptor.capture());
+        clearInvocations(mDelegate);
+    }
+
+    @Test
+    public void testOmniboxFocus_suppresses() {
+        assertFalse(mController.isInternallySuppressed());
+
+        mOmniboxFocusStateSupplier.set(true);
+        assertTrue(mController.isInternallySuppressed());
+        verify(mDelegate).onSuppressionStarted();
+
+        mOmniboxFocusStateSupplier.set(false);
+        assertFalse(mController.isInternallySuppressed());
+        verify(mDelegate).onSuppressionEnded();
+    }
+
+    @Test
+    public void testLayoutState_suppresses() {
+        LayoutStateObserver observer = mLayoutStateObserverCaptor.getValue();
+        assertFalse(mController.isInternallySuppressed());
+
+        observer.onStartedShowing(LayoutType.HUB);
+        assertTrue(mController.isInternallySuppressed());
+        verify(mDelegate).onSuppressionStarted();
+
+        when(mLayoutStateProvider.getNextLayoutType()).thenReturn(LayoutType.BROWSING);
+        observer.onStartedHiding(LayoutType.HUB);
+        assertFalse(mController.isInternallySuppressed());
+        verify(mDelegate).onSuppressionEnded();
+    }
+
+    @Test
+    public void testLayoutState_swipeSuppresses() {
+        LayoutStateObserver observer = mLayoutStateObserverCaptor.getValue();
+        assertFalse(mController.isInternallySuppressed());
+
+        observer.onStartedShowing(LayoutType.TOOLBAR_SWIPE);
+        assertTrue(mController.isInternallySuppressed());
+        verify(mDelegate).onSuppressionStarted();
+
+        when(mLayoutStateProvider.getNextLayoutType()).thenReturn(LayoutType.BROWSING);
+        observer.onStartedHiding(LayoutType.TOOLBAR_SWIPE);
+        assertFalse(mController.isInternallySuppressed());
+        verify(mDelegate).onSuppressionEnded();
+    }
+
+    @Test
+    public void testTabModel_incognitoSuppresses() {
+        when(mTabModel.isIncognito()).thenReturn(true);
+        mTabModelSupplier.set(mTabModel);
+
+        assertTrue(mController.isInternallySuppressed());
+        verify(mDelegate).onSuppressionStarted();
+
+        TabModel normalModel = mock(TabModel.class);
+        when(normalModel.isIncognito()).thenReturn(false);
+        mTabModelSupplier.set(normalModel);
+
+        assertFalse(mController.isInternallySuppressed());
+        verify(mDelegate).onSuppressionEnded();
+    }
+
+    @Test
+    public void testManualFillingComponent_suppresses() {
+        mManualFillingComponentSupplier.set(mManualFillingComponent);
+
+        mIsAccessoryRequestedSupplier.set(true);
+        assertTrue(mController.isInternallySuppressed());
+        verify(mDelegate).onSuppressionStarted();
+
+        mIsAccessoryRequestedSupplier.set(false);
+        assertFalse(mController.isInternallySuppressed());
+        verify(mDelegate).onSuppressionEnded();
+    }
+
+    @Test
+    public void testReadAloud_suppresses() {
+        mActivePlaybackTabSupplier.set(mTab);
+        assertTrue(mController.isInternallySuppressed());
+        verify(mDelegate).onSuppressionStarted();
+
+        mActivePlaybackTabSupplier.set(null);
+        assertFalse(mController.isInternallySuppressed());
+        verify(mDelegate).onSuppressionEnded();
+    }
+
+    @Test
+    public void testHandleReadAloudStopPlayback() {
+        Runnable stopCallback = mock(Runnable.class);
+        mController.initReadAloudIntegrationForTesting(mActivePlaybackTabSupplier, stopCallback);
+
+        // When not suppressed by read aloud, callback is not run.
+        mController.handleReadAloudStopPlayback();
+        verify(stopCallback, never()).run();
+
+        // Suppress by read aloud.
+        mActivePlaybackTabSupplier.set(mTab);
+        mController.handleReadAloudStopPlayback();
+        verify(stopCallback).run();
+    }
+
+    @Test
+    public void testTabObserver_closeRequestedForDistiller() {
+        mCurrentTabSupplier.set(mTab);
+        verify(mTab).addObserver(mTabObserverCaptor.capture());
+
+        GURL distillerUrl = new GURL(UrlConstants.DISTILLER_SCHEME + "://foo");
+        when(mNavigationHandle.getUrl()).thenReturn(distillerUrl);
+
+        mTabObserverCaptor
+                .getValue()
+                .onDidStartNavigationInPrimaryMainFrame(mTab, mNavigationHandle);
+
+        verify(mDelegate).onCloseRequested();
+    }
+
+    @Test
+    public void testDestroy() {
+        mController.destroy();
+        assertFalse(mOmniboxFocusStateSupplier.hasObservers());
+        assertFalse(mActivePlaybackTabSupplier.hasObservers());
+        assertFalse(mManualFillingComponentSupplier.hasObservers());
+        verify(mLayoutStateProvider).removeObserver(any());
+    }
+}
diff --git a/chrome/browser/dictation/session_ui_impl_browsertest.cc b/chrome/browser/dictation/session_ui_impl_browsertest.cc
index c2c0631..fbb7c555 100644
--- a/chrome/browser/dictation/session_ui_impl_browsertest.cc
+++ b/chrome/browser/dictation/session_ui_impl_browsertest.cc
@@ -168,6 +168,37 @@
 }
 
 IN_PROC_BROWSER_TEST_F(DictationSessionUiImplBrowserTest,
+                       ToastIsActivatableAfterCreation) {
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kWebContentsElementId);
+  const GURL url =
+      embedded_test_server()->GetURL("/textinput/simple_textarea.html");
+
+  // clang-format off
+  RunTestSequence(
+    InstrumentTab(kWebContentsElementId),
+    NavigateWebContents(kWebContentsElementId, url),
+    StartSessionWithTarget(kWebContentsElementId, "#text_id"),
+    WaitForShow(DictationBubbleUi::kViewElementIdForTesting),
+    // Ensure that showing the bubble did not steal focus from the page.
+    CheckView(DictationBubbleUi::kViewElementIdForTesting,
+              [](views::View* view) {
+                views::Widget* widget = view->GetWidget();
+                return widget && !widget->IsActive();
+              }),
+    // Ensure that the bubble can be activated. This is needed on Windows,
+    // otherwise we'd discard mouse activation messages from the OS and the
+    // buttons wouldn't be clickable. See https://crbug.com/542199776
+    CheckView(DictationBubbleUi::kViewElementIdForTesting,
+              [](views::View* view) {
+                views::Widget* widget = view->GetWidget();
+                return widget && widget->widget_delegate() &&
+                       widget->widget_delegate()->CanActivate();
+              })
+  );
+  // clang-format on
+}
+
+IN_PROC_BROWSER_TEST_F(DictationSessionUiImplBrowserTest,
                        EndSessionTearsDownUI) {
   // clang-format off
   RunTestSequence(
diff --git a/chrome/browser/enterprise/connectors/device_trust/BUILD.gn b/chrome/browser/enterprise/connectors/device_trust/BUILD.gn
index c57e921..21ade67 100644
--- a/chrome/browser/enterprise/connectors/device_trust/BUILD.gn
+++ b/chrome/browser/enterprise/connectors/device_trust/BUILD.gn
@@ -16,32 +16,15 @@
   testonly = true
   configs += [ "//components/enterprise:extra_warnings" ]
 
-  sources = [
-    "fake_device_trust_connector_service.cc",
-    "mock_device_trust_service.cc",
-  ]
+  sources = [ "mock_device_trust_service.cc" ]
 
-  public = [
-    "fake_device_trust_connector_service.h",
-    "mock_device_trust_service.h",
-  ]
+  public = [ "mock_device_trust_service.h" ]
 
   public_deps = [
     "//base",
     "//chrome/browser",
-    "//components/enterprise/device_trust/core:device_trust_connector_service",
     "//testing/gmock",
   ]
-
-  deps = [
-    ":features",
-    "//components/enterprise/connectors/core",
-    "//components/enterprise/device_trust",
-    "//components/enterprise/device_trust/core/signals:test_support",
-    "//components/pref_registry",
-    "//components/prefs:test_support",
-    "//components/sync_preferences:test_support",
-  ]
 }
 
 source_set("unit_tests") {
@@ -69,6 +52,7 @@
       "//chrome/test:test_support",
       "//components/device_signals/core/browser:test_support",
       "//components/enterprise/device_trust/core:device_trust_connector_service",
+      "//components/enterprise/device_trust/core:test_support",
       "//components/enterprise/device_trust/core/attestation:test_support",
       "//testing/gtest",
     ]
diff --git a/chrome/browser/enterprise/connectors/device_trust/navigation_throttle_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/navigation_throttle_unittest.cc
index 03c760b..20b73875 100644
--- a/chrome/browser/enterprise/connectors/device_trust/navigation_throttle_unittest.cc
+++ b/chrome/browser/enterprise/connectors/device_trust/navigation_throttle_unittest.cc
@@ -13,7 +13,6 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/enterprise/connectors/device_trust/common/metrics_utils.h"
-#include "chrome/browser/enterprise/connectors/device_trust/fake_device_trust_connector_service.h"
 #include "chrome/browser/enterprise/connectors/device_trust/mock_device_trust_service.h"
 #include "chrome/browser/enterprise/signals/user_permission_service_factory.h"
 #include "chrome/browser/ui/views/device_signals_consent/consent_dialog_coordinator.h"
@@ -25,6 +24,7 @@
 #include "components/enterprise/device_trust/core/common_types.h"
 #include "components/enterprise/device_trust/core/device_trust_connector_service.h"
 #include "components/enterprise/device_trust/core/device_trust_constants.h"
+#include "components/enterprise/device_trust/core/fake_device_trust_connector_service.h"
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/browser/navigation_throttle.h"
diff --git a/chrome/browser/enterprise/connectors/reporting/crash_reporting_context.cc b/chrome/browser/enterprise/connectors/reporting/crash_reporting_context.cc
index 928e04c..3ee405e 100644
--- a/chrome/browser/enterprise/connectors/reporting/crash_reporting_context.cc
+++ b/chrome/browser/enterprise/connectors/reporting/crash_reporting_context.cc
@@ -34,10 +34,6 @@
 
 namespace {
 
-constexpr char kKeyChannel[] = "channel";
-constexpr char kKeyVersion[] = "version";
-constexpr char kKeyReportId[] = "reportId";
-constexpr char kKeyPlatform[] = "platform";
 constexpr char kCrashpadPollingIntervalFlag[] = "crashpad-polling-interval";
 constexpr int kDefaultCrashpadPollingIntervalSeconds = 3600;
 
@@ -46,18 +42,6 @@
       ->chrome_browser_cloud_management_controller();
 }
 
-base::DictValue GetBrowserCrashEventDeprecated(const std::string& channel,
-                                               const std::string& version,
-                                               const std::string& report_id,
-                                               const std::string& platform) {
-  base::DictValue event;
-  event.Set(kKeyChannel, channel);
-  event.Set(kKeyVersion, version);
-  event.Set(kKeyReportId, report_id);
-  event.Set(kKeyPlatform, platform);
-
-  return event;
-}
 
 ::chrome::cros::reporting::proto::Event GetBrowserCrashEvent(
     const std::string& channel,
@@ -199,18 +183,10 @@
   int64_t latest_creation_time = -1;
 
   for (const auto& report : reports) {
-    if (base::FeatureList::IsEnabled(
-            policy::kUploadRealtimeReportingEventsUsingProto)) {
-      reporting_client->ReportEvent(
-          GetBrowserCrashEvent(channel, version, report.id, platform,
-                               report.creation_time),
-          settings.value());
-    } else {
-      reporting_client->ReportPastEvent(
-          kBrowserCrashEvent, settings.value(),
-          GetBrowserCrashEventDeprecated(channel, version, report.id, platform),
-          base::Time::FromTimeT(report.creation_time));
-    }
+    reporting_client->ReportEvent(
+        GetBrowserCrashEvent(channel, version, report.id, platform,
+                             report.creation_time),
+        settings.value());
 
     if (report.creation_time > latest_creation_time) {
       latest_creation_time = report.creation_time;
diff --git a/chrome/browser/enterprise/connectors/reporting/extension_install_event_router.cc b/chrome/browser/enterprise/connectors/reporting/extension_install_event_router.cc
index 9b83d14..d337ad4 100644
--- a/chrome/browser/enterprise/connectors/reporting/extension_install_event_router.cc
+++ b/chrome/browser/enterprise/connectors/reporting/extension_install_event_router.cc
@@ -25,29 +25,6 @@
 
 namespace enterprise_connectors {
 
-namespace {
-
-// Key names used with when building the dictionary to pass to the real-time
-// reporting API. These matches proto defined in
-// google3/chrome/cros/reporting/api/proto/browser_events.proto
-constexpr char kKeyId[] = "id";
-constexpr char kKeyName[] = "name";
-constexpr char kKeyDescription[] = "description";
-constexpr char kKeyExtensionAction[] = "extension_action_type";
-constexpr char kKeyVersion[] = "extension_version";
-constexpr char kKeyExtensionSource[] = "extension_source";
-
-// Extension action types
-constexpr char kInstallAction[] = "INSTALL";
-constexpr char kUpdateAction[] = "UPDATE";
-constexpr char kUninstallAction[] = "UNINSTALL";
-
-// Extension sources
-constexpr char kChromeWebstoreSource[] = "CHROME_WEBSTORE";
-constexpr char kExternalSource[] = "EXTERNAL";
-constexpr char kComponentSource[] = "COMPONENT";
-
-}  // namespace
 
 ExtensionInstallEventRouter::ExtensionInstallEventRouter(
     content::BrowserContext* context) {
@@ -70,8 +47,6 @@
 void ExtensionInstallEventRouter::ReportExtensionInstallEvent(
     const extensions::Extension* extension,
     const BrowserExtensionInstallEvent::ExtensionAction extension_action) {
-  DCHECK(base::FeatureList::IsEnabled(
-      policy::kUploadRealtimeReportingEventsUsingProto));
 
   std::optional<ReportingSettings> settings =
       reporting_client_->GetReportingSettings();
@@ -98,70 +73,26 @@
   reporting_client_->ReportEvent(std::move(event), std::move(settings.value()));
 }
 
-void ExtensionInstallEventRouter::ReportExtensionInstallEvent(
-    const extensions::Extension* extension,
-    const char* extension_action) {
-  std::optional<ReportingSettings> settings =
-      reporting_client_->GetReportingSettings();
-  if (!settings.has_value() ||
-      settings->enabled_event_names.count(kExtensionInstallEvent) == 0) {
-    return;
-  }
-
-  base::DictValue event;
-  event.Set(kKeyId, extension->id());
-  event.Set(kKeyName, extension->name());
-  event.Set(kKeyDescription,
-            extensions::DescriptionInfo::GetDescription(*extension));
-  event.Set(kKeyExtensionAction, extension_action);
-  event.Set(kKeyVersion, extension->GetVersionForDisplay());
-
-  // Set the source from which an extension was loaded from.
-  // TODO(crbug.com/410552409): Add other sources and refactor into helper
-  // function.
-  if (extension->location() ==
-      extensions::mojom::ManifestLocation::kComponent) {
-    event.Set(kKeyExtensionSource, kComponentSource);
-  } else if (extension->from_webstore()) {
-    event.Set(kKeyExtensionSource, kChromeWebstoreSource);
-  } else {
-    event.Set(kKeyExtensionSource, kExternalSource);
-  }
-
-  reporting_client_->ReportRealtimeEvent(
-      kExtensionInstallEvent, std::move(settings.value()), std::move(event));
-}
 
 void ExtensionInstallEventRouter::OnExtensionInstalled(
     content::BrowserContext* browser_context,
     const extensions::Extension* extension,
     bool is_update) {
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
-    ReportExtensionInstallEvent(
-        extension,
-        is_update ? BrowserExtensionInstallEvent::ExtensionAction::
-                        BrowserExtensionInstallEvent_ExtensionAction_UPDATE
-                  : BrowserExtensionInstallEvent::ExtensionAction::
-                        BrowserExtensionInstallEvent_ExtensionAction_INSTALL);
-  } else {
-    ReportExtensionInstallEvent(extension,
-                                is_update ? kUpdateAction : kInstallAction);
-  }
+  ReportExtensionInstallEvent(
+      extension,
+      is_update ? BrowserExtensionInstallEvent::ExtensionAction::
+                      BrowserExtensionInstallEvent_ExtensionAction_UPDATE
+                : BrowserExtensionInstallEvent::ExtensionAction::
+                      BrowserExtensionInstallEvent_ExtensionAction_INSTALL);
 }
 
 void ExtensionInstallEventRouter::OnExtensionUninstalled(
     content::BrowserContext* browser_context,
     const extensions::Extension* extension,
     extensions::UninstallReason reason) {
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
-    ReportExtensionInstallEvent(
-        extension, BrowserExtensionInstallEvent::ExtensionAction::
-                       BrowserExtensionInstallEvent_ExtensionAction_UNINSTALL);
-  } else {
-    ReportExtensionInstallEvent(extension, kUninstallAction);
-  }
+  ReportExtensionInstallEvent(
+      extension, BrowserExtensionInstallEvent::ExtensionAction::
+                     BrowserExtensionInstallEvent_ExtensionAction_UNINSTALL);
 }
 
 // static
diff --git a/chrome/browser/enterprise/connectors/reporting/extension_telemetry_event_router.cc b/chrome/browser/enterprise/connectors/reporting/extension_telemetry_event_router.cc
index 1f006cbb..70fb00d 100644
--- a/chrome/browser/enterprise/connectors/reporting/extension_telemetry_event_router.cc
+++ b/chrome/browser/enterprise/connectors/reporting/extension_telemetry_event_router.cc
@@ -62,33 +62,6 @@
   }
 }
 
-base::DictValue CreateExtensionInfoDict(const ExtensionInfo& extension_info) {
-  base::DictValue dict;
-  dict.Set(ExtensionTelemetryEventRouter::kKeyId, extension_info.id());
-  dict.Set(ExtensionTelemetryEventRouter::kKeyName, extension_info.name());
-  dict.Set(ExtensionTelemetryEventRouter::kKeyVersion,
-           extension_info.version());
-  dict.Set(
-      ExtensionTelemetryEventRouter::kKeyInstallLocation,
-      ExtensionInfo::InstallLocation_Name(extension_info.install_location()));
-  dict.Set(ExtensionTelemetryEventRouter::kKeyIsFromStore,
-           extension_info.is_from_store());
-  if (extension_info.file_infos_size() > 0) {
-    base::ListValue file_infos_list;
-    for (const auto& file_info : extension_info.file_infos()) {
-      base::DictValue file_info_dict;
-      file_info_dict.Set(ExtensionTelemetryEventRouter::kKeyName,
-                         file_info.name());
-      file_info_dict.Set(ExtensionTelemetryEventRouter::kKeyHash,
-                         file_info.hash());
-      file_infos_list.Append(std::move(file_info_dict));
-    }
-    dict.Set(ExtensionTelemetryEventRouter::kKeyFileInfo,
-             std::move(file_infos_list));
-  }
-
-  return dict;
-}
 
 void CopyCookiesGetAllArgsInfo(const CookiesGetAllInfo& cookies_get_all_info,
                                Report* redacted_report) {
@@ -110,37 +83,6 @@
   }
 }
 
-base::DictValue CreateCookiesGetAllInfoDict(
-    const CookiesGetAllInfo& cookies_get_all_info) {
-  base::ListValue get_all_args_list;
-  for (const auto& get_all_args_info :
-       cookies_get_all_info.get_all_args_info()) {
-    base::DictValue get_all_args_dict;
-    get_all_args_dict.Set(ExtensionTelemetryEventRouter::kKeyDomain,
-                          get_all_args_info.domain());
-    get_all_args_dict.Set(ExtensionTelemetryEventRouter::kKeyName,
-                          get_all_args_info.name());
-    get_all_args_dict.Set(ExtensionTelemetryEventRouter::kKeyPath,
-                          get_all_args_info.path());
-    get_all_args_dict.Set(ExtensionTelemetryEventRouter::kKeySecure,
-                          get_all_args_info.secure());
-    get_all_args_dict.Set(ExtensionTelemetryEventRouter::kKeyStoreId,
-                          get_all_args_info.store_id());
-    get_all_args_dict.Set(ExtensionTelemetryEventRouter::kKeyUrl,
-                          get_all_args_info.url());
-    get_all_args_dict.Set(ExtensionTelemetryEventRouter::kKeyIsSession,
-                          get_all_args_info.is_session());
-    get_all_args_dict.Set(ExtensionTelemetryEventRouter::kKeyCount,
-                          static_cast<int>(get_all_args_info.count()));
-
-    get_all_args_list.Append(std::move(get_all_args_dict));
-  }
-
-  base::DictValue signal_dict;
-  signal_dict.Set(ExtensionTelemetryEventRouter::kKeyGetAllArgsInfo,
-                  std::move(get_all_args_list));
-  return signal_dict;
-}
 
 void CopyCookiesGetInfo(const CookiesGetInfo& cookies_get_info,
                         Report* redacted_report) {
@@ -156,28 +98,6 @@
   }
 }
 
-base::DictValue CreateCookiesGetInfoDict(
-    const CookiesGetInfo& cookies_get_info) {
-  base::ListValue get_args_list;
-  for (const auto& get_args_info : cookies_get_info.get_args_info()) {
-    base::DictValue get_args_dict;
-    get_args_dict.Set(ExtensionTelemetryEventRouter::kKeyName,
-                      get_args_info.name());
-    get_args_dict.Set(ExtensionTelemetryEventRouter::kKeyUrl,
-                      get_args_info.url());
-    get_args_dict.Set(ExtensionTelemetryEventRouter::kKeyStoreId,
-                      get_args_info.store_id());
-    get_args_dict.Set(ExtensionTelemetryEventRouter::kKeyCount,
-                      static_cast<int>(get_args_info.count()));
-
-    get_args_list.Append(std::move(get_args_dict));
-  }
-
-  base::DictValue signal_dict;
-  signal_dict.Set(ExtensionTelemetryEventRouter::kKeyGetArgsInfo,
-                  std::move(get_args_list));
-  return signal_dict;
-}
 
 void CopyRemoteHostContactedInfo(
     const RemoteHostContactedInfo& remote_host_contacted_info,
@@ -196,34 +116,6 @@
   }
 }
 
-base::DictValue CreateRemoteHostContactedInfoDict(
-    const RemoteHostContactedInfo& remote_host_contacted_info) {
-  base::ListValue remote_host_info_list;
-  for (const auto& remote_host_info :
-       remote_host_contacted_info.remote_host()) {
-    base::DictValue remote_host_info_dict;
-    remote_host_info_dict.Set(ExtensionTelemetryEventRouter::kKeyUrl,
-                              remote_host_info.url());
-    remote_host_info_dict.Set(
-        ExtensionTelemetryEventRouter::kKeyConnectionProtocol,
-        RemoteHostInfo::ProtocolType_Name(
-            remote_host_info.connection_protocol()));
-    remote_host_info_dict.Set(
-        ExtensionTelemetryEventRouter::kKeyContactedBy,
-        RemoteHostInfo::ContactInitiator_Name(remote_host_info.contacted_by()));
-    remote_host_info_dict.Set(
-        ExtensionTelemetryEventRouter::kKeyContactCount,
-        static_cast<int>(remote_host_info.contact_count()));
-
-    remote_host_info_list.Append(std::move(remote_host_info_dict));
-  }
-
-  base::DictValue signal_dict;
-  signal_dict.Set(ExtensionTelemetryEventRouter::kKeyRemoteHost,
-                  std::move(remote_host_info_list));
-  return signal_dict;
-}
-
 void CopyTabsApiInfo(const TabsApiInfo& tabs_api_info,
                      Report* redacted_report) {
   TabsApiInfo* redacted_tabs_api_info =
@@ -238,27 +130,6 @@
   }
 }
 
-base::DictValue CreateTabsApiInfoDict(const TabsApiInfo& tabs_api_info) {
-  base::ListValue tabs_api_info_list;
-  for (const auto& call_detail : tabs_api_info.call_details()) {
-    base::DictValue tabs_api_info_dict;
-    tabs_api_info_dict.Set(ExtensionTelemetryEventRouter::kKeyMethod,
-                           TabsApiInfo::ApiMethod_Name(call_detail.method()));
-    tabs_api_info_dict.Set(ExtensionTelemetryEventRouter::kKeyNewUrl,
-                           call_detail.new_url());
-    tabs_api_info_dict.Set(ExtensionTelemetryEventRouter::kKeyCurrentUrl,
-                           call_detail.current_url());
-    tabs_api_info_dict.Set(ExtensionTelemetryEventRouter::kKeyCount,
-                           static_cast<int>(call_detail.count()));
-
-    tabs_api_info_list.Append(std::move(tabs_api_info_dict));
-  }
-
-  base::DictValue signal_dict;
-  signal_dict.Set(ExtensionTelemetryEventRouter::kKeyCallDetails,
-                  std::move(tabs_api_info_list));
-  return signal_dict;
-}
 
 void CopyDOMAccessInfo(const DOMAccessInfo& dom_access_info,
                        Report* redacted_report) {
@@ -275,31 +146,6 @@
   }
 }
 
-base::DictValue CreateDOMAccessInfoDict(const DOMAccessInfo& dom_access_info) {
-  base::ListValue dom_access_list;
-  for (const auto& dom_access : dom_access_info.dom_accesses()) {
-    base::DictValue dom_access_dict;
-    dom_access_dict.Set(ExtensionTelemetryEventRouter::kKeyApiName,
-                        dom_access.api_name());
-    dom_access_dict.Set(ExtensionTelemetryEventRouter::kKeyUrl,
-                        dom_access.url());
-    dom_access_dict.Set(
-        ExtensionTelemetryEventRouter::kKeyAccessType,
-        DOMAccessInfo::DOMAccess::AccessType_Name(dom_access.access_type()));
-    dom_access_dict.Set(ExtensionTelemetryEventRouter::kKeyCount,
-                        static_cast<int>(dom_access.count()));
-    dom_access_dict.Set(ExtensionTelemetryEventRouter::kKeyTimestampMs,
-                        base::NumberToString(dom_access.timestamp_ms()));
-
-    dom_access_list.Append(std::move(dom_access_dict));
-  }
-
-  base::DictValue signal_dict;
-  signal_dict.Set(ExtensionTelemetryEventRouter::kKeyDOMAccesses,
-                  std::move(dom_access_list));
-  return signal_dict;
-}
-
 void CopyScriptInjectionInfo(const ScriptInjectionInfo& script_injection_info,
                              Report* redacted_report) {
   ScriptInjectionInfo* redacted_script_injection_info =
@@ -320,40 +166,6 @@
   }
 }
 
-base::DictValue CreateScriptInjectionInfoDict(
-    const ScriptInjectionInfo& script_injection_info) {
-  base::ListValue script_injection_list;
-  for (const auto& script_injection :
-       script_injection_info.script_injections()) {
-    base::DictValue script_injection_dict;
-    script_injection_dict.Set(ExtensionTelemetryEventRouter::kKeyApiName,
-                              script_injection.api_name());
-    script_injection_dict.Set(ExtensionTelemetryEventRouter::kKeyUrl,
-                              script_injection.url());
-    script_injection_dict.Set(ExtensionTelemetryEventRouter::kKeyCount,
-                              static_cast<int>(script_injection.count()));
-    script_injection_dict.Set(
-        ExtensionTelemetryEventRouter::kKeyTimestampMs,
-        base::NumberToString(script_injection.timestamp_ms()));
-
-    base::ListValue args_list_value;
-    for (const auto& arg : script_injection.args_list()) {
-      args_list_value.Append(arg);
-    }
-    script_injection_dict.Set(ExtensionTelemetryEventRouter::kKeyArgsList,
-                              std::move(args_list_value));
-
-    script_injection_dict.Set(ExtensionTelemetryEventRouter::kKeyArgUrl,
-                              script_injection.arg_url());
-
-    script_injection_list.Append(std::move(script_injection_dict));
-  }
-
-  base::DictValue signal_dict;
-  signal_dict.Set(ExtensionTelemetryEventRouter::kKeyScriptInjections,
-                  std::move(script_injection_list));
-  return signal_dict;
-}
 
 std::unique_ptr<ExtensionTelemetryReportRequest>
 CreateRedactedExtensionTelemetryReportRequestProto(
@@ -391,118 +203,9 @@
   return redacted_request;
 }
 
-base::DictValue CreateExtensionTelemetryReportDict(
-    const ExtensionTelemetryReportRequest::Report& report) {
-  base::DictValue report_dict;
-  report_dict.Set(ExtensionTelemetryEventRouter::kKeyExtension,
-                  CreateExtensionInfoDict(report.extension()));
-
-  base::DictValue signals_dict;
-  for (const auto& signal : report.signals()) {
-    if (signal.has_cookies_get_all_info()) {
-      signals_dict.Set(
-          ExtensionTelemetryEventRouter::kKeyCookiesGetAllInfo,
-          CreateCookiesGetAllInfoDict(signal.cookies_get_all_info()));
-    } else if (signal.has_cookies_get_info()) {
-      signals_dict.Set(ExtensionTelemetryEventRouter::kKeyCookiesGetInfo,
-                       CreateCookiesGetInfoDict(signal.cookies_get_info()));
-    } else if (signal.has_remote_host_contacted_info()) {
-      signals_dict.Set(
-          ExtensionTelemetryEventRouter::kKeyRemoteHostContactedInfo,
-          CreateRemoteHostContactedInfoDict(
-              signal.remote_host_contacted_info()));
-    } else if (signal.has_tabs_api_info()) {
-      signals_dict.Set(ExtensionTelemetryEventRouter::kKeyTabsApiInfo,
-                       CreateTabsApiInfoDict(signal.tabs_api_info()));
-    } else if (signal.has_dom_access_info()) {
-      signals_dict.Set(ExtensionTelemetryEventRouter::kKeyDOMAccessInfo,
-                       CreateDOMAccessInfoDict(signal.dom_access_info()));
-    } else if (signal.has_script_injection_info()) {
-      signals_dict.Set(
-          ExtensionTelemetryEventRouter::kKeyScriptInjectionInfo,
-          CreateScriptInjectionInfoDict(signal.script_injection_info()));
-    }
-  }
-
-  report_dict.Set(ExtensionTelemetryEventRouter::kKeySignals,
-                  std::move(signals_dict));
-  return report_dict;
-}
-
-base::DictValue CreateExtensionTelemetryReportRequestDict(
-    const ExtensionTelemetryReportRequest& request) {
-  base::ListValue report_list;
-  for (const auto& telemetry_report : request.reports()) {
-    report_list.Append(CreateExtensionTelemetryReportDict(telemetry_report));
-  }
-
-  base::DictValue request_dict;
-  request_dict.Set(ExtensionTelemetryEventRouter::kKeyReports,
-                   std::move(report_list));
-  request_dict.Set(ExtensionTelemetryEventRouter::kKeyCreationTimeMsec,
-                   base::NumberToString(request.creation_timestamp_msec()));
-
-  return base::DictValue().Set(
-      ExtensionTelemetryEventRouter::kKeyExtensionTelemetryReport,
-      std::move(request_dict));
-}
 
 }  // namespace
 
-const char ExtensionTelemetryEventRouter::kKeyExtensionTelemetryReport[] =
-    "extension_telemetry_report";
-const char ExtensionTelemetryEventRouter::kKeyCreationTimeMsec[] =
-    "creation_timestamp_msec";
-const char ExtensionTelemetryEventRouter::kKeyReports[] = "reports";
-const char ExtensionTelemetryEventRouter::kKeyExtension[] = "extension";
-const char ExtensionTelemetryEventRouter::kKeySignals[] = "signals";
-const char ExtensionTelemetryEventRouter::kKeyCookiesGetAllInfo[] =
-    "cookies_get_all_info";
-const char ExtensionTelemetryEventRouter::kKeyGetAllArgsInfo[] =
-    "get_all_args_info";
-const char ExtensionTelemetryEventRouter::kKeyCookiesGetInfo[] =
-    "cookies_get_info";
-const char ExtensionTelemetryEventRouter::kKeyGetArgsInfo[] = "get_args_info";
-const char ExtensionTelemetryEventRouter::kKeyRemoteHostContactedInfo[] =
-    "remote_host_contacted_info";
-const char ExtensionTelemetryEventRouter::kKeyRemoteHost[] = "remote_host";
-const char ExtensionTelemetryEventRouter::kKeyTabsApiInfo[] = "tabs_api_info";
-const char ExtensionTelemetryEventRouter::kKeyCallDetails[] = "call_details";
-const char ExtensionTelemetryEventRouter::kKeyId[] = "id";
-const char ExtensionTelemetryEventRouter::kKeyVersion[] = "version";
-const char ExtensionTelemetryEventRouter::kKeyName[] = "name";
-const char ExtensionTelemetryEventRouter::kKeyInstallLocation[] =
-    "install_location";
-const char ExtensionTelemetryEventRouter::kKeyIsFromStore[] = "is_from_store";
-const char ExtensionTelemetryEventRouter::kKeyUrl[] = "url";
-const char ExtensionTelemetryEventRouter::kKeyConnectionProtocol[] =
-    "connection_protocol";
-const char ExtensionTelemetryEventRouter::kKeyContactedBy[] = "contacted_by";
-const char ExtensionTelemetryEventRouter::kKeyContactCount[] = "contact_count";
-const char ExtensionTelemetryEventRouter::kKeyDomain[] = "domain";
-const char ExtensionTelemetryEventRouter::kKeyPath[] = "path";
-const char ExtensionTelemetryEventRouter::kKeySecure[] = "secure";
-const char ExtensionTelemetryEventRouter::kKeyStoreId[] = "store_id";
-const char ExtensionTelemetryEventRouter::kKeyIsSession[] = "is_session";
-const char ExtensionTelemetryEventRouter::kKeyCount[] = "count";
-const char ExtensionTelemetryEventRouter::kKeyMethod[] = "method";
-const char ExtensionTelemetryEventRouter::kKeyNewUrl[] = "new_url";
-const char ExtensionTelemetryEventRouter::kKeyCurrentUrl[] = "current_url";
-const char ExtensionTelemetryEventRouter::kKeyFileInfo[] = "file_infos";
-const char ExtensionTelemetryEventRouter::kKeyHash[] = "hash";
-const char ExtensionTelemetryEventRouter::kKeyDOMAccessInfo[] =
-    "dom_access_info";
-const char ExtensionTelemetryEventRouter::kKeyDOMAccesses[] = "dom_accesses";
-const char ExtensionTelemetryEventRouter::kKeyScriptInjectionInfo[] =
-    "script_injection_info";
-const char ExtensionTelemetryEventRouter::kKeyScriptInjections[] =
-    "script_injections";
-const char ExtensionTelemetryEventRouter::kKeyApiName[] = "api_name";
-const char ExtensionTelemetryEventRouter::kKeyAccessType[] = "access_type";
-const char ExtensionTelemetryEventRouter::kKeyArgsList[] = "args_list";
-const char ExtensionTelemetryEventRouter::kKeyArgUrl[] = "arg_url";
-const char ExtensionTelemetryEventRouter::kKeyTimestampMs[] = "timestamp_ms";
-
 // static
 ExtensionTelemetryEventRouter* ExtensionTelemetryEventRouter::Get(
     Profile* profile) {
@@ -551,27 +254,20 @@
   std::optional<ReportingSettings> settings =
       reporting_client->GetReportingSettings();
 
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
-    chrome::cros::reporting::proto::ExtensionTelemetryEvent
-        extension_telemetry_event;
-    *extension_telemetry_event.mutable_extension_telemetry_report() =
-        *CreateRedactedExtensionTelemetryReportRequestProto(
-            telemetry_report_request.get());
-    extension_telemetry_event.set_profile_identifier(
-        reporting_client->GetProfileIdentifier());
-    extension_telemetry_event.set_profile_user_name(
-        reporting_client->GetProfileUserName());
+  chrome::cros::reporting::proto::ExtensionTelemetryEvent
+      extension_telemetry_event;
+  *extension_telemetry_event.mutable_extension_telemetry_report() =
+      *CreateRedactedExtensionTelemetryReportRequestProto(
+          telemetry_report_request.get());
+  extension_telemetry_event.set_profile_identifier(
+      reporting_client->GetProfileIdentifier());
+  extension_telemetry_event.set_profile_user_name(
+      reporting_client->GetProfileUserName());
 
-    chrome::cros::reporting::proto::Event event;
-    *event.mutable_extension_telemetry_event() = extension_telemetry_event;
+  chrome::cros::reporting::proto::Event event;
+  *event.mutable_extension_telemetry_event() = extension_telemetry_event;
 
-    reporting_client->ReportEvent(std::move(event), settings.value());
-  } else {
-    reporting_client->ReportRealtimeEvent(
-        kExtensionTelemetryEvent, std::move(settings.value()),
-        CreateExtensionTelemetryReportRequestDict(*telemetry_report_request));
-  }
+  reporting_client->ReportEvent(std::move(event), settings.value());
 }
 
 #undef COPY_IF_SET
diff --git a/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.cc b/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.cc
index c89c699..af83751d 100644
--- a/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.cc
+++ b/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.cc
@@ -143,46 +143,9 @@
   return service->GetReportingSettings();
 }
 
-void RealtimeReportingClient::ReportRealtimeEvent(
-    const std::string& name,
-    const ReportingSettings& settings,
-    base::DictValue event) {
-  ReportEventWithTimestampDeprecated(name, settings, std::move(event),
-                                     base::Time::Now(),
-                                     /*include_profile_user_name=*/true);
-}
-
-void RealtimeReportingClient::ReportPastEvent(const std::string& name,
-                                              const ReportingSettings& settings,
-                                              base::DictValue event,
-                                              const base::Time& time) {
-  // Do not include profile information for past events because for crash events
-  // we do not necessarily know which profile caused the crash .
-  ReportEventWithTimestampDeprecated(name, settings, std::move(event), time,
-                                     /*include_profile_user_name=*/false);
-}
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
-void AddCrowdstrikeSignalsToEvent(
-    base::DictValue& event,
-    const device_signals::SignalsAggregationResponse& response) {
-  if (!response.agent_signals_response ||
-      !response.agent_signals_response->crowdstrike_signals) {
-    return;
-  }
-  const auto& crowdstrike_signals =
-      response.agent_signals_response->crowdstrike_signals.value();
-
-  base::DictValue crowdstrike_agent_fields;
-  crowdstrike_agent_fields.Set("agent_id", crowdstrike_signals.agent_id);
-  crowdstrike_agent_fields.Set("customer_id", crowdstrike_signals.customer_id);
-  base::DictValue crowdstrike_agent;
-  crowdstrike_agent.Set("crowdstrike", std::move(crowdstrike_agent_fields));
-  base::ListValue agents;
-  agents.Append(std::move(crowdstrike_agent));
-  event.Set("securityAgents", std::move(agents));
-}
 
 void AddCrowdstrikeSignalsToEvent(
     Event& event,
@@ -373,42 +336,6 @@
   UploadSecurityEvent(std::move(event), client, settings);
 }
 
-void RealtimeReportingClient::MaybeCollectDeviceSignalsAndReportEventDeprecated(
-    base::DictValue event,
-    policy::CloudPolicyClient* client,
-    std::string name,
-    const ReportingSettings& settings,
-    base::Time time) {
-  Profile* profile = Profile::FromBrowserContext(context_);
-  device_signals::SignalsAggregator* signals_aggregator =
-      enterprise_signals::SignalsAggregatorFactory::GetForProfile(profile);
-  if (signals_aggregator) {
-    device_signals::SignalsAggregationRequest request;
-    request.signal_names.emplace(device_signals::SignalName::kAgent);
-    signals_aggregator->GetSignals(
-        request,
-        base::BindOnce(
-            &RealtimeReportingClient::PopulateSignalsAndReportEventDeprecated,
-            AsWeakPtrImpl(), std::move(event), client, name, settings, context_,
-            time));
-  } else {
-    UploadSecurityEventReportDeprecated(std::move(event), client, name,
-                                        settings, time);
-  }
-}
-
-void RealtimeReportingClient::PopulateSignalsAndReportEventDeprecated(
-    base::DictValue event,
-    policy::CloudPolicyClient* client,
-    std::string name,
-    ReportingSettings settings,
-    content::BrowserContext* context,
-    base::Time time,
-    device_signals::SignalsAggregationResponse response) {
-  AddCrowdstrikeSignalsToEvent(event, response);
-  UploadSecurityEventReportDeprecated(std::move(event), client, name, settings,
-                                      time);
-}
 #endif
 
 bool RealtimeReportingClient::ShouldIncludeDeviceInfo(bool per_profile) {
@@ -429,49 +356,6 @@
   return event_wrapper;
 }
 
-void RealtimeReportingClient::UploadCallbackDeprecated(
-    base::DictValue event_wrapper,
-    bool per_profile,
-    policy::CloudPolicyClient* client,
-    EnterpriseReportingEventType event_type,
-    base::TimeTicks upload_started_at,
-    policy::CloudPolicyClient::Result upload_result) {
-  // TODO(crbug.com/256553070): Do not crash if the client is unregistered.
-  CHECK(!upload_result.IsClientNotRegisteredError());
-
-// Device DM token is already set on ChromeOS by reporting::GetContext(...)
-#if !BUILDFLAG(IS_CHROMEOS)
-  if (!per_profile && client) {
-    event_wrapper.SetByDottedPath(
-        "context.device",
-        policy::ReportingJobConfigurationBase::DeviceDictionaryBuilder::
-            BuildDeviceDictionary(client->dm_token(), client->client_id()));
-  }
-#endif
-  base::DictValue error_details = ReportErrorDetails(upload_result);
-  event_wrapper.Merge(std::move(error_details));
-
-  safe_browsing::WebUIContentInfoSingleton::GetInstance()->AddToReportingEvents(
-      std::move(event_wrapper));
-
-  if (upload_result.IsSuccess()) {
-    base::UmaHistogramEnumeration("Enterprise.ReportingEventUploadSuccess",
-                                  event_type);
-    base::UmaHistogramCustomTimes(
-        GetSuccessfulUploadDurationUmaMetricName(event_type),
-        base::TimeTicks::Now() - upload_started_at, base::Milliseconds(1),
-        base::Minutes(5), 50);
-
-  } else {
-    base::UmaHistogramEnumeration("Enterprise.ReportingEventUploadFailure",
-                                  event_type);
-    base::UmaHistogramCustomTimes(
-        GetFailedUploadDurationUmaMetricName(event_type),
-        base::TimeTicks::Now() - upload_started_at, base::Milliseconds(1),
-        base::Minutes(5), 50);
-  }
-}
-
 void RealtimeReportingClient::UploadCallback(
     bool per_profile,
     policy::CloudPolicyClient* client,
@@ -499,10 +383,6 @@
   }
 }
 
-base::DictValue RealtimeReportingClient::GetContext() {
-  return reporting::GetContext(Profile::FromBrowserContext(context_));
-}
-
 ::chrome::cros::reporting::proto::UploadEventsRequest
 RealtimeReportingClient::CreateUploadEventsRequest() {
   return reporting::CreateUploadEventsRequest(
diff --git a/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.h b/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.h
index 273119d..525b6fe 100644
--- a/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.h
+++ b/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.h
@@ -75,37 +75,16 @@
   // policy::CloudPolicyClient::Observer overrides:
   void OnClientError(policy::CloudPolicyClient* client) override;
 
-  // Report safe browsing event through real-time reporting channel, if enabled.
-  // Declared as virtual for tests.
-  virtual void ReportRealtimeEvent(const std::string& name,
-                                   const ReportingSettings& settings,
-                                   base::DictValue event);
 
   base::DictValue ReportErrorDetails(
       const policy::CloudPolicyClient::Result& upload_result);
 
-  // Report safe browsing events that have occurred in the past but has not yet
-  // been reported. This is currently used for browser crash events, which are
-  // polled at a fixed time interval. Declared as virtual for tests.
-  virtual void ReportPastEvent(const std::string& name,
-                               const ReportingSettings& settings,
-                               base::DictValue event,
-                               const base::Time& time);
-
  private:
   // RealtimeReportingClientBase overrides (all overrides below):
   std::string GetBrowserClientId() override;
-  base::DictValue GetContext() override;
   ::chrome::cros::reporting::proto::UploadEventsRequest
   CreateUploadEventsRequest() override;
   bool ShouldIncludeDeviceInfo(bool per_profile) override;
-  void UploadCallbackDeprecated(
-      base::DictValue event_wrapper,
-      bool per_profile,
-      policy::CloudPolicyClient* client,
-      EnterpriseReportingEventType event_type,
-      base::TimeTicks upload_started_at,
-      policy::CloudPolicyClient::Result upload_result) override;
   void UploadCallback(
       bool per_profile,
       policy::CloudPolicyClient* client,
@@ -119,25 +98,6 @@
 #endif
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
-  // DEPRECATED: Use MaybeCollectDeviceSignalsAndReportEvent(Event, ...).
-  void MaybeCollectDeviceSignalsAndReportEventDeprecated(
-      base::DictValue event,
-      policy::CloudPolicyClient* client,
-      std::string name,
-      const ReportingSettings& settings,
-      base::Time time) override;
-
-  // Add Crowdstrike signals to event report and upload it.
-  // DEPRECATED: Use PopulateSignalsAndReportEvent(Event, ...) instead.
-  void PopulateSignalsAndReportEventDeprecated(
-      base::DictValue event,
-      policy::CloudPolicyClient* client,
-      std::string name,
-      ReportingSettings settings,
-      content::BrowserContext* context,
-      base::Time time,
-      device_signals::SignalsAggregationResponse response);
-
   void MaybeCollectDeviceSignalsAndReportEvent(
       ::chrome::cros::reporting::proto::Event event,
       policy::CloudPolicyClient* client,
@@ -169,22 +129,6 @@
 };
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
-// Populate event dict with CrowdStrike signal values. If those signals are
-// available in `response`, this function returns a Dict with the following
-// fields added:
-// "securityAgents" : [
-//   {
-//     "crowdstrike": {
-//       "agent_id": "agent-123",
-//       "customer_id": "customer-123"
-//     }
-//   }
-// ]
-// These must match proto specified in
-// chrome/cros/reporting/api/proto/browser_events.proto
-void AddCrowdstrikeSignalsToEvent(
-    base::DictValue& event,
-    const device_signals::SignalsAggregationResponse& response);
 void AddCrowdstrikeSignalsToEvent(
     ::chrome::cros::reporting::proto::Event& event,
     const device_signals::SignalsAggregationResponse& response);
diff --git a/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client_unittest.cc b/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client_unittest.cc
index 5f39516..15a26961 100644
--- a/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client_unittest.cc
+++ b/chrome/browser/enterprise/connectors/reporting/realtime_reporting_client_unittest.cc
@@ -402,32 +402,6 @@
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
-TEST_F(RealtimeReportingClientTestBase, TestCrowdstrikeSignalsPopulated) {
-  base::DictValue event;
-  device_signals::CrowdStrikeSignals signals;
-  signals.agent_id = "agent-123";
-  signals.customer_id = "customer-123";
-  device_signals::AgentSignalsResponse agent_signals;
-  agent_signals.crowdstrike_signals = signals;
-  device_signals::SignalsAggregationResponse response;
-  response.agent_signals_response = agent_signals;
-  AddCrowdstrikeSignalsToEvent(event, response);
-  const base::ListValue& agentList = event.Find("securityAgents")->GetList();
-  ASSERT_EQ(agentList.size(), 1u);
-  const base::DictValue& signalValues =
-      agentList[0].GetDict().Find("crowdstrike")->GetDict();
-  EXPECT_EQ(signalValues.Find("agent_id")->GetString(), "agent-123");
-  EXPECT_EQ(signalValues.Find("customer_id")->GetString(), "customer-123");
-}
-
-TEST_F(RealtimeReportingClientTestBase,
-       TestCrowdstrikeSignalsNotPopulatedForEmptyResponse) {
-  base::DictValue event;
-  device_signals::SignalsAggregationResponse response;
-  response.agent_signals_response = std::nullopt;
-  AddCrowdstrikeSignalsToEvent(event, response);
-  EXPECT_EQ(event.Find("securityAgents"), nullptr);
-}
 
 TEST_F(RealtimeReportingClientOidcTest, Username) {
   RealtimeReportingClient client(profile_);
@@ -437,14 +411,14 @@
 // TODO(b/342232001): Add more tests for the `RealtimeReportingClientOidcTest`
 // fixture to cover key use cases.
 
-class ProtoBasedCrowdStrikeSignalTest
+class CrowdStrikeSignalTest
     : public RealtimeReportingClientTestBase,
       public testing::WithParamInterface<Event::EventCase> {
  public:
-  ProtoBasedCrowdStrikeSignalTest() = default;
+  CrowdStrikeSignalTest() = default;
 };
 
-TEST_P(ProtoBasedCrowdStrikeSignalTest, TestCrowdstrikeSignalsPopulated) {
+TEST_P(CrowdStrikeSignalTest, TestCrowdstrikeSignalsPopulated) {
   device_signals::CrowdStrikeSignals signals;
   signals.agent_id = "agent-123";
   signals.customer_id = "customer-123";
@@ -464,7 +438,7 @@
   EXPECT_EQ(security_agent.crowdstrike().customer_id(), "customer-123");
 }
 
-TEST_P(ProtoBasedCrowdStrikeSignalTest,
+TEST_P(CrowdStrikeSignalTest,
        TestCrowdstrikeSignalsNotPopulatedForEmptyResponse) {
   device_signals::SignalsAggregationResponse response;
   auto event = GetTestEvent(GetParam());
@@ -475,7 +449,7 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(,
-                         ProtoBasedCrowdStrikeSignalTest,
+                         CrowdStrikeSignalTest,
                          testing::Values(Event::kPasswordReuseEvent,
                                          Event::kPasswordChangedEvent,
                                          Event::kDangerousDownloadEvent,
diff --git a/chrome/browser/enterprise/connectors/reporting/reporting_event_router_unittest.cc b/chrome/browser/enterprise/connectors/reporting/reporting_event_router_unittest.cc
index 69c4468..21e9bf2b 100644
--- a/chrome/browser/enterprise/connectors/reporting/reporting_event_router_unittest.cc
+++ b/chrome/browser/enterprise/connectors/reporting/reporting_event_router_unittest.cc
@@ -525,6 +525,51 @@
   run_loop.Run();
 }
 
+TEST_F(ReportingEventRouterTest,
+       TestOnUrlFilteringInterstitial_ScreenshotAudit) {
+  EnableEnhancedFieldsForSecOps();
+  test::SetOnSecurityEventReporting(
+      profile_->GetPrefs(), /*enabled=*/true,
+      /*enabled_event_names=*/{kKeyUrlFilteringInterstitialEvent},
+      /*enabled_opt_in_events=*/{});
+
+  test::EventReportValidatorBase validator(client_.get());
+  base::RunLoop run_loop;
+  validator.SetDoneClosure(run_loop.QuitClosure());
+  chrome::cros::reporting::proto::UrlFilteringInterstitialEvent expected_event;
+
+  expected_event.set_url("https://filteredurl.com/");
+  expected_event.set_event_result(
+      chrome::cros::reporting::proto::EVENT_RESULT_ALLOWED);
+  expected_event.set_threat_type(
+      chrome::cros::reporting::proto::UrlFilteringInterstitialEvent::
+          UNKNOWN_INTERSTITIAL_THREAT_TYPE);
+  expected_event.set_profile_user_name(profile_->GetProfileUserName());
+  expected_event.set_profile_identifier(GetProfileIdentifier());
+  *expected_event.add_triggered_rule_info() = test::MakeTriggeredRuleInfo(
+      /*action=*/TriggeredRuleInfo::ACTION_UNKNOWN, /*has_watermark=*/false,
+      /*has_screenshot_protection=*/true);
+  *expected_event.add_referrers() = test::MakeUrlInfoReferrer();
+  expected_event.set_web_app_signed_in_account(kFakeActiveUserEmail);
+
+  validator.ExpectUrlFilteringInterstitialEvent(expected_event);
+
+  safe_browsing::RTLookupResponse response;
+  auto* threat_info = response.add_threat_info();
+  auto* matched_url_navigation_rule =
+      threat_info->mutable_matched_url_navigation_rule();
+  matched_url_navigation_rule->set_rule_id("123");
+  matched_url_navigation_rule->set_rule_name("test rule name");
+  matched_url_navigation_rule->set_matched_url_category("test rule category");
+  matched_url_navigation_rule->set_block_screenshot(true);
+  ReferrerChain referrer_chain;
+  referrer_chain.Add(test::MakeReferrerChainEntry());
+
+  reporting_event_router_->OnUrlFilteringInterstitial(
+      GURL("https://filteredurl.com"), "", response, referrer_chain);
+  run_loop.Run();
+}
+
 TEST_F(ReportingEventRouterTest, TestInterstitialShownWarned) {
   EnableEnhancedFieldsForSecOps();
   test::SetOnSecurityEventReporting(
diff --git a/chrome/browser/enterprise/signals/BUILD.gn b/chrome/browser/enterprise/signals/BUILD.gn
index d2dba07..3f92b010 100644
--- a/chrome/browser/enterprise/signals/BUILD.gn
+++ b/chrome/browser/enterprise/signals/BUILD.gn
@@ -10,8 +10,8 @@
   deps = [
     "//base",
     "//chrome/browser/enterprise:impl",
-    "//chrome/browser/enterprise/connectors/device_trust:test_support",
     "//chrome/test:test_support",
+    "//components/enterprise/device_trust/core:test_support",
     "//testing/gtest",
   ]
 
diff --git a/chrome/browser/enterprise/signals/user_delegate_impl_unittest.cc b/chrome/browser/enterprise/signals/user_delegate_impl_unittest.cc
index edd8524f..3c20af2 100644
--- a/chrome/browser/enterprise/signals/user_delegate_impl_unittest.cc
+++ b/chrome/browser/enterprise/signals/user_delegate_impl_unittest.cc
@@ -26,7 +26,7 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if !BUILDFLAG(IS_ANDROID)
-#include "chrome/browser/enterprise/connectors/device_trust/fake_device_trust_connector_service.h"
+#include "components/enterprise/device_trust/core/fake_device_trust_connector_service.h"
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 namespace enterprise_signals {
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 4efa275..f13cdd4 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -482,9 +482,7 @@
     # TODO(crbug.com/353284909): Remove this circular dependency.
     "//chrome/browser/media/webrtc",
     "//chrome/browser/ui/extensions",
-    "//chrome/browser/ui/safety_hub",
     "//chrome/browser/ui/toolbar",
-    "//chrome/browser/supervised_user",
   ]
   deps = [
     "//base",
@@ -871,9 +869,6 @@
       # TODO(b/40251079): Remove this circular dependency.
       "//chrome/browser/apps/app_service/app_icon",
 
-      # Lots of APIs use headers from the list above.
-      "//chrome/browser/extensions/api:api_registration",
-
       # TODO(crbug.com/346472679): Remove this circular dependency.
       "//chrome/browser/web_applications/extensions",
 
@@ -884,14 +879,10 @@
       "//chrome/browser/themes",
 
       # TODO(crbug.com/344921703): Remove circular dependencies.
-      "//chrome/browser/sync",
       "//chrome/browser/sync_file_system",
 
       # TODO(crbug.com/345487716): Remove this circular dependency.
       "//chrome/browser/ui/views/side_panel",
-
-      "//chrome/browser/feedback",
-      "//chrome/browser/ui/views/toolbar",
     ]
 
     # Since browser and browser_extensions actually depend on each other,
@@ -1225,10 +1216,8 @@
 
       allow_circular_includes_from += [
         "//chrome/browser/chromeos/app_mode",
-        "//chrome/browser/chromeos/drivefs",
         "//chrome/browser/chromeos/extensions/component_extension_content_settings",
         "//chrome/browser/chromeos/extensions/desk_api",
-        "//chrome/browser/chromeos/extensions/file_system_provider",
         "//chrome/browser/chromeos/extensions/login_screen/login/cleanup",
       ]
     } else {
@@ -1418,46 +1407,27 @@
 
       allow_circular_includes_from += [
         "//chrome/browser/ash/accessibility",
-        "//chrome/browser/ash/account_manager",
-        "//chrome/browser/ash/attestation",
         "//chrome/browser/ash/app_list",
         "//chrome/browser/ash/app_list/app_service",
         "//chrome/browser/ash/app_mode",
         "//chrome/browser/ash/app_mode/web_app",
-        "//chrome/browser/ash/app_restore",
-        "//chrome/browser/ash/arc",
         "//chrome/browser/ash/arc/optin",
-        "//chrome/browser/ash/bruschetta",
-        "//chrome/browser/ash/child_accounts",
         "//chrome/browser/ash/child_accounts/time_limits",
-        "//chrome/browser/ash/crostini",
         "//chrome/browser/ash/customization",
-        "//chrome/browser/ash/drive",
         "//chrome/browser/ash/extensions",
         "//chrome/browser/ash/extensions/autotest_private",
         "//chrome/browser/ash/extensions/file_manager",
-        "//chrome/browser/ash/extensions/speech",
         "//chrome/browser/ash/extensions/users_private",
         "//chrome/browser/ash/file_manager",
         "//chrome/browser/ash/file_system_provider",
-        "//chrome/browser/ash/fileapi",
-        "//chrome/browser/ash/guest_os",
-        "//chrome/browser/ash/guest_os/public",
         "//chrome/browser/ash/input_method",
         "//chrome/browser/ash/login",
         "//chrome/browser/ash/login/app_mode",
-        "//chrome/browser/ash/login/demo_mode",
-        "//chrome/browser/ash/login/quick_unlock",
-        "//chrome/browser/ash/login/session",
         "//chrome/browser/ash/net",
-        "//chrome/browser/ash/platform_keys",
         "//chrome/browser/ash/policy/core",
         "//chrome/browser/ash/policy/handlers",
         "//chrome/browser/ash/system_web_apps/apps",
-        "//chrome/browser/ash/system_logs",
-        "//chrome/browser/ui/ash/login",
         "//chrome/browser/ui/ash/shelf/app_service",
-        "//chrome/browser/ui/ash/thumbnail_loader",
         "//chrome/browser/ui/webui/ash/settings/pages/a11y",
       ]
 
diff --git a/chrome/browser/extensions/api/processes/processes_api.cc b/chrome/browser/extensions/api/processes/processes_api.cc
index 785b611..c18f5b7f 100644
--- a/chrome/browser/extensions/api/processes/processes_api.cc
+++ b/chrome/browser/extensions/api/processes/processes_api.cc
@@ -500,39 +500,21 @@
   // Check if it's a renderer.
   auto* render_process_host =
       content::RenderProcessHost::FromID(child_process_host_id_);
-  if (render_process_host)
+  if (render_process_host) {
     return RespondNow(
         TerminateIfAllowed(render_process_host->GetProcess().Handle()));
-
-  // This could be a non-renderer child process like a plugin.
-  // Try to get its handle from the BrowserChildProcessHost on the IO thread.
-  content::GetIOThreadTaskRunner({})->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&ProcessesTerminateFunction::GetProcessHandleOnIO, this,
-                     child_process_host_id_),
-      base::BindOnce(&ProcessesTerminateFunction::OnProcessHandleOnUI, this));
-
-  // Promise to respond later.
-  return RespondLater();
-}
-
-base::ProcessHandle ProcessesTerminateFunction::GetProcessHandleOnIO(
-    int child_process_host_id) const {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  auto* host = content::BrowserChildProcessHost::FromID(child_process_host_id);
-  if (host) {
-    return host->GetData().GetProcess().Handle();
   }
 
-  return base::kNullProcessHandle;
-}
+  // Check if it's a non-renderer child process (e.g. utility, GPU).
+  auto* browser_child_process_host =
+      content::BrowserChildProcessHost::FromID(child_process_host_id_);
+  if (browser_child_process_host) {
+    return RespondNow(TerminateIfAllowed(
+        browser_child_process_host->GetData().GetProcess().Handle()));
+  }
 
-void ProcessesTerminateFunction::OnProcessHandleOnUI(
-    base::ProcessHandle handle) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  Respond(TerminateIfAllowed(handle));
+  return RespondNow(Error(errors::kProcessNotFound,
+                          base::NumberToString(child_process_host_id_)));
 }
 
 ExtensionFunction::ResponseValue
diff --git a/chrome/browser/extensions/api/processes/processes_api.h b/chrome/browser/extensions/api/processes/processes_api.h
index 0939467..8d535b1 100644
--- a/chrome/browser/extensions/api/processes/processes_api.h
+++ b/chrome/browser/extensions/api/processes/processes_api.h
@@ -146,10 +146,6 @@
  private:
   ~ProcessesTerminateFunction() override = default;
 
-  // Functions to get the process handle on the IO thread and post it back to
-  // the UI thread from processing.
-  base::ProcessHandle GetProcessHandleOnIO(int child_process_host_id) const;
-  void OnProcessHandleOnUI(base::ProcessHandle handle);
 
   // Terminates the process with `handle` if it's valid and is allowed to be
   // terminated. Returns the response value of this extension function to be
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index fe2c9128..6e9800cb 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -1398,6 +1398,8 @@
       settings_api::PrefType::kNumber;
   (*s_allowlist)[glic::prefs::kGlicKeepSidepanelOpenOnNewTabsEnabled] =
       settings_api::PrefType::kBoolean;
+  (*s_allowlist)[glic::prefs::kGlicShakeTriggerEnabled] =
+      settings_api::PrefType::kBoolean;
   (*s_allowlist)[glic::prefs::kGlicHotkeyGlobalScopeEnabled] =
       settings_api::PrefType::kBoolean;
 
diff --git a/chrome/browser/finds/android/junit/src/org/chromium/chrome/browser/finds/FindsManagerUnitTest.java b/chrome/browser/finds/android/junit/src/org/chromium/chrome/browser/finds/FindsManagerUnitTest.java
index 0c1eab8..7982c52 100644
--- a/chrome/browser/finds/android/junit/src/org/chromium/chrome/browser/finds/FindsManagerUnitTest.java
+++ b/chrome/browser/finds/android/junit/src/org/chromium/chrome/browser/finds/FindsManagerUnitTest.java
@@ -20,7 +20,6 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -31,7 +30,6 @@
 
 /** Unit tests for {@link FindsManager}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class FindsManagerUnitTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/finds/android/junit/src/org/chromium/chrome/browser/finds/FindsOptInCoordinatorUnitTest.java b/chrome/browser/finds/android/junit/src/org/chromium/chrome/browser/finds/FindsOptInCoordinatorUnitTest.java
index e9f223d..04459561 100644
--- a/chrome/browser/finds/android/junit/src/org/chromium/chrome/browser/finds/FindsOptInCoordinatorUnitTest.java
+++ b/chrome/browser/finds/android/junit/src/org/chromium/chrome/browser/finds/FindsOptInCoordinatorUnitTest.java
@@ -37,7 +37,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions.ChannelId;
@@ -57,7 +56,6 @@
 
 /** Unit tests for {@link FindsOptInCoordinator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class FindsOptInCoordinatorUnitTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/finds/core/finds_service.cc b/chrome/browser/finds/core/finds_service.cc
index 083b0e60..5ff0eca 100644
--- a/chrome/browser/finds/core/finds_service.cc
+++ b/chrome/browser/finds/core/finds_service.cc
@@ -190,8 +190,11 @@
 
 void RecordFindsResultAndRunCallback(
     base::OnceCallback<void(FindsService::Result)> callback,
-    FindsService::Result result) {
+    FindsService::Result result,
+    base::TimeTicks start_time) {
   base::UmaHistogramEnumeration("Finds.Result", result.status);
+  base::UmaHistogramTimes("Finds.ExecutionTime",
+                          base::TimeTicks::Now() - start_time);
   if (callback) {
     std::move(callback).Run(std::move(result));
   }
@@ -290,18 +293,23 @@
 
 void FindsService::ExecuteModelAndScheduleNotification(
     base::OnceCallback<void(Result)> callback) {
+  base::TimeTicks start_time = base::TimeTicks::Now();
   if (!IsAllowedByEnterprisePolicy(pref_service_)) {
     RecordFindsResultAndRunCallback(
-        std::move(callback), {Result::Status::kDisabledByEnterprisePolicy,
-                              "Error: Feature disabled by enterprise policy."});
+        std::move(callback),
+        {Result::Status::kDisabledByEnterprisePolicy,
+         "Error: Feature disabled by enterprise policy."},
+        start_time);
     return;
   }
 
   if (!IsHistorySyncAndMsbbEnabled(sync_service_, pref_service_)) {
     RecordFindsResultAndRunCallback(
-        std::move(callback), {Result::Status::kDisabledByHistorySyncOrMsbb,
-                              "Error: Feature disabled because History Sync or "
-                              "MSBB is not enabled."});
+        std::move(callback),
+        {Result::Status::kDisabledByHistorySyncOrMsbb,
+         "Error: Feature disabled because History Sync or "
+         "MSBB is not enabled."},
+        start_time);
     return;
   }
 
@@ -309,21 +317,24 @@
     RecordFindsResultAndRunCallback(
         std::move(callback),
         {Result::Status::kModelExecutionDisabledByParam,
-         "Error: Model execution disabled by feature parameter."});
+         "Error: Model execution disabled by feature parameter."},
+        start_time);
     return;
   }
 
   if (!IsModelExecutionCooldownPassed(pref_service_)) {
     RecordFindsResultAndRunCallback(std::move(callback),
                                     {Result::Status::kModelExecutionOnCooldown,
-                                     "Error: Model execution is on cooldown."});
+                                     "Error: Model execution is on cooldown."},
+                                    start_time);
     return;
   }
 
   if (!history_service_) {
     RecordFindsResultAndRunCallback(std::move(callback),
                                     {Result::Status::kHistoryServiceUnavailable,
-                                     "Error: HistoryService not available."});
+                                     "Error: HistoryService not available."},
+                                    start_time);
     return;
   }
 
@@ -331,7 +342,8 @@
     RecordFindsResultAndRunCallback(
         std::move(callback),
         {Result::Status::kOptimizationGuideUnavailable,
-         "Error: OptimizationGuideKeyedService not available."});
+         "Error: OptimizationGuideKeyedService not available."},
+        start_time);
     return;
   }
 
@@ -343,7 +355,8 @@
   history_service_->QueryHistory(
       std::u16string(), options,
       base::BindOnce(&FindsService::OnHistoryQueryComplete,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                     start_time),
       &history_task_tracker_);
 }
 
@@ -441,12 +454,14 @@
 
 void FindsService::OnHistoryQueryComplete(
     base::OnceCallback<void(Result)> callback,
+    base::TimeTicks start_time,
     history::QueryResults results) {
   if (!opt_guide_service_) {
     RecordFindsResultAndRunCallback(
         std::move(callback),
         {Result::Status::kOptimizationGuideUnavailable,
-         "Error: OptimizationGuideKeyedService not available."});
+         "Error: OptimizationGuideKeyedService not available."},
+        start_time);
     return;
   }
 
@@ -454,7 +469,8 @@
     RecordFindsResultAndRunCallback(
         std::move(callback),
         {Result::Status::kEmptyHistory,
-         "Error: No history available to suggest themes."});
+         "Error: No history available to suggest themes."},
+        start_time);
     return;
   }
 
@@ -472,11 +488,13 @@
       optimization_guide::ModelExecutionOptions{
           .execution_timeout = features::kModelExecutionRequestTimeout.Get()},
       base::BindOnce(&FindsService::OnModelExecutionComplete,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+                     start_time));
 }
 
 void FindsService::OnModelExecutionComplete(
     base::OnceCallback<void(Result)> callback,
+    base::TimeTicks start_time,
     optimization_guide::OptimizationGuideModelExecutionResult result,
     std::unique_ptr<optimization_guide::ModelQualityLogEntry> log_entry) {
   if (!result.response.has_value()) {
@@ -485,7 +503,7 @@
                            static_cast<int>(result.response.error().error()));
     RecordFindsResultAndRunCallback(
         std::move(callback),
-        {Result::Status::kModelExecutionFailed, error_message});
+        {Result::Status::kModelExecutionFailed, error_message}, start_time);
     return;
   }
 
@@ -496,14 +514,15 @@
     RecordFindsResultAndRunCallback(
         std::move(callback),
         {Result::Status::kResponseParsingFailed,
-         "Model execution successful, but failed to parse response."});
+         "Model execution successful, but failed to parse response."},
+        start_time);
     return;
   }
 
   if (response->suggested_themes().empty()) {
     RecordFindsResultAndRunCallback(
         std::move(callback),
-        {Result::Status::kNoThemesFound, "No themes found."});
+        {Result::Status::kNoThemesFound, "No themes found."}, start_time);
     return;
   }
 
@@ -513,7 +532,8 @@
     RecordFindsResultAndRunCallback(
         std::move(callback),
         {Result::Status::kNoNonCooldownThemesFound,
-         "No themes found that passed cooldown criteria."});
+         "No themes found that passed cooldown criteria."},
+        start_time);
     return;
   }
 
@@ -521,23 +541,28 @@
   // GetHighestScoredThemeIfPossible.
   if (best_theme->theme_suggested_contents().empty()) {
     RecordFindsResultAndRunCallback(
-        std::move(callback), {Result::Status::kNoSuggestionsForTheme,
-                              "No suggestions available for this theme."});
+        std::move(callback),
+        {Result::Status::kNoSuggestionsForTheme,
+         "No suggestions available for this theme."},
+        start_time);
     return;
   }
 
   bool schedule_success = ScheduleNotificationWithModelResult(*best_theme);
   if (!schedule_success) {
     RecordFindsResultAndRunCallback(
-        std::move(callback), {Result::Status::kFailedToScheduleNotification,
-                              "Could not schedule notification."});
+        std::move(callback),
+        {Result::Status::kFailedToScheduleNotification,
+         "Could not schedule notification."},
+        start_time);
     return;
   }
 
   RecordFindsResultAndRunCallback(
       std::move(callback),
       {Result::Status::kSuccess,
-       FindsSuggestionResponseToHumanReadableString(*response)});
+       FindsSuggestionResponseToHumanReadableString(*response)},
+      start_time);
 }
 
 void FindsService::OnGetClientOverview(notifications::ClientOverview overview) {
diff --git a/chrome/browser/finds/core/finds_service.h b/chrome/browser/finds/core/finds_service.h
index 959e88c..106acce 100644
--- a/chrome/browser/finds/core/finds_service.h
+++ b/chrome/browser/finds/core/finds_service.h
@@ -14,6 +14,7 @@
 #include "base/scoped_observation.h"
 #include "base/supports_user_data.h"
 #include "base/task/cancelable_task_tracker.h"
+#include "base/time/time.h"
 #include "chrome/browser/finds/core/finds_metrics.h"
 #include "components/history/core/browser/history_service_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -140,9 +141,11 @@
 
   void CheckFindsNotificationsEnabledAndMaybeExecute();
   void OnHistoryQueryComplete(base::OnceCallback<void(Result)> callback,
+                              base::TimeTicks start_time,
                               history::QueryResults results);
   void OnModelExecutionComplete(
       base::OnceCallback<void(Result)> callback,
+      base::TimeTicks start_time,
       optimization_guide::OptimizationGuideModelExecutionResult result,
       std::unique_ptr<optimization_guide::ModelQualityLogEntry> log_entry);
   void OnGetClientOverview(notifications::ClientOverview overview);
diff --git a/chrome/browser/finds/core/finds_service_unittest.cc b/chrome/browser/finds/core/finds_service_unittest.cc
index f3027e6..db346f3d0 100644
--- a/chrome/browser/finds/core/finds_service_unittest.cc
+++ b/chrome/browser/finds/core/finds_service_unittest.cc
@@ -373,6 +373,7 @@
   EXPECT_NE(0, prefs_.GetInt64(prefs::kFindsModelExecutionLastTimestamp));
   histogram_tester_.ExpectUniqueSample(
       "Finds.Result", FindsService::Result::Status::kSuccess, 1);
+  histogram_tester_.ExpectTotalCount("Finds.ExecutionTime", 1);
 }
 
 TEST_F(FindsServiceTest, ExecutionCooldownNotPassed) {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index f0a9147..77ab8c8 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -5585,6 +5585,14 @@
     "expiry_milestone": 150
   },
   {
+    "name": "glic-caa-guest-error",
+    "owners": [
+      "birnie@google.com",
+      "//chrome/browser/glic/OWNERS"
+    ],
+    "expiry_milestone": 160
+  },
+  {
     "name": "glic-capture-region",
     "owners": [ "gklassen@chromium.org" ],
     "expiry_milestone": 150
@@ -5728,14 +5736,6 @@
     "expiry_milestone": 160
   },
   {
-    "name": "glic-opt-in-dialog-link-a11y-fix",
-    "owners": [
-      "rosiey@google.com",
-      "//chrome/browser/glic/OWNERS"
-    ],
-    "expiry_milestone": 160
-  },
-  {
     "name": "glic-os-icon-variant",
     "owners": [ "andreaxg@google.com", "//chrome/browser/glic/OWNERS" ],
     "expiry_milestone": 160
@@ -7337,6 +7337,11 @@
     "expiry_milestone": 160
   },
   {
+    "name": "ntp-mvt-in-bottom-sheet",
+    "owners": [ "thegreenfrog@google.com", "bling-pandamonium@google.com" ],
+    "expiry_milestone": 160
+  },
+  {
     "name": "ntp-next-canvas-chip",
     "owners": [ "rtatum@chromium.com", "chrome-desktop-ntp@google.com" ],
     "expiry_milestone": 160
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 8ebc075..665389e 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1753,6 +1753,11 @@
 inline constexpr char kGlicGuestUrlPresetsDescription[] =
     "Overrides the Bluebird environment URL used for Glic with user-configured "
     "values. Intended for manual testing only.";
+
+inline constexpr char kGlicCaaGuestErrorName[] = "Glic CAA Guest Error";
+inline constexpr char kGlicCaaGuestErrorDescription[] =
+    "Enables the Glic CAA Guest Error UI when there are authentication issues.";
+
 inline constexpr char kGlicGeminiEnterpriseSettingsOverrideName[] =
     "Glic Gemini Enterprise Settings Override";
 inline constexpr char kGlicGeminiEnterpriseSettingsOverrideDescription[] =
@@ -1767,13 +1772,6 @@
 inline constexpr char kGlicGeminiEnterpriseConsentEnabledDescription[] =
     "Enables the user consent toggle in Chrome settings for the Gemini "
     "Enterprise integration.";
-
-inline constexpr char kGlicOptInDialogLinkA11yFixName[] =
-    "Glic Opt-in Dialog Link accessibility context fix";
-inline constexpr char kGlicOptInDialogLinkA11yFixDescription[] =
-    "Ensures that screen reader users hear proper context for links in the "
-    "Glic opt-in dialog.";
-
 inline constexpr char kGlicDisableActorSafetyChecksName[] =
     "Glic disable actor safety checks";
 inline constexpr char kGlicDisableActorSafetyChecksDescription[] =
@@ -6944,8 +6942,7 @@
 inline constexpr char kNtpScaledActionChipsSmallName[] =
     "NTP Scaled Action Chips Small";
 inline constexpr char kNtpScaledActionChipsSmallDescription[] =
-    "Enables small scaled merchandising action chips on the New Tab Page. "
-    "Requires #ntp-scaled-action-chips to be enabled too.";
+    "Enables small scaled merchandising action chips on the New Tab Page.";
 
 inline constexpr char kNtpSharepointModuleName[] = "NTP Sharepoint Module";
 inline constexpr char kNtpSharepointModuleDescription[] =
diff --git a/chrome/browser/geic/BUILD.gn b/chrome/browser/geic/BUILD.gn
new file mode 100644
index 0000000..8515a20e
--- /dev/null
+++ b/chrome/browser/geic/BUILD.gn
@@ -0,0 +1,7 @@
+# Copyright 2026 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("geic") {
+  sources = []
+}
diff --git a/chrome/browser/geic/DIR_METADATA b/chrome/browser/geic/DIR_METADATA
new file mode 100644
index 0000000..f1a9a82
--- /dev/null
+++ b/chrome/browser/geic/DIR_METADATA
@@ -0,0 +1,4 @@
+buganizer: {
+  component_id: 1734782
+}
+team_email: "cbe-productivity@google.com"
diff --git a/chrome/browser/geic/OWNERS b/chrome/browser/geic/OWNERS
new file mode 100644
index 0000000..a87cb05
--- /dev/null
+++ b/chrome/browser/geic/OWNERS
@@ -0,0 +1,4 @@
+alexwchen@google.com
+austinzzr@google.com
+esalma@google.com
+ftirelo@google.com
diff --git a/chrome/browser/geic/README.md b/chrome/browser/geic/README.md
new file mode 100644
index 0000000..95249386
--- /dev/null
+++ b/chrome/browser/geic/README.md
@@ -0,0 +1,5 @@
+# Gemini Enterprise in Chrome (GEIC)
+
+`//chrome/browser/geic` contains the browser-side implementation for Gemini
+Enterprise in Chrome (GEIC) integration.
+
diff --git a/chrome/browser/glic/BUILD.gn b/chrome/browser/glic/BUILD.gn
index f5a5cec..8a406397 100644
--- a/chrome/browser/glic/BUILD.gn
+++ b/chrome/browser/glic/BUILD.gn
@@ -374,6 +374,8 @@
     "host/glic_guest_observer.cc",
     "host/glic_guest_observer.h",
     "host/glic_internals_page_handler.cc",
+    "host/glic_local_storage_migration.cc",
+    "host/glic_local_storage_migration.h",
     "host/glic_page_handler.cc",
     "host/glic_skills_manager_impl.cc",
     "host/glic_skills_manager_impl.h",
@@ -498,6 +500,7 @@
     "//components/pdf/common:constants",
     "//components/pref_registry",
     "//components/prefs",
+    "//components/services/storage/public/mojom",
     "//components/sessions",
     "//components/skills:features",
     "//components/skills/public",
@@ -508,6 +511,7 @@
     "//components/webui/chrome_urls",
     "//extensions/buildflags",
     "//net",
+    "//third_party/blink/public/common",
     "//third_party/metrics_proto",
     "//ui/base",
     "//ui/base/clipboard:file_info",
@@ -722,6 +726,7 @@
     "glic_settings_util_unittest.cc",
     "host/auth_controller_unittest.cc",
     "host/context/glic_sharing_utils_unittest.cc",
+    "host/glic_local_storage_migration_unittest.cc",
     "host/glic_web_client_responsiveness_monitor_unittest.cc",
     "host/glic_web_contents_warming_pool_unittest.cc",
     "public/glic_cui_tracker_unittest.cc",
@@ -736,9 +741,12 @@
     "//base/test:test_support",
     "//chrome/browser:global_features",
     "//chrome/browser/background/glic",
+    "//chrome/browser/glic/actor",
     "//chrome/browser/glic/test_support",
     "//chrome/browser/sync",
     "//chrome/browser/ui:ui_features",
+    "//components/services/storage:test_support",
+    "//components/services/storage/public/mojom",
     "//components/tabs:test_support",
     "//testing/gmock",
     "//testing/gtest",
@@ -906,6 +914,7 @@
       "//chrome/browser/actor/ui",
       "//chrome/browser/actor/ui:test_support",
       "//chrome/browser/autocomplete:aim_eligibility_service",
+      "//chrome/browser/background/glic:browser_tests",
       "//chrome/browser/enterprise/connectors/test:test_support",
       "//chrome/browser/extensions",
       "//chrome/browser/glic/browser_ui:browser_tests",
diff --git a/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicTaskMenuCoordinatorUnitTest.java b/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicTaskMenuCoordinatorUnitTest.java
index 42c2f80..c717677 100644
--- a/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicTaskMenuCoordinatorUnitTest.java
+++ b/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicTaskMenuCoordinatorUnitTest.java
@@ -27,7 +27,6 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.actor.ActorTask;
 import org.chromium.chrome.browser.actor.ActorTaskState;
 import org.chromium.chrome.browser.tab.TabLaunchType;
@@ -45,7 +44,6 @@
 /** Unit tests for {@link GlicTaskMenuCoordinator}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 public class GlicTaskMenuCoordinatorUnitTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/glic/experimental_opt_in/glic_experimental_opt_in_dialog_browsertest.cc b/chrome/browser/glic/experimental_opt_in/glic_experimental_opt_in_dialog_browsertest.cc
index af6f4369..d8f1dbc6 100644
--- a/chrome/browser/glic/experimental_opt_in/glic_experimental_opt_in_dialog_browsertest.cc
+++ b/chrome/browser/glic/experimental_opt_in/glic_experimental_opt_in_dialog_browsertest.cc
@@ -37,7 +37,6 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/webui_url_constants.h"
-#include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -57,7 +56,6 @@
 #include "net/dns/mock_host_resolver.h"
 #include "services/network/public/cpp/network_switches.h"
 #include "ui/base/base_window.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/test/widget_test.h"
 #include "ui/views/view_tracker.h"
@@ -1231,75 +1229,4 @@
             window_b);
 }
 
-IN_PROC_BROWSER_TEST_F(GlicExperimentalOptInTest, VerifyA11yLabelsInDialog) {
-  service()->enabling().SetCompletedFre(glic::prefs::FreStatus::kIncomplete);
-  views::Widget* widget = ShowDialogAndWait();
-  ASSERT_TRUE(widget);
-
-  content::WebContents* guest_contents = WaitForGuestContents();
-  ASSERT_TRUE(guest_contents);
-
-  // The links are created dynamically after 100ms in page.html.
-  // We wait for the links to be created and have their aria-labels set by our
-  // injected script.
-  bool labels_correct = base::test::RunUntil([this, guest_contents]() {
-    // Check "safely" link label
-    auto safely_element = EvalJs(
-        guest_contents, "!!document.querySelector('a[href*=\"use-policy\"]')");
-    if (!safely_element.is_ok() || safely_element != true) {
-      return false;
-    }
-    auto safely_label = EvalJs(guest_contents,
-                               "document.querySelector('a[href*=\"use-policy\"]"
-                               "').getAttribute('aria-label')");
-    if (!safely_label.is_ok() ||
-        safely_label !=
-            l10n_util::GetStringUTF8(
-                IDS_SETTINGS_GLIC_PERMISSIONS_WEB_ACTUATION_TOGGLE_CONSIDER_SAFELY_ARIA_LABEL)) {
-      return false;
-    }
-
-    // Check "unexpected results" link label
-    auto unexpected_element =
-        EvalJs(guest_contents,
-               "!!document.querySelector('a[href*=\"unexpected_results\"]')");
-    if (!unexpected_element.is_ok() || unexpected_element != true) {
-      return false;
-    }
-    auto unexpected_label =
-        EvalJs(guest_contents,
-               "document.querySelector('a[href*=\"unexpected_results\"]')."
-               "getAttribute('aria-label')");
-    if (!unexpected_label.is_ok() ||
-        unexpected_label !=
-            l10n_util::GetStringUTF8(
-                IDS_SETTINGS_GLIC_PERMISSIONS_WEB_ACTUATION_TOGGLE_CONSIDER_UNEXPECTED_RESULTS_ARIA_LABEL)) {
-      return false;
-    }
-
-    // Check "Review the risks" link label
-    auto risks_element =
-        EvalJs(guest_contents,
-               "!!document.querySelector('a[href*=\"gemini_spark_safety\"]')");
-    if (!risks_element.is_ok() || risks_element != true) {
-      return false;
-    }
-    auto risks_label = EvalJs(guest_contents,
-                              "document.querySelector('a[href*=\"gemini_spark_"
-                              "safety\"]').getAttribute('aria-label')");
-    if (!risks_label.is_ok() ||
-        risks_label !=
-            l10n_util::GetStringUTF8(
-                IDS_SETTINGS_GLIC_EXPERIMENTAL_TRIGGERING_CONSIDER_REVIEW_RISKS_LINK_LABEL_SPARK)) {
-      return false;
-    }
-
-    return true;
-  });
-
-  EXPECT_TRUE(labels_correct);
-
-  service()->opt_in_controller().CloseDialog(false);
-}
-
 }  // namespace glic
diff --git a/chrome/browser/glic/experimental_opt_in/glic_experimental_opt_in_ui.cc b/chrome/browser/glic/experimental_opt_in/glic_experimental_opt_in_ui.cc
index 6f8ac193..be730c00 100644
--- a/chrome/browser/glic/experimental_opt_in/glic_experimental_opt_in_ui.cc
+++ b/chrome/browser/glic/experimental_opt_in/glic_experimental_opt_in_ui.cc
@@ -9,7 +9,6 @@
 #include "chrome/browser/glic/experimental_opt_in/glic_experimental_opt_in_metrics.h"
 #include "chrome/browser/glic/experimental_opt_in/glic_experimental_opt_in_page_handler.h"
 #include "chrome/browser/glic/experimental_opt_in/glic_experimental_opt_in_util.h"
-#include "chrome/browser/glic/public/features.h"
 #include "chrome/browser/glic/public/glic_enabling.h"
 #include "chrome/browser/glic/public/glic_keyed_service.h"
 #include "chrome/browser/glic/public/glic_keyed_service_factory.h"
@@ -130,9 +129,6 @@
       "glicDevEnabled",
       base::CommandLine::ForCurrentProcess()->HasSwitch(::switches::kGlicDev));
   source->AddBoolean(
-      "glicOptInDialogLinkA11yFixEnabled",
-      base::FeatureList::IsEnabled(features::kGlicOptInDialogLinkA11yFix));
-  source->AddBoolean(
       "glicOptInDialogA11yFixEnabled",
       base::FeatureList::IsEnabled(features::kGlicOptInDialogA11yFix));
 
@@ -144,12 +140,6 @@
       {"errorNoticeHeader", IDS_GLIC_ERROR_NOTICE_HEADER},
       {"experimentalOptInErrorNoticeMessage", IDS_GLIC_ERROR_NOTICE},
       {"tryAgainButtonLabel", IDS_GLIC_ERROR_NOTICE_ACTION_BUTTON},
-      {"safelyLinkLabel",
-       IDS_SETTINGS_GLIC_PERMISSIONS_WEB_ACTUATION_TOGGLE_CONSIDER_SAFELY_ARIA_LABEL},
-      {"unexpectedResultsLinkLabel",
-       IDS_SETTINGS_GLIC_PERMISSIONS_WEB_ACTUATION_TOGGLE_CONSIDER_UNEXPECTED_RESULTS_ARIA_LABEL},
-      {"reviewRisksLinkLabel",
-       IDS_SETTINGS_GLIC_EXPERIMENTAL_TRIGGERING_CONSIDER_REVIEW_RISKS_LINK_LABEL_SPARK},
   };
   source->AddLocalizedStrings(kStrings);
   source->AddLocalizedString(
diff --git a/chrome/browser/glic/experimental_triggering/glic_experimental_triggering_coordinator_unittest.cc b/chrome/browser/glic/experimental_triggering/glic_experimental_triggering_coordinator_unittest.cc
index 20c7b7e..2ef3ff8 100644
--- a/chrome/browser/glic/experimental_triggering/glic_experimental_triggering_coordinator_unittest.cc
+++ b/chrome/browser/glic/experimental_triggering/glic_experimental_triggering_coordinator_unittest.cc
@@ -10,24 +10,37 @@
 
 #include "base/functional/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/test_future.h"
 #include "chrome/browser/actor/actor_keyed_service_factory.h"
+#include "chrome/browser/glic/actor/glic_actor_task_manager.h"
+#include "chrome/browser/glic/experimental_opt_in/glic_experimental_opt_in_controller.h"
 #include "chrome/browser/glic/experimental_triggering/glic_experimental_triggering_metrics.h"
 #include "chrome/browser/glic/experimental_triggering/glic_experimental_triggering_types.h"
 #include "chrome/browser/glic/glic_pref_names.h"
 #include "chrome/browser/glic/glic_profile_manager.h"
 #include "chrome/browser/glic/public/glic_enabling.h"
+#include "chrome/browser/glic/public/glic_instance.h"
 #include "chrome/browser/glic/public/glic_keyed_service.h"
 #include "chrome/browser/glic/public/glic_keyed_service_factory.h"
+#include "chrome/browser/glic/service/glic_instance_helper.h"
 #include "chrome/browser/glic/suggestions/contextual_cueing_service_factory.h"
+#include "chrome/browser/glic/test_support/mock_glic_instance.h"
+#include "chrome/browser/glic/test_support/mock_glic_keyed_service.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "components/sync_device_info/device_info.h"
+#include "components/tabs/public/mock_tab_interface.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/unowned_user_data/unowned_user_data_host.h"
 
 namespace glic {
 
@@ -42,16 +55,18 @@
       : GlicExperimentalTriggeringCoordinator(profile) {}
   ~TestGlicExperimentalTriggeringCoordinator() override = default;
 
-  tabs::TabInterface* GetActiveTab() const override { return nullptr; }
+  tabs::TabInterface* GetActiveTab() const override { return active_tab_; }
   BrowserWindowInterface* GetBrowserWindow() const override {
     return browser_window_;
   }
   void set_browser_window(BrowserWindowInterface* window) {
     browser_window_ = window;
   }
+  void set_active_tab(tabs::TabInterface* tab) { active_tab_ = tab; }
 
  private:
   raw_ptr<BrowserWindowInterface> browser_window_ = nullptr;
+  raw_ptr<tabs::TabInterface> active_tab_ = nullptr;
 };
 
 class GlicExperimentalTriggeringCoordinatorTest : public testing::Test {
@@ -77,6 +92,10 @@
     coordinator_ = std::make_unique<TestGlicExperimentalTriggeringCoordinator>(
         profile_);
     OptIn();
+
+    ON_CALL(mock_glic_instance_, GetActorTaskManager())
+        .WillByDefault(testing::Return(
+            reinterpret_cast<GlicActorTaskManager*>(dummy_task_manager_buf_)));
   }
 
   void TearDown() override {
@@ -87,12 +106,26 @@
   std::unique_ptr<KeyedService> CreateGlicKeyedService(
       content::BrowserContext* context) {
     Profile* profile = Profile::FromBrowserContext(context);
-    return std::make_unique<GlicKeyedService>(
+    auto service = std::make_unique<testing::NiceMock<MockGlicKeyedService>>(
         profile, IdentityManagerFactory::GetForProfile(profile),
         TestingBrowserProcess::GetGlobal()->profile_manager(),
         &glic_profile_manager_,
         ContextualCueingServiceFactory::GetForProfile(profile),
         actor::ActorKeyedServiceFactory::GetActorKeyedService(profile));
+
+    ON_CALL(*service, InvokeWithAutoSubmit(testing::_, testing::_, testing::_))
+        .WillByDefault(
+            [this](InvokeWithAutoSubmitPasskey passkey,
+                   GlicInvokeOptions options,
+                   GlicInvokeWithAutoSubmitOptions auto_submit_options) {
+              if (options.on_client_connected) {
+                std::move(options.on_client_connected)
+                    .Run(mock_glic_instance_.GetWeakPtr());
+              }
+              return mock_glic_instance_.GetWeakPtr();
+            });
+
+    return service;
   }
 
   void OptIn() {
@@ -106,7 +139,11 @@
   }
 
   std::optional<ExperimentalTriggeringResponse> SendRequest(
-      const ExperimentalTriggeringRequest& request) {
+      const ExperimentalTriggeringRequest& request,
+      tabs::TabInterface* prepared_tab = nullptr) {
+    if (prepared_tab) {
+      coordinator_->set_active_tab(prepared_tab);
+    }
     return coordinator_->OnRequest(
         kTestContextId, request,
         ScopedIncomingMessageResultLogger(
@@ -121,6 +158,9 @@
   raw_ptr<TestingProfile> profile_;
   GlicProfileManager glic_profile_manager_;
   std::unique_ptr<TestGlicExperimentalTriggeringCoordinator> coordinator_;
+  testing::NiceMock<MockGlicInstance> mock_glic_instance_;
+  alignas(GlicActorTaskManager) char dummy_task_manager_buf_[sizeof(
+      GlicActorTaskManager)] = {0};
 };
 
 TEST_F(GlicExperimentalTriggeringCoordinatorTest, NoTaskMetadata) {
@@ -226,5 +266,207 @@
             "No browser window found for current profile.");
 }
 
+TEST_F(GlicExperimentalTriggeringCoordinatorTest, RejectRequestWhenNotOptedIn) {
+  auto* glic_service =
+      GlicKeyedServiceFactory::GetGlicKeyedService(profile_, /*create=*/true);
+  ASSERT_TRUE(glic_service);
+  glic_service->enabling().SetCompletedFre(glic::prefs::FreStatus::kNotStarted);
+  glic_service->enabling().SetUserEnabledActuationOnWeb(false);
+  glic_service->enabling().SetExperimentalTriggeringEnabled(false);
+
+  base::HistogramTester histogram_tester;
+
+  ExperimentalTriggeringRequest request;
+  request.version = 1;
+  request.context_id = kTestContextId;
+  request.task_metadata = TaskMetadata{
+      .conversation_id = "conv_123",
+      .sender_sequence_number = 42,
+  };
+  request.payload = TriggerActuationRequest{.initial_prompt = "hello"};
+
+  auto response = SendRequest(request);
+
+  histogram_tester.ExpectUniqueSample(
+      "Glic.ExperimentalTriggering.StateOnActuationRequest",
+      syncer::DeviceInfo::GlicExperimentalTriggeringState::kNeedsOptIn, 1);
+
+  ASSERT_TRUE(response.has_value());
+  ASSERT_TRUE(response->task_update.has_value());
+  EXPECT_EQ(response->task_update->state, TaskUpdate::State::kFailed);
+  EXPECT_EQ(response->task_update->data_type,
+            TaskUpdate::DataType::kErrorMessage);
+  EXPECT_EQ(response->task_update->data,
+            "User is not opted in to experimental triggering.");
+  EXPECT_EQ(response->task_metadata->sender_sequence_number, 0);
+  EXPECT_EQ(response->task_metadata->last_seen_sequence_number, 42);
+  EXPECT_EQ(coordinator_->GetUpdatesHandlerMapSizeForTesting(), 0u);
+}
+
+TEST_F(GlicExperimentalTriggeringCoordinatorTest,
+       CleansUpUpdatesHandlerOnOptInRejection) {
+  auto* glic_service =
+      GlicKeyedServiceFactory::GetGlicKeyedService(profile_, /*create=*/true);
+  ASSERT_TRUE(glic_service);
+  glic_service->enabling().SetCompletedFre(glic::prefs::FreStatus::kNotStarted);
+  glic_service->enabling().SetUserEnabledActuationOnWeb(false);
+  glic_service->enabling().SetExperimentalTriggeringEnabled(false);
+
+  ExperimentalTriggeringRequest request;
+  request.version = 1;
+  request.context_id = kTestContextId;
+  request.task_metadata = TaskMetadata{
+      .conversation_id = "conv_123",
+      .sender_sequence_number = 42,
+  };
+  request.payload = TriggerActuationRequest{.initial_prompt = "hello"};
+
+  auto response = SendRequest(request);
+  ASSERT_TRUE(response.has_value());
+  ASSERT_TRUE(response->task_update.has_value());
+  EXPECT_EQ(response->task_update->state, TaskUpdate::State::kFailed);
+  EXPECT_EQ(response->task_metadata->sender_sequence_number, 0);
+  EXPECT_EQ(response->task_metadata->last_seen_sequence_number, 42);
+
+  EXPECT_EQ(coordinator_->GetUpdatesHandlerMapSizeForTesting(), 0u);
+}
+
+TEST_F(GlicExperimentalTriggeringCoordinatorTest,
+       CleansUpUpdatesHandlerOnStopActuation) {
+  ExperimentalTriggeringRequest request;
+  request.version = 1;
+  request.context_id = kTestContextId;
+  request.task_metadata = TaskMetadata{
+      .conversation_id = "conv_123",
+      .sender_sequence_number = 42,
+  };
+  request.payload = StopActuationRequest{.stop_reason = "STOPPED_BY_USER"};
+
+  auto response = SendRequest(request);
+  ASSERT_TRUE(response.has_value());
+  ASSERT_TRUE(response->task_update.has_value());
+  EXPECT_EQ(response->task_update->state, TaskUpdate::State::kFailed);
+  EXPECT_EQ(response->task_update->data,
+            "Failed to stop task due to missing glic instance.");
+
+  EXPECT_EQ(coordinator_->GetUpdatesHandlerMapSizeForTesting(), 0u);
+}
+
+#if !BUILDFLAG(IS_ANDROID)
+TEST_F(GlicExperimentalTriggeringCoordinatorTest,
+       HandlesMultipleConcurrentDeviceOptInRequests) {
+  auto web_contents = content::WebContents::Create(
+      content::WebContents::CreateParams(profile_));
+  tabs::MockTabInterface mock_tab;
+  ON_CALL(mock_tab, GetContents())
+      .WillByDefault(testing::Return(web_contents.get()));
+  coordinator_->set_active_tab(&mock_tab);
+
+  base::test::TestFuture<ExperimentalTriggeringResponse> future1;
+  base::test::TestFuture<ExperimentalTriggeringResponse> future2;
+
+  ExperimentalTriggeringRequest request1;
+  request1.version = 1;
+  request1.context_id = "context_1";
+  request1.task_metadata = TaskMetadata{.sender_sequence_number = 42};
+  request1.payload = DeviceOptInRequest{.triggering_source = "ChromeOS"};
+
+  ExperimentalTriggeringRequest request2;
+  request2.version = 1;
+  request2.context_id = "context_2";
+  request2.task_metadata = TaskMetadata{.sender_sequence_number = 43};
+  request2.payload = DeviceOptInRequest{.triggering_source = "ChromeOS"};
+
+  auto response1 = coordinator_->OnRequest(
+      "context_1", request1,
+      ScopedIncomingMessageResultLogger(
+          ScopedIncomingMessageResultLogger::Channel::kSharingMessage),
+      future1.GetRepeatingCallback(), nullptr);
+  EXPECT_FALSE(response1.has_value());
+
+  auto response2 = coordinator_->OnRequest(
+      "context_2", request2,
+      ScopedIncomingMessageResultLogger(
+          ScopedIncomingMessageResultLogger::Channel::kSharingMessage),
+      future2.GetRepeatingCallback(), nullptr);
+  EXPECT_FALSE(response2.has_value());
+
+  auto* glic_service =
+      GlicKeyedServiceFactory::GetGlicKeyedService(profile_, /*create=*/true);
+  ASSERT_TRUE(glic_service);
+  glic_service->opt_in_controller().CloseDialog(/*accepted=*/true);
+
+  auto result1 = future1.Take();
+  ASSERT_TRUE(result1.device_opt_in_result.has_value());
+  EXPECT_EQ(*result1.device_opt_in_result, DeviceOptInResult::kAccepted);
+  EXPECT_EQ(result1.task_metadata->sender_sequence_number, 0);
+  EXPECT_EQ(result1.task_metadata->last_seen_sequence_number, 42);
+
+  auto result2 = future2.Take();
+  ASSERT_TRUE(result2.device_opt_in_result.has_value());
+  EXPECT_EQ(*result2.device_opt_in_result, DeviceOptInResult::kAccepted);
+  EXPECT_EQ(result2.task_metadata->sender_sequence_number, 0);
+  EXPECT_EQ(result2.task_metadata->last_seen_sequence_number, 43);
+}
+
+TEST_F(GlicExperimentalTriggeringCoordinatorTest,
+       HandlesMultipleConcurrentDeviceOptInRequestsDeclinedOnTeardown) {
+  auto* glic_service =
+      GlicKeyedServiceFactory::GetGlicKeyedService(profile_, /*create=*/true);
+  ASSERT_TRUE(glic_service);
+  glic_service->enabling().SetExperimentalTriggeringEnabled(false);
+
+  auto web_contents = content::WebContents::Create(
+      content::WebContents::CreateParams(profile_));
+  tabs::MockTabInterface mock_tab;
+  ON_CALL(mock_tab, GetContents())
+      .WillByDefault(testing::Return(web_contents.get()));
+  coordinator_->set_active_tab(&mock_tab);
+
+  base::test::TestFuture<ExperimentalTriggeringResponse> future1;
+  base::test::TestFuture<ExperimentalTriggeringResponse> future2;
+
+  ExperimentalTriggeringRequest request1;
+  request1.version = 1;
+  request1.context_id = "context_1";
+  request1.task_metadata = TaskMetadata{.sender_sequence_number = 42};
+  request1.payload = DeviceOptInRequest{.triggering_source = "ChromeOS"};
+
+  ExperimentalTriggeringRequest request2;
+  request2.version = 1;
+  request2.context_id = "context_2";
+  request2.task_metadata = TaskMetadata{.sender_sequence_number = 43};
+  request2.payload = DeviceOptInRequest{.triggering_source = "ChromeOS"};
+
+  auto response1 = coordinator_->OnRequest(
+      "context_1", request1,
+      ScopedIncomingMessageResultLogger(
+          ScopedIncomingMessageResultLogger::Channel::kSharingMessage),
+      future1.GetRepeatingCallback(), nullptr);
+  EXPECT_FALSE(response1.has_value());
+
+  auto response2 = coordinator_->OnRequest(
+      "context_2", request2,
+      ScopedIncomingMessageResultLogger(
+          ScopedIncomingMessageResultLogger::Channel::kSharingMessage),
+      future2.GetRepeatingCallback(), nullptr);
+  EXPECT_FALSE(response2.has_value());
+
+  glic_service->opt_in_controller().CloseDialog(/*accepted=*/false);
+
+  auto result1 = future1.Take();
+  ASSERT_TRUE(result1.device_opt_in_result.has_value());
+  EXPECT_EQ(*result1.device_opt_in_result, DeviceOptInResult::kDeclined);
+  EXPECT_EQ(result1.task_metadata->sender_sequence_number, 0);
+  EXPECT_EQ(result1.task_metadata->last_seen_sequence_number, 42);
+
+  auto result2 = future2.Take();
+  ASSERT_TRUE(result2.device_opt_in_result.has_value());
+  EXPECT_EQ(*result2.device_opt_in_result, DeviceOptInResult::kDeclined);
+  EXPECT_EQ(result2.task_metadata->sender_sequence_number, 0);
+  EXPECT_EQ(result2.task_metadata->last_seen_sequence_number, 43);
+}
+#endif
+
 }  // namespace
 }  // namespace glic
diff --git a/chrome/browser/glic/glic_metrics.cc b/chrome/browser/glic/glic_metrics.cc
index 54dd08e..77912283 100644
--- a/chrome/browser/glic/glic_metrics.cc
+++ b/chrome/browser/glic/glic_metrics.cc
@@ -301,6 +301,9 @@
   base::UmaHistogramBoolean(
       "Glic.Preferences.DefaultTabContextEnabled",
       profile_prefs->GetBoolean(prefs::kGlicDefaultTabContextEnabled));
+  base::UmaHistogramBoolean(
+      "Glic.Preferences.ShakeTriggerEnabled",
+      profile_prefs->GetBoolean(prefs::kGlicShakeTriggerEnabled));
   base::UmaHistogramBoolean("Glic.Preferences.ActuationOnWeb",
                             enabling_->GetUserEnabledActuationOnWeb());
 }
diff --git a/chrome/browser/glic/glic_metrics_unittest.cc b/chrome/browser/glic/glic_metrics_unittest.cc
index 591d593..d55fd25a 100644
--- a/chrome/browser/glic/glic_metrics_unittest.cc
+++ b/chrome/browser/glic/glic_metrics_unittest.cc
@@ -281,6 +281,7 @@
   profile()->GetPrefs()->SetBoolean(prefs::kGlicGeolocationEnabled, true);
   profile()->GetPrefs()->SetBoolean(prefs::kGlicMicrophoneEnabled, true);
   profile()->GetPrefs()->SetBoolean(prefs::kGlicDefaultTabContextEnabled, true);
+  profile()->GetPrefs()->SetBoolean(prefs::kGlicShakeTriggerEnabled, true);
   enabling()->SetUserEnabledActuationOnWeb(true);
 
   metrics()->RecordGlicProfilePreferences();
@@ -298,6 +299,8 @@
                                         true, 1);
   histogram_tester().ExpectUniqueSample(
       "Glic.Preferences.DefaultTabContextEnabled", true, 1);
+  histogram_tester().ExpectUniqueSample("Glic.Preferences.ShakeTriggerEnabled",
+                                        true, 1);
   histogram_tester().ExpectUniqueSample("Glic.Preferences.ActuationOnWeb", true,
                                         1);
 
@@ -310,15 +313,16 @@
   profile()->GetPrefs()->SetBoolean(prefs::kGlicMicrophoneEnabled, false);
   profile()->GetPrefs()->SetBoolean(prefs::kGlicDefaultTabContextEnabled,
                                     false);
+  profile()->GetPrefs()->SetBoolean(prefs::kGlicShakeTriggerEnabled, false);
   enabling()->SetUserEnabledActuationOnWeb(false);
 
   metrics()->RecordGlicProfilePreferences();
 
   // Both true and false should be recorded once.
   histogram_tester().ExpectBucketCount("Glic.Preferences.PinnedToTabstrip",
-                                       true, 1);
+                                        true, 1);
   histogram_tester().ExpectBucketCount("Glic.Preferences.PinnedToTabstrip",
-                                       false, 1);
+                                        false, 1);
 
   histogram_tester().ExpectBucketCount("Glic.Preferences.LauncherEnabled", true,
                                        1);
@@ -345,6 +349,11 @@
   histogram_tester().ExpectBucketCount(
       "Glic.Preferences.DefaultTabContextEnabled", false, 1);
 
+  histogram_tester().ExpectBucketCount("Glic.Preferences.ShakeTriggerEnabled",
+                                       true, 1);
+  histogram_tester().ExpectBucketCount("Glic.Preferences.ShakeTriggerEnabled",
+                                       false, 1);
+
   histogram_tester().ExpectBucketCount("Glic.Preferences.ActuationOnWeb", true,
                                        1);
   histogram_tester().ExpectBucketCount("Glic.Preferences.ActuationOnWeb", false,
diff --git a/chrome/browser/glic/glic_pref_names.cc b/chrome/browser/glic/glic_pref_names.cc
index 2b903071..c76b305 100644
--- a/chrome/browser/glic/glic_pref_names.cc
+++ b/chrome/browser/glic/glic_pref_names.cc
@@ -110,6 +110,9 @@
   registry->RegisterBooleanPref(prefs::kGlicKeepSidepanelOpenOnNewTabsEnabled,
                                 true);
 
+  // Boolean pref for enabling shake trigger.
+  registry->RegisterBooleanPref(prefs::kGlicShakeTriggerEnabled, false);
+
   // Boolean pref that enables or disables experimental triggering.
   registry->RegisterBooleanPref(prefs::kGlicExperimentalTriggeringEnabled,
                                 false);
@@ -134,6 +137,8 @@
   registry->RegisterBooleanPref(prefs::kGlicUserEnabledActuationOnWeb, false);
 
   registry->RegisterBooleanPref(prefs::kGlicPartitionNeedsCookieSync, true);
+  registry->RegisterBooleanPref(prefs::kGlicLocalStorageCopiedToMainPartition,
+                                false);
   registry->RegisterBooleanPref(prefs::kGlicPreviouslyNotAllowed, false);
 
   registry->RegisterDictionaryPref(prefs::kGlicGeminiEnterpriseSettings);
diff --git a/chrome/browser/glic/glic_pref_names.h b/chrome/browser/glic/glic_pref_names.h
index 44879c55..89a5a9a 100644
--- a/chrome/browser/glic/glic_pref_names.h
+++ b/chrome/browser/glic/glic_pref_names.h
@@ -153,6 +153,9 @@
 // Bool pref for the daisy chain new tabs setting.
 inline constexpr char kGlicKeepSidepanelOpenOnNewTabsEnabled[] =
     "glic.keep_sidepanel_open_on_new_tabs_enabled";
+
+// Bool pref for enabling the shake trigger to capture region.
+inline constexpr char kGlicShakeTriggerEnabled[] = "glic.shake_trigger_enabled";
 // Integer pref that determines if Glic actuation is enabled. This is
 // controlled from the enterprise policy. Only applicable to enterprise
 // accounts.
@@ -177,6 +180,11 @@
 inline constexpr char kGlicPartitionNeedsCookieSync[] =
     "glic.partition_needs_cookie_sync";
 
+// Boolean pref that tracks if the Glic local storage keys have been copied from
+// the Glic partition to the main partition.
+inline constexpr char kGlicLocalStorageCopiedToMainPartition[] =
+    "glic.local_storage_copied_to_main_partition";
+
 // Boolean pref that tracks if the Glic profile was previously ineligible.
 inline constexpr char kGlicPreviouslyNotAllowed[] =
     "glic.previously_not_allowed";
diff --git a/chrome/browser/glic/glic_selection_observer.cc b/chrome/browser/glic/glic_selection_observer.cc
index fb9e6363..0bd228b 100644
--- a/chrome/browser/glic/glic_selection_observer.cc
+++ b/chrome/browser/glic/glic_selection_observer.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/feature_engagement/tracker_factory.h"
 #include "chrome/browser/glic/browser_ui/glic_nudge_controller.h"
 #include "chrome/browser/glic/browser_ui/glic_selection_widget.h"
+#include "chrome/browser/glic/glic_pref_names.h"
 #include "chrome/browser/glic/glic_zero_state_suggestions_manager.h"
 #include "chrome/browser/glic/host/context/glic_sharing_utils.h"
 #include "chrome/browser/glic/host/glic.mojom.h"
@@ -135,6 +136,14 @@
   return context;
 }
 
+// Minimum distance in pixels required for a mouse move step to establish or
+// change movement direction.
+constexpr float kMinShakeDistance = 10.0f;
+// Required number of direction changes to trigger region capture.
+constexpr int kRequiredDirectionChanges = 4;
+// Maximum time allowed between direction changes before the shake detector resets.
+constexpr base::TimeDelta kShakeTimeout = base::Milliseconds(1000);
+
 bool IsListenedToInputEvent(blink::WebInputEvent::Type type) {
   switch (type) {
     case blink::WebInputEvent::Type::kMouseDown:
@@ -152,6 +161,7 @@
     case blink::WebInputEvent::Type::kKeyDown:
     case blink::WebInputEvent::Type::kGestureScrollBegin:
     case blink::WebInputEvent::Type::kMouseWheel:
+    case blink::WebInputEvent::Type::kMouseMove:
       return true;
     default:
       return false;
@@ -371,10 +381,18 @@
   }
 
   switch (event->GetType()) {
+    case blink::WebInputEvent::Type::kMouseMove: {
+      const auto& mouse_event =
+          static_cast<const blink::WebMouseEvent&>(*event);
+      ProcessMouseMoveForShake(mouse_event);
+      break;
+    }
+
     case blink::WebInputEvent::Type::kMouseDown:
     case blink::WebInputEvent::Type::kPointerDown:
     case blink::WebInputEvent::Type::kGestureTapDown:
     case blink::WebInputEvent::Type::kTouchStart: {
+      ResetShakeDetector();
       bool is_left_click_or_touch = true;
       if (event->GetType() == blink::WebInputEvent::Type::kMouseDown ||
           event->GetType() == blink::WebInputEvent::Type::kPointerDown) {
@@ -413,6 +431,7 @@
     case blink::WebInputEvent::Type::kTouchEnd:
     case blink::WebInputEvent::Type::kTouchCancel:
     case blink::WebInputEvent::Type::kGestureTapCancel:
+      ResetShakeDetector();
       // Process the selection received so far. If the final selection IPC is
       // delayed, OnTextSelectionChanged will handle it since `is_selecting_`
       // becomes false.
@@ -427,6 +446,7 @@
 
     case blink::WebInputEvent::Type::kRawKeyDown:
     case blink::WebInputEvent::Type::kKeyDown: {
+      ResetShakeDetector();
       if (is_key_selection_) {
         break;
       }
@@ -465,6 +485,7 @@
 
     case blink::WebInputEvent::Type::kGestureScrollBegin:
     case blink::WebInputEvent::Type::kMouseWheel:
+      ResetShakeDetector();
       DismissUI(/*keep_nudge=*/true);
       break;
 
@@ -1204,4 +1225,94 @@
   }
 }
 
+bool GlicSelectionObserver::IsShakeTriggerEnabled() const {
+  if (!base::FeatureList::IsEnabled(features::kGlicShakeTrigger)) {
+    return false;
+  }
+  if (!web_contents()) {
+    return false;
+  }
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
+  if (!profile || !profile->GetPrefs()) {
+    return false;
+  }
+  return profile->GetPrefs()->GetBoolean(prefs::kGlicShakeTriggerEnabled);
+}
+
+void GlicSelectionObserver::TriggerRegionCapture() {
+  if (!IsSelectionPromptEnabled() || !IsShakeTriggerEnabled()) {
+    return;
+  }
+  if (!glic_keyed_service_) {
+    return;
+  }
+  auto* tab_interface =
+      tabs::TabInterface::MaybeGetFromContents(web_contents());
+  if (!tab_interface) {
+    return;
+  }
+  GlicInvokeOptions options(
+      Target(*tab_interface),
+      glic::mojom::InvocationSource::kCaptureRegionHotkey);
+  options.wait_for_panel_open = true;
+  glic_keyed_service_->Invoke(std::move(options));
+}
+
+void GlicSelectionObserver::ProcessMouseMoveForShake(
+    const blink::WebMouseEvent& mouse_event) {
+  if (!IsShakeTriggerEnabled()) {
+    return;
+  }
+  if (direction_change_count_ > 0 &&
+      (base::TimeTicks::Now() - last_direction_change_time_) > kShakeTimeout) {
+    ResetShakeDetector();
+  }
+
+  gfx::PointF current_pos = mouse_event.PositionInWidget();
+
+  if (!last_shake_point_.has_value()) {
+    last_shake_point_ = current_pos;
+    return;
+  }
+
+  gfx::Vector2dF delta = current_pos - *last_shake_point_;
+  float dist = delta.Length();
+  if (dist < kMinShakeDistance) {
+    return;
+  }
+
+  gfx::Vector2dF current_dir(delta.x() / dist, delta.y() / dist);
+
+  if (!last_shake_dir_.has_value()) {
+    last_shake_dir_ = current_dir;
+    last_shake_point_ = current_pos;
+    last_direction_change_time_ = base::TimeTicks::Now();
+    return;
+  }
+
+  float dot = last_shake_dir_->x() * current_dir.x() +
+              last_shake_dir_->y() * current_dir.y();
+  if (dot < -0.5f) {
+    direction_change_count_++;
+    last_shake_dir_ = current_dir;
+    last_shake_point_ = current_pos;
+    last_direction_change_time_ = base::TimeTicks::Now();
+
+    if (direction_change_count_ >= kRequiredDirectionChanges) {
+      ResetShakeDetector();
+      TriggerRegionCapture();
+    }
+  } else {
+    last_shake_point_ = current_pos;
+  }
+}
+
+void GlicSelectionObserver::ResetShakeDetector() {
+  last_shake_point_.reset();
+  last_shake_dir_.reset();
+  direction_change_count_ = 0;
+  last_direction_change_time_ = base::TimeTicks();
+}
+
 }  // namespace glic
diff --git a/chrome/browser/glic/glic_selection_observer.h b/chrome/browser/glic/glic_selection_observer.h
index 1667207..67a67b2b 100644
--- a/chrome/browser/glic/glic_selection_observer.h
+++ b/chrome/browser/glic/glic_selection_observer.h
@@ -26,6 +26,8 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/link_to_text/link_to_text.mojom.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/vector2d_f.h"
 
 namespace content {
 class Page;
@@ -89,6 +91,14 @@
   virtual void ShowSelectionAffordance(const std::u16string& selected_text,
                                        BrowserWindowInterface* bwi);
 
+  // Triggers Glic region capture when a mouse shake is detected.
+  // Virtual for testing.
+  virtual void TriggerRegionCapture();
+
+  // Returns true if mouse shake trigger is enabled by feature flag and pref.
+  // Virtual for testing.
+  virtual bool IsShakeTriggerEnabled() const;
+
   // content::WebContentsObserver:
   void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
   void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
@@ -186,6 +196,14 @@
   // True when an inline explanation is currently being fetched or displayed.
   bool is_explaining_ = false;
 
+  void ProcessMouseMoveForShake(const blink::WebMouseEvent& mouse_event);
+  void ResetShakeDetector();
+
+  std::optional<gfx::PointF> last_shake_point_;
+  std::optional<gfx::Vector2dF> last_shake_dir_;
+  int direction_change_count_ = 0;
+  base::TimeTicks last_direction_change_time_;
+
   // Private bridge implementation of
   // GlicSelectionWidgetDelegate::ActionDelegate. This is required because
   // GlicSelectionObserver (in the //chrome/browser/glic) cannot directly
diff --git a/chrome/browser/glic/glic_selection_observer_unittest.cc b/chrome/browser/glic/glic_selection_observer_unittest.cc
index f39bcb1..0ed9b382 100644
--- a/chrome/browser/glic/glic_selection_observer_unittest.cc
+++ b/chrome/browser/glic/glic_selection_observer_unittest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/glic/glic_pref_names.h"
 #include "chrome/browser/glic/glic_selection_observer.h"
 
 #include <string>
@@ -32,6 +33,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/test_renderer_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/input/web_mouse_event.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/clipboard_buffer.h"
 #include "ui/base/clipboard/test/test_clipboard.h"
@@ -99,6 +101,7 @@
     last_sent_context_.reset();
     show_selection_affordance_called_ = false;
     last_affordance_text_.reset();
+    trigger_region_capture_called_ = false;
   }
 
   // Expose methods for testing.
@@ -124,6 +127,10 @@
     return last_affordance_text_;
   }
 
+  bool trigger_region_capture_called() const {
+    return trigger_region_capture_called_;
+  }
+
  protected:
   bool IsSelectionPromptEnabled() const override { return true; }
 
@@ -148,6 +155,11 @@
     last_affordance_text_ = selected_text;
   }
 
+  void TriggerRegionCapture() override {
+    trigger_region_capture_called_ = true;
+    GlicSelectionObserver::TriggerRegionCapture();
+  }
+
  private:
   std::optional<std::u16string> last_processed_text_;
   int update_count_ = 0;
@@ -160,6 +172,7 @@
   std::optional<std::u16string> last_sent_context_;
   bool show_selection_affordance_called_ = false;
   std::optional<std::u16string> last_affordance_text_;
+  bool trigger_region_capture_called_ = false;
 };
 
 }  // namespace
@@ -1096,4 +1109,155 @@
                 url, GURL(), ContentSettingsType::INLINE_CUE_MENU));
 }
 
+TEST_F(GlicSelectionObserverTest, ShakeTriggerSucceedsWhenFeatureAndPrefEnabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{features::kGlicSelectionPrompt,
+                            features::kGlicShakeTrigger},
+      /*disabled_features=*/{});
+  profile()->GetPrefs()->SetBoolean(prefs::kGlicShakeTriggerEnabled, true);
+  NavigateAndCommit(GURL("https://example.com/"));
+
+  auto* observer = GetObserver();
+  ASSERT_TRUE(observer);
+
+  auto simulate_mouse_move = [&](float x, float y) {
+    blink::WebMouseEvent event(
+        blink::WebInputEvent::Type::kMouseMove,
+        blink::WebInputEvent::kNoModifiers,
+        blink::WebInputEvent::GetStaticTimeStampForTests());
+    event.SetPositionInWidget(x, y);
+    observer->OnInputEvent(
+        *main_rfh()->GetRenderWidgetHost(), event,
+        content::RenderWidgetHost::InputEventObserver::InputEventSource::kUnknown);
+    task_environment()->RunUntilIdle();
+  };
+
+  EXPECT_FALSE(observer->trigger_region_capture_called());
+
+  // Move right 20px (establishes initial direction: RIGHT).
+  simulate_mouse_move(0.0f, 0.0f);
+  simulate_mouse_move(20.0f, 0.0f);
+
+  // Move left 20px (Direction change 1: LEFT).
+  simulate_mouse_move(0.0f, 0.0f);
+  EXPECT_FALSE(observer->trigger_region_capture_called());
+
+  // Move right 20px (Direction change 2: RIGHT).
+  simulate_mouse_move(20.0f, 0.0f);
+  EXPECT_FALSE(observer->trigger_region_capture_called());
+
+  // Move left 20px (Direction change 3: LEFT).
+  simulate_mouse_move(0.0f, 0.0f);
+  EXPECT_FALSE(observer->trigger_region_capture_called());
+
+  // Move right 20px (Direction change 4: RIGHT -> Shake triggered!).
+  simulate_mouse_move(20.0f, 0.0f);
+  EXPECT_TRUE(observer->trigger_region_capture_called());
+}
+
+TEST_F(GlicSelectionObserverTest, ShakeTriggerDisabledByFeatureFlag) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{features::kGlicSelectionPrompt},
+      /*disabled_features=*/{features::kGlicShakeTrigger});
+  profile()->GetPrefs()->SetBoolean(prefs::kGlicShakeTriggerEnabled, true);
+  NavigateAndCommit(GURL("https://example.com/"));
+
+  auto* observer = GetObserver();
+  ASSERT_TRUE(observer);
+
+  auto simulate_mouse_move = [&](float x, float y) {
+    blink::WebMouseEvent event(
+        blink::WebInputEvent::Type::kMouseMove,
+        blink::WebInputEvent::kNoModifiers,
+        blink::WebInputEvent::GetStaticTimeStampForTests());
+    event.SetPositionInWidget(x, y);
+    observer->OnInputEvent(
+        *main_rfh()->GetRenderWidgetHost(), event,
+        content::RenderWidgetHost::InputEventObserver::InputEventSource::kUnknown);
+    task_environment()->RunUntilIdle();
+  };
+
+  // Perform 4 direction changes.
+  simulate_mouse_move(0.0f, 0.0f);
+  simulate_mouse_move(20.0f, 0.0f);
+  simulate_mouse_move(0.0f, 0.0f);
+  simulate_mouse_move(20.0f, 0.0f);
+  simulate_mouse_move(0.0f, 0.0f);
+  simulate_mouse_move(20.0f, 0.0f);
+
+  EXPECT_FALSE(observer->trigger_region_capture_called());
+}
+
+TEST_F(GlicSelectionObserverTest, ShakeTriggerDisabledByPref) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{features::kGlicSelectionPrompt,
+                            features::kGlicShakeTrigger},
+      /*disabled_features=*/{});
+  profile()->GetPrefs()->SetBoolean(prefs::kGlicShakeTriggerEnabled, false);
+  NavigateAndCommit(GURL("https://example.com/"));
+
+  auto* observer = GetObserver();
+  ASSERT_TRUE(observer);
+
+  auto simulate_mouse_move = [&](float x, float y) {
+    blink::WebMouseEvent event(
+        blink::WebInputEvent::Type::kMouseMove,
+        blink::WebInputEvent::kNoModifiers,
+        blink::WebInputEvent::GetStaticTimeStampForTests());
+    event.SetPositionInWidget(x, y);
+    observer->OnInputEvent(
+        *main_rfh()->GetRenderWidgetHost(), event,
+        content::RenderWidgetHost::InputEventObserver::InputEventSource::kUnknown);
+    task_environment()->RunUntilIdle();
+  };
+
+  // Perform 4 direction changes.
+  simulate_mouse_move(0.0f, 0.0f);
+  simulate_mouse_move(20.0f, 0.0f);
+  simulate_mouse_move(0.0f, 0.0f);
+  simulate_mouse_move(20.0f, 0.0f);
+  simulate_mouse_move(0.0f, 0.0f);
+  simulate_mouse_move(20.0f, 0.0f);
+
+  EXPECT_FALSE(observer->trigger_region_capture_called());
+}
+
+TEST_F(GlicSelectionObserverTest, ContinuousMoveDoesNotTriggerShake) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{features::kGlicSelectionPrompt,
+                            features::kGlicShakeTrigger},
+      /*disabled_features=*/{});
+  profile()->GetPrefs()->SetBoolean(prefs::kGlicShakeTriggerEnabled, true);
+  NavigateAndCommit(GURL("https://example.com/"));
+
+  auto* observer = GetObserver();
+  ASSERT_TRUE(observer);
+
+  auto simulate_mouse_move = [&](float x, float y) {
+    blink::WebMouseEvent event(
+        blink::WebInputEvent::Type::kMouseMove,
+        blink::WebInputEvent::kNoModifiers,
+        blink::WebInputEvent::GetStaticTimeStampForTests());
+    event.SetPositionInWidget(x, y);
+    observer->OnInputEvent(
+        *main_rfh()->GetRenderWidgetHost(), event,
+        content::RenderWidgetHost::InputEventObserver::InputEventSource::kUnknown);
+    task_environment()->RunUntilIdle();
+  };
+
+  // Move continuously in the positive X direction.
+  simulate_mouse_move(0.0f, 0.0f);
+  simulate_mouse_move(20.0f, 0.0f);
+  simulate_mouse_move(40.0f, 0.0f);
+  simulate_mouse_move(60.0f, 0.0f);
+  simulate_mouse_move(80.0f, 0.0f);
+  simulate_mouse_move(100.0f, 0.0f);
+
+  EXPECT_FALSE(observer->trigger_region_capture_called());
+}
+
 }  // namespace glic
diff --git a/chrome/browser/glic/glic_zero_state_suggestions_manager.cc b/chrome/browser/glic/glic_zero_state_suggestions_manager.cc
index 409c7cd88..278b9db 100644
--- a/chrome/browser/glic/glic_zero_state_suggestions_manager.cc
+++ b/chrome/browser/glic/glic_zero_state_suggestions_manager.cc
@@ -138,10 +138,12 @@
           : nullptr;
 
   if (contextual_cueing_service_ && active_web_contents) {
-    // Notify host that suggestions are pending.
-    host().NotifyZeroStateSuggestion(
-        MakePendingSuggestionsPtr(host().invocation_source()),
-        mojom::ZeroStateSuggestionsOptions(is_first_run, supported_tools));
+    if (client_remote_.is_bound()) {
+      client_remote_->NotifyZeroStateSuggestionsChanged(
+          MakePendingSuggestionsPtr(host().invocation_source()),
+          mojom::ZeroStateSuggestionsOptions::New(is_first_run,
+                                                  supported_tools));
+    }
 
     if (caching_zero_state_manager_) {
       caching_zero_state_manager_
@@ -241,14 +243,22 @@
     }
 
     if (suggestions_pending) {
-      // Notify host that suggestions are pending.
-      host().NotifyZeroStateSuggestion(
-          MakePendingSuggestionsPtr(host().invocation_source()),
-          mojom::ZeroStateSuggestionsOptions(is_first_run, supported_tools));
+      if (client_remote_.is_bound()) {
+        client_remote_->NotifyZeroStateSuggestionsChanged(
+            MakePendingSuggestionsPtr(host().invocation_source()),
+            mojom::ZeroStateSuggestionsOptions::New(is_first_run,
+                                                    supported_tools));
+      }
     }
   }
 }
 
+void GlicZeroStateSuggestionsManager::Bind(
+    mojo::PendingReceiver<mojom::ZeroStateSuggestionsHandler> receiver) {
+  receiver_.reset();
+  receiver_.Bind(std::move(receiver));
+}
+
 void GlicZeroStateSuggestionsManager::
     NotifyZeroStateSuggestionsOnPinnedTabDataChanged(
         bool is_first_run,
@@ -272,14 +282,14 @@
       is_first_run, supported_tools, sharing_manager_->GetPinnedTabs());
 }
 
-void GlicZeroStateSuggestionsManager::ObserveZeroStateSuggestions(
-    bool is_notifying,
-    bool is_first_run,
-    const std::vector<std::string>& supported_tools,
-    glic::mojom::WebClientHandler::GetZeroStateSuggestionsAndSubscribeCallback
-        callback) {
-  // Subscribe to changes in sharing.
-  if (is_notifying) {
+void GlicZeroStateSuggestionsManager::GetZeroStateSuggestionsAndSubscribe(
+    mojo::PendingRemote<mojom::ZeroStateSuggestionsClient> client,
+    mojom::ZeroStateSuggestionsOptionsPtr options,
+    GetZeroStateSuggestionsAndSubscribeCallback callback) {
+  client_remote_.reset();
+  if (client.is_valid()) {
+    client_remote_.Bind(std::move(client));
+    // Subscribe to changes in sharing.
     // Skip ZSS generation for unconsented users.
     if (!GlicEnabling::HasConsentedForProfile(host().profile())) {
       std::move(callback).Run(
@@ -294,17 +304,17 @@
         sharing_manager_->AddFocusedTabDataChangedCallback(base::BindRepeating(
             &GlicZeroStateSuggestionsManager::
                 NotifyZeroStateSuggestionsOnFocusedTabDataChanged,
-            GetWeakPtr(), is_first_run, supported_tools));
+            GetWeakPtr(), options->is_first_run, options->supported_tools));
     current_zero_state_suggestions_pinned_tab_change_subscription_ =
         sharing_manager_->AddPinnedTabsChangedCallback(base::BindRepeating(
             &GlicZeroStateSuggestionsManager::
                 NotifyZeroStateSuggestionsOnPinnedTabChanged,
-            GetWeakPtr(), is_first_run, supported_tools));
+            GetWeakPtr(), options->is_first_run, options->supported_tools));
     current_zero_state_suggestions_pinned_tab_data_change_subscription_ =
         sharing_manager_->AddPinnedTabDataChangedCallback(base::BindRepeating(
             &GlicZeroStateSuggestionsManager::
                 NotifyZeroStateSuggestionsOnPinnedTabDataChanged,
-            GetWeakPtr(), is_first_run, supported_tools));
+            GetWeakPtr(), options->is_first_run, options->supported_tools));
 
     if (!contextual_cueing_service_) {
       // Do nothing
@@ -320,7 +330,8 @@
       } else if (caching_zero_state_manager_) {
         caching_zero_state_manager_
             ->GetContextualGlicZeroStateSuggestionsForPinnedTabs(
-                pinned_contents, is_first_run, supported_tools,
+                pinned_contents, options->is_first_run,
+                options->supported_tools,
                 /* focused_tab=*/nullptr,
                 base::BindOnce(&GlicZeroStateSuggestionsManager::
                                    OnZeroStateSuggestionsFetched,
@@ -329,7 +340,8 @@
       } else {
         contextual_cueing_service_
             ->GetContextualGlicZeroStateSuggestionsForPinnedTabs(
-                pinned_contents, is_first_run, supported_tools,
+                pinned_contents, options->is_first_run,
+                options->supported_tools,
                 /* focused_tab=*/nullptr,
                 mojo::WrapCallbackWithDefaultInvokeIfNotRun(
                     base::BindOnce(&GlicZeroStateSuggestionsManager::
@@ -347,7 +359,8 @@
         if (caching_zero_state_manager_) {
           caching_zero_state_manager_
               ->GetContextualGlicZeroStateSuggestionsForFocusedTab(
-                  active_web_contents, is_first_run, supported_tools,
+                  active_web_contents, options->is_first_run,
+                  options->supported_tools,
                   base::BindOnce(&GlicZeroStateSuggestionsManager::
                                      OnZeroStateSuggestionsFetched,
                                  GetWeakPtr(), std::move(callback)));
@@ -355,7 +368,8 @@
         } else {
           contextual_cueing_service_
               ->GetContextualGlicZeroStateSuggestionsForFocusedTab(
-                  active_web_contents, is_first_run, supported_tools,
+                  active_web_contents, options->is_first_run,
+                  options->supported_tools,
                   mojo::WrapCallbackWithDefaultInvokeIfNotRun(
                       base::BindOnce(&GlicZeroStateSuggestionsManager::
                                          OnZeroStateSuggestionsFetched,
@@ -366,7 +380,6 @@
       }
     }
   } else {
-    // If is_notifying is false we need to reset the subscriptions.
     Reset();
   }
 
@@ -374,8 +387,7 @@
 }
 
 void GlicZeroStateSuggestionsManager::OnZeroStateSuggestionsFetched(
-    mojom::WebClientHandler::GetZeroStateSuggestionsAndSubscribeCallback
-        callback,
+    GetZeroStateSuggestionsAndSubscribeCallback callback,
     std::vector<std::string> returned_suggestions) {
   auto suggestions = mojom::ZeroStateSuggestionsV2::New();
   std::vector<mojom::SuggestionContentPtr> output_suggestions;
@@ -388,36 +400,32 @@
 
   std::move(callback).Run(std::move(suggestions));
 }
-
 void GlicZeroStateSuggestionsManager::OnZeroStateSuggestionsNotify(
     bool is_first_run,
     const std::vector<std::string>& supported_tools,
     std::vector<std::string> returned_suggestions) {
-  auto suggestions_v2 = mojom::ZeroStateSuggestionsV2::New();
-  std::vector<mojom::SuggestionContentPtr> output_suggestions;
-  for (const std::string& suggestion_string : returned_suggestions) {
-    output_suggestions.push_back(
-        mojom::SuggestionContent::New(suggestion_string));
+  if (client_remote_.is_bound()) {
+    auto suggestions_v2 = mojom::ZeroStateSuggestionsV2::New();
+    std::vector<mojom::SuggestionContentPtr> output_suggestions;
+    for (const std::string& suggestion_string : returned_suggestions) {
+      output_suggestions.push_back(
+          mojom::SuggestionContent::New(suggestion_string));
+    }
+    suggestions_v2->suggestions = std::move(output_suggestions);
+    suggestions_v2->is_pending = false;
+    client_remote_->NotifyZeroStateSuggestionsChanged(
+        std::move(suggestions_v2),
+        mojom::ZeroStateSuggestionsOptions::New(is_first_run, supported_tools));
   }
-  suggestions_v2->suggestions = std::move(output_suggestions);
-  suggestions_v2->is_pending = false;
-  host().NotifyZeroStateSuggestion(
-      std::move(suggestions_v2),
-      mojom::ZeroStateSuggestionsOptions(is_first_run, supported_tools));
 }
 
 void GlicZeroStateSuggestionsManager::Reset() {
   current_zero_state_suggestions_focus_change_subscription_ = {};
   current_zero_state_suggestions_pinned_tab_change_subscription_ = {};
   current_zero_state_suggestions_pinned_tab_data_change_subscription_ = {};
+  client_remote_.reset();
+  receiver_.reset();
 }
-
-bool GlicZeroStateSuggestionsManager::WasAutoOpenedForPdf() {
-  return GlicEnabling::IsAutoOpenForPdfEnabled(host().profile()) &&
-         host().invocation_source() ==
-             mojom::InvocationSource::kAutoOpenedForPdf;
-}
-
 base::WeakPtr<GlicZeroStateSuggestionsManager>
 GlicZeroStateSuggestionsManager::GetWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
diff --git a/chrome/browser/glic/glic_zero_state_suggestions_manager.h b/chrome/browser/glic/glic_zero_state_suggestions_manager.h
index 40a1d1c..c7ab32c 100644
--- a/chrome/browser/glic/glic_zero_state_suggestions_manager.h
+++ b/chrome/browser/glic/glic_zero_state_suggestions_manager.h
@@ -13,6 +13,9 @@
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/glic/host/context/glic_tab_data.h"
+#include "chrome/browser/glic/host/glic.mojom.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace glic {
 class CachingZeroStateSuggestionsManager;
@@ -22,13 +25,14 @@
 class Host;
 
 // A class for managing sending zero state suggestions through the mojo api.
-class GlicZeroStateSuggestionsManager {
+class GlicZeroStateSuggestionsManager
+    : public mojom::ZeroStateSuggestionsHandler {
  public:
   GlicZeroStateSuggestionsManager(
       GlicSharingManagerInternal* sharing_manager,
       GlicInstanceImpl* glic_instance,
       ContextualCueingService* contextual_cueing_service);
-  virtual ~GlicZeroStateSuggestionsManager();
+  ~GlicZeroStateSuggestionsManager() override;
 
   // Callback to send zero state suggestions to the webui on tab changes.
   void NotifyZeroStateSuggestionsOnFocusedTabDataChanged(
@@ -49,28 +53,25 @@
       const std::vector<std::string>& supported_tools,
       const TabDataChange& data);
 
-  // This handles calls from the webui to return a suggestion, and begin to
-  // notify the webui of changes to the zero state suggestsions.
-  void ObserveZeroStateSuggestions(
-      bool is_notifying,
-      bool is_first_run,
-      const std::vector<std::string>& supported_tools,
-      glic::mojom::WebClientHandler::GetZeroStateSuggestionsAndSubscribeCallback
-          callback);
+  void Bind(mojo::PendingReceiver<mojom::ZeroStateSuggestionsHandler> receiver);
+
+  // mojom::ZeroStateSuggestionsHandler implementation.
+  void GetZeroStateSuggestionsAndSubscribe(
+      mojo::PendingRemote<mojom::ZeroStateSuggestionsClient> client,
+      mojom::ZeroStateSuggestionsOptionsPtr options,
+      mojom::ZeroStateSuggestionsHandler::
+          GetZeroStateSuggestionsAndSubscribeCallback callback) override;
 
   void Reset();
 
  private:
-  bool WasAutoOpenedForPdf();
   void FilterTabs(std::vector<raw_ptr<content::WebContents>>& tabs);
 
   // A helper function to route GetZeroStateSuggestionsForFocusedTabCallback
   // callbacks.
   void OnZeroStateSuggestionsFetched(
-      mojom::WebClientHandler::GetZeroStateSuggestionsAndSubscribeCallback
-          callback,
+      GetZeroStateSuggestionsAndSubscribeCallback callback,
       std::vector<std::string> returned_suggestions);
-
   // A helper function to route NotifyZeroStateSuggestions callbacks.
   void OnZeroStateSuggestionsNotify(
       bool is_first_run,
@@ -107,6 +108,9 @@
 
   bool pause_pinned_subscription_updates_ = false;
 
+  mojo::Receiver<mojom::ZeroStateSuggestionsHandler> receiver_{this};
+  mojo::Remote<mojom::ZeroStateSuggestionsClient> client_remote_;
+
   base::WeakPtrFactory<GlicZeroStateSuggestionsManager> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/glic/host/glic.mojom b/chrome/browser/glic/host/glic.mojom
index 0d94c8e..1347100 100644
--- a/chrome/browser/glic/host/glic.mojom
+++ b/chrome/browser/glic/host/glic.mojom
@@ -1460,6 +1460,10 @@
   CreateSkillsHandler(pending_receiver<SkillsHandler> receiver,
                       pending_remote<SkillsClient> client);
 
+  // Creates the zero state suggestions handler.
+  CreateZeroStateSuggestionsHandler(
+      pending_receiver<ZeroStateSuggestionsHandler> receiver);
+
   // Creates a new tab. `url` must have a http or https protocol.
   // `tab_data` is null if the tab could not be created, which may happen if the
   // url or window_id is invalid.
@@ -1709,14 +1713,6 @@
   // Get the status of the OS Microphone permission currently granted to Chrome.
   GetOsMicrophonePermissionStatus() => (bool enabled);
 
-  // If is_live==true this will return the zero state suggestions for the
-  // currently shared content. Suggestions will continue to emit via
-  // NotifyZeroStateSuggestionsChanged until this is called with the same
-  // options but with is_live == false.
-  GetZeroStateSuggestionsAndSubscribe(
-      bool is_live, ZeroStateSuggestionsOptions options)
-      => (ZeroStateSuggestionsV2? zero_state_suggestions);
-
   // Returns zero-state suggestions from the currently active tab.
   GetZeroStateSuggestionsForFocusedTab(bool? is_first_run)
       => (ZeroStateSuggestions? suggestions);
@@ -1758,6 +1754,24 @@
       int32 tab_id, pending_remote<TabFaviconHandler> receiver);
 };
 
+// Accesses zero state suggestions from the web client.
+interface ZeroStateSuggestionsHandler {
+  // If client is not null, this will return the zero state suggestions for the
+  // currently shared content. Suggestions will continue to emit via
+  // NotifyZeroStateSuggestionsChanged until this is called with a null client.
+  GetZeroStateSuggestionsAndSubscribe(
+      pending_remote<ZeroStateSuggestionsClient>? client,
+      ZeroStateSuggestionsOptions options)
+      => (ZeroStateSuggestionsV2? zero_state_suggestions);
+};
+
+// Notifies the web client of changes to the zero state suggestions.
+interface ZeroStateSuggestionsClient {
+  // The zero state suggestions have changed.
+  NotifyZeroStateSuggestionsChanged(ZeroStateSuggestionsV2 suggestions,
+                                    ZeroStateSuggestionsOptions options);
+};
+
 // Receives updates for a specific tab.
 interface TabDataHandler {
   // Tab data has changed for this tab.
@@ -2025,6 +2039,7 @@
 
 union InvocationPayload {
   UniversalCartPayload universal_cart;
+
   [MinVersion=3]
   SkillsPayload skills_payload;
 };
@@ -2371,10 +2386,6 @@
   // Notify that the TabData for a pinned tab has changed.
   NotifyPinnedTabDataChanged(TabData tab_data);
 
-  // Informs the client that the zero state suggestions have changed.
-  NotifyZeroStateSuggestionsChanged(ZeroStateSuggestionsV2? suggestions,
-                                    ZeroStateSuggestionsOptions options);
-
   // Informs the client that the metadata tags have changed.
   // Contents from meta tags are passed unsanitized from the renderer.
   NotifyPageMetadataChanged(
diff --git a/chrome/browser/glic/host/glic_api_browsertest.cc b/chrome/browser/glic/host/glic_api_browsertest.cc
index c7e12503..a1223352 100644
--- a/chrome/browser/glic/host/glic_api_browsertest.cc
+++ b/chrome/browser/glic/host/glic_api_browsertest.cc
@@ -171,7 +171,6 @@
 DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kFirstTab);
 DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kSecondTab);
 DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kThirdTab);
-DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kSettingsTab);
 std::vector<std::string> GetTestSuiteNames() {
   return {
       "GlicApiTest",
@@ -764,42 +763,6 @@
                                      2 /*COOKIE_SYNC_ERROR*/, 1);
 }
 
-IN_PROC_BROWSER_TEST_P(GlicApiTest, testInvoke) {
-  RunTestSequence(OpenGlic(GlicInstrumentMode::kHostAndContents));
-  auto options = mojom::InvokeOptions::New();
-  options->invocation_source = mojom::InvocationSource::kTopChromeButton;
-  GetGlicInstanceImpl()->host().GetPrimaryWebClient()->Invoke(
-      std::move(options), base::DoNothing());
-  ExecuteJsTest();
-}
-
-IN_PROC_BROWSER_TEST_P(GlicApiTestWithOneTab, testOpenGlicSettingsPage) {
-  ExecuteJsTest();
-
-  RunTestSequence(
-      InstrumentTab(kSettingsTab),
-      WaitForWebContentsReady(
-          kSettingsTab, chrome::GetSettingsUrl(chrome::kGlicSettingsSubpage)));
-
-  // Confirm that this no-response request does not get latency metrics
-  // recorded.
-  histogram_tester->ExpectTotalCount(
-      "Glic.Api.RequestHostLatency.OpenGlicSettingsPage", 0);
-}
-
-IN_PROC_BROWSER_TEST_P(GlicApiTestWithOneTab,
-                       testOpenPasswordManagerSettingsPage) {
-  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kPasswordManagerTab);
-
-  const GURL settings_url =
-      base::FeatureList::IsEnabled(features::kFedCmEmbedderInitiatedLogin)
-          ? chrome::GetSettingsUrl(chrome::kGlicLoginSettingsSubpage)
-          : GURL(GetGooglePasswordManagerSubPageURLStr());
-  RunTestSequence(InstrumentNextTab(kPasswordManagerTab),
-                  Do([this]() { ExecuteJsTest(); }),
-                  WaitForWebContentsReady(kPasswordManagerTab, settings_url));
-}
-
 class GlicApiTestWithDaisyChain : public GlicApiTest {
  public:
   GlicApiTestWithDaisyChain() {
@@ -817,24 +780,6 @@
   base::test::ScopedFeatureList daisy_chain_features_;
 };
 
-IN_PROC_BROWSER_TEST_P(GlicApiTestWithDaisyChain,
-                       testCanAttachPanelToFallbackEmbedder) {
-  RunTestSequence(OpenGlic(GlicInstrumentMode::kHostAndContents),
-                  CheckTabCount(1));
-
-  // Runs the JS test until the first `advanceToNextStep()`.
-  ExecuteJsTest();
-
-  // The JS test is now paused.
-  auto* tab = browser()->tab_strip_model()->GetActiveTab();
-  ASSERT_TRUE(tab);
-  tab->Close();
-
-  // Continue the JS test to verify canAttachPanel is still true since it will
-  // now attach to the fallback embedder.
-  ContinueJsTest();
-}
-
 IN_PROC_BROWSER_TEST_P(GlicApiTest,
                        testSwitchConversationToOldConversationNewInstance) {
   RunTestSequence(OpenGlic(GlicInstrumentMode::kHostAndContents,
diff --git a/chrome/browser/glic/host/glic_cookie_synchronizer.cc b/chrome/browser/glic/host/glic_cookie_synchronizer.cc
index e95cf7b..1a1d268 100644
--- a/chrome/browser/glic/host/glic_cookie_synchronizer.cc
+++ b/chrome/browser/glic/host/glic_cookie_synchronizer.cc
@@ -20,6 +20,7 @@
 #include "base/metrics/user_metrics.h"
 #include "base/time/time.h"
 #include "chrome/browser/glic/host/auth_controller.h"
+#include "chrome/browser/glic/host/guest_util.h"
 #include "chrome/browser/glic/public/glic_enabling.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/webui_url_constants.h"
@@ -204,13 +205,9 @@
 GlicCookieSynchronizer::GlicCookieSynchronizer(
     content::BrowserContext* context,
     signin::IdentityManager* identity_manager)
-    : GlicCookieSynchronizer(
-          context,
-          identity_manager,
-          content::StoragePartitionConfig::Create(context,
-                                                  chrome::kChromeUIGlicHost,
-                                                  /*partition_name=*/"glicpart",
-                                                  /*in_memory=*/false)) {}
+    : GlicCookieSynchronizer(context,
+                             identity_manager,
+                             GetGlicStoragePartitionConfig(context)) {}
 
 GlicCookieSynchronizer::GlicCookieSynchronizer(
     content::BrowserContext* context,
diff --git a/chrome/browser/glic/host/glic_local_storage_migration.cc b/chrome/browser/glic/host/glic_local_storage_migration.cc
new file mode 100644
index 0000000..d2aa24c
--- /dev/null
+++ b/chrome/browser/glic/host/glic_local_storage_migration.cc
@@ -0,0 +1,213 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/glic/host/glic_local_storage_migration.h"
+
+#include <algorithm>
+#include <optional>
+#include <string_view>
+#include <vector>
+
+#include "base/containers/span.h"
+#include "base/feature_list.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback.h"
+#include "base/functional/callback_helpers.h"
+#include "base/logging.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/glic/glic_pref_names.h"
+#include "chrome/browser/glic/host/guest_util.h"
+#include "chrome/browser/glic/public/features.h"
+#include "chrome/browser/glic/public/glic_enabling.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/webui_url_constants.h"
+#include "components/prefs/pref_service.h"
+#include "components/services/storage/public/mojom/local_storage_control.mojom.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/storage_partition_config.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "third_party/blink/public/mojom/dom_storage/storage_area.mojom.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace glic {
+
+namespace {
+
+// Checks whether a DOMStorage key matches a target ASCII string.
+//
+// Background on Blink LocalStorage encoding:
+// In `third_party/blink/renderer/modules/storage/cached_storage_area.cc`
+// (under `FormatOption::kLocalStorageDetectFormat`), Blink serializes ASCII/
+// Latin-1 LocalStorage keys by allocating a vector of `input.length() + 1`
+// bytes. It writes a 1-byte header (`format[0] = StorageFormat::Latin1`, which
+// is 0x01) at index 0, and copies the ASCII character bytes into `payload`
+// (index 1..end).
+//
+// We can't use that code directly here, so we reproduce the logic. This is
+// temporary migration code, and can be deleted in a short time horizon, which
+// at least limits the cost of the duplication.
+bool DOMStorageKeyMatchesASCII(base::span<const uint8_t> key_bytes,
+                               std::string_view ascii_key) {
+  if (key_bytes == base::as_byte_span(ascii_key)) {
+    return true;
+  }
+  if (key_bytes.size() == ascii_key.size() + 1 &&
+      key_bytes.subspan(1u) == base::as_byte_span(ascii_key)) {
+    return true;
+  }
+  return false;
+}
+
+bool IsKeyToCopy(const std::vector<uint8_t>& key_bytes) {
+  constexpr std::string_view kKeys[] = {
+      "BARD_EMBED_CHAT_STORAGE_KEY_V2",
+      "WEB_EMBEDDED_CHROME_CAPABILITIES_STORAGE",
+  };
+  for (std::string_view target_key : kKeys) {
+    if (DOMStorageKeyMatchesASCII(key_bytes, target_key)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+class GlicLocalStorageMigrator {
+ public:
+  static void Run(Profile* profile,
+                  content::StoragePartition* source_partition,
+                  content::StoragePartition* target_partition,
+                  const blink::StorageKey& storage_key) {
+    new GlicLocalStorageMigrator(profile, source_partition, target_partition,
+                                 storage_key);
+  }
+
+ private:
+  GlicLocalStorageMigrator(Profile* profile,
+                           content::StoragePartition* source_partition,
+                           content::StoragePartition* target_partition,
+                           const blink::StorageKey& storage_key)
+      : profile_(profile->GetWeakPtr()) {
+    storage::mojom::LocalStorageControl* source_control =
+        source_partition->GetLocalStorageControl();
+    storage::mojom::LocalStorageControl* target_control =
+        target_partition->GetLocalStorageControl();
+    if (!source_control || !target_control) {
+      Finish(/*success=*/false);
+      return;
+    }
+
+    source_control->BindStorageArea(storage_key,
+                                    source_area_.BindNewPipeAndPassReceiver());
+    target_control->BindStorageArea(storage_key,
+                                    target_area_.BindNewPipeAndPassReceiver());
+
+    source_area_.set_disconnect_handler(
+        base::BindOnce(&GlicLocalStorageMigrator::Finish,
+                       weak_ptr_factory_.GetWeakPtr(), /*success=*/false));
+    target_area_.set_disconnect_handler(
+        base::BindOnce(&GlicLocalStorageMigrator::Finish,
+                       weak_ptr_factory_.GetWeakPtr(), /*success=*/false));
+
+    source_area_->GetAll(
+        /*new_observer=*/mojo::NullRemote(),
+        base::BindOnce(&GlicLocalStorageMigrator::OnGetAll,
+                       weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  ~GlicLocalStorageMigrator() = default;
+
+  void OnGetAll(std::vector<blink::mojom::KeyValuePtr> data) {
+    std::vector<blink::mojom::KeyValuePtr> to_copy;
+    for (auto& kv : data) {
+      if (IsKeyToCopy(kv->key)) {
+        to_copy.push_back(std::move(kv));
+      }
+    }
+
+    if (to_copy.empty()) {
+      // Deletes this.
+      Finish(/*success=*/true);
+      return;
+    }
+
+    pending_puts_ = to_copy.size();
+    for (auto& kv : to_copy) {
+      target_area_->Put(kv->key, kv->value, std::nullopt, /*source=*/nullptr,
+                        base::BindOnce(&GlicLocalStorageMigrator::OnPut,
+                                       weak_ptr_factory_.GetWeakPtr()));
+    }
+  }
+
+  void OnPut(bool success) {
+    if (!success) {
+      // Deletes this.
+      Finish(/*success=*/false);
+      return;
+    }
+    CHECK_GT(pending_puts_, 0u);
+    --pending_puts_;
+    if (pending_puts_ == 0) {
+      // Deletes this.
+      Finish(/*success=*/true);
+    }
+  }
+
+  void Finish(bool success) {
+    if (finished_) {
+      return;
+    }
+    finished_ = true;
+    if (success && profile_) {
+      profile_->GetPrefs()->SetBoolean(
+          prefs::kGlicLocalStorageCopiedToMainPartition, true);
+    }
+    delete this;
+  }
+
+  bool finished_ = false;
+  base::WeakPtr<Profile> profile_;
+  mojo::Remote<blink::mojom::StorageArea> source_area_;
+  mojo::Remote<blink::mojom::StorageArea> target_area_;
+  std::vector<std::string_view> target_keys_present_;
+  size_t pending_puts_ = 0;
+  base::WeakPtrFactory<GlicLocalStorageMigrator> weak_ptr_factory_{this};
+};
+
+}  // namespace
+
+void MaybeMigrateGlicLocalStorage(content::BrowserContext* browser_context) {
+  if (!base::FeatureList::IsEnabled(features::kGlicNoWebview) ||
+      !GlicEnabling::IsEnabledByGlobalCriteria()) {
+    return;
+  }
+  Profile* profile = Profile::FromBrowserContext(browser_context);
+  if (!profile || profile->IsOffTheRecord() ||
+      profile->GetPrefs()->GetBoolean(
+          prefs::kGlicLocalStorageCopiedToMainPartition)) {
+    return;
+  }
+
+  content::StoragePartition* source_partition =
+      browser_context->GetStoragePartition(
+          GetGlicStoragePartitionConfig(browser_context));
+  if (!source_partition) {
+    // No glic partition, nothing to copy.
+    return;
+  }
+
+  content::StoragePartition* target_partition =
+      browser_context->GetDefaultStoragePartition();
+
+  blink::StorageKey storage_key =
+      blink::StorageKey::CreateFirstParty(GetGuestOrigin());
+
+  GlicLocalStorageMigrator::Run(profile, source_partition, target_partition,
+                                storage_key);
+}
+
+}  // namespace glic
diff --git a/chrome/browser/glic/host/glic_local_storage_migration.h b/chrome/browser/glic/host/glic_local_storage_migration.h
new file mode 100644
index 0000000..2faa79c6
--- /dev/null
+++ b/chrome/browser/glic/host/glic_local_storage_migration.h
@@ -0,0 +1,24 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_GLIC_HOST_GLIC_LOCAL_STORAGE_MIGRATION_H_
+#define CHROME_BROWSER_GLIC_HOST_GLIC_LOCAL_STORAGE_MIGRATION_H_
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace glic {
+
+// Performs a one-time migration of specified Glic local storage keys
+// (BARD_EMBED_CHAT_STORAGE_KEY_V2 and WEB_EMBEDDED_CHROME_CAPABILITIES_STORAGE)
+// from the Glic storage partition to the default/main storage partition.
+// The migration is skipped if disabled by flags, or already completed.
+// TODO(b/534807813): We should defer loading the web client until after this
+// migration is complete, otherwise it will race.
+void MaybeMigrateGlicLocalStorage(content::BrowserContext* browser_context);
+
+}  // namespace glic
+
+#endif  // CHROME_BROWSER_GLIC_HOST_GLIC_LOCAL_STORAGE_MIGRATION_H_
diff --git a/chrome/browser/glic/host/glic_local_storage_migration_unittest.cc b/chrome/browser/glic/host/glic_local_storage_migration_unittest.cc
new file mode 100644
index 0000000..271eb6c3
--- /dev/null
+++ b/chrome/browser/glic/host/glic_local_storage_migration_unittest.cc
@@ -0,0 +1,179 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/glic/host/glic_local_storage_migration.h"
+
+#include <string>
+#include <vector>
+
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/test/run_until.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/glic/glic_pref_names.h"
+#include "chrome/browser/glic/host/guest_util.h"
+#include "chrome/browser/glic/public/features.h"
+#include "chrome/browser/glic/public/glic_enabling.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/prefs/pref_service.h"
+#include "components/services/storage/public/mojom/local_storage_control.mojom.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/test/browser_task_environment.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "third_party/blink/public/mojom/dom_storage/storage_area.mojom.h"
+
+namespace glic {
+namespace {
+
+class GlicLocalStorageMigrationTest : public testing::Test {
+ public:
+  GlicLocalStorageMigrationTest() {
+    scoped_feature_list_.InitWithFeatures(
+        {features::kGlicNoWebview, features::kGlic}, {});
+    GlicEnabling::SetBypassEnablementChecksForTesting(true);
+  }
+  ~GlicLocalStorageMigrationTest() override {
+    GlicEnabling::SetBypassEnablementChecksForTesting(false);
+  }
+
+  content::StoragePartition* GetGlicPartition() {
+    return profile_.GetStoragePartition(
+        GetGlicStoragePartitionConfig(&profile_));
+  }
+
+  content::StoragePartition* GetDefaultPartition() {
+    return profile_.GetDefaultStoragePartition();
+  }
+
+  void PutInPartition(content::StoragePartition* partition,
+                      const blink::StorageKey& storage_key,
+                      std::string_view key,
+                      std::string_view value) {
+    mojo::Remote<blink::mojom::StorageArea> area;
+    partition->GetLocalStorageControl()->BindStorageArea(
+        storage_key, area.BindNewPipeAndPassReceiver());
+    std::vector<uint8_t> key_vec;
+    key_vec.push_back(0x01);
+    key_vec.insert(key_vec.end(), key.begin(), key.end());
+    std::vector<uint8_t> val_vec(value.begin(), value.end());
+    base::RunLoop loop;
+    area->Put(key_vec, val_vec, std::nullopt, nullptr,
+              base::BindLambdaForTesting([&](bool success) {
+                EXPECT_TRUE(success);
+                loop.Quit();
+              }));
+    loop.Run();
+  }
+
+  std::optional<std::vector<uint8_t>> GetFromPartition(
+      content::StoragePartition* partition,
+      const blink::StorageKey& storage_key,
+      std::string_view key) {
+    mojo::Remote<blink::mojom::StorageArea> area;
+    partition->GetLocalStorageControl()->BindStorageArea(
+        storage_key, area.BindNewPipeAndPassReceiver());
+    std::vector<uint8_t> key_vec;
+    key_vec.push_back(0x01);
+    key_vec.insert(key_vec.end(), key.begin(), key.end());
+    std::optional<std::vector<uint8_t>> result;
+    base::RunLoop loop;
+    area->GetAll(
+        /*new_observer=*/mojo::NullRemote(),
+        base::BindLambdaForTesting(
+            [&](std::vector<blink::mojom::KeyValuePtr> data) {
+              for (const auto& key_value : data) {
+                if (key_value->key == key_vec) {
+                  result = key_value->value;
+                  break;
+                }
+              }
+              loop.Quit();
+            }));
+    loop.Run();
+    return result;
+  }
+
+ protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  content::BrowserTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::MainThreadType::IO};
+  TestingProfile profile_;
+};
+
+TEST_F(GlicLocalStorageMigrationTest, MigratesKeysAndSetsPref) {
+  EXPECT_FALSE(profile_.GetPrefs()->GetBoolean(
+      prefs::kGlicLocalStorageCopiedToMainPartition));
+
+  GURL guest_url = GetGuestURL();
+  ASSERT_TRUE(guest_url.is_valid());
+  blink::StorageKey storage_key =
+      blink::StorageKey::CreateFirstParty(url::Origin::Create(guest_url));
+
+  PutInPartition(GetGlicPartition(), storage_key,
+                 "BARD_EMBED_CHAT_STORAGE_KEY_V2", "bard_value");
+  PutInPartition(GetGlicPartition(), storage_key,
+                 "WEB_EMBEDDED_CHROME_CAPABILITIES_STORAGE",
+                 "capabilities_val");
+  PutInPartition(GetGlicPartition(), storage_key, "OTHER_UNRELATED_KEY",
+                 "unrelated_val");
+
+  MaybeMigrateGlicLocalStorage(&profile_);
+  EXPECT_TRUE(base::test::RunUntil([&]() {
+    return profile_.GetPrefs()->GetBoolean(
+        prefs::kGlicLocalStorageCopiedToMainPartition);
+  }));
+
+  auto bard_val = GetFromPartition(GetDefaultPartition(), storage_key,
+                                   "BARD_EMBED_CHAT_STORAGE_KEY_V2");
+  ASSERT_TRUE(bard_val.has_value());
+  EXPECT_EQ(std::string(bard_val->begin(), bard_val->end()), "bard_value");
+
+  auto caps_val = GetFromPartition(GetDefaultPartition(), storage_key,
+                                   "WEB_EMBEDDED_CHROME_CAPABILITIES_STORAGE");
+  ASSERT_TRUE(caps_val.has_value());
+  EXPECT_EQ(std::string(caps_val->begin(), caps_val->end()),
+            "capabilities_val");
+
+  auto other_val = GetFromPartition(GetDefaultPartition(), storage_key,
+                                    "OTHER_UNRELATED_KEY");
+  EXPECT_FALSE(other_val.has_value());
+}
+
+TEST_F(GlicLocalStorageMigrationTest, NoOpIfPrefAlreadySet) {
+  profile_.GetPrefs()->SetBoolean(prefs::kGlicLocalStorageCopiedToMainPartition,
+                                  true);
+
+  GURL guest_url = GetGuestURL();
+  ASSERT_TRUE(guest_url.is_valid());
+  blink::StorageKey storage_key =
+      blink::StorageKey::CreateFirstParty(url::Origin::Create(guest_url));
+
+  PutInPartition(GetGlicPartition(), storage_key,
+                 "BARD_EMBED_CHAT_STORAGE_KEY_V2", "bard_value");
+
+  MaybeMigrateGlicLocalStorage(&profile_);
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return task_environment_.MainThreadIsIdle(); }));
+
+  auto bard_val = GetFromPartition(GetDefaultPartition(), storage_key,
+                                   "BARD_EMBED_CHAT_STORAGE_KEY_V2");
+  EXPECT_FALSE(bard_val.has_value());
+}
+
+TEST_F(GlicLocalStorageMigrationTest, EmptyGlicPartitionSetsPref) {
+  EXPECT_FALSE(profile_.GetPrefs()->GetBoolean(
+      prefs::kGlicLocalStorageCopiedToMainPartition));
+
+  MaybeMigrateGlicLocalStorage(&profile_);
+  EXPECT_TRUE(base::test::RunUntil([&]() {
+    return profile_.GetPrefs()->GetBoolean(
+        prefs::kGlicLocalStorageCopiedToMainPartition);
+  }));
+}
+
+}  // namespace
+}  // namespace glic
diff --git a/chrome/browser/glic/host/glic_web_client_access.h b/chrome/browser/glic/host/glic_web_client_access.h
index 3fceddb..eee1ac7 100644
--- a/chrome/browser/glic/host/glic_web_client_access.h
+++ b/chrome/browser/glic/host/glic_web_client_access.h
@@ -54,10 +54,6 @@
   // Informs the web client that an actor task list row was clicked.
   virtual void NotifyActorTaskListRowClicked(int32_t task_id) = 0;
 
-  virtual void NotifyZeroStateSuggestionsChanged(
-      mojom::ZeroStateSuggestionsV2Ptr suggestions,
-      mojom::ZeroStateSuggestionsOptionsPtr options) = 0;
-
   // Informs the web client that the browser wants to invoke Glic.
   virtual void Invoke(mojom::InvokeOptionsPtr options,
                       base::OnceClosure callback) = 0;
diff --git a/chrome/browser/glic/host/glic_web_client_handler.cc b/chrome/browser/glic/host/glic_web_client_handler.cc
index a953e2b..177f5d05 100644
--- a/chrome/browser/glic/host/glic_web_client_handler.cc
+++ b/chrome/browser/glic/host/glic_web_client_handler.cc
@@ -609,14 +609,6 @@
     }
   }
 
-  void GetZeroStateSuggestionsAndSubscribe(
-      bool has_active_subscription,
-      mojom::ZeroStateSuggestionsOptionsPtr options,
-      GetZeroStateSuggestionsAndSubscribeCallback callback) override {
-    host().instance_delegate().GetZeroStateSuggestionsAndSubscribe(
-        has_active_subscription, *options, std::move(callback));
-  }
-
   void CreateTab(const ::GURL& url,
                  glic::mojom::CreateTabOptionsPtr create_options,
                  CreateTabCallback callback) override {
@@ -912,6 +904,13 @@
                                                      std::move(client));
   }
 
+  void CreateZeroStateSuggestionsHandler(
+      mojo::PendingReceiver<mojom::ZeroStateSuggestionsHandler> receiver)
+      override {
+    host().instance_delegate().CreateZeroStateSuggestionsHandler(
+        std::move(receiver));
+  }
+
   void ActivateTab(int32_t tab_id) override {
     tabs::TabInterface* tab = tabs::TabHandle(tab_id).Get();
     if (!tab) {
@@ -1530,17 +1529,6 @@
     web_client_->NotifyPinnedTabDataChanged(change.tab_data->Clone());
   }
 
-  void NotifyZeroStateSuggestionsChanged(
-      glic::mojom::ZeroStateSuggestionsV2Ptr suggestions,
-      mojom::ZeroStateSuggestionsOptionsPtr options) override {
-    // Ideally, we should redesign this to avoid zss suggestions being delivered
-    // when there's no client.
-    if (web_client_) {
-      web_client_->NotifyZeroStateSuggestionsChanged(std::move(suggestions),
-                                                     std::move(options));
-    }
-  }
-
   void NotifyActOnWebCapabilityChanged(bool can_act_on_web) {
     web_client_->NotifyActOnWebCapabilityChanged(can_act_on_web);
   }
diff --git a/chrome/browser/glic/host/guest_util.cc b/chrome/browser/glic/host/guest_util.cc
index d43c9f1..fdd7f19 100644
--- a/chrome/browser/glic/host/guest_util.cc
+++ b/chrome/browser/glic/host/guest_util.cc
@@ -52,6 +52,7 @@
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/storage_partition_config.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
@@ -349,6 +350,14 @@
   glic_ui->host()->CreateWebClient(std::move(receiver));
 }
 
+content::StoragePartitionConfig GetGlicStoragePartitionConfig(
+    content::BrowserContext* browser_context) {
+  return content::StoragePartitionConfig::Create(browser_context,
+                                                 chrome::kChromeUIGlicHost,
+                                                 /*partition_name=*/"glicpart",
+                                                 /*in_memory=*/false);
+}
+
 GURL MaybeApplyPresetGuestUrl(GURL guest_url) {
   if (!base::FeatureList::IsEnabled(features::kGlicGuestUrlPresets)) {
     return guest_url;
diff --git a/chrome/browser/glic/host/guest_util.h b/chrome/browser/glic/host/guest_util.h
index 2f78a2b8..9294122 100644
--- a/chrome/browser/glic/host/guest_util.h
+++ b/chrome/browser/glic/host/guest_util.h
@@ -7,6 +7,7 @@
 
 #include "base/feature_list.h"
 #include "chrome/browser/glic/host/glic.mojom.h"
+#include "content/public/browser/storage_partition_config.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "ui/base/device_form_factor.h"
 #include "url/gurl.h"
@@ -39,6 +40,10 @@
 bool IsOriginAllowedGlicApi(const url::Origin& origin);
 bool IsFrameAllowedGlicApi(content::RenderFrameHost& frame_host);
 
+// Returns the StoragePartitionConfig for the Glic webview storage partition.
+content::StoragePartitionConfig GetGlicStoragePartitionConfig(
+    content::BrowserContext* browser_context);
+
 // Checks if a preset url is enabled and returns it if so. Otherwise, returns
 // `guest_url`.
 GURL MaybeApplyPresetGuestUrl(GURL guest_url);
diff --git a/chrome/browser/glic/host/host.cc b/chrome/browser/glic/host/host.cc
index c36bef51..dbdc968 100644
--- a/chrome/browser/glic/host/host.cc
+++ b/chrome/browser/glic/host/host.cc
@@ -644,18 +644,6 @@
   observers_.Notify(&Observer::WebUiStateChanged, primary_webui_state_);
 }
 
-void Host::NotifyZeroStateSuggestion(
-    mojom::ZeroStateSuggestionsV2Ptr suggestions,
-    mojom::ZeroStateSuggestionsOptions options) {
-  if (auto* client = GetPrimaryWebClient()) {
-    auto opt = mojom::ZeroStateSuggestionsOptions::New();
-    opt->is_first_run = std::move(options.is_first_run);
-    opt->supported_tools = std::move(options.supported_tools);
-    client->NotifyZeroStateSuggestionsChanged(std::move(suggestions),
-                                              std::move(opt));
-  }
-}
-
 void Host::NotifyInstanceActivationChanged(bool is_active) {
   if (auto* client = GetPrimaryWebClient()) {
     client->NotifyInstanceActivationChanged(is_active);
diff --git a/chrome/browser/glic/host/host.h b/chrome/browser/glic/host/host.h
index 225729cb..8ddb7930 100644
--- a/chrome/browser/glic/host/host.h
+++ b/chrome/browser/glic/host/host.h
@@ -103,11 +103,8 @@
         glic::mojom::WebClientHandler::
             GetZeroStateSuggestionsForFocusedTabCallback callback) = 0;
 
-    virtual void GetZeroStateSuggestionsAndSubscribe(
-        bool has_active_subscription,
-        const mojom::ZeroStateSuggestionsOptions& options,
-        mojom::WebClientHandler::GetZeroStateSuggestionsAndSubscribeCallback
-            callback) = 0;
+    virtual void CreateZeroStateSuggestionsHandler(
+        mojo::PendingReceiver<mojom::ZeroStateSuggestionsHandler> receiver) = 0;
 
     virtual void RegisterConversation(
         glic::mojom::ConversationInfoPtr info,
@@ -321,10 +318,6 @@
     return primary_webui_state_;
   }
 
-  // Informs the host that the Zero State Suggestions have changed.
-  void NotifyZeroStateSuggestion(mojom::ZeroStateSuggestionsV2Ptr suggestions,
-                                 mojom::ZeroStateSuggestionsOptions options);
-
   void NotifyInstanceActivationChanged(bool is_active);
   void OnActuatingChanged(bool actuating);
   void OnTaskTabsVisibilityChanged(bool has_visible_tab);
diff --git a/chrome/browser/glic/host/new_glic_api_browsertest.cc b/chrome/browser/glic/host/new_glic_api_browsertest.cc
index a5aa66b..f077442 100644
--- a/chrome/browser/glic/host/new_glic_api_browsertest.cc
+++ b/chrome/browser/glic/host/new_glic_api_browsertest.cc
@@ -116,6 +116,7 @@
 #include "chrome/browser/skills/skills_service_factory.h"
 #include "chrome/browser/skills/skills_ui_tab_controller.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/passwords/ui_utils.h"  // nogncheck
 #include "chrome/test/base/ui_test_utils.h"
 #include "ui/display/screen.h"
 #endif
@@ -1684,6 +1685,21 @@
 #endif
 
 #if !BUILDFLAG(IS_ANDROID)
+IN_PROC_BROWSER_TEST_P(NewGlicApiTestWithNewTabDaisyChain,
+                       testCanAttachPanelToFallbackEmbedder) {
+  ASSERT_OK(OpenGlicForActiveTab());
+
+  ExecuteJsTest();
+
+  tabs::TabInterface* active_tab = GetTabListInterface()->GetActiveTab();
+  ASSERT_TRUE(active_tab);
+  GetTabListInterface()->CloseTab(active_tab->GetHandle());
+
+  ContinueJsTest();
+}
+#endif
+
+#if !BUILDFLAG(IS_ANDROID)
 // TODO(crbug.com/520959831): Fix flaky test.
 IN_PROC_BROWSER_TEST_P(NewGlicApiTest, DISABLED_testEnableDragResize) {
   ASSERT_OK(OpenGlicForActiveTabAndDetach());
@@ -2245,6 +2261,48 @@
   ASSERT_OK(RunUntilEqual([&]() { return GetPopupCount(); }, 1));
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+IN_PROC_BROWSER_TEST_P(NewGlicApiTest, testOpenGlicSettingsPage) {
+  glic::GlicHistogramTester histogram_tester;
+  ASSERT_OK(OpenGlicForActiveTab());
+  ExecuteJsTest();
+  ASSERT_OK(
+      RunUntilEqual([&]() { return GetTabListInterface()->GetTabCount(); }, 2));
+  EXPECT_EQ(
+      GetTabListInterface()->GetActiveTab()->GetContents()->GetVisibleURL(),
+      chrome::GetSettingsUrl(chrome::kGlicSettingsSubpage));
+  histogram_tester.ExpectTotalCount(
+      "Glic.Api.RequestHostLatency.OpenGlicSettingsPage", 0);
+}
+#endif
+
+#if !BUILDFLAG(IS_ANDROID)
+IN_PROC_BROWSER_TEST_P(NewGlicApiTest, testOpenPasswordManagerSettingsPage) {
+  glic::GlicHistogramTester histogram_tester;
+  ASSERT_OK(OpenGlicForActiveTab());
+  ExecuteJsTest();
+  ASSERT_OK(
+      RunUntilEqual([&]() { return GetTabListInterface()->GetTabCount(); }, 2));
+  const GURL settings_url =
+      base::FeatureList::IsEnabled(features::kFedCmEmbedderInitiatedLogin)
+          ? chrome::GetSettingsUrl(chrome::kGlicLoginSettingsSubpage)
+          : GURL(GetGooglePasswordManagerSubPageURLStr());
+  EXPECT_EQ(
+      GetTabListInterface()->GetActiveTab()->GetContents()->GetVisibleURL(),
+      settings_url);
+}
+#endif
+
+IN_PROC_BROWSER_TEST_P(NewGlicApiTest, testInvoke) {
+  ASSERT_OK_AND_ASSIGN(auto* instance, OpenGlicForActiveTab());
+  ASSERT_OK(WaitForGlicClient());
+  auto options = mojom::InvokeOptions::New();
+  options->invocation_source = mojom::InvocationSource::kTopChromeButton;
+  instance->host().GetPrimaryWebClient()->Invoke(std::move(options),
+                                                 base::DoNothing());
+  ExecuteJsTest();
+}
+
 IN_PROC_BROWSER_TEST_P(NewGlicApiTest, testGetContextFromFocusedTabWithIframe) {
   ASSERT_TRUE(content::NavigateToURL(
       GetTabListInterface()->GetActiveTab()->GetContents(),
diff --git a/chrome/browser/glic/host/webui_contents_container.cc b/chrome/browser/glic/host/webui_contents_container.cc
index 6a831d0..6fce5b5 100644
--- a/chrome/browser/glic/host/webui_contents_container.cc
+++ b/chrome/browser/glic/host/webui_contents_container.cc
@@ -80,8 +80,7 @@
 
 WebUIContentsContainerImpl::WebUIContentsContainerImpl(Profile* profile,
                                                        bool initially_hidden)
-    : profile_keep_alive_(profile, ProfileKeepAliveOrigin::kGlicView),
-      web_contents_(content::WebContents::Create(
+    : web_contents_(content::WebContents::Create(
           MakeCreateParams(profile, initially_hidden))),
       web_contents_ptr_(web_contents_.get()),
       profile_(profile) {
diff --git a/chrome/browser/glic/host/webui_contents_container.h b/chrome/browser/glic/host/webui_contents_container.h
index 01d3c4ec..41891e5 100644
--- a/chrome/browser/glic/host/webui_contents_container.h
+++ b/chrome/browser/glic/host/webui_contents_container.h
@@ -10,7 +10,6 @@
 #include "base/functional/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
-#include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
 #include "content/public/browser/visibility.h"
 #include "content/public/browser/web_contents_observer.h"
 
@@ -75,7 +74,6 @@
   void UpdateActuationTracker();
 
   base::TimeTicks navigation_commit_time_;
-  ScopedProfileKeepAlive profile_keep_alive_;
   std::unique_ptr<content::WebContents> web_contents_;
   raw_ptr<content::WebContents> web_contents_ptr_ = nullptr;
   const raw_ptr<Profile> profile_;
diff --git a/chrome/browser/glic/public/features.cc b/chrome/browser/glic/public/features.cc
index 67860b0..9e38d1b 100644
--- a/chrome/browser/glic/public/features.cc
+++ b/chrome/browser/glic/public/features.cc
@@ -233,8 +233,6 @@
 BASE_FEATURE(kGlicSparkSettingsAccessibleLabels,
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kGlicOptInDialogLinkA11yFix, base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kGlicOptInDialogA11yFix, base::FEATURE_ENABLED_BY_DEFAULT);
 BASE_FEATURE(kGlicStructuredYieldMetadata, base::FEATURE_DISABLED_BY_DEFAULT);
 // Whether to allow Mojo in the glic guest frame.
@@ -243,4 +241,7 @@
 // Runs the glic client in a PrivilegedWebContents instead of a webview.
 // This is a work in progress. See b/534807813.
 BASE_FEATURE(kGlicNoWebview, base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kGlicShakeTrigger,
+             "GlicShakeTrigger",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 }  // namespace features
diff --git a/chrome/browser/glic/public/features.h b/chrome/browser/glic/public/features.h
index 5d21340..a30ce8b 100644
--- a/chrome/browser/glic/public/features.h
+++ b/chrome/browser/glic/public/features.h
@@ -127,7 +127,6 @@
 
 BASE_DECLARE_FEATURE(kGlicPasteEligibilityCheck);
 BASE_DECLARE_FEATURE(kGlicWebPasteEligibilityCheck);
-BASE_DECLARE_FEATURE(kGlicOptInDialogLinkA11yFix);
 
 BASE_DECLARE_FEATURE(kGlicTabGroups);
 extern const base::FeatureParam<bool> kGlicTabGroupsUseFullTabEmbedder;
@@ -140,6 +139,8 @@
 
 BASE_DECLARE_FEATURE(kGlicNoWebview);
 
+BASE_DECLARE_FEATURE(kGlicShakeTrigger);
+
 }  // namespace features
 
 #endif  // CHROME_BROWSER_GLIC_PUBLIC_FEATURES_H_
diff --git a/chrome/browser/glic/public/glic_keyed_service.cc b/chrome/browser/glic/public/glic_keyed_service.cc
index c88e3447..e384194 100644
--- a/chrome/browser/glic/public/glic_keyed_service.cc
+++ b/chrome/browser/glic/public/glic_keyed_service.cc
@@ -41,6 +41,7 @@
 #include "chrome/browser/glic/host/context/glic_tab_data_observer.h"
 #include "chrome/browser/glic/host/context/glic_tab_favicon_observer.h"
 #include "chrome/browser/glic/host/glic.mojom.h"
+#include "chrome/browser/glic/host/glic_local_storage_migration.h"
 #include "chrome/browser/glic/host/glic_web_client_access.h"
 #include "chrome/browser/glic/host/host.h"
 #include "chrome/browser/glic/host/webui_contents_container.h"
@@ -215,6 +216,7 @@
     GlicMediaIntegration::GetFor(profile_);
   }
 #endif
+  MaybeMigrateGlicLocalStorage(profile_);
 }
 
 GlicKeyedService::~GlicKeyedService() {
diff --git a/chrome/browser/glic/service/glic_instance_coordinator_browsertest.cc b/chrome/browser/glic/service/glic_instance_coordinator_browsertest.cc
index e434e1d..d076d7d 100644
--- a/chrome/browser/glic/service/glic_instance_coordinator_browsertest.cc
+++ b/chrome/browser/glic/service/glic_instance_coordinator_browsertest.cc
@@ -32,11 +32,14 @@
 #include "chrome/browser/glic/service/glic_instance_impl.h"
 #include "chrome/browser/glic/service/glic_invoke_handler.h"
 #include "chrome/browser/glic/service/glic_invoke_task.h"
+#include "chrome/browser/glic/service/glic_ui_types.h"
 #include "chrome/browser/glic/service/metrics/glic_instance_helper_metrics.h"
 #include "chrome/browser/glic/test_support/glic_browser_test.h"
 #include "chrome/browser/glic/test_support/glic_histogram_tester.h"
 #include "chrome/browser/glic/widget/glic_floating_ui.h"
+#include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/tab_list/tab_list_interface.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
@@ -48,6 +51,7 @@
 #include "chrome/common/chrome_features.h"
 #include "components/enterprise/browser/reporting/common_pref_names.h"
 #include "components/enterprise/browser/reporting/reporting_features.h"
+#include "components/keep_alive_registry/keep_alive_registry.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/prefs/pref_service.h"
@@ -1886,6 +1890,28 @@
   EXPECT_TRUE(instance->HasActiveEmbedder());
   EXPECT_TRUE(instance->GetBoundTabs().empty());
 }
+
+IN_PROC_BROWSER_TEST_F(GlicInstanceCoordinatorBrowserTest,
+                       DetachedPanelAcquiresAndReleasesProfileKeepAlive) {
+  auto* profile_manager = g_browser_process->profile_manager();
+  EXPECT_FALSE(profile_manager->HasKeepAliveForTesting(
+      GetProfile(), ProfileKeepAliveOrigin::kGlicView));
+
+  // Open Glic and detach it into a floating window.
+  ASSERT_OK_AND_ASSIGN(GlicInstanceImpl * instance,
+                       OpenGlicForActiveTabAndDetach());
+  ASSERT_TRUE(instance->IsDetached());
+
+  EXPECT_TRUE(profile_manager->HasKeepAliveForTesting(
+      GetProfile(), ProfileKeepAliveOrigin::kGlicView));
+
+  // Close the detached window.
+  instance->CloseAllEmbedders();
+  ASSERT_OK(WaitForGlicClose(instance));
+
+  EXPECT_FALSE(profile_manager->HasKeepAliveForTesting(
+      GetProfile(), ProfileKeepAliveOrigin::kGlicView));
+}
 #endif
 
 IN_PROC_BROWSER_TEST_F(GlicInstanceCoordinatorBrowserTest,
diff --git a/chrome/browser/glic/service/glic_instance_impl.cc b/chrome/browser/glic/service/glic_instance_impl.cc
index 661a402d..9822302d 100644
--- a/chrome/browser/glic/service/glic_instance_impl.cc
+++ b/chrome/browser/glic/service/glic_instance_impl.cc
@@ -780,14 +780,11 @@
                                  /*success=*/true, created_tab, source_tab);
 }
 
-void GlicInstanceImpl::GetZeroStateSuggestionsAndSubscribe(
-    bool has_active_subscription,
-    const mojom::ZeroStateSuggestionsOptions& options,
-    mojom::WebClientHandler::GetZeroStateSuggestionsAndSubscribeCallback
-        callback) {
-  zero_state_suggestions_manager_->ObserveZeroStateSuggestions(
-      has_active_subscription, options.is_first_run, options.supported_tools,
-      std::move(callback));
+void GlicInstanceImpl::CreateZeroStateSuggestionsHandler(
+    mojo::PendingReceiver<mojom::ZeroStateSuggestionsHandler> receiver) {
+  if (zero_state_suggestions_manager_) {
+    zero_state_suggestions_manager_->Bind(std::move(receiver));
+  }
 }
 
 void GlicInstanceImpl::PrepareForOpen() {
diff --git a/chrome/browser/glic/service/glic_instance_impl.h b/chrome/browser/glic/service/glic_instance_impl.h
index 18d9639..d040e2c0 100644
--- a/chrome/browser/glic/service/glic_instance_impl.h
+++ b/chrome/browser/glic/service/glic_instance_impl.h
@@ -264,11 +264,7 @@
   void RegisterConversation(
       glic::mojom::ConversationInfoPtr info,
       mojom::WebClientHandler::RegisterConversationCallback callback) override;
-  void GetZeroStateSuggestionsAndSubscribe(
-      bool has_active_subscription,
-      const mojom::ZeroStateSuggestionsOptions& options,
-      mojom::WebClientHandler::GetZeroStateSuggestionsAndSubscribeCallback
-          callback) override;
+
   void OnWebClientCleared() override;
   void PrepareForOpen() override;
   void OnUserInputSubmitted(mojom::WebClientMode mode) override;
@@ -280,7 +276,9 @@
 
   std::unique_ptr<WebUIContentsContainer> CreateWebUIContentsContainer()
       override;
-
+  void CreateZeroStateSuggestionsHandler(
+      mojo::PendingReceiver<mojom::ZeroStateSuggestionsHandler> receiver)
+      override;
   // GlicUiEmbedder::Delegate:
 
   void OnEmbedderWindowActivationChanged(bool has_focus) override;
diff --git a/chrome/browser/glic/widget/glic_floating_ui.cc b/chrome/browser/glic/widget/glic_floating_ui.cc
index 1f8a73e8..c3db43d 100644
--- a/chrome/browser/glic/widget/glic_floating_ui.cc
+++ b/chrome/browser/glic/widget/glic_floating_ui.cc
@@ -20,6 +20,8 @@
 #include "chrome/browser/glic/widget/glic_window_animator.h"
 #include "chrome/browser/picture_in_picture/picture_in_picture_occlusion_tracker.h"
 #include "chrome/browser/picture_in_picture/picture_in_picture_window_manager.h"
+#include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
+#include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
@@ -63,7 +65,8 @@
     : profile_(profile),
       delegate_(delegate),
       instance_metrics_(instance_metrics),
-      source_tab_(source_tab) {
+      source_tab_(source_tab),
+      profile_keep_alive_(profile, ProfileKeepAliveOrigin::kGlicView) {
   if (!base::FeatureList::IsEnabled(features::kGlicOrphanedReattachment)) {
     if (auto* helper = GlicInstanceHelper::From(source_tab_.Get())) {
       source_tab_destruction_subscription_ =
diff --git a/chrome/browser/glic/widget/glic_floating_ui.h b/chrome/browser/glic/widget/glic_floating_ui.h
index 3e0565d..1a77685 100644
--- a/chrome/browser/glic/widget/glic_floating_ui.h
+++ b/chrome/browser/glic/widget/glic_floating_ui.h
@@ -15,6 +15,7 @@
 #include "chrome/browser/glic/host/host.h"
 #include "chrome/browser/glic/service/glic_ui_embedder.h"
 #include "chrome/browser/glic/widget/browser_conditions.h"
+#include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
 #include "components/tabs/public/tab_interface.h"
 #include "components/web_modal/web_contents_modal_dialog_host.h"
 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
@@ -168,6 +169,7 @@
   base::CallbackListSubscription source_tab_destruction_subscription_;
 
   std::unique_ptr<BrowserAttachObservation> browser_attach_observation_;
+  ScopedProfileKeepAlive profile_keep_alive_;
 
   base::WeakPtrFactory<GlicFloatingUi> weak_ptr_factory_{this};
 };
diff --git a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/FullScreenIncognitoReauthCoordinatorTest.java b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/FullScreenIncognitoReauthCoordinatorTest.java
index 52bc46d..7e254be 100644
--- a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/FullScreenIncognitoReauthCoordinatorTest.java
+++ b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/FullScreenIncognitoReauthCoordinatorTest.java
@@ -10,8 +10,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import static org.chromium.base.test.util.Batch.UNIT_TESTS;
-
 import android.content.Context;
 import android.view.View;
 
@@ -28,7 +26,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.ui.listmenu.ListMenuDelegate;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
@@ -38,7 +35,6 @@
 /** Robolectric tests for {@link FullScreenIncognitoReauthCoordinator}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(UNIT_TESTS)
 public class FullScreenIncognitoReauthCoordinatorTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Mock private Context mContextMock;
diff --git a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthCoordinatorFactoryTest.java b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthCoordinatorFactoryTest.java
index af189f6..52fecbd7 100644
--- a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthCoordinatorFactoryTest.java
+++ b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthCoordinatorFactoryTest.java
@@ -14,8 +14,6 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
-import static org.chromium.base.test.util.Batch.UNIT_TESTS;
-
 import android.content.Context;
 import android.content.Intent;
 
@@ -36,7 +34,6 @@
 
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRule;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.hub.HubManager;
 import org.chromium.chrome.browser.hub.PaneId;
 import org.chromium.chrome.browser.hub.PaneManager;
@@ -59,7 +56,6 @@
  */
 @RunWith(ParameterizedRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(UNIT_TESTS)
 public class IncognitoReauthCoordinatorFactoryTest {
     public @Rule MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthDialogTest.java b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthDialogTest.java
index 1104e58..904225f 100644
--- a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthDialogTest.java
+++ b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthDialogTest.java
@@ -11,8 +11,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
-import static org.chromium.base.test.util.Batch.UNIT_TESTS;
-
 import android.view.View;
 
 import androidx.activity.OnBackPressedCallback;
@@ -29,7 +27,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
@@ -38,7 +35,6 @@
 /** Robolectric tests for {@link IncognitoReauthDialog}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(UNIT_TESTS)
 public class IncognitoReauthDialogTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Mock private ModalDialogManager mModalDialogManagerMock;
diff --git a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/TabSwitcherIncognitoReauthCoordinatorTest.java b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/TabSwitcherIncognitoReauthCoordinatorTest.java
index 9233539..727c5442 100644
--- a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/TabSwitcherIncognitoReauthCoordinatorTest.java
+++ b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/TabSwitcherIncognitoReauthCoordinatorTest.java
@@ -9,8 +9,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import static org.chromium.base.test.util.Batch.UNIT_TESTS;
-
 import android.content.Context;
 import android.view.View;
 
@@ -27,7 +25,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.tab_ui.TabSwitcherCustomViewManager;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
@@ -35,7 +32,6 @@
 /** Robolectric tests for {@link TabSwitcherIncognitoReauthCoordinator}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(UNIT_TESTS)
 public class TabSwitcherIncognitoReauthCoordinatorTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Mock private Context mContextMock;
diff --git a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/AppLocaleUtilsTest.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/AppLocaleUtilsTest.java
index 3aecc65..e2bc964 100644
--- a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/AppLocaleUtilsTest.java
+++ b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/AppLocaleUtilsTest.java
@@ -14,7 +14,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
 
@@ -23,7 +22,6 @@
 
 /** Tests for the AppLocalUtils class. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 // Fails on SDK 33+ because AppLocaleUtils uses system-managed locales (Android 13+).
 @Config(sdk = {BaseRobolectricTestRunner.MIN_SDK, 32})
 public class AppLocaleUtilsTest {
diff --git a/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensSupportStatusHelperUnitTest.java b/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensSupportStatusHelperUnitTest.java
index 8cb80c2..81ef0880 100644
--- a/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensSupportStatusHelperUnitTest.java
+++ b/chrome/browser/lens/java/src/org/chromium/chrome/browser/lens/LensSupportStatusHelperUnitTest.java
@@ -18,7 +18,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -30,7 +29,6 @@
 /** Unit tests for {@link LensSupportStatusHelper}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 @DisableFeatures({ChromeFeatureList.LENS_OVERLAY_ANDROID})
 public class LensSupportStatusHelperUnitTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/browser/mandatory_reauth/android/internal/java/src/org/chromium/chrome/browser/mandatory_reauth/MandatoryReauthOptInBottomSheetModuleTest.java b/chrome/browser/mandatory_reauth/android/internal/java/src/org/chromium/chrome/browser/mandatory_reauth/MandatoryReauthOptInBottomSheetModuleTest.java
index 5ba15e5d..78b3abf 100644
--- a/chrome/browser/mandatory_reauth/android/internal/java/src/org/chromium/chrome/browser/mandatory_reauth/MandatoryReauthOptInBottomSheetModuleTest.java
+++ b/chrome/browser/mandatory_reauth/android/internal/java/src/org/chromium/chrome/browser/mandatory_reauth/MandatoryReauthOptInBottomSheetModuleTest.java
@@ -32,7 +32,6 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.autofill.PaymentsUiClosedReason;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
@@ -41,7 +40,6 @@
 
 /** Tests for {@link MandatoryReauthOptInBottomSheetViewBridge} */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.PER_CLASS)
 public class MandatoryReauthOptInBottomSheetModuleTest {
     private MandatoryReauthOptInBottomSheetViewBridge mViewBridge;
     ArgumentCaptor<BottomSheetContent> mContentCaptor =
diff --git a/chrome/browser/metrics/BUILD.gn b/chrome/browser/metrics/BUILD.gn
index b36846c..3cc0659 100644
--- a/chrome/browser/metrics/BUILD.gn
+++ b/chrome/browser/metrics/BUILD.gn
@@ -768,6 +768,7 @@
 
     sources = [
       "dwa_browsertest.cc",
+      "metrics_consent_migration_browsertest.cc",
       "metrics_reporting_state_browsertest.cc",
       "metrics_service_browsertest.cc",
       "metrics_service_user_demographics_browsertest.cc",
diff --git a/chrome/browser/metrics/metrics_consent_migration_browsertest.cc b/chrome/browser/metrics/metrics_consent_migration_browsertest.cc
new file mode 100644
index 0000000..ae832542b
--- /dev/null
+++ b/chrome/browser/metrics/metrics_consent_migration_browsertest.cc
@@ -0,0 +1,643 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/path_service.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/metrics/testing/metrics_consent_override.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile_test_util.h"
+#include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
+#include "chrome/browser/sync/test/integration/sync_test.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_paths.h"
+#include "components/metrics/enabled_state_provider.h"
+#include "components/metrics/metrics_features.h"
+#include "components/metrics/metrics_profile_pref_names.h"
+#include "components/metrics_services_manager/metrics_services_manager.h"
+#include "components/prefs/pref_service.h"
+#include "components/sync/base/pref_names.h"
+#include "components/sync/base/user_selectable_type.h"
+#include "components/sync/service/sync_user_settings.h"
+#include "components/ukm/ukm_service.h"
+#include "components/ukm/ukm_test_helper.h"
+#include "components/unified_consent/pref_names.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/test_launcher.h"
+
+class MetricsConsentMigrationBrowserTest : public SyncTest {
+ public:
+  MetricsConsentMigrationBrowserTest() : SyncTest(SINGLE_CLIENT) {
+    metrics::EnabledStateProvider::SetIgnoreForceFieldTrialsForTesting(true);
+    if (content::IsPreTest()) {
+      // PRE_ test: disable the feature to keep profile in pre-migration state.
+      scoped_feature_list_.InitAndDisableFeature(
+          metrics::features::kRestructureMetricsConsentSettings);
+    } else {
+      // Non-PRE_ test: enable the feature to trigger migration on startup.
+      scoped_feature_list_.InitAndEnableFeature(
+          metrics::features::kRestructureMetricsConsentSettings);
+    }
+  }
+
+  MetricsConsentMigrationBrowserTest(
+      const MetricsConsentMigrationBrowserTest&) = delete;
+  MetricsConsentMigrationBrowserTest& operator=(
+      const MetricsConsentMigrationBrowserTest&) = delete;
+
+  ~MetricsConsentMigrationBrowserTest() override = default;
+
+ protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// 1. Migrate User with All Sync Types Enabled
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationBrowserTest,
+                       PRE_MigrateSignedIn_AllEnabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+  ASSERT_TRUE(SetupClients());
+  Profile* profile = GetProfile(0);
+  std::unique_ptr<SyncServiceImplHarness> harness =
+      SyncServiceImplHarness::Create(
+          profile, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness->SetupSync());
+
+  // Set MSBB to true.
+  profile->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  // Sync is already fully enabled by default.
+  // Check that migration has not run yet.
+  EXPECT_FALSE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+}
+
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationBrowserTest,
+                       MigrateSignedIn_AllEnabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+  ASSERT_TRUE(SetupClients());
+  Profile* profile = GetProfile(0);
+
+  std::unique_ptr<SyncServiceImplHarness> harness =
+      SyncServiceImplHarness::Create(
+          profile, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness->AwaitSyncTransportActive());
+
+  // Migration should run on startup.
+  EXPECT_TRUE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+  EXPECT_TRUE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingEnabled));
+
+  ukm::UkmTestHelper ukm_test_helper(
+      g_browser_process->GetMetricsServicesManager()->GetUkmService());
+  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
+}
+
+// 2. Migrate User with Apps Sync Disabled
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationBrowserTest,
+                       PRE_MigrateSignedIn_AppsDisabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+  ASSERT_TRUE(SetupClients());
+  Profile* profile = GetProfile(0);
+  std::unique_ptr<SyncServiceImplHarness> harness =
+      SyncServiceImplHarness::Create(
+          profile, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness->SetupSync());
+
+  // Disable Apps sync.
+#if BUILDFLAG(IS_CHROMEOS)
+  syncer::UserSelectableOsTypeSet os_types =
+      harness->service()->GetUserSettings()->GetSelectedOsTypes();
+  os_types.Remove(syncer::UserSelectableOsType::kOsApps);
+  harness->service()->GetUserSettings()->SetSelectedOsTypes(
+      /*sync_all_os_types=*/false, os_types);
+#else
+  syncer::UserSelectableTypeSet types =
+      harness->service()->GetUserSettings()->GetSelectedTypes();
+  types.Remove(syncer::UserSelectableType::kApps);
+  harness->service()->GetUserSettings()->SetSelectedTypes(
+      /*sync_everything=*/false, types);
+#endif
+
+  // Set MSBB to true.
+  profile->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  EXPECT_FALSE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+}
+
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationBrowserTest,
+                       MigrateSignedIn_AppsDisabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+  ASSERT_TRUE(SetupClients());
+  Profile* profile = GetProfile(0);
+
+  std::unique_ptr<SyncServiceImplHarness> harness =
+      SyncServiceImplHarness::Create(
+          profile, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness->AwaitSyncTransportActive());
+
+  EXPECT_TRUE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+
+  // On desktop platforms and ChromeOS, apps sync is supported but
+  // disabled, so migration fails (false).
+  EXPECT_FALSE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingEnabled));
+  ukm::UkmTestHelper ukm_test_helper(
+      g_browser_process->GetMetricsServicesManager()->GetUkmService());
+  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+}
+
+// 3. Migrate User with MSBB Disabled
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationBrowserTest,
+                       PRE_MigrateSignedIn_MsbbDisabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+  ASSERT_TRUE(SetupClients());
+  Profile* profile = GetProfile(0);
+  std::unique_ptr<SyncServiceImplHarness> harness =
+      SyncServiceImplHarness::Create(
+          profile, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness->SetupSync());
+
+  // Set MSBB to false.
+  profile->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, false);
+
+  EXPECT_FALSE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+}
+
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationBrowserTest,
+                       MigrateSignedIn_MsbbDisabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+  ASSERT_TRUE(SetupClients());
+  Profile* profile = GetProfile(0);
+
+  std::unique_ptr<SyncServiceImplHarness> harness =
+      SyncServiceImplHarness::Create(
+          profile, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness->AwaitSyncTransportActive());
+
+  EXPECT_TRUE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+  EXPECT_FALSE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingEnabled));
+
+  ukm::UkmTestHelper ukm_test_helper(
+      g_browser_process->GetMetricsServicesManager()->GetUkmService());
+  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+}
+
+// 4. Migrate Signed-Out User with MSBB Enabled
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationBrowserTest,
+                       PRE_MigrateSignedOut_MsbbEnabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+  ASSERT_TRUE(SetupClients());
+  Profile* profile = GetProfile(0);
+  // Do not sign in.
+
+  // Set MSBB to true.
+  profile->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  EXPECT_FALSE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+}
+
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationBrowserTest,
+                       MigrateSignedOut_MsbbEnabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+  ASSERT_TRUE(SetupClients());
+  Profile* profile = GetProfile(0);
+
+  EXPECT_TRUE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+  EXPECT_TRUE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingEnabled));
+
+  ukm::UkmTestHelper ukm_test_helper(
+      g_browser_process->GetMetricsServicesManager()->GetUkmService());
+  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
+}
+
+class MetricsConsentMigrationRollbackBrowserTest : public SyncTest {
+ public:
+  MetricsConsentMigrationRollbackBrowserTest() : SyncTest(SINGLE_CLIENT) {
+    metrics::EnabledStateProvider::SetIgnoreForceFieldTrialsForTesting(true);
+    if (content::IsPreTest()) {
+      scoped_feature_list_.InitAndEnableFeature(
+          metrics::features::kRestructureMetricsConsentSettings);
+    } else {
+      scoped_feature_list_.InitAndDisableFeature(
+          metrics::features::kRestructureMetricsConsentSettings);
+    }
+  }
+
+ protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationRollbackBrowserTest,
+                       PRE_MigrationRollback) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+  ASSERT_TRUE(SetupClients());
+  Profile* profile = GetProfile(0);
+  std::unique_ptr<SyncServiceImplHarness> harness =
+      SyncServiceImplHarness::Create(
+          profile, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness->SetupSync());
+
+  // Disable Apps sync.
+#if BUILDFLAG(IS_CHROMEOS)
+  syncer::UserSelectableOsTypeSet os_types =
+      harness->service()->GetUserSettings()->GetSelectedOsTypes();
+  os_types.Remove(syncer::UserSelectableOsType::kOsApps);
+  harness->service()->GetUserSettings()->SetSelectedOsTypes(
+      /*sync_all_os_types=*/false, os_types);
+#else
+  syncer::UserSelectableTypeSet types =
+      harness->service()->GetUserSettings()->GetSelectedTypes();
+  types.Remove(syncer::UserSelectableType::kApps);
+  harness->service()->GetUserSettings()->SetSelectedTypes(
+      /*sync_everything=*/false, types);
+#endif
+
+  // Set MSBB to true.
+  profile->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  // Since the feature is enabled in PRE_ test, migration will run.
+  // Since apps sync is disabled, kAdvancedReportingEnabled
+  // will migrate to false, which disables UKM.
+  EXPECT_TRUE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+  EXPECT_FALSE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingEnabled));
+
+  ukm::UkmTestHelper ukm_test_helper(
+      g_browser_process->GetMetricsServicesManager()->GetUkmService());
+  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+}
+
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationRollbackBrowserTest,
+                       MigrationRollback) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+  ASSERT_TRUE(SetupClients());
+  Profile* profile = GetProfile(0);
+
+  std::unique_ptr<SyncServiceImplHarness> harness =
+      SyncServiceImplHarness::Create(
+          profile, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness->AwaitSyncTransportActive());
+
+  // Migration had run in the PRE_ test.
+  EXPECT_TRUE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+  EXPECT_FALSE(profile->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingEnabled));
+
+  // The feature is now disabled (rollback).
+  // UKM recording should revert to the old logic: MSBB is enabled, so
+  // UKM recording should be ENABLED, ignoring that kAdvancedReportingEnabled is
+  // false.
+  ukm::UkmTestHelper ukm_test_helper(
+      g_browser_process->GetMetricsServicesManager()->GetUkmService());
+  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
+}
+
+// Multi-profile settings tests
+class MetricsConsentMigrationMultiProfileBrowserTest : public SyncTest {
+ public:
+  MetricsConsentMigrationMultiProfileBrowserTest() : SyncTest(SINGLE_CLIENT) {
+    metrics::EnabledStateProvider::SetIgnoreForceFieldTrialsForTesting(true);
+    if (content::IsPreTest()) {
+      scoped_feature_list_.InitAndDisableFeature(
+          metrics::features::kRestructureMetricsConsentSettings);
+    } else {
+      scoped_feature_list_.InitAndEnableFeature(
+          metrics::features::kRestructureMetricsConsentSettings);
+    }
+  }
+
+ protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationMultiProfileBrowserTest,
+                       PRE_MigrateMultiProfile_AllEnabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  ASSERT_TRUE(SetupClients());
+
+  // Profile 0: Enable MSBB
+  Profile* profile_0 = GetProfile(0);
+  std::unique_ptr<SyncServiceImplHarness> harness_0 =
+      SyncServiceImplHarness::Create(
+          profile_0, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_0->SetupSync());
+  profile_0->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  // Profile 1: Enable MSBB
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  base::FilePath user_data_dir;
+  base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+  base::FilePath profile_path_1 = user_data_dir.Append(GetProfileBaseName(1));
+  Profile& profile_1 =
+      profiles::testing::CreateProfileSync(profile_manager, profile_path_1);
+
+  std::unique_ptr<SyncServiceImplHarness> harness_1 =
+      SyncServiceImplHarness::Create(
+          &profile_1, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_1->SetupSync());
+  profile_1.GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  EXPECT_FALSE(profile_0->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+  EXPECT_FALSE(profile_1.GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+}
+
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationMultiProfileBrowserTest,
+                       MigrateMultiProfile_AllEnabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  ASSERT_TRUE(SetupClients());
+  Profile* profile_0 = GetProfile(0);
+  std::unique_ptr<SyncServiceImplHarness> harness_0 =
+      SyncServiceImplHarness::Create(
+          profile_0, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_0->AwaitSyncTransportActive());
+
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  base::FilePath user_data_dir;
+  base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+  base::FilePath profile_path_1 = user_data_dir.Append(GetProfileBaseName(1));
+  Profile& profile_1 =
+      profiles::testing::CreateProfileSync(profile_manager, profile_path_1);
+
+  std::unique_ptr<SyncServiceImplHarness> harness_1 =
+      SyncServiceImplHarness::Create(
+          &profile_1, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_1->AwaitSyncTransportActive());
+
+  // Migration should run on both.
+  EXPECT_TRUE(profile_0->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+  EXPECT_TRUE(profile_0->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingEnabled));
+
+  EXPECT_TRUE(profile_1.GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+  EXPECT_TRUE(profile_1.GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingEnabled));
+
+  ukm::UkmTestHelper ukm_test_helper(
+      g_browser_process->GetMetricsServicesManager()->GetUkmService());
+  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
+}
+
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationMultiProfileBrowserTest,
+                       PRE_MigrateMultiProfile_OneDisabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  ASSERT_TRUE(SetupClients());
+
+  // Profile 0: Enable MSBB
+  Profile* profile_0 = GetProfile(0);
+  std::unique_ptr<SyncServiceImplHarness> harness_0 =
+      SyncServiceImplHarness::Create(
+          profile_0, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_0->SetupSync());
+  profile_0->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  // Profile 1: Disable MSBB
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  base::FilePath user_data_dir;
+  base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+  base::FilePath profile_path_1 = user_data_dir.Append(GetProfileBaseName(1));
+  Profile& profile_1 =
+      profiles::testing::CreateProfileSync(profile_manager, profile_path_1);
+
+  std::unique_ptr<SyncServiceImplHarness> harness_1 =
+      SyncServiceImplHarness::Create(
+          &profile_1, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_1->SetupSync());
+  profile_1.GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, false);
+
+  EXPECT_FALSE(profile_0->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+  EXPECT_FALSE(profile_1.GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+}
+
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationMultiProfileBrowserTest,
+                       MigrateMultiProfile_OneDisabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  ASSERT_TRUE(SetupClients());
+  Profile* profile_0 = GetProfile(0);
+  std::unique_ptr<SyncServiceImplHarness> harness_0 =
+      SyncServiceImplHarness::Create(
+          profile_0, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_0->AwaitSyncTransportActive());
+
+  // Migration should run on Profile 0.
+  EXPECT_TRUE(profile_0->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+  EXPECT_TRUE(profile_0->GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingEnabled));
+
+  // Verify that UKM is enabled because Profile 0 (the only opened profile) has
+  // advanced metrics enabled.
+  ukm::UkmTestHelper ukm_test_helper(
+      g_browser_process->GetMetricsServicesManager()->GetUkmService());
+  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
+
+  // Now open Profile 1, which has already migrated to
+  // `kAdvancedReportingEnabled = false` (as configured in the PRE_ test).
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  base::FilePath user_data_dir;
+  base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+  base::FilePath profile_path_1 = user_data_dir.Append(GetProfileBaseName(1));
+  Profile& profile_1 =
+      profiles::testing::CreateProfileSync(profile_manager, profile_path_1);
+
+  std::unique_ptr<SyncServiceImplHarness> harness_1 =
+      SyncServiceImplHarness::Create(
+          &profile_1, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_1->AwaitSyncTransportActive());
+
+  EXPECT_TRUE(profile_1.GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingProfileMigrationDone));
+  EXPECT_FALSE(profile_1.GetPrefs()->GetBoolean(
+      metrics::prefs::kAdvancedReportingEnabled));
+
+  // Since Profile 1 has advanced reporting false, UKM recording must be
+  // DISABLED!
+  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+}
+
+// Multi-profile Rollback tests
+class MetricsConsentMigrationRollbackMultiProfileBrowserTest : public SyncTest {
+ public:
+  MetricsConsentMigrationRollbackMultiProfileBrowserTest()
+      : SyncTest(TWO_CLIENT) {
+    metrics::EnabledStateProvider::SetIgnoreForceFieldTrialsForTesting(true);
+    if (content::IsPreTest()) {
+      scoped_feature_list_.InitAndEnableFeature(
+          metrics::features::kRestructureMetricsConsentSettings);
+    } else {
+      scoped_feature_list_.InitAndDisableFeature(
+          metrics::features::kRestructureMetricsConsentSettings);
+    }
+  }
+
+ protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationRollbackMultiProfileBrowserTest,
+                       PRE_MigrationRollback_MultiProfile_AllEnabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  ASSERT_TRUE(SetupClients());
+
+  // Profile 0: Enable MSBB
+  Profile* profile_0 = GetProfile(0);
+  std::unique_ptr<SyncServiceImplHarness> harness_0 =
+      SyncServiceImplHarness::Create(
+          profile_0, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_0->SetupSync());
+  profile_0->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  // Profile 1: Enable MSBB
+  Profile* profile_1 = GetProfile(1);
+  std::unique_ptr<SyncServiceImplHarness> harness_1 =
+      SyncServiceImplHarness::Create(
+          profile_1, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_1->SetupSync());
+  profile_1->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+}
+
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationRollbackMultiProfileBrowserTest,
+                       MigrationRollback_MultiProfile_AllEnabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  ASSERT_TRUE(SetupClients());
+  Profile* profile_0 = GetProfile(0);
+  std::unique_ptr<SyncServiceImplHarness> harness_0 =
+      SyncServiceImplHarness::Create(
+          profile_0, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_0->AwaitSyncTransportActive());
+
+  Profile* profile_1 = GetProfile(1);
+  std::unique_ptr<SyncServiceImplHarness> harness_1 =
+      SyncServiceImplHarness::Create(
+          profile_1, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_1->AwaitSyncTransportActive());
+
+  // Since feature flag is disabled, advanced reporting prefs don't matter.
+  // UKM should be allowed because both profiles have MSBB enabled (under old
+  // logic).
+  ukm::UkmTestHelper ukm_test_helper(
+      g_browser_process->GetMetricsServicesManager()->GetUkmService());
+  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
+}
+
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationRollbackMultiProfileBrowserTest,
+                       PRE_MigrationRollback_MultiProfile_OneDisabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  ASSERT_TRUE(SetupClients());
+
+  // Profile 0: Enable MSBB
+  Profile* profile_0 = GetProfile(0);
+  std::unique_ptr<SyncServiceImplHarness> harness_0 =
+      SyncServiceImplHarness::Create(
+          profile_0, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_0->SetupSync());
+  profile_0->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  // Profile 1: Disable MSBB
+  Profile* profile_1 = GetProfile(1);
+  std::unique_ptr<SyncServiceImplHarness> harness_1 =
+      SyncServiceImplHarness::Create(
+          profile_1, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_1->SetupSync());
+  profile_1->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, false);
+}
+
+IN_PROC_BROWSER_TEST_F(MetricsConsentMigrationRollbackMultiProfileBrowserTest,
+                       MigrationRollback_MultiProfile_OneDisabled) {
+  metrics::test::MetricsConsentOverride consent(true);
+  browser()->GetProfile()->GetPrefs()->SetBoolean(
+      unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled, true);
+
+  ASSERT_TRUE(SetupClients());
+  Profile* profile_0 = GetProfile(0);
+  std::unique_ptr<SyncServiceImplHarness> harness_0 =
+      SyncServiceImplHarness::Create(
+          profile_0, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_0->AwaitSyncTransportActive());
+
+  Profile* profile_1 = GetProfile(1);
+  std::unique_ptr<SyncServiceImplHarness> harness_1 =
+      SyncServiceImplHarness::Create(
+          profile_1, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
+  ASSERT_TRUE(harness_1->AwaitSyncTransportActive());
+
+  // Since Profile 1 has MSBB disabled, UKM must be disabled under old logic.
+  ukm::UkmTestHelper ukm_test_helper(
+      g_browser_process->GetMetricsServicesManager()->GetUkmService());
+  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
+}
diff --git a/chrome/browser/net/net_error_tab_helper.cc b/chrome/browser/net/net_error_tab_helper.cc
index a98240f..05b6acb 100644
--- a/chrome/browser/net/net_error_tab_helper.cc
+++ b/chrome/browser/net/net_error_tab_helper.cc
@@ -53,6 +53,12 @@
 static NetErrorTabHelper::TestingState testing_state_ =
     NetErrorTabHelper::TESTING_DEFAULT;
 
+bool IsValidEasterEggTarget(content::RenderFrameHost& target_frame) {
+  // Only the primary main-frame error document lives in the isolated
+  // error-page process; subframe error documents can be attacker-controlled.
+  return target_frame.IsErrorDocument() && target_frame.IsInPrimaryMainFrame();
+}
+
 }  // namespace
 
 NetErrorTabHelper::~NetErrorTabHelper() = default;
@@ -313,7 +319,9 @@
 #endif  // BUILDFLAG(ENABLE_OFFLINE_PAGES)
 
 void NetErrorTabHelper::GetHighScore(GetHighScoreCallback callback) {
-  if (!network_easter_egg_receivers_.CurrentTargetFrame().IsErrorDocument()) {
+  content::RenderFrameHost& target_frame =
+      network_easter_egg_receivers_.CurrentTargetFrame();
+  if (!IsValidEasterEggTarget(target_frame)) {
     // IsInMessageDispatch() is checked to avoid calling ReportBadMessage()
     // and crashing when unit tests invoke these methods directly.
     if (mojo::IsInMessageDispatch()) {
@@ -328,7 +336,9 @@
 }
 
 void NetErrorTabHelper::UpdateHighScore(uint32_t high_score) {
-  if (!network_easter_egg_receivers_.CurrentTargetFrame().IsErrorDocument()) {
+  content::RenderFrameHost& target_frame =
+      network_easter_egg_receivers_.CurrentTargetFrame();
+  if (!IsValidEasterEggTarget(target_frame)) {
     // IsInMessageDispatch() is checked to avoid calling ReportBadMessage()
     // and crashing when unit tests invoke these methods directly.
     if (mojo::IsInMessageDispatch()) {
@@ -343,7 +353,9 @@
 }
 
 void NetErrorTabHelper::ResetHighScore() {
-  if (!network_easter_egg_receivers_.CurrentTargetFrame().IsErrorDocument()) {
+  content::RenderFrameHost& target_frame =
+      network_easter_egg_receivers_.CurrentTargetFrame();
+  if (!IsValidEasterEggTarget(target_frame)) {
     // IsInMessageDispatch() is checked to avoid calling ReportBadMessage()
     // and crashing when unit tests invoke these methods directly.
     if (mojo::IsInMessageDispatch()) {
diff --git a/chrome/browser/net/profile_network_context_service_browsertest.cc b/chrome/browser/net/profile_network_context_service_browsertest.cc
index bca9b70..37a8d7d 100644
--- a/chrome/browser/net/profile_network_context_service_browsertest.cc
+++ b/chrome/browser/net/profile_network_context_service_browsertest.cc
@@ -957,7 +957,6 @@
       /*incognito=*/browser()->GetProfile()->IsIncognitoProfile());
   privacy_sandbox_settings->SetDelegateForTesting(
       std::move(privacy_sandbox_delegate));
-  privacy_sandbox_settings->SetAllPrivacySandboxAllowedForTesting();
   auto* host_content_settings_map =
       HostContentSettingsMapFactory::GetForProfile(browser()->GetProfile());
   Flush();
diff --git a/chrome/browser/on_device_translation/on_device_translation_browsertest.cc b/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
index 4e5fda94..ea5e32b 100644
--- a/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
+++ b/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
@@ -808,6 +808,33 @@
   WaitForConsoleObserver(*console_observer);
 }
 
+// Tests that a relative library path causes a crash/failure (reproducing the
+// bug/crash in PreLockdownSandboxHook on Windows).
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
+                       CreateTranslatorErrorLibraryPathRelative) {
+  MockComponentManager mock_component_manager(GetTempDir());
+  EXPECT_CALL(mock_component_manager, RegisterTranslateKitComponentImpl())
+      .Times(1);
+
+  // Set the TranslateKit binary path to a relative path.
+  g_browser_process->local_state()->SetFilePath(
+      prefs::kTranslateKitBinaryPath,
+      base::FilePath(FILE_PATH_LITERAL("relative/path/to/fakefile")));
+
+  mock_component_manager.ExpectCallRegisterLanguagePackComponentAndInstall(
+      {LanguagePackKey::kEn_Ja});
+  NavigateToEmptyPage();
+
+  EXPECT_EQ(EvalJsCatchingError(R"(
+            const translator = await Translator.create({
+              sourceLanguage: 'en',
+              targetLanguage: 'ja',
+            });
+      )"),
+            "NotSupportedError: Unable to create translator for the given "
+            "source and target language.");
+}
+
 // Tests the behavior of failing to load the library as a result of the
 // incompatibility of the library.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
diff --git a/chrome/browser/page_image_service/android/junit/src/org/chromium/chrome/browser/page_image_service/ImageServiceBridgeUnitTest.java b/chrome/browser/page_image_service/android/junit/src/org/chromium/chrome/browser/page_image_service/ImageServiceBridgeUnitTest.java
index 62f1cdd..117248b 100644
--- a/chrome/browser/page_image_service/android/junit/src/org/chromium/chrome/browser/page_image_service/ImageServiceBridgeUnitTest.java
+++ b/chrome/browser/page_image_service/android/junit/src/org/chromium/chrome/browser/page_image_service/ImageServiceBridgeUnitTest.java
@@ -30,7 +30,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.page_image_service.ImageServiceMetrics.SalientImageUrlFetchResult;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -40,7 +39,6 @@
 import org.chromium.url.JUnitTestGURLs;
 
 /** Unit tests for {@link ImageServiceBridge}. */
-@Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public final class ImageServiceBridgeUnitTest {
diff --git a/chrome/browser/partnercustomizations/junit/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizationsUnitTest.java b/chrome/browser/partnercustomizations/junit/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizationsUnitTest.java
index d462874..c0985a20 100644
--- a/chrome/browser/partnercustomizations/junit/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizationsUnitTest.java
+++ b/chrome/browser/partnercustomizations/junit/src/org/chromium/chrome/browser/partnercustomizations/PartnerBrowserCustomizationsUnitTest.java
@@ -14,7 +14,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
@@ -24,7 +23,6 @@
 
 /** Unit tests for {@link PartnerBrowserCustomizations}. */
 @RunWith(BaseJUnit4ClassRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class PartnerBrowserCustomizationsUnitTest {
     private static final String TEST_HOMEPAGE = "http://example.com/";
 
diff --git a/chrome/browser/password_manager/android/add_username_dialog/java/src/org/chromium/chrome/browser/add_username_dialog/AddUsernameDialogBridgeTest.java b/chrome/browser/password_manager/android/add_username_dialog/java/src/org/chromium/chrome/browser/add_username_dialog/AddUsernameDialogBridgeTest.java
index 75cec0a7..1b7d58b7 100644
--- a/chrome/browser/password_manager/android/add_username_dialog/java/src/org/chromium/chrome/browser/add_username_dialog/AddUsernameDialogBridgeTest.java
+++ b/chrome/browser/password_manager/android/add_username_dialog/java/src/org/chromium/chrome/browser/add_username_dialog/AddUsernameDialogBridgeTest.java
@@ -24,7 +24,6 @@
 import org.robolectric.android.controller.ActivityController;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.test.util.modaldialog.FakeModalDialogManager;
 
@@ -32,7 +31,6 @@
 
 /** Tests for {@link AddUsernameDialogBridge} */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.PER_CLASS)
 public class AddUsernameDialogBridgeTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
 
diff --git a/chrome/browser/password_manager/android/add_username_dialog/java/src/org/chromium/chrome/browser/add_username_dialog/AddUsernameDialogModuleTest.java b/chrome/browser/password_manager/android/add_username_dialog/java/src/org/chromium/chrome/browser/add_username_dialog/AddUsernameDialogModuleTest.java
index ed5154d..859be57 100644
--- a/chrome/browser/password_manager/android/add_username_dialog/java/src/org/chromium/chrome/browser/add_username_dialog/AddUsernameDialogModuleTest.java
+++ b/chrome/browser/password_manager/android/add_username_dialog/java/src/org/chromium/chrome/browser/add_username_dialog/AddUsernameDialogModuleTest.java
@@ -28,7 +28,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
 import org.chromium.ui.modaldialog.ModalDialogProperties.ButtonType;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -36,7 +35,6 @@
 
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class AddUsernameDialogModuleTest {
     private AddUsernameDialogController mController;
     private final FakeModalDialogManager mModalDialogManager = new FakeModalDialogManager(0);
diff --git a/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetCoordinatorRobolectricTest.java b/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetCoordinatorRobolectricTest.java
index 36e33b2..798d92c 100644
--- a/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetCoordinatorRobolectricTest.java
+++ b/chrome/browser/password_manager/android/bottom_sheet/java/src/org/chromium/chrome/browser/bottom_sheet/SimpleNoticeSheetCoordinatorRobolectricTest.java
@@ -29,7 +29,6 @@
 import org.chromium.base.CallbackUtils;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
@@ -41,7 +40,6 @@
 /** Tests for {@link TouchToFillPasswordGenerationBridge} */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class SimpleNoticeSheetCoordinatorRobolectricTest {
     private SimpleNoticeSheetCoordinator mCoordinator;
diff --git a/chrome/browser/password_manager/android/grouped_affiliations/java/src/org/chromium/chrome/browser/grouped_affiliations/AcknowledgeGroupedCredentialSheetModuleTest.java b/chrome/browser/password_manager/android/grouped_affiliations/java/src/org/chromium/chrome/browser/grouped_affiliations/AcknowledgeGroupedCredentialSheetModuleTest.java
index 965a6e8..d88b8dc 100644
--- a/chrome/browser/password_manager/android/grouped_affiliations/java/src/org/chromium/chrome/browser/grouped_affiliations/AcknowledgeGroupedCredentialSheetModuleTest.java
+++ b/chrome/browser/password_manager/android/grouped_affiliations/java/src/org/chromium/chrome/browser/grouped_affiliations/AcknowledgeGroupedCredentialSheetModuleTest.java
@@ -25,7 +25,6 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerFactory;
@@ -36,7 +35,6 @@
 /** Tests for {@link AcknowledgeGroupedCredentialSheetController} */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class AcknowledgeGroupedCredentialSheetModuleTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
     @Mock private AcknowledgeGroupedCredentialSheetBridge.Natives mBridgeJniMock;
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakeCredentialManagerLauncherTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakeCredentialManagerLauncherTest.java
index 07bd17d..8e97c11 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakeCredentialManagerLauncherTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakeCredentialManagerLauncherTest.java
@@ -24,7 +24,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.PayloadCallbackHelper;
 import org.chromium.chrome.browser.password_manager.CredentialManagerLauncher.CredentialManagerBackendException;
 import org.chromium.chrome.browser.password_manager.CredentialManagerLauncher.CredentialManagerError;
@@ -32,7 +31,6 @@
 /** Tests for {@link FakeCredentialManagerLauncher}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 public class FakeCredentialManagerLauncherTest {
     private static final String TEST_EMAIL_ADDRESS = "test@email.com";
     private FakeCredentialManagerLauncher mFakeLauncher;
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakePasswordManagerBackendSupportHelperTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakePasswordManagerBackendSupportHelperTest.java
index fadb965..2eb492e 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakePasswordManagerBackendSupportHelperTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakePasswordManagerBackendSupportHelperTest.java
@@ -12,11 +12,9 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 
 /** Tests for {@link FakePasswordManagerBackendSupportHelper}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class FakePasswordManagerBackendSupportHelperTest {
     private FakePasswordManagerBackendSupportHelper mFakeHelper;
 
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakePasswordSettingsAccessorFactoryImplTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakePasswordSettingsAccessorFactoryImplTest.java
index e77d3aa27..9276386 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakePasswordSettingsAccessorFactoryImplTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/FakePasswordSettingsAccessorFactoryImplTest.java
@@ -12,12 +12,10 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 
 /** Tests for the methods of {@link FakePasswordSettingsAccessorFactoryImpl}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class FakePasswordSettingsAccessorFactoryImplTest {
     FakePasswordSettingsAccessorFactoryImpl mFakePasswordSettingsAccessorFactoryImpl;
 
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/LoginDbDeprecationUtilBridgeTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/LoginDbDeprecationUtilBridgeTest.java
index 35c0d323..2477def 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/LoginDbDeprecationUtilBridgeTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/LoginDbDeprecationUtilBridgeTest.java
@@ -17,7 +17,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.profiles.Profile;
 
 import java.io.File;
@@ -26,7 +25,6 @@
 /** Unit tests for the {@link LoginDbDeprecationUtilBridge}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class LoginDbDeprecationUtilBridgeTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerAndroidBackendUtilTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerAndroidBackendUtilTest.java
index 90a845e..e77b288 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerAndroidBackendUtilTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerAndroidBackendUtilTest.java
@@ -23,7 +23,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.password_manager.PasswordStoreAndroidBackend.BackendException;
 
 /**
@@ -32,7 +31,6 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class PasswordManagerAndroidBackendUtilTest {
 
     @Test
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerCheckupHelperTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerCheckupHelperTest.java
index eb8c204..96f190f 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerCheckupHelperTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerCheckupHelperTest.java
@@ -49,7 +49,6 @@
 import org.chromium.base.supplier.SettableNonNullObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.RobolectricUtil;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.R;
@@ -79,7 +78,6 @@
 @Config(
         manifest = Config.NONE,
         shadows = {ShadowSystemClock.class})
-@Batch(Batch.PER_CLASS)
 public class PasswordManagerCheckupHelperTest {
     private static final String TEST_EMAIL_ADDRESS = "test@email.com";
     private static final String TEST_NO_EMAIL_ADDRESS = null;
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
index 95914d2..ddfa2b6b 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
@@ -46,7 +46,6 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.RobolectricUtil;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.loading_modal.LoadingModalDialogCoordinator;
 import org.chromium.chrome.browser.password_manager.CredentialManagerLauncher.CredentialManagerBackendException;
@@ -81,7 +80,6 @@
 @Config(
         manifest = Config.NONE,
         shadows = {ShadowSystemClock.class})
-@Batch(Batch.PER_CLASS)
 public class PasswordManagerHelperTest {
     private static final String TEST_EMAIL_ADDRESS = "test@email.com";
     private static final String TEST_NO_EMAIL_ADDRESS = null;
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerLifecycleHelperTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerLifecycleHelperTest.java
index 09e95bb..bd6a3eb 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerLifecycleHelperTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerLifecycleHelperTest.java
@@ -19,12 +19,10 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 
 /** Test class for {@link PasswordManagerLifecycleHelper}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class PasswordManagerLifecycleHelperTest {
     private static final long sFakeNativePointer = 96024;
 
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterDispatcherBridgeTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterDispatcherBridgeTest.java
index bdf6cda..ca5fdfd 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterDispatcherBridgeTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterDispatcherBridgeTest.java
@@ -25,7 +25,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.signin.AccountUtils;
 import org.chromium.ui.test.util.MockitoHelper;
 
@@ -34,7 +33,6 @@
 /** Tests that bridge calls invoked by the settings updater call the accessor. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class PasswordSettingsUpdaterDispatcherBridgeTest {
 
     private static final String sTestAccountEmail = "test@email.com";
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterReceiverBridgeTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterReceiverBridgeTest.java
index e276b77b..631a2690 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterReceiverBridgeTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSettingsUpdaterReceiverBridgeTest.java
@@ -25,12 +25,10 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 
 /** Tests that settings updater callbacks invoke the right native callbacks. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class PasswordSettingsUpdaterReceiverBridgeTest {
 
     private static final long sFakeNativePointer = 7;
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendDispatcherBridgeTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendDispatcherBridgeTest.java
index 265749b..5137992 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendDispatcherBridgeTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendDispatcherBridgeTest.java
@@ -32,7 +32,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.password_manager.core.browser.proto.ListAffiliatedPasswordsResult;
 import org.chromium.components.password_manager.core.browser.proto.ListAffiliatedPasswordsResult.AffiliatedPassword;
 import org.chromium.components.password_manager.core.browser.proto.ListPasswordsResult;
@@ -48,7 +47,6 @@
 /** Tests that dispatcher bridge calls as invoked by the password store reach the backend. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class PasswordStoreAndroidBackendDispatcherBridgeTest {
 
     private static final PasswordSpecificsData.Builder sTestProfile =
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendReceiverBridgeTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendReceiverBridgeTest.java
index d755ef3..8f64450 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendReceiverBridgeTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendReceiverBridgeTest.java
@@ -21,7 +21,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.password_manager.core.browser.proto.ListAffiliatedPasswordsResult;
 import org.chromium.components.password_manager.core.browser.proto.ListAffiliatedPasswordsResult.AffiliatedPassword;
 import org.chromium.components.password_manager.core.browser.proto.ListPasswordsResult;
@@ -33,7 +32,6 @@
 /** Tests that backend consumer bridge calls for operation callbacks reach native backend. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class PasswordStoreAndroidBackendReceiverBridgeTest {
 
     private static final PasswordSpecificsData.Builder sTestProfile =
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSyncControllerDelegateBridgeTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSyncControllerDelegateBridgeTest.java
index 3b257c87..91ca7b9 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSyncControllerDelegateBridgeTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordSyncControllerDelegateBridgeTest.java
@@ -25,7 +25,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.ui.test.util.MockitoHelper;
 
 /**
@@ -34,7 +33,6 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class PasswordSyncControllerDelegateBridgeTest {
 
     private static final long sFakeNativePointer = 4;
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/tests/utils/FakePasswordCheckupClientHelperTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/tests/utils/FakePasswordCheckupClientHelperTest.java
index c73f58a..a3e03776 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/tests/utils/FakePasswordCheckupClientHelperTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/tests/utils/FakePasswordCheckupClientHelperTest.java
@@ -21,7 +21,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.PayloadCallbackHelper;
 import org.chromium.chrome.browser.password_manager.FakePasswordCheckupClientHelper;
 import org.chromium.chrome.browser.password_manager.PasswordCheckReferrer;
@@ -30,7 +29,6 @@
 /** Tests for {@link FakePasswordCheckupClientHelper}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 public class FakePasswordCheckupClientHelperTest {
     private static final String TEST_ACCOUNT = "test@example.com";
     private FakePasswordCheckupClientHelper mFakeHelper;
diff --git a/chrome/browser/password_manager/android/pwm_disabled/java/src/org/chromium/chrome/browser/pwm_disabled/PasswordCsvDownloadDialogTest.java b/chrome/browser/password_manager/android/pwm_disabled/java/src/org/chromium/chrome/browser/pwm_disabled/PasswordCsvDownloadDialogTest.java
index fc755a1..81a5c09 100644
--- a/chrome/browser/password_manager/android/pwm_disabled/java/src/org/chromium/chrome/browser/pwm_disabled/PasswordCsvDownloadDialogTest.java
+++ b/chrome/browser/password_manager/android/pwm_disabled/java/src/org/chromium/chrome/browser/pwm_disabled/PasswordCsvDownloadDialogTest.java
@@ -36,7 +36,6 @@
 import org.robolectric.shadows.ShadowDialog;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.browser_ui.settings.SettingsCustomTabLauncher;
 import org.chromium.components.browser_ui.test.BrowserUiTestFragmentActivity;
 import org.chromium.ui.text.ChromeClickableSpan;
@@ -48,7 +47,6 @@
 /** Tests for {@link PasswordsCsvDownloadDialogController} */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class PasswordCsvDownloadDialogTest {
     private static final Uri SAVED_EXPORT_FILE_URI = Uri.parse("fake/test/path/file.ext");
 
diff --git a/chrome/browser/password_manager/android/pwm_disabled/java/src/org/chromium/chrome/browser/pwm_disabled/PasswordManagerUnavailableDialogTest.java b/chrome/browser/password_manager/android/pwm_disabled/java/src/org/chromium/chrome/browser/pwm_disabled/PasswordManagerUnavailableDialogTest.java
index a5e73777..4593e2f4 100644
--- a/chrome/browser/password_manager/android/pwm_disabled/java/src/org/chromium/chrome/browser/pwm_disabled/PasswordManagerUnavailableDialogTest.java
+++ b/chrome/browser/password_manager/android/pwm_disabled/java/src/org/chromium/chrome/browser/pwm_disabled/PasswordManagerUnavailableDialogTest.java
@@ -25,7 +25,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 import org.chromium.ui.modaldialog.ModalDialogManager;
@@ -36,7 +35,6 @@
 /** Tests for {@link PasswordManagerUnavailableDialogCoordinator} */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class PasswordManagerUnavailableDialogTest {
 
     private final PasswordManagerUnavailableDialogCoordinator mCoordinator =
diff --git a/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/WindowAndroidIntentLauncherUnitTest.java b/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/WindowAndroidIntentLauncherUnitTest.java
index 8aba2de..d953b31 100644
--- a/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/WindowAndroidIntentLauncherUnitTest.java
+++ b/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/WindowAndroidIntentLauncherUnitTest.java
@@ -24,7 +24,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.R;
 import org.chromium.components.payments.PaymentAppError;
 import org.chromium.content_public.browser.WebContents;
@@ -34,7 +33,6 @@
 
 /** Tests for the Android intent-based payment app launcher. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class WindowAndroidIntentLauncherUnitTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/preferences/BUILD.gn b/chrome/browser/preferences/BUILD.gn
index 100ea30..56493df 100644
--- a/chrome/browser/preferences/BUILD.gn
+++ b/chrome/browser/preferences/BUILD.gn
@@ -67,6 +67,7 @@
     "//components/feed/core/shared_prefs/pref_names.cc",
     "//components/history/core/common/pref_names.cc",
     "//components/language/core/browser/pref_names.h",
+    "//components/metrics/metrics_profile_pref_names.h",
     "//components/ntp_tiles/pref_names.h",
     "//components/omnibox/browser/omnibox_pref_names.h",
     "//components/optimization_guide/core/feature_registry/feature_registration.cc",
@@ -80,6 +81,7 @@
     "//components/signin/public/base/signin_pref_names.cc",
     "//components/supervised_user/core/common/pref_names.h",
     "//components/translate/core/browser/translate_pref_names.h",
+    "//components/unified_consent/pref_names.cc",
   ]
 
   class_name = "org.chromium.chrome.browser.preferences.Pref"
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 28a0551..7c1fd52 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -944,6 +944,12 @@
     "autofill.wallet_import_enabled";
 constexpr char kObsoleteAutofillWalletImportEnabledMigrated[] =
     "sync.autofill_wallet_import_enabled_migrated";
+constexpr char kPrivacySandboxTopicsDataAccessibleSince[] =
+    "privacy_sandbox.topics_data_accessible_since";
+constexpr char kPrivacySandboxBlockedTopics[] =
+    "privacy_sandbox.blocked_topics";
+constexpr char kPrivacySandboxFledgeJoinBlocked[] =
+    "privacy_sandbox.fledge_join_blocked";
 
 #if BUILDFLAG(IS_CHROMEOS)
 // Deprecated 07/2026.
@@ -1296,6 +1302,10 @@
 
   // Deprecated 08/2026.
   registry->RegisterDictionaryPref(kPrivacySandboxNotices);
+  registry->RegisterTimePref(kPrivacySandboxTopicsDataAccessibleSince,
+                             base::Time());
+  registry->RegisterListPref(kPrivacySandboxBlockedTopics);
+  registry->RegisterDictionaryPref(kPrivacySandboxFledgeJoinBlocked);
 
 #if BUILDFLAG(IS_CHROMEOS)
   // Deprecated 07/2026.
@@ -2625,6 +2635,9 @@
   // Added 08/2026.
   profile_prefs->ClearPref(kObsoleteAutofillWalletImportEnabled);
   profile_prefs->ClearPref(kObsoleteAutofillWalletImportEnabledMigrated);
+  profile_prefs->ClearPref(kPrivacySandboxTopicsDataAccessibleSince);
+  profile_prefs->ClearPref(kPrivacySandboxBlockedTopics);
+  profile_prefs->ClearPref(kPrivacySandboxFledgeJoinBlocked);
 
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_PROFILE_PREFS
diff --git a/chrome/browser/privacy_sandbox/BUILD.gn b/chrome/browser/privacy_sandbox/BUILD.gn
index d742c574..b3e1765 100644
--- a/chrome/browser/privacy_sandbox/BUILD.gn
+++ b/chrome/browser/privacy_sandbox/BUILD.gn
@@ -96,34 +96,10 @@
 }
 
 if (!is_android) {
-  source_set("attestations_browser_test") {
-    testonly = true
-    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
-    sources = [ "privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_browsertest.cc" ]
-    deps = [
-      "//base/test:test_support",
-      "//chrome/browser:browser_process",
-      "//chrome/browser/component_updater",
-      "//chrome/browser/component_updater:test_support",
-      "//chrome/test:test_support",
-      "//chrome/test:test_support_ui",
-      "//components/component_updater:component_updater_paths",
-      "//components/privacy_sandbox:features",
-      "//components/privacy_sandbox:test_support",
-      "//components/privacy_sandbox/privacy_sandbox_attestations",
-      "//components/privacy_sandbox/privacy_sandbox_attestations:metrics",
-      "//components/privacy_sandbox/privacy_sandbox_attestations/proto",
-      "//content/test:test_support",
-    ]
-  }
-
   source_set("settings_browser_test") {
     testonly = true
     defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
-    sources = [
-      "privacy_sandbox_ad_privacy_deprecation_browsertest.cc",
-      "privacy_sandbox_settings_browsertest.cc",
-    ]
+    sources = [ "privacy_sandbox_ad_privacy_deprecation_browsertest.cc" ]
     deps = [
       "//base/test:test_support",
       "//chrome/browser/browsing_data:constants",
@@ -143,39 +119,15 @@
     ]
   }
 } else {
-  source_set("attestations_component_installer_android_browsertest") {
-    testonly = true
-    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
-    sources = [ "privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_android_browsertest.cc" ]
-    deps = [
-      "//base/test:test_support",
-      "//chrome/browser:browser_process",
-      "//chrome/browser/component_updater",
-      "//chrome/browser/component_updater:test_support",
-      "//chrome/test:test_support",
-      "//chrome/test:test_support_ui_android",
-      "//components/component_updater:component_updater_paths",
-      "//components/privacy_sandbox:features",
-      "//components/privacy_sandbox:test_support",
-      "//components/privacy_sandbox/privacy_sandbox_attestations",
-      "//components/privacy_sandbox/privacy_sandbox_attestations:metrics",
-      "//components/privacy_sandbox/privacy_sandbox_attestations/preload",
-      "//components/privacy_sandbox/privacy_sandbox_attestations/proto",
-      "//content/test:test_support",
-    ]
-  }
 }
 
 group("browser_tests") {
   testonly = true
 
   if (!is_android) {
-    deps = [
-      ":attestations_browser_test",
-      ":settings_browser_test",
-    ]
+    deps = [ ":settings_browser_test" ]
   } else {
-    deps = [ ":attestations_component_installer_android_browsertest" ]
+    deps = []
   }
 }
 
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_android_browsertest.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_android_browsertest.cc
deleted file mode 100644
index 5222090d..0000000
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_android_browsertest.cc
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/component_updater/privacy_sandbox_attestations_component_installer.h"
-
-#include <optional>
-#include <string>
-#include <utility>
-
-#include "base/android/apk_assets.h"
-#include "base/containers/span.h"
-#include "base/files/file.h"
-#include "base/files/memory_mapped_file.h"
-#include "base/run_loop.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/scoped_path_override.h"
-#include "base/threading/thread_restrictions.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/component_updater/privacy_sandbox_attestations_component_installer_test_util.h"
-#include "chrome/test/base/android/android_browser_test.h"
-#include "components/component_updater/component_updater_paths.h"
-#include "components/privacy_sandbox/privacy_sandbox_attestations/preload/android_apk_assets.h"
-#include "components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.h"
-#include "components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_histograms.h"
-#include "components/privacy_sandbox/privacy_sandbox_attestations/proto/privacy_sandbox_attestations.pb.h"
-#include "components/privacy_sandbox/privacy_sandbox_features.h"
-#include "components/privacy_sandbox/privacy_sandbox_test_util.h"
-#include "content/public/test/browser_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace privacy_sandbox {
-
-class PrivacySandboxAttestationsAPKAssetAndroidBrowserTest
-    : public AndroidBrowserTest {
- public:
-  PrivacySandboxAttestationsAPKAssetAndroidBrowserTest() = default;
-  ~PrivacySandboxAttestationsAPKAssetAndroidBrowserTest() override = default;
-};
-
-// Check that attestations list exists in Android APK assets. The content of
-// the pre-installed attestations list is the same as those delivered via
-// component updater. For tests covering the parsing of attestations list, see:
-// components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_parser_unittest.cc.
-IN_PROC_BROWSER_TEST_F(PrivacySandboxAttestationsAPKAssetAndroidBrowserTest,
-                       APKAssetBundledAttestationsList) {
-  // base::MemoryMappedFile requires blocking.
-  base::ScopedAllowBlockingForTesting scoped_allow_blocking;
-
-  // Opening the attestations list from APK assets.
-  base::MemoryMappedFile::Region region =
-      base::MemoryMappedFile::Region::kWholeFile;
-  int descriptor = base::android::OpenApkAsset(
-      std::string(kAttestationsListAssetPath), &region);
-  ASSERT_NE(descriptor, -1);
-}
-
-class LoadFromAPKAssetAndroidBrowserTest
-    : public PrivacySandboxAttestationsAPKAssetAndroidBrowserTest {
- public:
-  LoadFromAPKAssetAndroidBrowserTest() = default;
-  ~LoadFromAPKAssetAndroidBrowserTest() override = default;
-
-  const base::HistogramTester& histogram_tester() const {
-    return histogram_tester_;
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_{
-      kPrivacySandboxAttestationsLoadFromAPKAsset};
-
-  // Override the user component directories that may have the downloaded
-  // attestation file.
-  base::ScopedPathOverride user_dir_override_{
-      component_updater::DIR_COMPONENT_USER};
-
-  base::HistogramTester histogram_tester_;
-};
-
-// If there is available attestations component in user component directory, the
-// pre-installed one in APK assets is not loaded.
-IN_PROC_BROWSER_TEST_F(
-    LoadFromAPKAssetAndroidBrowserTest,
-    DoNotLoadAttestationsFromAPKAssetIfUserComponentAvailable) {
-  PrivacySandboxAttestationsProto proto;
-
-  // Create an attestations file that contains the site with attestation for
-  // Topics API.
-  PrivacySandboxAttestationsProto::PrivacySandboxAttestedAPIsProto
-      site_attestation;
-  site_attestation.add_attested_apis(TOPICS);
-  std::string site{"https://example.com"};
-  (*proto.mutable_site_attestations())[site] = site_attestation;
-
-  // Install an attestations component in user component directory.
-  base::Version version("12345.0.0.0");
-  ASSERT_TRUE(
-      component_updater::InstallPrivacySandboxAttestationsComponentForTesting(
-          proto, version, /*is_pre_installed=*/false));
-
-  base::RunLoop run_loop;
-  PrivacySandboxAttestations::GetInstance()
-      ->SetLoadAttestationsDoneCallbackForTesting(run_loop.QuitClosure());
-
-  // Register the privacy sandbox attestations component, which should detect
-  // the existing attestations file on disk and start parsing.
-  RegisterPrivacySandboxAttestationsComponent(
-      g_browser_process->component_updater());
-
-  // Wait until the attestations parsing is done.
-  run_loop.Run();
-
-  // Load from attestations component in user component directory succeeds.
-  ASSERT_TRUE(PrivacySandboxAttestations::GetInstance()
-                  ->GetVersionForTesting()
-                  .IsValid());
-  EXPECT_FALSE(PrivacySandboxAttestations::GetInstance()->is_pre_installed());
-  EXPECT_EQ(PrivacySandboxAttestations::GetInstance()->GetVersionForTesting(),
-            version);
-
-  // The parsing status histogram should record a successful parsing.
-  histogram_tester().ExpectTotalCount(kAttestationsFileParsingStatusUMA, 1);
-  histogram_tester().ExpectBucketCount(kAttestationsFileParsingStatusUMA,
-                                       ParsingStatus::kSuccess, 1);
-
-  // The attestations component in APK assets should not be loaded.
-  histogram_tester().ExpectTotalCount(kAttestationsLoadAPKAssetStatusUMA, 0);
-
-  // Make an attestation check to verify the file source is not pre-installed.
-  EXPECT_TRUE(
-      privacy_sandbox_test_util::PrivacySandboxSettingsTestPeer::IsAllowed(
-          PrivacySandboxAttestations::GetInstance()->IsSiteAttested(
-              net::SchemefulSite(GURL(site)),
-              PrivacySandboxAttestationsGatedAPI::kTopics)));
-
-  histogram_tester().ExpectTotalCount(kAttestationsFileSource, 1);
-  histogram_tester().ExpectBucketCount(kAttestationsFileSource,
-                                       FileSource::kDownloaded, 1);
-}
-
-// Verify that the pre-installed attestations component in Android APK assets
-// is loaded to populate the in-memory attestations map when there is no
-// attestations list available in user component directory.
-IN_PROC_BROWSER_TEST_F(LoadFromAPKAssetAndroidBrowserTest,
-                       LoadAttestationsFromAPKAsset) {
-  base::RunLoop run_loop;
-  PrivacySandboxAttestations::GetInstance()
-      ->SetLoadAttestationsDoneCallbackForTesting(run_loop.QuitClosure());
-
-  // Register the privacy sandbox attestations component. There is no available
-  // attestations list in user component directory. The pre-installed
-  // attestations component in Android APK assets is loaded.
-  RegisterPrivacySandboxAttestationsComponent(
-      g_browser_process->component_updater());
-
-  // Wait until the attestations parsing is done.
-  run_loop.Run();
-
-  EXPECT_TRUE(PrivacySandboxAttestations::GetInstance()
-                  ->GetVersionForTesting()
-                  .IsValid());
-  ASSERT_EQ(PrivacySandboxAttestations::GetInstance()->GetVersionForTesting(),
-            base::Version(kAttestationsListAssetVersion));
-  EXPECT_TRUE(PrivacySandboxAttestations::GetInstance()->is_pre_installed());
-
-  // The parsing status histogram should record a successful parsing.
-  histogram_tester().ExpectTotalCount(kAttestationsFileParsingStatusUMA, 1);
-  histogram_tester().ExpectBucketCount(kAttestationsFileParsingStatusUMA,
-                                       ParsingStatus::kSuccess, 1);
-
-  // The loading from APK asset histogram should also record a successful load.
-  histogram_tester().ExpectTotalCount(kAttestationsLoadAPKAssetStatusUMA, 1);
-  histogram_tester().ExpectBucketCount(kAttestationsLoadAPKAssetStatusUMA,
-                                       LoadAPKAssetStatus::kSuccess, 1);
-
-  // Make an attestation check to verify the file source is pre-installed.
-  PrivacySandboxAttestations::GetInstance()->IsSiteAttested(
-      net::SchemefulSite(GURL("https://example.com")),
-      PrivacySandboxAttestationsGatedAPI::kTopics);
-  histogram_tester().ExpectTotalCount(kAttestationsFileSource, 1);
-  histogram_tester().ExpectBucketCount(kAttestationsFileSource,
-                                       FileSource::kPreInstalled, 1);
-}
-
-}  // namespace privacy_sandbox
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_browsertest.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_browsertest.cc
deleted file mode 100644
index 4fae3e3..0000000
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_browsertest.cc
+++ /dev/null
@@ -1,271 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/component_updater/privacy_sandbox_attestations_component_installer.h"
-
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/path_service.h"
-#include "base/run_loop.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/run_until.h"
-#include "base/test/scoped_path_override.h"
-#include "base/test/with_feature_override.h"
-#include "base/version.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/component_updater/privacy_sandbox_attestations_component_installer_test_util.h"
-#include "chrome/browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_mixin.h"
-#include "chrome/test/base/mixin_based_in_process_browser_test.h"
-#include "components/component_updater/component_updater_paths.h"
-#include "components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.h"
-#include "components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_histograms.h"
-#include "components/privacy_sandbox/privacy_sandbox_attestations/proto/privacy_sandbox_attestations.pb.h"
-#include "components/privacy_sandbox/privacy_sandbox_features.h"
-#include "components/privacy_sandbox/privacy_sandbox_test_util.h"
-#include "content/public/test/browser_test.h"
-
-namespace privacy_sandbox {
-
-using ::privacy_sandbox_test_util::PrivacySandboxSettingsTestPeer;
-using Status = PrivacySandboxSettingsTestPeer::Status;
-
-class PrivacySandboxAttestationsBrowserTestBase
-    : public MixinBasedInProcessBrowserTest {
- public:
-  PrivacySandboxAttestationsBrowserTestBase() = default;
-
-  ~PrivacySandboxAttestationsBrowserTestBase() override = default;
-
-  const base::HistogramTester& histogram_tester() const {
-    return histogram_tester_;
-  }
-
- private:
-  PrivacySandboxAttestationsMixin privacy_sandbox_attestations_mixin_{
-      &mixin_host_};
-
-  // Override the user component directory that may have the downloaded
-  // attestation file.
-  base::ScopedPathOverride user_dir_override_{
-      component_updater::DIR_COMPONENT_USER};
-
-  base::HistogramTester histogram_tester_;
-};
-
-class PrivacySandboxAttestationsBrowserTest
-    : public PrivacySandboxAttestationsBrowserTestBase {
- public:
-  PrivacySandboxAttestationsBrowserTest() = default;
-
-  ~PrivacySandboxAttestationsBrowserTest() override = default;
-
- private:
-  // Override the pre-install component directory that may have the
-  // pre-installed attestation file.
-  base::ScopedPathOverride preinstalled_dir_override_{
-      component_updater::DIR_COMPONENT_PREINSTALLED};
-};
-
-IN_PROC_BROWSER_TEST_F(
-    PrivacySandboxAttestationsBrowserTest,
-    CallComponentReadyWhenRegistrationFindsExistingComponent) {
-  PrivacySandboxAttestationsProto proto;
-
-  // Create an attestations file that contains the site with attestation for
-  // Topics API.
-  std::string site = "https://example.com";
-  PrivacySandboxAttestationsProto::PrivacySandboxAttestedAPIsProto
-      site_attestation;
-  site_attestation.add_attested_apis(TOPICS);
-  (*proto.mutable_site_attestations())[site] = site_attestation;
-
-  // There is an existing pre-installed attestations component that is shipped
-  // with Chromium. Choose a version number that is sure to be higher. This
-  // makes sure that the component installer will ignore the shipped
-  // pre-installed attestations component.
-  base::Version version("12345.0.0.0");
-
-  ASSERT_TRUE(
-      component_updater::InstallPrivacySandboxAttestationsComponentForTesting(
-          proto, version, /*is_pre_installed=*/false));
-
-  base::RunLoop run_loop;
-  PrivacySandboxAttestations::GetInstance()
-      ->SetLoadAttestationsDoneCallbackForTesting(run_loop.QuitClosure());
-
-  // Register the privacy sandbox attestations component, which should detect
-  // the existing attestations file on disk and start parsing.
-  RegisterPrivacySandboxAttestationsComponent(
-      g_browser_process->component_updater());
-
-  // Wait until the attestations parsing is done.
-  run_loop.Run();
-
-  ASSERT_TRUE(PrivacySandboxAttestations::GetInstance()
-                  ->GetVersionForTesting()
-                  .IsValid());
-  EXPECT_EQ(PrivacySandboxAttestations::GetInstance()->GetVersionForTesting(),
-            version);
-  EXPECT_TRUE(PrivacySandboxSettingsTestPeer::IsAllowed(
-      PrivacySandboxAttestations::GetInstance()->IsSiteAttested(
-          net::SchemefulSite(GURL(site)),
-          PrivacySandboxAttestationsGatedAPI::kTopics)));
-
-  histogram_tester().ExpectTotalCount(kAttestationsFileSource, 1);
-  histogram_tester().ExpectBucketCount(kAttestationsFileSource,
-                                       FileSource::kDownloaded, 1);
-}
-
-// Depending on whether the component installer has checked the attestations
-// file or not, the attestation check status should be recorded in different
-// histogram buckets.
-IN_PROC_BROWSER_TEST_F(PrivacySandboxAttestationsBrowserTest,
-                       DifferentHistogramAfterAttestationsFileCheck) {
-  std::string site = "https://example.com";
-  EXPECT_FALSE(PrivacySandboxSettingsTestPeer::IsAllowed(
-      PrivacySandboxAttestations::GetInstance()->IsSiteAttested(
-          net::SchemefulSite(GURL(site)),
-          PrivacySandboxAttestationsGatedAPI::kTopics)));
-
-  // The attestation component has not yet checked the attestations file.
-  histogram_tester().ExpectTotalCount(kAttestationStatusUMA, 1);
-  histogram_tester().ExpectBucketCount(
-      kAttestationStatusUMA, Status::kAttestationsFileNotYetChecked, 1);
-
-  base::RunLoop run_loop;
-  PrivacySandboxAttestations::GetInstance()
-      ->SetComponentRegistrationCallbackForTesting(run_loop.QuitClosure());
-
-  // Register the privacy sandbox attestations component.
-  RegisterPrivacySandboxAttestationsComponent(
-      g_browser_process->component_updater());
-
-  // Wait until the point where attestations component has checked the
-  // attestations file but could not find it on disk.
-  run_loop.Run();
-
-  // Check attestation again.
-  EXPECT_FALSE(PrivacySandboxSettingsTestPeer::IsAllowed(
-      PrivacySandboxAttestations::GetInstance()->IsSiteAttested(
-          net::SchemefulSite(GURL(site)),
-          PrivacySandboxAttestationsGatedAPI::kTopics)));
-
-  // It should record in a different histogram bucket because the file check has
-  // completed but no file was found.
-  histogram_tester().ExpectTotalCount(kAttestationStatusUMA, 2);
-  histogram_tester().ExpectBucketCount(kAttestationStatusUMA,
-                                       Status::kAttestationsFileNotPresent, 1);
-}
-
-// This test verifies there is a copy of pre-installed attestation list in the
-// pre-installed component directory.
-IN_PROC_BROWSER_TEST_F(PrivacySandboxAttestationsBrowserTestBase,
-                       PreinstalledAttestationListPresent) {
-  base::RunLoop run_loop;
-  PrivacySandboxAttestations::GetInstance()
-      ->SetLoadAttestationsDoneCallbackForTesting(run_loop.QuitClosure());
-
-  // Register the privacy sandbox attestations component, which should parse
-  // the pre-installed attestations file on disk if feature
-  // "PrivacySandboxAttestationsLoadPreInstalledComponent" is enabled. This
-  // pre-installed file is shipped with Chromium.
-  RegisterPrivacySandboxAttestationsComponent(
-      g_browser_process->component_updater());
-
-  // Wait until the attestations parsing is done.
-  run_loop.Run();
-
-  EXPECT_TRUE(PrivacySandboxAttestations::GetInstance()
-                  ->GetVersionForTesting()
-                  .IsValid());
-
-  // Make an attestation check to verify the data point is recorded to the
-  // correct histogram bucket.
-  PrivacySandboxAttestations::GetInstance()->IsSiteAttested(
-      net::SchemefulSite(GURL("https://example.com")),
-      PrivacySandboxAttestationsGatedAPI::kTopics);
-  histogram_tester().ExpectTotalCount(kAttestationsFileSource, 1);
-  histogram_tester().ExpectBucketCount(kAttestationsFileSource,
-                                       FileSource::kPreInstalled, 1);
-}
-
-class PrivacySandboxAttestationPreInstallInteractionWithDownloadTest
-    : public PrivacySandboxAttestationsBrowserTest {
- public:
-  PrivacySandboxAttestationPreInstallInteractionWithDownloadTest() = default;
-
-  ~PrivacySandboxAttestationPreInstallInteractionWithDownloadTest() override =
-      default;
-
- private:
-  base::ScopedPathOverride preinstalled_dir_override_{
-      component_updater::DIR_COMPONENT_PREINSTALLED};
-};
-
-// If both pre-installed and downloaded attestation lists are available and they
-// have the same version, the component installer will:
-// 1. if feature off, select the pre-installed attestation list, but not parse
-// it.
-// 2. if feature on, select and parse the pre-installed attestation list.
-IN_PROC_BROWSER_TEST_F(
-    PrivacySandboxAttestationPreInstallInteractionWithDownloadTest,
-    BothPreinstalledAndDownloadedAttestationsAvailable) {
-  // Override the pre-install component directories that have the pre-installed
-  // attestation file shipped with Chromium. A test pre-installed attestation
-  // file will be written into directory `DIR_COMPONENT_PREINSTALLED`.
-  PrivacySandboxAttestationsProto proto;
-
-  // Create an attestations file that contains the site with attestation for
-  // Topics API.
-  std::string site = "https://example.com";
-  PrivacySandboxAttestationsProto::PrivacySandboxAttestedAPIsProto
-      site_attestation;
-  site_attestation.add_attested_apis(TOPICS);
-  (*proto.mutable_site_attestations())[site] = site_attestation;
-
-  // There is an existing pre-installed attestations component that is shipped
-  // with Chromium. Choose a version number that is sure to be higher. This
-  // makes sure that the component installer will ignore the shipped
-  // pre-installed attestations component.
-  base::Version version("12345.0.0.0");
-
-  // Install the attestation component to both pre-install and download
-  // directories.
-  ASSERT_TRUE(
-      component_updater::InstallPrivacySandboxAttestationsComponentForTesting(
-          proto, version, /*is_pre_installed=*/false));
-  ASSERT_TRUE(
-      component_updater::InstallPrivacySandboxAttestationsComponentForTesting(
-          proto, version, /*is_pre_installed=*/true));
-
-  base::RunLoop run_loop;
-  PrivacySandboxAttestations::GetInstance()
-      ->SetLoadAttestationsDoneCallbackForTesting(run_loop.QuitClosure());
-
-  // Register the privacy sandbox attestations component, which searches for the
-  // attestation file and starts parsing.
-  RegisterPrivacySandboxAttestationsComponent(
-      g_browser_process->component_updater());
-
-  // Component installer selects the pre-installed attestation list. Wait until
-  // the attestations parsing is done.
-  run_loop.Run();
-
-  EXPECT_TRUE(PrivacySandboxAttestations::GetInstance()
-                  ->GetVersionForTesting()
-                  .IsValid());
-
-  // Make an attestation check to verify the data point is recorded to the
-  // correct histogram bucket.
-  ASSERT_TRUE(PrivacySandboxSettingsTestPeer::IsAllowed(
-      PrivacySandboxAttestations::GetInstance()->IsSiteAttested(
-          net::SchemefulSite(GURL("https://example.com")),
-          PrivacySandboxAttestationsGatedAPI::kTopics)));
-  histogram_tester().ExpectTotalCount(kAttestationsFileSource, 1);
-  histogram_tester().ExpectBucketCount(kAttestationsFileSource,
-                                       FileSource::kPreInstalled, 1);
-}
-
-}  // namespace privacy_sandbox
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_settings_browsertest.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_settings_browsertest.cc
deleted file mode 100644
index da1ba3ba..0000000
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_settings_browsertest.cc
+++ /dev/null
@@ -1,134 +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.
-
-#include "components/privacy_sandbox/privacy_sandbox_settings.h"
-
-#include <memory>
-#include <string>
-#include <utility>
-
-#include "base/command_line.h"
-#include "base/test/bind.h"
-#include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h"
-#include "chrome/browser/privacy_sandbox/privacy_sandbox_settings_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/mixin_based_in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/privacy_sandbox/privacy_sandbox_test_util.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/browsing_data_remover_test_util.h"
-#include "content/public/test/test_host_resolver.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/test/embedded_test_server/http_request.h"
-#include "net/test/embedded_test_server/http_response.h"
-
-class PrivacySandboxSettingsBrowserTest
-    : public MixinBasedInProcessBrowserTest {
- public:
-  void SetUpOnMainThread() override {
-    host_resolver()->AddRule("*", "127.0.0.1");
-    https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
-    https_server_.AddDefaultHandlers(GetChromeTestDataDir());
-
-    FinishSetUp();
-  }
-
-  // TODO(crbug.com/40285326): This fails with the field trial testing config.
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch("disable-field-trial-config");
-  }
-
-  // Virtual so that derived classes can delay starting the server and/or
-  // register different handlers.
-  virtual void FinishSetUp() {
-    https_server_.RegisterRequestHandler(
-        base::BindRepeating(&PrivacySandboxSettingsBrowserTest::HandleRequest,
-                            base::Unretained(this)));
-
-    content::SetupCrossSiteRedirector(&https_server_);
-    ASSERT_TRUE(https_server_.Start());
-  }
-
-  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
-      const net::test_server::HttpRequest& request) {
-    const GURL& url = request.GetURL();
-
-    if (url.GetPath() == "/clear_site_data_header_cookies") {
-      auto response = std::make_unique<net::test_server::BasicHttpResponse>();
-      response->AddCustomHeader("Clear-Site-Data", "\"cookies\"");
-      response->set_code(net::HTTP_OK);
-      response->set_content_type("text/html");
-      response->set_content(std::string());
-      return std::move(response);
-    }
-
-    // Use the default handler for unrelated requests.
-    return nullptr;
-  }
-
-  void ClearAllCookies() {
-    content::BrowsingDataRemover* remover =
-        browser()->GetProfile()->GetBrowsingDataRemover();
-    content::BrowsingDataRemoverCompletionObserver observer(remover);
-    remover->RemoveAndReply(
-        base::Time(), base::Time::Max(),
-        content::BrowsingDataRemover::DATA_TYPE_COOKIES,
-        content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB, &observer);
-    observer.BlockUntilCompletion();
-  }
-
-  privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings() {
-    return PrivacySandboxSettingsFactory::GetForProfile(
-        browser()->GetProfile());
-  }
-
-  content::WebContents* web_contents() {
-    return browser()->tab_strip_model()->GetActiveWebContents();
-  }
-
- protected:
-  net::EmbeddedTestServer https_server_{
-      net::test_server::EmbeddedTestServer::TYPE_HTTPS};
-};
-
-// Test that cookie clearings triggered by "Clear browsing data" will trigger
-// an update to topics-data-accessible-since and invoke the corresponding
-// observer method.
-IN_PROC_BROWSER_TEST_F(PrivacySandboxSettingsBrowserTest, ClearAllCookies) {
-  EXPECT_EQ(base::Time(),
-            privacy_sandbox_settings()->TopicsDataAccessibleSince());
-
-  privacy_sandbox_test_util::MockPrivacySandboxObserver observer;
-  privacy_sandbox_settings()->AddObserver(&observer);
-  EXPECT_CALL(observer, OnTopicsDataAccessibleSinceUpdated());
-
-  ClearAllCookies();
-
-  EXPECT_NE(base::Time(),
-            privacy_sandbox_settings()->TopicsDataAccessibleSince());
-}
-
-// Test that cookie clearings triggered by Clear-Site-Data header won't trigger
-// an update to topics-data-accessible-since or invoke the corresponding
-// observer method.
-IN_PROC_BROWSER_TEST_F(PrivacySandboxSettingsBrowserTest,
-                       ClearSiteDataCookies) {
-  EXPECT_EQ(base::Time(),
-            privacy_sandbox_settings()->TopicsDataAccessibleSince());
-
-  privacy_sandbox_test_util::MockPrivacySandboxObserver observer;
-  privacy_sandbox_settings()->AddObserver(&observer);
-  EXPECT_CALL(observer, OnTopicsDataAccessibleSinceUpdated()).Times(0);
-
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(),
-      https_server_.GetURL("a.test", "/clear_site_data_header_cookies")));
-
-  EXPECT_EQ(base::Time(),
-            privacy_sandbox_settings()->TopicsDataAccessibleSince());
-}
diff --git a/chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/ProfileKeyedMap.java b/chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/ProfileKeyedMap.java
index a343aba..1003d277 100644
--- a/chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/ProfileKeyedMap.java
+++ b/chrome/browser/profiles/android/java/src/org/chromium/chrome/browser/profiles/ProfileKeyedMap.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.profiles;
 
+import android.util.ArrayMap;
+
 import androidx.annotation.IntDef;
 
 import org.chromium.base.Callback;
@@ -14,7 +16,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
@@ -49,7 +50,9 @@
         int REDIRECTED_TO_ORIGINAL = 1;
     }
 
-    private final Map<Profile, T> mData = new HashMap<>();
+    // Initial capacity 2 covers the common profile combinations (regular and incognito/OTR)
+    // without incurring HashMap entry node allocations.
+    private final Map<Profile, T> mData = new ArrayMap<>(2);
     @ProfileSelection private final int mProfileSelection;
     private final @Nullable Callback<T> mDestroyAction;
 
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index 3704d410..d995fdb4 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -15,6 +15,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/observer_list.h"
 #include "base/path_service.h"
+#include "base/rand_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
@@ -621,6 +622,19 @@
   return weak_factory_.GetWeakPtr();
 }
 
+uint64_t Profile::GetLomProfileId() {
+  PrefService* prefs = GetPrefs();
+  CHECK(prefs);
+  uint64_t id = prefs->GetUint64(prefs::kLomProfileId);
+  if (id == 0u) {
+    while (id == 0u) {
+      id = base::RandUint64();
+    }
+    prefs->SetUint64(prefs::kLomProfileId, id);
+  }
+  return id;
+}
+
 std::string Profile::ToDebugString() const {
   std::ostringstream out;
   out << "(" << this << "):" << (IsRegularProfile() ? " regular" : "")
diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h
index 955b07af5..b678b30 100644
--- a/chrome/browser/profiles/profile.h
+++ b/chrome/browser/profiles/profile.h
@@ -448,6 +448,10 @@
     return 0 == accessibility_pause_level_;
   }
 
+  // Returns the LOM profile ID for this profile, generating one if it doesn't
+  // exist yet.
+  virtual uint64_t GetLomProfileId();
+
   // Returns whether the profile is new.  A profile is new if the browser has
   // not been shut down since the profile was created.
   virtual bool IsNewProfile() const = 0;
diff --git a/chrome/browser/profiles/profile_browsertest.cc b/chrome/browser/profiles/profile_browsertest.cc
index 55dc3721..cdad5bf 100644
--- a/chrome/browser/profiles/profile_browsertest.cc
+++ b/chrome/browser/profiles/profile_browsertest.cc
@@ -1090,4 +1090,14 @@
   EXPECT_EQ(2, entry->GetAiSubscriptionTier());
 }
 
+IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, LomProfileId) {
+  Profile* profile = browser()->GetProfile();
+  uint64_t lom_id1 = profile->GetLomProfileId();
+  EXPECT_NE(0u, lom_id1);
+
+  // Subsequent calls return the same ID.
+  uint64_t lom_id2 = profile->GetLomProfileId();
+  EXPECT_EQ(lom_id1, lom_id2);
+}
+
 #endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index f3ca43e..b42e7ab 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -454,6 +454,8 @@
   registry->RegisterBooleanPref(prefs::kPdfAnnotationsEnabled, true);
 #endif
   registry->RegisterIntegerPref(prefs::kEnterpriseBadgingTemporarySetting, 0);
+
+  registry->RegisterUint64Pref(prefs::kLomProfileId, 0u);
 }
 
 #if !BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteDialogDelegateUnitTest.java b/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteDialogDelegateUnitTest.java
index 8567364a..48f170d 100644
--- a/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteDialogDelegateUnitTest.java
+++ b/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteDialogDelegateUnitTest.java
@@ -32,7 +32,6 @@
 import org.chromium.base.Callback;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browsing_data.TimePeriod;
@@ -52,7 +51,6 @@
 
 /** Unit tests for Quick Delete dialog. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class QuickDeleteDialogDelegateUnitTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteMediatorTest.java b/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteMediatorTest.java
index 8fb58f0b..b3496f5 100644
--- a/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteMediatorTest.java
+++ b/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteMediatorTest.java
@@ -25,7 +25,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.browsing_data.TimePeriod;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
@@ -38,7 +37,6 @@
 /** Robolectric tests for {@link QuickDeleteMediator}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 public class QuickDeleteMediatorTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteMetricsDelegateTest.java b/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteMetricsDelegateTest.java
index 931da602..3cae92d 100644
--- a/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteMetricsDelegateTest.java
+++ b/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteMetricsDelegateTest.java
@@ -15,7 +15,6 @@
 import org.chromium.base.test.params.ParameterProvider;
 import org.chromium.base.test.params.ParameterSet;
 import org.chromium.base.test.params.ParameterizedRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.components.browsing_data.DeleteBrowsingDataAction;
 
@@ -25,7 +24,6 @@
 /** JUnit tests of the class {@link QuickDeleteMetricsDelegate}. */
 @RunWith(ParameterizedRunner.class)
 @UseRunnerDelegate(BlockJUnit4RunnerDelegate.class)
-@Batch(Batch.PER_CLASS)
 public class QuickDeleteMetricsDelegateTest {
     /**
      * Class to parameterize the params for {@link
diff --git a/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteViewBinderTest.java b/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteViewBinderTest.java
index 4f95ade..795f08e 100644
--- a/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteViewBinderTest.java
+++ b/chrome/browser/quick_delete/android/junit/src/org/chromium/chrome/browser/quick_delete/QuickDeleteViewBinderTest.java
@@ -22,7 +22,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browsing_data.TimePeriod;
 import org.chromium.ui.base.TestActivity;
@@ -33,7 +32,6 @@
 /** Robolectric tests for {@link QuickDeleteViewBinder}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 public class QuickDeleteViewBinderTest {
     private Activity mActivity;
     private View mQuickDeleteView;
diff --git a/chrome/browser/readaloud/android/BUILD.gn b/chrome/browser/readaloud/android/BUILD.gn
index e1ea61c1..999f1d1 100644
--- a/chrome/browser/readaloud/android/BUILD.gn
+++ b/chrome/browser/readaloud/android/BUILD.gn
@@ -14,6 +14,7 @@
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerSupplier.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudIphController.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudMiniPlayerSceneLayer.java",
+    "java/src/org/chromium/chrome/browser/readaloud/ReadAloudNativeBridge.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudPrefs.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudToolbarButtonController.java",
     "java/src/org/chromium/chrome/browser/readaloud/TabModelTabObserver.java",
@@ -69,8 +70,8 @@
 
 generate_jni("jni_headers") {
   sources = [
-    "java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudMiniPlayerSceneLayer.java",
+    "java/src/org/chromium/chrome/browser/readaloud/ReadAloudNativeBridge.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudPrefs.java",
   ]
 }
@@ -230,6 +231,7 @@
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudIphControllerUnitTest.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudMiniPlayerSceneLayerUnitTest.java",
+    "java/src/org/chromium/chrome/browser/readaloud/ReadAloudNativeBridgeUnitTest.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudPrefsUnitTest.java",
     "java/src/org/chromium/chrome/browser/readaloud/ReadAloudToolbarButtonControllerUnitTest.java",
     "java/src/org/chromium/chrome/browser/readaloud/TabModelTabObserverUnitTest.java",
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/NativePlayback.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/NativePlayback.java
index a465385..ec965bba 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/NativePlayback.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/NativePlayback.java
@@ -18,8 +18,8 @@
 
 /**
  * Session adapter for active tab article audio, bridging the Java {@link Playback} interface to the
- * on-device C++ {@code ReadAloudService} via JNI and driving UI updates via {@link
- * PlaybackListener} observers.
+ * on-device C++ {@code ReadAloudService} via {@link ReadAloudNativeBridge} and driving UI updates
+ * via {@link PlaybackListener} observers.
  *
  * <p>This class is not thread-safe and should only be accessed from a single thread. All lifecycle
  * operations and JNI callbacks must run on the Android Main UI thread.
@@ -29,10 +29,9 @@
  * <ul>
  *   <li>Created by {@link ReadAloudController} when audio playback is requested on an active tab.
  *   <li>Reuses a single anonymous {@link PlaybackListener.PlaybackData} instance across
- *       high-frequency JNI progress notifications ({@code notifyPlaybackProgressUpdated}) to
- *       guarantee zero heap allocations and prevent GC pauses.
- *   <li>Nullifies native pointer via {@code setNativeServicePtr(0)} upon profile or controller
- *       teardown.
+ *       high-frequency progress notifications ({@code notifyPlaybackProgressUpdated}) to guarantee
+ *       zero heap allocations and prevent GC pauses.
+ *   <li>Delegates native control commands and pointer safety to {@link ReadAloudNativeBridge}.
  * </ul>
  */
 @NullMarked
@@ -41,19 +40,19 @@
     private final NativeMetadata mMetadata;
     private final @Nullable WebContents mWebContents;
     private final PlaybackListener.PlaybackData mPlaybackData;
-    private long mNativeServicePtr;
+    private final ReadAloudNativeBridge mNativeBridge;
     private @PlaybackListener.State int mState = PlaybackListener.State.BUFFERING;
     private long mAbsolutePositionNanos;
     private long mTotalDurationNanos;
 
     NativePlayback(
-            long nativeServicePtr,
+            ReadAloudNativeBridge nativeBridge,
             @Nullable WebContents webContents,
             @Nullable String languageCode,
             @Nullable String canonicalUrl,
             @Nullable PlaybackMode playbackMode) {
         ThreadUtils.assertOnUiThread();
-        mNativeServicePtr = nativeServicePtr;
+        mNativeBridge = nativeBridge;
         mWebContents = webContents;
         mMetadata = new NativeMetadata(languageCode, canonicalUrl, playbackMode);
         // Reused across progress updates to prevent heap allocations and GC pauses.
@@ -100,15 +99,6 @@
     }
 
     /**
-     * Called by {@link ReadAloudController} during profile or service teardown (passing 0) to
-     * prevent subsequent JNI calls on a destroyed native service.
-     */
-    void setNativeServicePtr(long nativeServicePtr) {
-        ThreadUtils.assertOnUiThread();
-        mNativeServicePtr = nativeServicePtr;
-    }
-
-    /**
      * Called by {@link ReadAloudController} when the C++ service dispatches {@code
      * OnMetadataAvailable} via JNI with dynamic title and publisher updates.
      */
@@ -174,33 +164,25 @@
     @Override
     public void play() {
         ThreadUtils.assertOnUiThread();
-        if (mNativeServicePtr != 0 && mWebContents != null && !mWebContents.isDestroyed()) {
-            ReadAloudControllerJni.get().play(mNativeServicePtr, mWebContents);
-        }
+        mNativeBridge.play(mWebContents);
     }
 
     @Override
     public void pause() {
         ThreadUtils.assertOnUiThread();
-        if (mNativeServicePtr != 0) {
-            ReadAloudControllerJni.get().pause(mNativeServicePtr);
-        }
+        mNativeBridge.pause();
     }
 
     @Override
     public void seekRelative(long seekDurationNanos) {
         ThreadUtils.assertOnUiThread();
-        if (mNativeServicePtr != 0) {
-            ReadAloudControllerJni.get().seekRelative(mNativeServicePtr, seekDurationNanos);
-        }
+        mNativeBridge.seekRelative(seekDurationNanos);
     }
 
     @Override
     public void seek(long absolutePositionNanos) {
         ThreadUtils.assertOnUiThread();
-        if (mNativeServicePtr != 0) {
-            ReadAloudControllerJni.get().seek(mNativeServicePtr, absolutePositionNanos);
-        }
+        mNativeBridge.seek(absolutePositionNanos);
     }
 
     @Override
@@ -218,18 +200,13 @@
     @Override
     public void setRate(float rate) {
         ThreadUtils.assertOnUiThread();
-        if (mNativeServicePtr != 0) {
-            ReadAloudControllerJni.get().setPlaybackRate(mNativeServicePtr, rate);
-        }
+        mNativeBridge.setPlaybackRate(rate);
     }
 
     @Override
     public void release() {
         ThreadUtils.assertOnUiThread();
-        if (mNativeServicePtr != 0) {
-            ReadAloudControllerJni.get().stop(mNativeServicePtr);
-            mNativeServicePtr = 0;
-        }
+        mNativeBridge.stop();
         mListeners.clear();
     }
 
@@ -241,15 +218,15 @@
         ThreadUtils.assertOnUiThread();
         // TODO(b/522834235): Pass negativeFeedbackReason to native service and invoke callback via
         // JNI once the native feedback operation completes.
-        if (mNativeServicePtr != 0) {
-            ReadAloudControllerJni.get().sendFeedback(mNativeServicePtr, feedbackType.getValue());
+        if (mNativeBridge.isInitialized()) {
+            mNativeBridge.sendFeedback(feedbackType.getValue());
             if (callback != null) {
                 // NOTE: Currently invoked synchronously as a placeholder. UI must NOT rely on
                 // immediate execution, as this flow will become asynchronous when fully wired.
                 callback.onSuccess();
             }
         } else if (callback != null) {
-            callback.onFailure(new Exception("Native service pointer is null"));
+            callback.onFailure(new Exception("Native bridge is not initialized"));
         }
     }
 }
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/NativePlaybackUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/NativePlaybackUnitTest.java
index 2eae035..b402f9e 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/NativePlaybackUnitTest.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/NativePlaybackUnitTest.java
@@ -8,11 +8,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertSame;
 import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyFloat;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyLong;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -40,13 +35,12 @@
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class NativePlaybackUnitTest {
-    private static final long NATIVE_PTR = 12345L;
     private static final String LANGUAGE = "en";
     private static final String CANONICAL_URL = "https://example.com/article";
 
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
-    @Mock private ReadAloudController.Natives mNativeMock;
+    @Mock private ReadAloudNativeBridge mBridgeMock;
     @Mock private WebContents mWebContents;
     @Mock private PlaybackListener mListener;
     @Mock private SendFeedbackCallback mFeedbackCallback;
@@ -56,10 +50,10 @@
 
     @Before
     public void setUp() {
-        ReadAloudControllerJni.setInstanceForTesting(mNativeMock);
+        when(mBridgeMock.isInitialized()).thenReturn(true);
         mPlayback =
                 new NativePlayback(
-                        NATIVE_PTR, mWebContents, LANGUAGE, CANONICAL_URL, PlaybackMode.CLASSIC);
+                        mBridgeMock, mWebContents, LANGUAGE, CANONICAL_URL, PlaybackMode.CLASSIC);
     }
 
     @Test
@@ -123,23 +117,15 @@
 
     @Test
     public void testPlay() {
-        when(mWebContents.isDestroyed()).thenReturn(false);
         mPlayback.play();
-        verify(mNativeMock).play(eq(NATIVE_PTR), eq(mWebContents));
-    }
-
-    @Test
-    public void testPlay_destroyedWebContents() {
-        when(mWebContents.isDestroyed()).thenReturn(true);
-        mPlayback.play();
-        verify(mNativeMock, never()).play(anyLong(), any());
+        verify(mBridgeMock).play(mWebContents);
     }
 
     @Test
     public void testNullConstructorInputs_safeFallback() {
         NativePlayback nullPlayback =
                 new NativePlayback(
-                        NATIVE_PTR,
+                        mBridgeMock,
                         /* webContents= */ null,
                         /* languageCode= */ null,
                         /* canonicalUrl= */ null,
@@ -149,48 +135,34 @@
         assertEquals(PlaybackMode.CLASSIC, nullPlayback.getMetadata().playbackMode());
 
         nullPlayback.play();
-        verify(mNativeMock, never()).play(anyLong(), any());
+        verify(mBridgeMock).play(null);
     }
 
     @Test
     public void testPauseSeekRateAndRelease() {
         mPlayback.pause();
-        verify(mNativeMock).pause(eq(NATIVE_PTR));
+        verify(mBridgeMock).pause();
 
         mPlayback.seek(100L);
-        verify(mNativeMock).seek(eq(NATIVE_PTR), eq(100L));
+        verify(mBridgeMock).seek(100L);
 
         mPlayback.seekRelative(50L);
-        verify(mNativeMock).seekRelative(eq(NATIVE_PTR), eq(50L));
+        verify(mBridgeMock).seekRelative(50L);
 
         mPlayback.setRate(1.5f);
-        verify(mNativeMock).setPlaybackRate(eq(NATIVE_PTR), eq(1.5f));
+        verify(mBridgeMock).setPlaybackRate(1.5f);
 
         mPlayback.release();
-        verify(mNativeMock).stop(eq(NATIVE_PTR));
+        verify(mBridgeMock).stop();
     }
 
     @Test
-    public void testSetNativeServicePtrToZero_preventsJniCalls() {
-        mPlayback.setNativeServicePtr(0);
-        when(mWebContents.isDestroyed()).thenReturn(false);
+    public void testUninitializedBridge_failsFeedback() {
+        when(mBridgeMock.isInitialized()).thenReturn(false);
 
-        mPlayback.play();
-        mPlayback.pause();
-        mPlayback.seek(100L);
-        mPlayback.seekRelative(50L);
-        mPlayback.setRate(1.5f);
-        mPlayback.release();
         mPlayback.sendFeedback(
                 FeedbackType.POSITIVE, NegativeFeedbackReason.OTHER, mFeedbackCallback);
 
-        verify(mNativeMock, never()).play(anyLong(), any());
-        verify(mNativeMock, never()).pause(anyLong());
-        verify(mNativeMock, never()).seek(anyLong(), anyLong());
-        verify(mNativeMock, never()).seekRelative(anyLong(), anyLong());
-        verify(mNativeMock, never()).setPlaybackRate(anyLong(), anyFloat());
-        verify(mNativeMock, never()).stop(anyLong());
-        verify(mNativeMock, never()).sendFeedback(anyLong(), anyInt());
         verify(mFeedbackCallback).onFailure(any(Exception.class));
     }
 
@@ -198,7 +170,7 @@
     public void testSendFeedback() {
         mPlayback.sendFeedback(
                 FeedbackType.POSITIVE, NegativeFeedbackReason.OTHER, mFeedbackCallback);
-        verify(mNativeMock).sendFeedback(eq(NATIVE_PTR), eq(FeedbackType.POSITIVE.getValue()));
+        verify(mBridgeMock).sendFeedback(FeedbackType.POSITIVE.getValue());
         verify(mFeedbackCallback).onSuccess();
     }
 }
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
index bce5aa2..29d58c3 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudController.java
@@ -24,11 +24,6 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.hash.Hashing;
 
-import org.jni_zero.CalledByNative;
-import org.jni_zero.JNINamespace;
-import org.jni_zero.JniType;
-import org.jni_zero.NativeMethods;
-
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationState;
 import org.chromium.base.ApplicationStatus;
@@ -121,7 +116,6 @@
  * The main entrypoint component for Read Aloud feature. It's responsible for checking its
  * availability and triggering playback. Only instantiate after native is initialized.
  */
-@JNINamespace("readaloud")
 @NullMarked
 public class ReadAloudController
         implements Player.Observer,
@@ -200,6 +194,7 @@
     private boolean mHasKeyboardInsets;
     private boolean mIsInTabSwitcher;
     @Nullable private CallbackController mCallbackController;
+    private final ReadAloudNativeBridge mNativeBridge = new ReadAloudNativeBridge();
 
     @Nullable private List<String> mUrls;
     private int mCurrentUrlIndex;
@@ -707,6 +702,10 @@
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
     public void onProfileAvailable(Profile profile) {
         TraceEvent.begin("ReadAloudController#onProfileAvailable");
+        // Initialize native C++ ReadAloudService binding when native Read Aloud is enabled.
+        if (ReadAloudFeatures.isNativeEnabled()) {
+            mNativeBridge.initialize(profile, this);
+        }
         ReadAloudReadabilityHooksFactory factory =
                 ServiceLoaderUtil.maybeCreate(ReadAloudReadabilityHooksFactory.class);
         if (factory != null) {
@@ -1377,6 +1376,8 @@
             mCallbackController.destroy();
             mCallbackController = null;
         }
+        // Unregister controller delegate from C++ service and reset native pointer.
+        mNativeBridge.destroy();
         sInstances.remove(this);
         mIsDestroyed = true;
         if (mVoicePreviewPlayback != null) {
@@ -2267,20 +2268,17 @@
     }
 
     // ============================================================================
-    // JNI Callbacks (Called by C++ -> Java)
+    // Listener Callbacks (Invoked by ReadAloudNativeBridge)
     // ============================================================================
 
     // Called when the active article's metadata (title and publisher) is loaded.
-    @CalledByNative
-    private void onMetadataAvailable(
-            @JniType("std::string") String title, @JniType("std::string") String publisher) {
+    void onMetadataAvailable(String title, String publisher) {
         // TODO: Update property model with title and publisher.
         Log.d(TAG, "onMetadataAvailable: title = %s, publisher = %s", title, publisher);
     }
 
     // Called periodically to report the current playback progress and total duration.
-    @CalledByNative
-    private void onPlaybackProgressUpdated(long elapsedNanos, long durationNanos) {
+    void onPlaybackProgressUpdated(long elapsedNanos, long durationNanos) {
         // TODO: Update property model with playback progress.
         Log.d(
                 TAG,
@@ -2290,18 +2288,13 @@
     }
 
     // Called when the audio playback state transitions (e.g., playing, paused, stopped).
-    @CalledByNative
-    private void onPlaybackStateChanged(int playbackState) {
+    void onPlaybackStateChanged(int playbackState) {
         // TODO: Update property model with playback state.
         Log.d(TAG, "onPlaybackStateChanged: playbackState = %d", playbackState);
     }
 
     // Called when the list of available synthesis voices is loaded or changed.
-    @CalledByNative
-    private void onVoicesAvailable(
-            @JniType("std::vector<std::string>") String[] voiceIds,
-            @JniType("std::vector<std::string>") String[] voiceDisplayNames,
-            @JniType("std::string") String selectedVoiceId) {
+    void onVoicesAvailable(String[] voiceIds, String[] voiceDisplayNames, String selectedVoiceId) {
         // TODO: Update property model with available voices.
         Log.d(
                 TAG,
@@ -2313,8 +2306,7 @@
     }
 
     // Called when the active word highlight boundary shifts in the text.
-    @CalledByNative
-    private void onWordHighlightUpdated(int absoluteStartIndex, int absoluteEndIndex) {
+    void onWordHighlightUpdated(int absoluteStartIndex, int absoluteEndIndex) {
         // TODO: Update property model with word highlight boundaries.
         Log.d(
                 TAG,
@@ -2324,30 +2316,25 @@
     }
 
     // Called to notify if synchronized word highlighting is supported for the current content.
-    @CalledByNative
-    private void onHighlightingSupported(boolean supported) {
+    void onHighlightingSupported(boolean supported) {
         // TODO: Update property to toggle highlight visibility.
         Log.d(TAG, "onHighlightingSupported: supported = %b", supported);
     }
 
     // Called when playback switches to the on-device system TTS engine.
-    @CalledByNative
-    private void onFallbackEngaged() {
+    void onFallbackEngaged() {
         // TODO: Update property to toggle fallback state.
         Log.d(TAG, "onFallbackEngaged");
     }
 
     // Called when an unrecoverable playback error occurs.
-    @CalledByNative
-    private void onPlaybackError(@JniType("std::string") String errorMessage) {
+    void onPlaybackError(String errorMessage) {
         // TODO: Handle playback error.
         Log.d(TAG, "onPlaybackError: errorMessage = %s", errorMessage);
     }
 
     // Called when the playback state of a voice preview changes in settings.
-    @CalledByNative
-    private void onVoicePreviewPlaybackStateChanged(
-            @JniType("std::string") String voiceId, int playbackState) {
+    void onVoicePreviewPlaybackStateChanged(String voiceId, int playbackState) {
         // TODO: Update property model with voice preview playback state.
         Log.d(
                 TAG,
@@ -2357,76 +2344,13 @@
     }
 
     // Called with the result of an asynchronous page readability check.
-    @CalledByNative
-    private void onReadabilityResult(@JniType("GURL") GURL url, boolean isReadable) {
+    void onReadabilityResult(GURL url, boolean isReadable) {
         // TODO: Update property model with readability result.
         Log.d(TAG, "onReadabilityResult: url = %s, isReadable = %b", url.getSpec(), isReadable);
     }
 
     // Called immediately before the native service is destroyed.
-    @CalledByNative
-    private void onNativeDestroyed() {
-        // TODO: Clean up native controller bindings.
-        Log.d(TAG, "onNativeDestroyed");
-    }
-
-    // ============================================================================
-    // JNI Native Methods (Called by Java -> C++)
-    // ============================================================================
-
-    @NativeMethods
-    interface Natives {
-        // Retrieves the pointer to the native ReadAloudService for the profile.
-        long getReadAloudService(@JniType("Profile*") Profile profile);
-
-        // Registers the Java controller as the native service delegate.
-        void setController(long readAloudServicePtr, ReadAloudController caller);
-
-        // Unregisters the Java controller from the native service.
-        void clearController(long readAloudServicePtr);
-
-        // Starts or resumes audio playback.
-        void play(
-                long readAloudServicePtr,
-                @JniType("content::WebContents*") WebContents webContents);
-
-        // Pauses the current audio playback.
-        void pause(long readAloudServicePtr);
-
-        // Stops audio playback and releases playback resources.
-        void stop(long readAloudServicePtr);
-
-        // Seeks to the start of the word at the specified index in the text.
-        void seekToWordIndex(long readAloudServicePtr, int wordIndex);
-
-        // Seeks to a specific absolute time offset from the beginning of the audio.
-        void seek(long readAloudServicePtr, long absoluteTimeNanos);
-
-        // Seeks forward or backward relatively (e.g., for the +10s / -10s skip buttons).
-        void seekRelative(long readAloudServicePtr, long offsetNanos);
-
-        // Adjusts the audio playback speed (rate multiplier).
-        void setPlaybackRate(long readAloudServicePtr, float rate);
-
-        // Sets the voice to be used for text-to-speech synthesis.
-        void setVoice(long readAloudServicePtr, @JniType("std::string") String voiceId);
-
-        // Plays a short audio sample of the specified voice.
-        void previewVoice(long readAloudServicePtr, @JniType("std::string") String voiceId);
-
-        // Stops the active voice preview playback.
-        void stopVoicePreview(long readAloudServicePtr);
-
-        // Sets the playback mode (classic full read or summary overview).
-        void setPlaybackMode(long readAloudServicePtr, int mode);
-
-        // Toggles synchronized word highlighting in the UI.
-        void setHighlightingEnabled(long readAloudServicePtr, boolean enabled);
-
-        // Submits user feedback (e.g., thumbs up/down) for logging.
-        void sendFeedback(long readAloudServicePtr, int feedbackType);
-
-        // Initiates an asynchronous check to determine if the URL is readable.
-        void checkReadability(long readAloudServicePtr, @JniType("GURL") GURL url);
+    void onNativeDestroyed() {
+        // TODO(b/542628333): Reset active NativePlayback session when NativePlayback is wired up.
     }
 }
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
index 59cfeb8..560a3b7 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudControllerUnitTest.java
@@ -136,7 +136,11 @@
 /** Unit tests for {@link ReadAloudController}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@DisableFeatures({ChromeFeatureList.READALOUD_AUDIO_OVERVIEWS, ChromeFeatureList.GLIC})
+@DisableFeatures({
+    ChromeFeatureList.READALOUD_AUDIO_OVERVIEWS,
+    ChromeFeatureList.GLIC,
+    AccessibilityFeatures.READ_ALOUD_NATIVE
+})
 public class ReadAloudControllerUnitTest {
     private static final GURL sTestGURL = JUnitTestGURLs.EXAMPLE_URL;
     private static final GURL sTestRedirectGURL = JUnitTestGURLs.URL_1_WITH_PATH;
@@ -193,6 +197,7 @@
     @Mock private LayoutManager mLayoutManager;
     @Mock private ReadAloudPrefs.Natives mReadAloudPrefsNatives;
     @Mock private ReadAloudFeatures.Natives mReadAloudFeaturesNatives;
+    @Mock private ReadAloudNativeBridge.Natives mNativeBridgeNatives;
     @Mock private UserPrefsJni mUserPrefsNatives;
     @Mock private PrefService mPrefService;
     @Mock private ActorUiTabController mActorUiTabController;
@@ -273,6 +278,7 @@
         TranslateBridgeJni.setInstanceForTesting(mFakeTranslateBridge);
         ReadAloudPrefsJni.setInstanceForTesting(mReadAloudPrefsNatives);
         ReadAloudFeaturesJni.setInstanceForTesting(mReadAloudFeaturesNatives);
+        ReadAloudNativeBridgeJni.setInstanceForTesting(mNativeBridgeNatives);
         UserPrefsJni.setInstanceForTesting(mUserPrefsNatives);
         doReturn(mPrefService).when(mUserPrefsNatives).get(any());
         when(mPrefService.hasPrefPath("readaloud.playback_mode")).thenReturn(true);
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudNativeBridge.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudNativeBridge.java
new file mode 100644
index 0000000..84df44b5
--- /dev/null
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudNativeBridge.java
@@ -0,0 +1,365 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.readaloud;
+
+import org.jni_zero.CalledByNative;
+import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
+import org.jni_zero.NativeMethods;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.url.GURL;
+
+/**
+ * Dedicated JNI bridge class managing 2-way communications between Java and C++ native ReadAloud.
+ *
+ * <p>Responsibilities:
+ *
+ * <ul>
+ *   <li>Encapsulates native C++ control methods (play, pause, stop, seek, etc.) with null pointer
+ *       checks on {@code mNativeReadAloudBridge}.
+ *   <li>Receives JNI {@code @CalledByNative} callbacks from C++ and forwards them to {@link
+ *       ReadAloudController}.
+ * </ul>
+ *
+ * <p>Lifecycle:
+ *
+ * <ul>
+ *   <li><b>Instantiation:</b> Created by {@link ReadAloudController} during setup.
+ *   <li><b>Initialization:</b> {@link #initialize(Profile, ReadAloudController)} is called when
+ *       profile becomes available, acquiring the native C++ pointer address and registering the
+ *       controller delegate.
+ *   <li><b>Active Usage:</b> Dispatches outbound control commands to C++ and receives inbound
+ *       status callbacks.
+ *   <li><b>Teardown:</b> Cleaned up via {@link #destroy()} during Java destruction or via {@link
+ *       #onNativeDestroyed()} when C++ native service is destroyed.
+ * </ul>
+ */
+@JNINamespace("readaloud")
+@NullMarked
+class ReadAloudNativeBridge {
+    // Address of the native C++ ReadAloudBridge instance (for handling Java -> native calls).
+    private long mNativeReadAloudBridge;
+    // ReadAloudController reference (for handling native -> Java calls).
+    private @Nullable ReadAloudController mController;
+
+    /**
+     * Initializes the native C++ ReadAloudBridge binding.
+     *
+     * <p>Acquires the native bridge pointer for the given {@link Profile} and registers the {@link
+     * ReadAloudController} instance as the delegate for C++ to Java callbacks.
+     *
+     * @param profile Active user profile.
+     * @param controller Controller instance acting as the native service delegate.
+     */
+    public void initialize(Profile profile, ReadAloudController controller) {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0) {
+            destroy();
+        }
+        mController = controller;
+        mNativeReadAloudBridge = ReadAloudNativeBridgeJni.get().init(profile, this);
+    }
+
+    /** Returns true if the native bridge is initialized and active. */
+    public boolean isInitialized() {
+        ThreadUtils.assertOnUiThread();
+        return mNativeReadAloudBridge != 0;
+    }
+
+    /** Unregisters the delegate in native and resets the pointer to 0. */
+    public void destroy() {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0) {
+            ReadAloudNativeBridgeJni.get().destroy(mNativeReadAloudBridge);
+            mNativeReadAloudBridge = 0;
+        }
+        mController = null;
+    }
+
+    // ============================================================================
+    // Java -> C++ Public Control API (Safe Wrappers)
+    //
+    // Public instance methods called by Java components (e.g. NativePlayback). These
+    // wrappers enforce thread safety and verify mNativeReadAloudBridge pointer validity
+    // before supplying the native handle to JNI Zero.
+    // ============================================================================
+
+    /** Starts or resumes audio playback. */
+    public void play(@Nullable WebContents webContents) {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0 && webContents != null && !webContents.isDestroyed()) {
+            ReadAloudNativeBridgeJni.get().play(mNativeReadAloudBridge, webContents);
+        }
+    }
+
+    /** Pauses active audio playback. */
+    public void pause() {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0) {
+            ReadAloudNativeBridgeJni.get().pause(mNativeReadAloudBridge);
+        }
+    }
+
+    /** Stops audio playback and releases playback resources. */
+    public void stop() {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0) {
+            ReadAloudNativeBridgeJni.get().stop(mNativeReadAloudBridge);
+        }
+    }
+
+    /** Seeks to the start of the word at the specified index in the text. */
+    public void seekToWordIndex(int wordIndex) {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0) {
+            ReadAloudNativeBridgeJni.get().seekToWordIndex(mNativeReadAloudBridge, wordIndex);
+        }
+    }
+
+    /** Seeks to a specific absolute time offset from the beginning of the audio. */
+    public void seek(long absoluteTimeNanos) {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0) {
+            ReadAloudNativeBridgeJni.get().seek(mNativeReadAloudBridge, absoluteTimeNanos);
+        }
+    }
+
+    /** Seeks forward or backward relatively (e.g., for skip buttons). */
+    public void seekRelative(long offsetNanos) {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0) {
+            ReadAloudNativeBridgeJni.get().seekRelative(mNativeReadAloudBridge, offsetNanos);
+        }
+    }
+
+    /** Adjusts the audio playback speed (rate multiplier). */
+    public void setPlaybackRate(float rate) {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0) {
+            ReadAloudNativeBridgeJni.get().setPlaybackRate(mNativeReadAloudBridge, rate);
+        }
+    }
+
+    /** Sets the voice to be used for text-to-speech synthesis. */
+    public void setVoice(String voiceId) {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0) {
+            ReadAloudNativeBridgeJni.get().setVoice(mNativeReadAloudBridge, voiceId);
+        }
+    }
+
+    /** Plays a short audio sample of the specified voice. */
+    public void previewVoice(String voiceId) {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0) {
+            ReadAloudNativeBridgeJni.get().previewVoice(mNativeReadAloudBridge, voiceId);
+        }
+    }
+
+    /** Stops the active voice preview playback. */
+    public void stopVoicePreview() {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0) {
+            ReadAloudNativeBridgeJni.get().stopVoicePreview(mNativeReadAloudBridge);
+        }
+    }
+
+    /** Sets the playback mode (classic full read or summary overview). */
+    public void setPlaybackMode(int mode) {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0) {
+            ReadAloudNativeBridgeJni.get().setPlaybackMode(mNativeReadAloudBridge, mode);
+        }
+    }
+
+    /** Toggles synchronized word highlighting in the UI. */
+    public void setHighlightingEnabled(boolean enabled) {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0) {
+            ReadAloudNativeBridgeJni.get().setHighlightingEnabled(mNativeReadAloudBridge, enabled);
+        }
+    }
+
+    /** Submits user feedback (e.g., thumbs up/down) for logging. */
+    public void sendFeedback(int feedbackType) {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0) {
+            ReadAloudNativeBridgeJni.get().sendFeedback(mNativeReadAloudBridge, feedbackType);
+        }
+    }
+
+    /** Initiates an asynchronous check to determine if the URL is readable. */
+    public void checkReadability(@Nullable GURL url) {
+        ThreadUtils.assertOnUiThread();
+        if (mNativeReadAloudBridge != 0 && url != null) {
+            ReadAloudNativeBridgeJni.get().checkReadability(mNativeReadAloudBridge, url);
+        }
+    }
+
+    // ============================================================================
+    // C++ -> Java Inbound Callbacks (@CalledByNative)
+    // ============================================================================
+
+    @CalledByNative
+    void onMetadataAvailable(
+            @JniType("std::string") String title, @JniType("std::string") String publisher) {
+        ThreadUtils.assertOnUiThread();
+        if (mController != null) {
+            mController.onMetadataAvailable(title, publisher);
+        }
+    }
+
+    @CalledByNative
+    void onPlaybackProgressUpdated(long elapsedNanos, long durationNanos) {
+        ThreadUtils.assertOnUiThread();
+        if (mController != null) {
+            mController.onPlaybackProgressUpdated(elapsedNanos, durationNanos);
+        }
+    }
+
+    @CalledByNative
+    void onPlaybackStateChanged(int playbackState) {
+        ThreadUtils.assertOnUiThread();
+        if (mController != null) {
+            mController.onPlaybackStateChanged(playbackState);
+        }
+    }
+
+    @CalledByNative
+    void onVoicesAvailable(
+            @JniType("std::vector<std::string>") String[] voiceIds,
+            @JniType("std::vector<std::string>") String[] voiceDisplayNames,
+            @JniType("std::string") String selectedVoiceId) {
+        ThreadUtils.assertOnUiThread();
+        if (mController != null) {
+            mController.onVoicesAvailable(voiceIds, voiceDisplayNames, selectedVoiceId);
+        }
+    }
+
+    @CalledByNative
+    void onWordHighlightUpdated(int absoluteStartIndex, int absoluteEndIndex) {
+        ThreadUtils.assertOnUiThread();
+        if (mController != null) {
+            mController.onWordHighlightUpdated(absoluteStartIndex, absoluteEndIndex);
+        }
+    }
+
+    @CalledByNative
+    void onHighlightingSupported(boolean supported) {
+        ThreadUtils.assertOnUiThread();
+        if (mController != null) {
+            mController.onHighlightingSupported(supported);
+        }
+    }
+
+    @CalledByNative
+    void onFallbackEngaged() {
+        ThreadUtils.assertOnUiThread();
+        if (mController != null) {
+            mController.onFallbackEngaged();
+        }
+    }
+
+    @CalledByNative
+    void onPlaybackError(@JniType("std::string") String errorMessage) {
+        ThreadUtils.assertOnUiThread();
+        if (mController != null) {
+            mController.onPlaybackError(errorMessage);
+        }
+    }
+
+    @CalledByNative
+    void onVoicePreviewPlaybackStateChanged(
+            @JniType("std::string") String voiceId, int playbackState) {
+        ThreadUtils.assertOnUiThread();
+        if (mController != null) {
+            mController.onVoicePreviewPlaybackStateChanged(voiceId, playbackState);
+        }
+    }
+
+    @CalledByNative
+    void onReadabilityResult(@JniType("GURL") GURL url, boolean isReadable) {
+        ThreadUtils.assertOnUiThread();
+        if (mController != null) {
+            mController.onReadabilityResult(url, isReadable);
+        }
+    }
+
+    @CalledByNative
+    void onNativeDestroyed() {
+        ThreadUtils.assertOnUiThread();
+        mNativeReadAloudBridge = 0;
+        if (mController != null) {
+            mController.onNativeDestroyed();
+        }
+    }
+
+    // ============================================================================
+    // JNI Native Methods (JNI Zero Contract Interface for C++ Code Generation)
+    //
+    // Abstract method declarations parsed by Chromium's jni_zero tool to generate
+    // the C++ JNI binding code (ReadAloudNativeBridgeJni). Each method accepts
+    // long nativeReadAloudBridge as its first argument to map to ReadAloudBridge
+    // instance methods in C++.
+    // ============================================================================
+
+    @NativeMethods
+    interface Natives {
+        // Initializes the native ReadAloudBridge C++ instance.
+        long init(@JniType("Profile*") Profile profile, ReadAloudNativeBridge bridge);
+
+        // Starts or resumes audio playback.
+        void play(
+                long nativeReadAloudBridge,
+                @JniType("content::WebContents*") WebContents webContents);
+
+        // Pauses the current audio playback.
+        void pause(long nativeReadAloudBridge);
+
+        // Stops audio playback and releases playback resources.
+        void stop(long nativeReadAloudBridge);
+
+        // Seeks to the start of the word at the specified index in the text.
+        void seekToWordIndex(long nativeReadAloudBridge, int wordIndex);
+
+        // Seeks to a specific absolute time offset from the beginning of the audio.
+        void seek(long nativeReadAloudBridge, long absoluteTimeNanos);
+
+        // Seeks forward or backward relatively.
+        void seekRelative(long nativeReadAloudBridge, long offsetNanos);
+
+        // Adjusts the audio playback speed (rate multiplier).
+        void setPlaybackRate(long nativeReadAloudBridge, float rate);
+
+        // Sets the voice to be used for text-to-speech synthesis.
+        void setVoice(long nativeReadAloudBridge, @JniType("std::string") String voiceId);
+
+        // Plays a short audio sample of the specified voice.
+        void previewVoice(long nativeReadAloudBridge, @JniType("std::string") String voiceId);
+
+        // Stops the active voice preview playback.
+        void stopVoicePreview(long nativeReadAloudBridge);
+
+        // Sets the playback mode (classic full read or summary overview).
+        void setPlaybackMode(long nativeReadAloudBridge, int mode);
+
+        // Toggles synchronized word highlighting in the UI.
+        void setHighlightingEnabled(long nativeReadAloudBridge, boolean enabled);
+
+        // Submits user feedback (e.g., thumbs up/down) for logging.
+        void sendFeedback(long nativeReadAloudBridge, int feedbackType);
+
+        // Initiates an asynchronous check to determine if the URL is readable.
+        void checkReadability(long nativeReadAloudBridge, @JniType("GURL") GURL url);
+
+        // Destroys the native bridge delegate.
+        void destroy(long nativeReadAloudBridge);
+    }
+}
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudNativeBridgeUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudNativeBridgeUnitTest.java
new file mode 100644
index 0000000..6a2957d8
--- /dev/null
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/ReadAloudNativeBridgeUnitTest.java
@@ -0,0 +1,263 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.readaloud;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.url.GURL;
+import org.chromium.url.JUnitTestGURLs;
+
+/** Unit tests for {@link ReadAloudNativeBridge}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class ReadAloudNativeBridgeUnitTest {
+    private static final long NATIVE_PTR = 12345L;
+    private static final GURL TEST_GURL = JUnitTestGURLs.EXAMPLE_URL;
+
+    @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock private ReadAloudNativeBridge.Natives mNativeBridgeNatives;
+    @Mock private Profile mProfile;
+    @Mock private ReadAloudController mController;
+    @Mock private WebContents mWebContents;
+
+    private ReadAloudNativeBridge mBridge;
+
+    @Before
+    public void setUp() {
+        ReadAloudNativeBridgeJni.setInstanceForTesting(mNativeBridgeNatives);
+        when(mNativeBridgeNatives.init(any(), any())).thenReturn(NATIVE_PTR);
+        when(mWebContents.isDestroyed()).thenReturn(false);
+
+        mBridge = new ReadAloudNativeBridge();
+    }
+
+    @Test
+    public void testInitializeAndIsInitialized() {
+        assertFalse(mBridge.isInitialized());
+
+        mBridge.initialize(mProfile, mController);
+
+        assertTrue(mBridge.isInitialized());
+        verify(mNativeBridgeNatives).init(eq(mProfile), eq(mBridge));
+    }
+
+    @Test
+    public void testReinitialize_cleansUpPreviousInstance() {
+        mBridge.initialize(mProfile, mController);
+        assertTrue(mBridge.isInitialized());
+
+        long secondNativePtr = 67890L;
+        when(mNativeBridgeNatives.init(any(), any())).thenReturn(secondNativePtr);
+
+        // Re-initializing should destroy the first native instance before creating the second.
+        mBridge.initialize(mProfile, mController);
+
+        verify(mNativeBridgeNatives).destroy(eq(NATIVE_PTR));
+        verify(mNativeBridgeNatives, times(2)).init(eq(mProfile), eq(mBridge));
+        assertTrue(mBridge.isInitialized());
+    }
+
+    @Test
+    public void testDestroy() {
+        mBridge.initialize(mProfile, mController);
+        assertTrue(mBridge.isInitialized());
+
+        mBridge.destroy();
+
+        verify(mNativeBridgeNatives).destroy(eq(NATIVE_PTR));
+        assertFalse(mBridge.isInitialized());
+
+        // Subsequent destroy calls should be safe no-ops.
+        mBridge.destroy();
+        verify(mNativeBridgeNatives, times(1)).destroy(anyLong());
+    }
+
+    @Test
+    public void testOutboundCommands_whenInitialized() {
+        mBridge.initialize(mProfile, mController);
+
+        mBridge.play(mWebContents);
+        verify(mNativeBridgeNatives).play(eq(NATIVE_PTR), eq(mWebContents));
+
+        mBridge.pause();
+        verify(mNativeBridgeNatives).pause(eq(NATIVE_PTR));
+
+        mBridge.stop();
+        verify(mNativeBridgeNatives).stop(eq(NATIVE_PTR));
+
+        mBridge.seekToWordIndex(42);
+        verify(mNativeBridgeNatives).seekToWordIndex(eq(NATIVE_PTR), eq(42));
+
+        mBridge.seek(5000L);
+        verify(mNativeBridgeNatives).seek(eq(NATIVE_PTR), eq(5000L));
+
+        mBridge.seekRelative(200L);
+        verify(mNativeBridgeNatives).seekRelative(eq(NATIVE_PTR), eq(200L));
+
+        mBridge.setPlaybackRate(1.5f);
+        verify(mNativeBridgeNatives).setPlaybackRate(eq(NATIVE_PTR), eq(1.5f));
+
+        mBridge.setVoice("voice_1");
+        verify(mNativeBridgeNatives).setVoice(eq(NATIVE_PTR), eq("voice_1"));
+
+        mBridge.previewVoice("voice_1");
+        verify(mNativeBridgeNatives).previewVoice(eq(NATIVE_PTR), eq("voice_1"));
+
+        mBridge.stopVoicePreview();
+        verify(mNativeBridgeNatives).stopVoicePreview(eq(NATIVE_PTR));
+
+        mBridge.setPlaybackMode(1);
+        verify(mNativeBridgeNatives).setPlaybackMode(eq(NATIVE_PTR), eq(1));
+
+        mBridge.setHighlightingEnabled(true);
+        verify(mNativeBridgeNatives).setHighlightingEnabled(eq(NATIVE_PTR), eq(true));
+
+        mBridge.sendFeedback(2);
+        verify(mNativeBridgeNatives).sendFeedback(eq(NATIVE_PTR), eq(2));
+
+        mBridge.checkReadability(TEST_GURL);
+        verify(mNativeBridgeNatives).checkReadability(eq(NATIVE_PTR), eq(TEST_GURL));
+    }
+
+    @Test
+    public void testOutboundCommands_whenUninitialized_safeNoOps() {
+        assertFalse(mBridge.isInitialized());
+
+        mBridge.play(mWebContents);
+        mBridge.pause();
+        mBridge.stop();
+        mBridge.seekToWordIndex(42);
+        mBridge.seek(5000L);
+        mBridge.seekRelative(200L);
+        mBridge.setPlaybackRate(1.5f);
+        mBridge.setVoice("voice_1");
+        mBridge.previewVoice("voice_1");
+        mBridge.stopVoicePreview();
+        mBridge.setPlaybackMode(1);
+        mBridge.setHighlightingEnabled(true);
+        mBridge.sendFeedback(2);
+        mBridge.checkReadability(TEST_GURL);
+
+        verify(mNativeBridgeNatives, never()).play(anyLong(), any());
+        verify(mNativeBridgeNatives, never()).pause(anyLong());
+        verify(mNativeBridgeNatives, never()).stop(anyLong());
+        verify(mNativeBridgeNatives, never()).seekToWordIndex(anyLong(), anyInt());
+        verify(mNativeBridgeNatives, never()).seek(anyLong(), anyLong());
+        verify(mNativeBridgeNatives, never()).seekRelative(anyLong(), anyLong());
+        verify(mNativeBridgeNatives, never()).setPlaybackRate(anyLong(), anyFloat());
+        verify(mNativeBridgeNatives, never()).setVoice(anyLong(), anyString());
+        verify(mNativeBridgeNatives, never()).previewVoice(anyLong(), anyString());
+        verify(mNativeBridgeNatives, never()).stopVoicePreview(anyLong());
+        verify(mNativeBridgeNatives, never()).setPlaybackMode(anyLong(), anyInt());
+        verify(mNativeBridgeNatives, never()).setHighlightingEnabled(anyLong(), anyBoolean());
+        verify(mNativeBridgeNatives, never()).sendFeedback(anyLong(), anyInt());
+        verify(mNativeBridgeNatives, never()).checkReadability(anyLong(), any());
+    }
+
+    @Test
+    public void testPlay_destroyedOrNullWebContents_preventDispatch() {
+        mBridge.initialize(mProfile, mController);
+
+        mBridge.play(null);
+        verify(mNativeBridgeNatives, never()).play(anyLong(), any());
+
+        when(mWebContents.isDestroyed()).thenReturn(true);
+        mBridge.play(mWebContents);
+        verify(mNativeBridgeNatives, never()).play(anyLong(), any());
+    }
+
+    @Test
+    public void testCheckReadability_nullUrl_preventDispatch() {
+        mBridge.initialize(mProfile, mController);
+
+        mBridge.checkReadability(null);
+        verify(mNativeBridgeNatives, never()).checkReadability(anyLong(), any());
+    }
+
+    @Test
+    public void testInboundCallbacks_forwardToController() {
+        mBridge.initialize(mProfile, mController);
+
+        mBridge.onMetadataAvailable("Article Title", "Publisher Name");
+        verify(mController).onMetadataAvailable("Article Title", "Publisher Name");
+
+        mBridge.onPlaybackProgressUpdated(1000L, 5000L);
+        verify(mController).onPlaybackProgressUpdated(1000L, 5000L);
+
+        mBridge.onPlaybackStateChanged(2);
+        verify(mController).onPlaybackStateChanged(2);
+
+        String[] ids = new String[] {"id1", "id2"};
+        String[] names = new String[] {"Name 1", "Name 2"};
+        mBridge.onVoicesAvailable(ids, names, "id1");
+        verify(mController).onVoicesAvailable(ids, names, "id1");
+
+        mBridge.onWordHighlightUpdated(10, 20);
+        verify(mController).onWordHighlightUpdated(10, 20);
+
+        mBridge.onHighlightingSupported(true);
+        verify(mController).onHighlightingSupported(true);
+
+        mBridge.onFallbackEngaged();
+        verify(mController).onFallbackEngaged();
+
+        mBridge.onPlaybackError("Synthesis error");
+        verify(mController).onPlaybackError("Synthesis error");
+
+        mBridge.onVoicePreviewPlaybackStateChanged("id1", 1);
+        verify(mController).onVoicePreviewPlaybackStateChanged("id1", 1);
+
+        mBridge.onReadabilityResult(TEST_GURL, true);
+        verify(mController).onReadabilityResult(TEST_GURL, true);
+
+        mBridge.onNativeDestroyed();
+        verify(mController).onNativeDestroyed();
+        assertFalse(mBridge.isInitialized());
+    }
+
+    @Test
+    public void testInboundCallbacks_whenControllerIsNull_safeNoOps() {
+        // Calling inbound callbacks without initializing controller must not crash.
+        mBridge.onMetadataAvailable("Title", "Publisher");
+        mBridge.onPlaybackProgressUpdated(100L, 500L);
+        mBridge.onPlaybackStateChanged(1);
+        mBridge.onVoicesAvailable(new String[0], new String[0], "");
+        mBridge.onWordHighlightUpdated(0, 5);
+        mBridge.onHighlightingSupported(false);
+        mBridge.onFallbackEngaged();
+        mBridge.onPlaybackError("Error");
+        mBridge.onVoicePreviewPlaybackStateChanged("voice", 0);
+        mBridge.onReadabilityResult(TEST_GURL, false);
+        mBridge.onNativeDestroyed();
+
+        verifyNoInteractions(mController);
+    }
+}
diff --git a/chrome/browser/readaloud/android/read_aloud_bridge.cc b/chrome/browser/readaloud/android/read_aloud_bridge.cc
index 17e2ee6..4a752e39 100644
--- a/chrome/browser/readaloud/android/read_aloud_bridge.cc
+++ b/chrome/browser/readaloud/android/read_aloud_bridge.cc
@@ -15,7 +15,7 @@
 #include "url/android/gurl_android.h"
 
 // JNI header generated automatically by JNI Zero.
-#include "chrome/browser/readaloud/android/jni_headers/ReadAloudController_jni.h"
+#include "chrome/browser/readaloud/android/jni_headers/ReadAloudNativeBridge_jni.h"
 
 using jni_zero::AttachCurrentThread;
 using jni_zero::JavaRef;
@@ -24,9 +24,9 @@
 namespace readaloud {
 
 ReadAloudBridge::ReadAloudBridge(JNIEnv* env,
-                                 const JavaRef<jobject>& j_controller,
+                                 const JavaRef<jobject>& j_native_bridge,
                                  ReadAloudService* service)
-    : weak_java_controller_(env, j_controller), service_(service) {}
+    : weak_java_native_bridge_(env, j_native_bridge), service_(service) {}
 
 ReadAloudBridge::~ReadAloudBridge() = default;
 
@@ -37,34 +37,34 @@
 void ReadAloudBridge::OnMetadataAvailable(std::string_view title,
                                           std::string_view publisher) {
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> java_controller = weak_java_controller_.get(env);
-  if (!java_controller) {
+  ScopedJavaLocalRef<jobject> j_bridge = weak_java_native_bridge_.get(env);
+  if (!j_bridge) {
     return;
   }
-  Java_ReadAloudController_onMetadataAvailable(
-      env, java_controller, std::string(title), std::string(publisher));
+  Java_ReadAloudNativeBridge_onMetadataAvailable(
+      env, j_bridge, std::string(title), std::string(publisher));
 }
 
 void ReadAloudBridge::OnPlaybackProgressUpdated(base::TimeDelta elapsed,
                                                 base::TimeDelta duration) {
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> java_controller = weak_java_controller_.get(env);
-  if (!java_controller) {
+  ScopedJavaLocalRef<jobject> j_bridge = weak_java_native_bridge_.get(env);
+  if (!j_bridge) {
     return;
   }
-  Java_ReadAloudController_onPlaybackProgressUpdated(
-      env, java_controller, elapsed.InNanoseconds(), duration.InNanoseconds());
+  Java_ReadAloudNativeBridge_onPlaybackProgressUpdated(
+      env, j_bridge, elapsed.InNanoseconds(), duration.InNanoseconds());
 }
 
 void ReadAloudBridge::OnPlaybackStateChanged(
     ReadAloudService::PlaybackState playback_state) {
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> java_controller = weak_java_controller_.get(env);
-  if (!java_controller) {
+  ScopedJavaLocalRef<jobject> j_bridge = weak_java_native_bridge_.get(env);
+  if (!j_bridge) {
     return;
   }
-  Java_ReadAloudController_onPlaybackStateChanged(
-      env, java_controller, static_cast<jint>(playback_state));
+  Java_ReadAloudNativeBridge_onPlaybackStateChanged(
+      env, j_bridge, static_cast<jint>(playback_state));
 }
 
 void ReadAloudBridge::OnVoicesAvailable(
@@ -80,268 +80,200 @@
   }
 
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> java_controller = weak_java_controller_.get(env);
-  if (!java_controller) {
+  ScopedJavaLocalRef<jobject> j_bridge = weak_java_native_bridge_.get(env);
+  if (!j_bridge) {
     return;
   }
-  Java_ReadAloudController_onVoicesAvailable(
-      env, java_controller, ids, display_names, std::string(selected_voice_id));
+  Java_ReadAloudNativeBridge_onVoicesAvailable(
+      env, j_bridge, ids, display_names, std::string(selected_voice_id));
 }
 
 void ReadAloudBridge::OnWordHighlightUpdated(int absolute_start_index,
                                              int absolute_end_index) {
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> java_controller = weak_java_controller_.get(env);
-  if (!java_controller) {
+  ScopedJavaLocalRef<jobject> j_bridge = weak_java_native_bridge_.get(env);
+  if (!j_bridge) {
     return;
   }
-  Java_ReadAloudController_onWordHighlightUpdated(
-      env, java_controller, absolute_start_index, absolute_end_index);
+  Java_ReadAloudNativeBridge_onWordHighlightUpdated(
+      env, j_bridge, absolute_start_index, absolute_end_index);
 }
 
 void ReadAloudBridge::OnHighlightingSupported(bool supported) {
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> java_controller = weak_java_controller_.get(env);
-  if (!java_controller) {
+  ScopedJavaLocalRef<jobject> j_bridge = weak_java_native_bridge_.get(env);
+  if (!j_bridge) {
     return;
   }
-  Java_ReadAloudController_onHighlightingSupported(env, java_controller,
-                                                   supported);
+  Java_ReadAloudNativeBridge_onHighlightingSupported(env, j_bridge, supported);
 }
 
 void ReadAloudBridge::OnFallbackEngaged() {
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> java_controller = weak_java_controller_.get(env);
-  if (!java_controller) {
+  ScopedJavaLocalRef<jobject> j_bridge = weak_java_native_bridge_.get(env);
+  if (!j_bridge) {
     return;
   }
-  Java_ReadAloudController_onFallbackEngaged(env, java_controller);
+  Java_ReadAloudNativeBridge_onFallbackEngaged(env, j_bridge);
 }
 
 void ReadAloudBridge::OnPlaybackError(std::string_view error_message) {
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> java_controller = weak_java_controller_.get(env);
-  if (!java_controller) {
+  ScopedJavaLocalRef<jobject> j_bridge = weak_java_native_bridge_.get(env);
+  if (!j_bridge) {
     return;
   }
-  Java_ReadAloudController_onPlaybackError(env, java_controller,
-                                           std::string(error_message));
+  Java_ReadAloudNativeBridge_onPlaybackError(env, j_bridge,
+                                             std::string(error_message));
 }
 
 void ReadAloudBridge::OnVoicePreviewPlaybackStateChanged(
     std::string_view voice_id,
     ReadAloudService::PlaybackState playback_state) {
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> java_controller = weak_java_controller_.get(env);
-  if (!java_controller) {
+  ScopedJavaLocalRef<jobject> j_bridge = weak_java_native_bridge_.get(env);
+  if (!j_bridge) {
     return;
   }
-  Java_ReadAloudController_onVoicePreviewPlaybackStateChanged(
-      env, java_controller, std::string(voice_id),
-      static_cast<jint>(playback_state));
+  Java_ReadAloudNativeBridge_onVoicePreviewPlaybackStateChanged(
+      env, j_bridge, std::string(voice_id), static_cast<jint>(playback_state));
 }
 
 void ReadAloudBridge::OnReadabilityResult(const GURL& url, bool is_readable) {
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> java_controller = weak_java_controller_.get(env);
-  if (!java_controller) {
+  ScopedJavaLocalRef<jobject> j_bridge = weak_java_native_bridge_.get(env);
+  if (!j_bridge) {
     return;
   }
-  Java_ReadAloudController_onReadabilityResult(env, java_controller, url,
-                                               is_readable);
+  Java_ReadAloudNativeBridge_onReadabilityResult(env, j_bridge, url,
+                                                 is_readable);
 }
 
 void ReadAloudBridge::OnNativeDestroyed() {
   JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> java_controller = weak_java_controller_.get(env);
-  if (!java_controller) {
-    return;
+  ScopedJavaLocalRef<jobject> j_bridge = weak_java_native_bridge_.get(env);
+  if (j_bridge) {
+    Java_ReadAloudNativeBridge_onNativeDestroyed(env, j_bridge);
   }
-  Java_ReadAloudController_onNativeDestroyed(env, java_controller);
+  weak_java_native_bridge_.reset();
+  service_ = nullptr;
 }
 
 // ============================================================================
-// JNI Inbound Methods (Called by Java -> C++)
+// JNI Inbound Methods (Java -> C++ Commands)
 // ============================================================================
 
-static jlong JNI_ReadAloudController_GetReadAloudService(JNIEnv* env,
-                                                         Profile* profile) {
+static jlong JNI_ReadAloudNativeBridge_Init(
+    JNIEnv* env,
+    Profile* profile,
+    const JavaRef<jobject>& j_native_bridge) {
   if (!profile) {
     return 0;
   }
   ReadAloudService* service = ReadAloudServiceFactory::GetForProfile(profile);
-  return reinterpret_cast<jlong>(service);
-}
-
-static void JNI_ReadAloudController_SetController(
-    JNIEnv* env,
-    jlong read_aloud_service_ptr,
-    const JavaRef<jobject>& j_caller) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
   if (!service) {
-    return;
+    return 0;
   }
-  auto bridge = std::make_unique<ReadAloudBridge>(env, j_caller, service);
+  auto bridge =
+      std::make_unique<ReadAloudBridge>(env, j_native_bridge, service);
+  auto* bridge_ptr = bridge.get();
   service->SetDelegate(std::move(bridge));
+  return reinterpret_cast<jlong>(bridge_ptr);
 }
 
-static void JNI_ReadAloudController_ClearController(
-    JNIEnv* env,
-    jlong read_aloud_service_ptr) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::Play(JNIEnv* env, content::WebContents* web_contents) {
+  if (service_) {
+    service_->Play(web_contents);
   }
-  service->SetDelegate(nullptr);
 }
 
-static void JNI_ReadAloudController_Play(JNIEnv* env,
-                                         jlong read_aloud_service_ptr,
-                                         content::WebContents* web_contents) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::Pause(JNIEnv* env) {
+  if (service_) {
+    service_->Pause();
   }
-  service->Play(web_contents);
 }
 
-static void JNI_ReadAloudController_Pause(JNIEnv* env,
-                                          jlong read_aloud_service_ptr) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::Stop(JNIEnv* env) {
+  if (service_) {
+    service_->Stop();
   }
-  service->Pause();
 }
 
-static void JNI_ReadAloudController_Stop(JNIEnv* env,
-                                         jlong read_aloud_service_ptr) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::SeekToWordIndex(JNIEnv* env, jint word_index) {
+  if (service_) {
+    service_->SeekToWordIndex(word_index);
   }
-  service->Stop();
 }
 
-static void JNI_ReadAloudController_SeekToWordIndex(
-    JNIEnv* env,
-    jlong read_aloud_service_ptr,
-    jint word_index) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::Seek(JNIEnv* env, jlong absolute_time_nanos) {
+  if (service_ && absolute_time_nanos >= 0) {
+    service_->Seek(base::Nanoseconds(absolute_time_nanos));
   }
-  service->SeekToWordIndex(word_index);
 }
 
-static void JNI_ReadAloudController_Seek(JNIEnv* env,
-                                         jlong read_aloud_service_ptr,
-                                         jlong absolute_time_nanos) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::SeekRelative(JNIEnv* env, jlong offset_nanos) {
+  if (service_) {
+    service_->SeekRelative(base::Nanoseconds(offset_nanos));
   }
-  if (absolute_time_nanos < 0) {
-    return;
-  }
-  service->Seek(base::Nanoseconds(absolute_time_nanos));
 }
 
-static void JNI_ReadAloudController_SeekRelative(JNIEnv* env,
-                                                 jlong read_aloud_service_ptr,
-                                                 jlong offset_nanos) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::SetPlaybackRate(JNIEnv* env, jfloat rate) {
+  if (service_) {
+    service_->SetPlaybackRate(rate);
   }
-  service->SeekRelative(base::Nanoseconds(offset_nanos));
 }
 
-static void JNI_ReadAloudController_SetPlaybackRate(
-    JNIEnv* env,
-    jlong read_aloud_service_ptr,
-    jfloat rate) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::SetVoice(JNIEnv* env, const std::string& voice_id) {
+  if (service_) {
+    service_->SetVoice(voice_id);
   }
-  service->SetPlaybackRate(rate);
 }
 
-static void JNI_ReadAloudController_SetVoice(JNIEnv* env,
-                                             jlong read_aloud_service_ptr,
-                                             const std::string& voice_id) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::PreviewVoice(JNIEnv* env, const std::string& voice_id) {
+  if (service_) {
+    service_->PreviewVoice(voice_id);
   }
-  service->SetVoice(voice_id);
 }
 
-static void JNI_ReadAloudController_PreviewVoice(JNIEnv* env,
-                                                 jlong read_aloud_service_ptr,
-                                                 const std::string& voice_id) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::StopVoicePreview(JNIEnv* env) {
+  if (service_) {
+    service_->StopVoicePreview();
   }
-  service->PreviewVoice(voice_id);
 }
 
-static void JNI_ReadAloudController_StopVoicePreview(
-    JNIEnv* env,
-    jlong read_aloud_service_ptr) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::SetPlaybackMode(JNIEnv* env, jint mode) {
+  if (service_) {
+    service_->SetPlaybackMode(
+        static_cast<ReadAloudService::PlaybackMode>(mode));
   }
-  service->StopVoicePreview();
 }
 
-static void JNI_ReadAloudController_SetPlaybackMode(
-    JNIEnv* env,
-    jlong read_aloud_service_ptr,
-    jint mode) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::SetHighlightingEnabled(JNIEnv* env, jboolean enabled) {
+  if (service_) {
+    service_->SetHighlightingEnabled(enabled);
   }
-  service->SetPlaybackMode(static_cast<ReadAloudService::PlaybackMode>(mode));
 }
 
-static void JNI_ReadAloudController_SetHighlightingEnabled(
-    JNIEnv* env,
-    jlong read_aloud_service_ptr,
-    jboolean enabled) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::SendFeedback(JNIEnv* env, jint feedback_type) {
+  if (service_) {
+    service_->SendFeedback(
+        static_cast<ReadAloudService::FeedbackType>(feedback_type));
   }
-  service->SetHighlightingEnabled(enabled);
 }
 
-static void JNI_ReadAloudController_SendFeedback(JNIEnv* env,
-                                                 jlong read_aloud_service_ptr,
-                                                 jint feedback_type) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::CheckReadability(JNIEnv* env, const GURL& url) {
+  if (service_) {
+    service_->CheckReadability(url);
   }
-  service->SendFeedback(
-      static_cast<ReadAloudService::FeedbackType>(feedback_type));
 }
 
-static void JNI_ReadAloudController_CheckReadability(
-    JNIEnv* env,
-    jlong read_aloud_service_ptr,
-    const GURL& url) {
-  auto* service = reinterpret_cast<ReadAloudService*>(read_aloud_service_ptr);
-  if (!service) {
-    return;
+void ReadAloudBridge::Destroy(JNIEnv* env) {
+  weak_java_native_bridge_.reset();
+  if (service_) {
+    service_->SetDelegate(nullptr);
   }
-  service->CheckReadability(url);
 }
 
 }  // namespace readaloud
 
-DEFINE_JNI(ReadAloudController)
+DEFINE_JNI(ReadAloudNativeBridge)
diff --git a/chrome/browser/readaloud/android/read_aloud_bridge.h b/chrome/browser/readaloud/android/read_aloud_bridge.h
index 65a8dc1..5040833e 100644
--- a/chrome/browser/readaloud/android/read_aloud_bridge.h
+++ b/chrome/browser/readaloud/android/read_aloud_bridge.h
@@ -13,18 +13,34 @@
 #include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
 #include "chrome/browser/readaloud/read_aloud_service.h"
+#include "content/public/browser/web_contents.h"
 #include "third_party/jni_zero/jni_zero.h"
 #include "url/gurl.h"
 
 namespace readaloud {
 
-// C++ JNI bridge that implements ReadAloudService::Delegate to marshal events
-// from the native service to the Java ReadAloudController, and hosts the JNI
-// entry points for inbound controller commands.
+// C++ JNI bridge class managing 2-way communications between native
+// ReadAloudService and Java ReadAloudNativeBridge.
+//
+// Objective:
+// Implements ReadAloudService::Delegate to marshal engine events (metadata,
+// playback progress, state transitions, readability results) from C++ to Java,
+// and hosts JNI Zero instance method dispatch targets for inbound Java playback
+// control commands (Play, Pause, Stop, Seek, etc.).
+//
+// Lifecycle & Threading:
+// - Instantiated in JNI_ReadAloudNativeBridge_Init() when the Java
+//   ReadAloudNativeBridge initializes for a Profile.
+// - Registered as the ReadAloudService::Delegate via service->SetDelegate().
+// - Retains a JavaObjectWeakGlobalRef to ReadAloudNativeBridge to prevent
+// Activity leaks.
+// - Destroyed when ReadAloudNativeBridge.destroy() calls Destroy() or when
+//   the profile/ReadAloudService is torn down.
+// - All JNI calls and delegate callbacks execute on the UI thread.
 class ReadAloudBridge : public ReadAloudService::Delegate {
  public:
   ReadAloudBridge(JNIEnv* env,
-                  const jni_zero::JavaRef<jobject>& j_controller,
+                  const jni_zero::JavaRef<jobject>& j_native_bridge,
                   ReadAloudService* service);
 
   ReadAloudBridge(const ReadAloudBridge&) = delete;
@@ -32,7 +48,7 @@
 
   ~ReadAloudBridge() override;
 
-  // ReadAloudService::Delegate:
+  // ReadAloudService::Delegate (C++ -> Java Callbacks):
   void OnMetadataAvailable(std::string_view title,
                            std::string_view publisher) override;
   void OnPlaybackProgressUpdated(base::TimeDelta elapsed,
@@ -52,11 +68,28 @@
   void OnReadabilityResult(const GURL& url, bool is_readable) override;
   void OnNativeDestroyed() override;
 
+  // JNI Zero instance method dispatch targets (Java -> C++ Commands):
+  void Play(JNIEnv* env, content::WebContents* web_contents);
+  void Pause(JNIEnv* env);
+  void Stop(JNIEnv* env);
+  void SeekToWordIndex(JNIEnv* env, jint word_index);
+  void Seek(JNIEnv* env, jlong absolute_time_nanos);
+  void SeekRelative(JNIEnv* env, jlong offset_nanos);
+  void SetPlaybackRate(JNIEnv* env, jfloat rate);
+  void SetVoice(JNIEnv* env, const std::string& voice_id);
+  void PreviewVoice(JNIEnv* env, const std::string& voice_id);
+  void StopVoicePreview(JNIEnv* env);
+  void SetPlaybackMode(JNIEnv* env, jint mode);
+  void SetHighlightingEnabled(JNIEnv* env, jboolean enabled);
+  void SendFeedback(JNIEnv* env, jint feedback_type);
+  void CheckReadability(JNIEnv* env, const GURL& url);
+  void Destroy(JNIEnv* env);
+
  private:
-  // Weak global reference to the Java ReadAloudController instance to prevent
-  // Activity leaks.
-  JavaObjectWeakGlobalRef weak_java_controller_;
-  // Pointer to the native service owning this delegate.
+  // Weak global reference to Java ReadAloudNativeBridge to prevent Activity
+  // leaks.
+  JavaObjectWeakGlobalRef weak_java_native_bridge_;
+  // ReadAloudService pointer (for handling Java -> native calls).
   raw_ptr<ReadAloudService> service_;
 };
 
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 620b0ebc..d6ac45c7 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -429,6 +429,9 @@
   return callback.get();
 }
 
+// This IDC_ "value" is a sentinel for the UMA max value.
+constexpr int kUmaMaxValueKey = 0;
+
 // LINT.IfChange(GlicWebContentsContextMenuResult)
 enum class GlicWebContentsContextMenuResult {
   kShownAndIgnored = 0,
@@ -651,7 +654,7 @@
        //   - Increment the UMA value in that latter line.
        //   - Add the new item to the RenderViewContextMenuItem enum in
        //     tools/metrics/histograms/metadata/ui/enums.xml.
-       {0, 170}});
+       {kUmaMaxValueKey, 170}});
   // LINT.ThenChange(//tools/metrics/histograms/metadata/ui/enums.xml:RenderViewContextMenuItem)
 
   // LINT.IfChange(ContextMenuOptionDesktop)
@@ -698,7 +701,7 @@
        //   - Increment the UMA value in that latter line.
        //   - Add the new item to the ContextMenuOptionDesktop enum in
        //     tools/metrics/histograms/metadata/ui/enums.xml.
-       {0, 35}});
+       {kUmaMaxValueKey, 35}});
   // LINT.ThenChange(//tools/metrics/histograms/metadata/ui/enums.xml:ContextMenuOptionDesktop)
 
   return *(type == UmaEnumIdLookupType::GeneralEnumId ? kGeneralMap
@@ -706,8 +709,7 @@
 }
 
 int GetUmaValueMax(UmaEnumIdLookupType type) {
-  // The IDC_ "value" of 0 is really a sentinel for the UMA max value.
-  return GetIdcToUmaMap(type).find(0)->second;
+  return GetIdcToUmaMap(type).find(kUmaMaxValueKey)->second;
 }
 
 // Collapses large ranges of ids before looking for UMA enum.
@@ -1682,56 +1684,56 @@
     return;
   }
 
+  const int uma_value_max =
+      GetUmaValueMax(UmaEnumIdLookupType::ContextSpecificEnumId);
   if (content_type_->SupportsGroup(
           ContextMenuContentType::ITEM_GROUP_MEDIA_VIDEO)) {
-    UMA_HISTOGRAM_EXACT_LINEAR(
-        "ContextMenu.SelectedOptionDesktop.Video", enum_id,
-        GetUmaValueMax(UmaEnumIdLookupType::ContextSpecificEnumId));
+    UMA_HISTOGRAM_EXACT_LINEAR("ContextMenu.SelectedOptionDesktop.Video",
+                               enum_id, uma_value_max);
   } else if (content_type_->SupportsGroup(
                  ContextMenuContentType::ITEM_GROUP_LINK) &&
              content_type_->SupportsGroup(
                  ContextMenuContentType::ITEM_GROUP_MEDIA_IMAGE)) {
-    UMA_HISTOGRAM_EXACT_LINEAR(
-        "ContextMenu.SelectedOptionDesktop.ImageLink", enum_id,
-        GetUmaValueMax(UmaEnumIdLookupType::ContextSpecificEnumId));
+    UMA_HISTOGRAM_EXACT_LINEAR("ContextMenu.SelectedOptionDesktop.ImageLink",
+                               enum_id, uma_value_max);
   } else if (content_type_->SupportsGroup(
                  ContextMenuContentType::ITEM_GROUP_MEDIA_IMAGE)) {
-    UMA_HISTOGRAM_EXACT_LINEAR(
-        "ContextMenu.SelectedOptionDesktop.Image", enum_id,
-        GetUmaValueMax(UmaEnumIdLookupType::ContextSpecificEnumId));
+    UMA_HISTOGRAM_EXACT_LINEAR("ContextMenu.SelectedOptionDesktop.Image",
+                               enum_id, uma_value_max);
   } else if (!params_.misspelled_word.empty()) {
     UMA_HISTOGRAM_EXACT_LINEAR(
         "ContextMenu.SelectedOptionDesktop.MisspelledWord", enum_id,
-        GetUmaValueMax(UmaEnumIdLookupType::ContextSpecificEnumId));
+        uma_value_max);
   } else if ((!params_.selection_text.empty() ||
               params_.annotation_type.has_value()) &&
              params_.media_type == ContextMenuDataMediaType::kNone) {
     // Probably just text.
-    UMA_HISTOGRAM_EXACT_LINEAR(
-        "ContextMenu.SelectedOptionDesktop.SelectedText", enum_id,
-        GetUmaValueMax(UmaEnumIdLookupType::ContextSpecificEnumId));
+    UMA_HISTOGRAM_EXACT_LINEAR("ContextMenu.SelectedOptionDesktop.SelectedText",
+                               enum_id, uma_value_max);
   } else {
-    UMA_HISTOGRAM_EXACT_LINEAR(
-        "ContextMenu.SelectedOptionDesktop.Other", enum_id,
-        GetUmaValueMax(UmaEnumIdLookupType::ContextSpecificEnumId));
+    UMA_HISTOGRAM_EXACT_LINEAR("ContextMenu.SelectedOptionDesktop.Other",
+                               enum_id, uma_value_max);
   }
 }
 
 void RenderViewContextMenu::RecordShownItem(int id, bool is_submenu) {
   // The "RenderViewContextMenu.Shown" histogram is not recorded for submenus.
-  if (!is_submenu) {
-    int enum_id =
-        FindUMAEnumValueForCommand(id, UmaEnumIdLookupType::GeneralEnumId);
-    if (enum_id != -1) {
-      UMA_HISTOGRAM_EXACT_LINEAR(
-          "RenderViewContextMenu.Shown", enum_id,
-          GetUmaValueMax(UmaEnumIdLookupType::GeneralEnumId));
-    } else {
-      // Just warning here. It's harder to maintain list of all possibly
-      // visible items than executable items.
-      DLOG(ERROR) << "Update GetIdcToUmaMap. Unhandled IDC: " << id;
-    }
+  if (is_submenu) {
+    return;
   }
+
+  int enum_id =
+      FindUMAEnumValueForCommand(id, UmaEnumIdLookupType::GeneralEnumId);
+  if (enum_id == -1) {
+    // Just warning here. It's harder to maintain list of all possibly
+    // visible items than executable items.
+    DLOG(ERROR) << "Update GetIdcToUmaMap. Unhandled IDC: " << id;
+    return;
+  }
+
+  UMA_HISTOGRAM_EXACT_LINEAR(
+      "RenderViewContextMenu.Shown", enum_id,
+      GetUmaValueMax(UmaEnumIdLookupType::GeneralEnumId));
 }
 
 bool RenderViewContextMenu::IsHTML5Fullscreen() const {
diff --git a/chrome/browser/resources/context_hub/taskbox/ai_taskbox.html.ts b/chrome/browser/resources/context_hub/taskbox/ai_taskbox.html.ts
index 60903c9..72fc529 100644
--- a/chrome/browser/resources/context_hub/taskbox/ai_taskbox.html.ts
+++ b/chrome/browser/resources/context_hub/taskbox/ai_taskbox.html.ts
@@ -21,11 +21,11 @@
             <!-- Gmail Todos Section -->
             <section class="todo-column">
                 <div class="column-header">
-                    <h2>Gmail Todos</h2>
+                    <h2>Workspace Todos</h2>
                     <cr-button class="tonal-button"
                         ?disabled="${!this.autoTodosEnabled_ || this.isGeneratingGmailTodos_}"
                         @click="${this.onGenerateGmailTodosClick_}">
-                      ${this.isGeneratingGmailTodos_ ? 'Generating...' : 'Generate my Gmail Todos'}
+                      ${this.isGeneratingGmailTodos_ ? 'Generating...' : 'Generate Workspace Todos'}
                     </cr-button>
                 </div>
 
@@ -46,15 +46,15 @@
                     `) :
                               this.hasGmailGenerationError_ ? html`
                       <div class="placeholder-card">
-                        <p class="placeholder-text error-text">Failed to generate Gmail Todos. Please try again.</p>
+                        <p class="placeholder-text error-text">Failed to generate. Please try again.</p>
                       </div>
                     ` : this.hasGeneratedGmail_ ? html`
                       <div class="placeholder-card">
-                        <p class="placeholder-text">You're all caught up! No Gmail Todos found.</p>
+                        <p class="placeholder-text">You're all caught up!</p>
                       </div>
                     ` : html`
                       <div class="placeholder-card">
-                        <p class="placeholder-text">No Gmail Todos yet.</p>
+                        <p class="placeholder-text">No Workspace Todos yet.</p>
                       </div>
                     `}
                 </div>
@@ -63,10 +63,10 @@
             <!-- Tab-based Todos Section -->
             <section class="todo-column">
                 <div class="column-header">
-                    <h2>Tab-based Todos</h2>
+                    <h2>Browser Todos</h2>
                     <cr-button class="tonal-button" disabled
                         @click="${this.onGenerateTabTodosClick_}">
-                      ${this.isGeneratingTabTodos_ ? 'Generating...' : 'Generate my Tab Todos'}
+                      ${this.isGeneratingTabTodos_ ? 'Generating...' : 'Generate Browser Todos'}
                     </cr-button>
                 </div>
 
@@ -83,26 +83,16 @@
                     `) :
       this.hasTabGenerationError_ ? html`
                       <div class="placeholder-card">
-                        <p class="placeholder-text error-text">Failed to generate Tab Todos. Please try again.</p>
+                        <p class="placeholder-text error-text">Failed to generate. Please try again.</p>
                       </div>
                     ` : this.hasGeneratedTab_ ? html`
                       <div class="placeholder-card">
-                        <p class="placeholder-text">You're all caught up! No Tab Todos found.</p>
+                        <p class="placeholder-text">You're all caught up!</p>
                       </div>
                     ` : html`
-                      <todo-item
-                          .id="${'placeholder-tab-1'}"
-                          .heading="${'Example Tab One'}"
-                          .description="${
-              'Page hasn\'t been active in X days and contains an unfinished action'}"
-                          .variant="${TodoItemVariant.TAB}">
-                      </todo-item>
-                      <todo-item
-                          .id="${'placeholder-tab-2'}"
-                          .heading="${'Example Tab Two'}"
-                          .description="${'Page hasn\'t been active in X days'}"
-                          .variant="${TodoItemVariant.TAB}">
-                      </todo-item>
+                      <div class="placeholder-card">
+                        <p class="placeholder-text">No Browser Todos yet.</p>
+                      </div>
                     `}
                 </div>
             </section>
diff --git a/chrome/browser/resources/contextual_tasks/overflow_menu.ts b/chrome/browser/resources/contextual_tasks/overflow_menu.ts
index d1eec55fc..0f5c7b3 100644
--- a/chrome/browser/resources/contextual_tasks/overflow_menu.ts
+++ b/chrome/browser/resources/contextual_tasks/overflow_menu.ts
@@ -58,6 +58,8 @@
       contextualTasksEnableSpatialModelToolbarLayoutNewThreadInOverflow:
           {type: Boolean},
       isAimEligible: {type: Boolean},
+      isCobrowseEligible: {type: Boolean},
+      isHandshakeComplete: {type: Boolean},
       webuiRoundedIconsEnabled_: {type: Boolean},
     };
   }
@@ -79,6 +81,8 @@
       boolean = loadTimeData.getBoolean(
           'contextualTasksEnableSpatialModelToolbarLayoutNewThreadInOverflow');
   accessor isAimEligible: boolean = false;
+  accessor isCobrowseEligible: boolean = false;
+  accessor isHandshakeComplete: boolean = false;
 
   protected accessor webuiRoundedIconsEnabled_: boolean =
       loadTimeData.getBoolean('webuiRoundedIconsEnabled');
@@ -145,7 +149,8 @@
   }
 
   protected shouldShowPinButton_(): boolean {
-    return this.isPinButtonEnabled && this.isAiPage;
+    return this.isPinButtonEnabled && this.isAiPage &&
+        this.isCobrowseEligible && this.isHandshakeComplete;
   }
 
   protected getPinButtonTooltip_(): string {
diff --git a/chrome/browser/resources/contextual_tasks/top_toolbar.html.ts b/chrome/browser/resources/contextual_tasks/top_toolbar.html.ts
index e2b8720..938d8b5 100644
--- a/chrome/browser/resources/contextual_tasks/top_toolbar.html.ts
+++ b/chrome/browser/resources/contextual_tasks/top_toolbar.html.ts
@@ -106,6 +106,8 @@
         .isPinButtonEnabled="${this.isPinButtonEnabled}"
         .isAiPage="${this.isAiPage}"
         .isAimEligible="${this.isAimEligible}"
+        .isCobrowseEligible="${this.isCobrowseEligible}"
+        .isHandshakeComplete="${this.isHandshakeComplete}"
         .contextualTasksEnableSpatialModelToolbarLayout="${this.contextualTasksEnableSpatialModelToolbarLayout_}"
         .contextualTasksEnableSpatialModelToolbarLayoutNewThreadInOverflow="${this.contextualTasksEnableSpatialModelToolbarLayoutNewThreadInOverflow_}"
         @pin-click="${this.onPinClick_}"
diff --git a/chrome/browser/resources/contextual_tasks/top_toolbar.ts b/chrome/browser/resources/contextual_tasks/top_toolbar.ts
index 210fc11..e3393dde 100644
--- a/chrome/browser/resources/contextual_tasks/top_toolbar.ts
+++ b/chrome/browser/resources/contextual_tasks/top_toolbar.ts
@@ -95,6 +95,8 @@
         type: Boolean,
         reflect: true,
       },
+      isCobrowseEligible: {type: Boolean},
+      isHandshakeComplete: {type: Boolean},
       isUserSignedIn: {type: Boolean},
       onboardingTooltipShowing: {type: Boolean},
       lensSearchTooltipShowing: {type: Boolean},
@@ -113,6 +115,9 @@
   accessor darkMode: boolean = false;
   accessor isAiPage: boolean = loadTimeData.getBoolean('isAiPage');
   accessor isAimEligible: boolean = loadTimeData.getBoolean('isAimEligible');
+  accessor isCobrowseEligible: boolean =
+      loadTimeData.getBoolean('isCobrowseEligible');
+  accessor isHandshakeComplete: boolean = false;
   accessor permissionDashboardState: PermissionDashboardState|null = null;
   protected accessor isSidePanelRearchitectureEnabled_: boolean =
       loadTimeData.getBoolean('contextualTasksSidePanelRearchitectureEnabled');
@@ -161,6 +166,9 @@
       callbackRouter.setExpandButtonEnabled.addListener((enabled: boolean) => {
         this.isExpandButtonEnabled = enabled;
       }),
+      callbackRouter.onHandshakeComplete.addListener(() => {
+        this.isHandshakeComplete = true;
+      }),
     ];
   }
 
@@ -196,6 +204,9 @@
         changedProperties.has('lensSearchTooltipShowing')) {
       this.hideOverflowMenuButton_ =
           this.isAiPage && this.hideOverflowMenuOnAiPageEnabled_;
+      if (changedProperties.has('isAiPage') && !this.isAiPage) {
+        this.isHandshakeComplete = false;
+      }
       // <if expr="not is_android">
       if (this.isAiPage) {
         if (!this.onboardingTooltipShowing && !this.lensSearchTooltipShowing) {
@@ -214,15 +225,6 @@
          !!this.permissionDashboardState?.requestChip?.isVisible);
   }
 
-  protected shouldShowPinButton_(): boolean {
-    return this.isPinButtonEnabled && this.isAiPage;
-  }
-
-  protected getPinButtonTooltip_(): string {
-    return this.isPinned ? loadTimeData.getString('unpinTooltip') :
-                           loadTimeData.getString('pinTooltip');
-  }
-
   protected shouldShowSourcesMenuButton_(): boolean {
     return this.contextInfos.length > 0;
   }
diff --git a/chrome/browser/resources/glic/BUILD.gn b/chrome/browser/resources/glic/BUILD.gn
index 1cb4468..2afadec 100644
--- a/chrome/browser/resources/glic/BUILD.gn
+++ b/chrome/browser/resources/glic/BUILD.gn
@@ -32,7 +32,6 @@
     "glic_api_impl/skills/skills_types.ts",
     "glic_api_impl/client/glic_api_client.ts",
     "glic_api_impl/client/image_utils.ts",
-    "glic_api_impl/client/glic_client_common.ts",
     "glic_api_impl/client/observable_set_by_tab_id.ts",
     "glic_api_impl/conversions.ts",
     "glic_api_impl/experimental_triggering/experimental_triggering_client.ts",
@@ -48,6 +47,10 @@
     "glic_api_impl/request_types.ts",
     "glic_api_impl/transport/messaging.ts",
     "glic_api_impl/transport/post_message_transport.ts",
+    "glic_api_impl/zero_state_suggestions/zero_state_suggestions_client.ts",
+    "glic_api_impl/zero_state_suggestions/zero_state_suggestions_host.ts",
+    "glic_api_impl/zero_state_suggestions/zero_state_suggestions_types.ts",
+    "glic_api_impl/client/glic_client_common.ts",
     "glic_api/glic_api.ts",
     "glic_api/glic_api_generated.ts",
     "glic_app_controller.ts",
diff --git a/chrome/browser/resources/glic/experimental_opt_in/BUILD.gn b/chrome/browser/resources/glic/experimental_opt_in/BUILD.gn
index 1e30e726..eb1a4bf 100644
--- a/chrome/browser/resources/glic/experimental_opt_in/BUILD.gn
+++ b/chrome/browser/resources/glic/experimental_opt_in/BUILD.gn
@@ -27,6 +27,5 @@
     "//tools/typescript/definitions/chrome_event.d.ts",
     "//tools/typescript/definitions/chrome_histograms.d.ts",
     "//tools/typescript/definitions/webview_tag.d.ts",
-    "//tools/typescript/definitions/runtime.d.ts",
   ]
 }
diff --git a/chrome/browser/resources/glic/experimental_opt_in/experimental_opt_in.ts b/chrome/browser/resources/glic/experimental_opt_in/experimental_opt_in.ts
index ac9ad53..b729d377 100644
--- a/chrome/browser/resources/glic/experimental_opt_in/experimental_opt_in.ts
+++ b/chrome/browser/resources/glic/experimental_opt_in/experimental_opt_in.ts
@@ -156,85 +156,6 @@
       this.webview_.classList.add('autosized');
       this.webview_.hidden = false;
 
-      if (loadTimeData.getBoolean('glicOptInDialogLinkA11yFixEnabled')) {
-        // Inject script to add aria-labels to the links for accessibility.
-        const safelyLabel = loadTimeData.getString('safelyLinkLabel');
-        const unexpectedResultsLabel =
-            loadTimeData.getString('unexpectedResultsLinkLabel');
-        const reviewRisksLabel = loadTimeData.getString('reviewRisksLinkLabel');
-
-        const code = `
-          (function() {
-            const safelyLabel = ${JSON.stringify(safelyLabel)};
-            const unexpectedResultsLabel = ${
-            JSON.stringify(unexpectedResultsLabel)};
-            const reviewRisksLabel = ${JSON.stringify(reviewRisksLabel)};
-
-            // We match against default substrings from URL feature parameters defined in
-            // chrome/common/chrome_features.cc (kGlicWebActuationToggleConsiderSafelyURL,
-            // kGlicWebActuationToggleConsiderUnexpectedResultsURL, and
-            // kGlicExperimentalTriggeringSafetyURL). If those URLs are overridden to different
-            // domains, this matching logic will need to be updated.
-            function updateLink(link) {
-              const href = link.getAttribute('href');
-              if (href) {
-                if (href.includes('use-policy')) {
-                  link.setAttribute('aria-label', safelyLabel);
-                } else if (href.includes('unexpected_results')) {
-                  link.setAttribute('aria-label', unexpectedResultsLabel);
-                } else if (href.includes('gemini_spark_safety')) {
-                  link.setAttribute('aria-label', reviewRisksLabel);
-                }
-              }
-            }
-
-            // Update existing links immediately.
-            const links = document.querySelectorAll('a');
-            for (const link of links) {
-              updateLink(link);
-            }
-
-            // Observe future changes for dynamic content.
-            const observer = new MutationObserver((mutations) => {
-              for (const mutation of mutations) {
-                if (mutation.type === 'childList') {
-                  for (const node of mutation.addedNodes) {
-                    if (node.nodeType === Node.ELEMENT_NODE) {
-                      if (node.tagName === 'A') {
-                        updateLink(node);
-                      } else {
-                        const childLinks = node.querySelectorAll('a');
-                        for (const link of childLinks) {
-                          updateLink(link);
-                        }
-                      }
-                    }
-                  }
-                } else if (
-                    mutation.type === 'attributes' &&
-                    mutation.target.tagName === 'A') {
-                  updateLink(mutation.target);
-                }
-              }
-            });
-
-            observer.observe(document.documentElement, {
-              childList: true,
-              subtree: true,
-              attributes: true,
-              attributeFilter: ['href']
-            });
-          })();
-        `;
-        this.webview_.executeScript({code}, () => {
-          if (chrome.runtime.lastError) {
-            console.warn(
-                'Failed to inject accessibility labels: ' +
-                chrome.runtime.lastError.message);
-          }
-        });
-      }
-
       if (this.isInitialLoad_) {
         this.isInitialLoad_ = false;
         this.scheduleInstantHeadingChecks_();
diff --git a/chrome/browser/resources/glic/glic_api_impl/client/glic_api_client.ts b/chrome/browser/resources/glic/glic_api_impl/client/glic_api_client.ts
index 0ca1398..09c38c0 100644
--- a/chrome/browser/resources/glic/glic_api_impl/client/glic_api_client.ts
+++ b/chrome/browser/resources/glic/glic_api_impl/client/glic_api_client.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {CaptureRegionErrorReason, HostCapability} from '../../glic_api/glic_api.js';
-import type {ActivateTabOptions, AdditionalContext, AnnotatedPageData, CaptureRegionParams, CaptureRegionResult, ChromeVersion, ClientCapabilities, ClientErrorDialogType, ConversationInfo, CounterAbuseVerdict, CreateTabOptions, FileUploadPolicyState, FocusedTabData, FormFactor, GeminiEnterpriseSettings, GetPinCandidatesOptions, GlicBrowserHost, GlicBrowserHostMetrics, GlicHostRegistry, GlicWebClient, ImageBytesResult, ImageInfo, InvokeOptions, MicrophoneStatus, Observable, ObservableValue, OnResponseStoppedDetails, OpenPanelInfo, OpenSettingsOptions, PageMetadata, PanelOpeningData, PanelState, PdfDocumentData, PinCandidate, PinTabsOptions, Platform, ResizeWindowOptions, ResumeActorTaskResult, Screenshot, TabContextOptions, TabContextResult, TabData, UnpinTabsOptions, UserProfileInfo, WebClientMode, ZeroStateSuggestions, ZeroStateSuggestionsOptions, ZeroStateSuggestionsV2} from '../../glic_api/glic_api.js';
+import type {ActivateTabOptions, AdditionalContext, AnnotatedPageData, CaptureRegionParams, CaptureRegionResult, ChromeVersion, ClientCapabilities, ClientErrorDialogType, ConversationInfo, CounterAbuseVerdict, CreateTabOptions, FileUploadPolicyState, FocusedTabData, FormFactor, GeminiEnterpriseSettings, GetPinCandidatesOptions, GlicBrowserHost, GlicBrowserHostMetrics, GlicHostRegistry, GlicWebClient, ImageBytesResult, ImageInfo, InvokeOptions, MicrophoneStatus, Observable, ObservableValue, OnResponseStoppedDetails, OpenPanelInfo, OpenSettingsOptions, PageMetadata, PanelOpeningData, PanelState, PdfDocumentData, PinCandidate, PinTabsOptions, Platform, ResizeWindowOptions, ResumeActorTaskResult, Screenshot, TabContextOptions, TabContextResult, TabData, UnpinTabsOptions, UserProfileInfo, WebClientMode, ZeroStateSuggestions} from '../../glic_api/glic_api.js';
 import {ObservableValue as ObservableValueImpl, Subject} from '../../observable.js';
 import {GlicBrowserHostActor} from '../actor/actor_client.js';
 import {GlicBrowserHostAnnotation} from '../annotation/annotation_client.js';
@@ -12,6 +12,7 @@
 import {assertNever} from '../transport/messaging.js';
 import {createBidirectionalPostMessageTransport} from '../transport/post_message_transport.js';
 import type {PendingRemote, PostMessageHandler, PostMessageReceiver, PostMessageRemote, PostMessageRouter} from '../transport/post_message_transport.js';
+import {GlicBrowserHostZeroStateSuggestions} from '../zero_state_suggestions/zero_state_suggestions_client.js';
 
 import {replaceProperties} from './../conversions.js';
 import {ERROR_CODEC, ErrorWithReasonImpl, newTransferableException, WebClientDef, WebClientHostDef, WebClientPinCandidatesObserverDef, WebClientRegionCaptureDef, WebClientTabDataObserverDef, WebClientTabFaviconObserverDef} from './../request_types.js';
@@ -96,12 +97,6 @@
     this.host.getPanelState?.().assignAndSignal(payload.panelState);
   }
 
-  zeroStateSuggestionsChanged(payload: {
-    suggestions: ZeroStateSuggestionsV2,
-    options: ZeroStateSuggestionsOptions,
-  }): void {
-    this.host.currentZeroStateObserver?.assignAndSignal(payload.suggestions);
-  }
 
   canAttachStateChanged(payload: {canAttach: boolean}): void {
     this.host.canAttachPanelValue.assignAndSignal(payload.canAttach);
@@ -292,6 +287,8 @@
   readonly skillsClient: GlicBrowserHostSkills;
   readonly experimentalTriggeringClient =
       new GlicBrowserHostExperimentalTriggering();
+  readonly suggestionsClient: GlicBrowserHostZeroStateSuggestions;
+
   private chromeVersion?: ChromeVersion;
   private platform?: Platform;
   private formFactor?: FormFactor;
@@ -335,12 +332,6 @@
   pinCandidates: PinCandidatesObservable|undefined;
   captureRegionObservable?: CaptureRegionObservable;
 
-  private currentZeroStateSuggestionOptions: ZeroStateSuggestionsOptions = {
-    isFirstRun: false,
-    supportedTools: [],
-  };
-  currentZeroStateObserver =
-      ObservableValueImpl.withNoValue<ZeroStateSuggestionsV2>();
   private hostCapabilities: Set<HostCapability> = new Set();
   readonly additionalContextSubject = new Subject<AdditionalContext>();
   pageMetadataObservers: Map<string, ObservableValueImpl<PageMetadata>> =
@@ -372,6 +363,7 @@
     this.actorClient = new GlicBrowserHostActor(this);
     this.annotationClient = new GlicBrowserHostAnnotation(this);
     this.skillsClient = new GlicBrowserHostSkills();
+    this.suggestionsClient = new GlicBrowserHostZeroStateSuggestions(this);
 
     this.getTabByIdObservableSet =
         new ObservableSetByTabId<TabData, WebClientTabDataObserver>(
@@ -386,6 +378,7 @@
       this.actorClient,
       this.annotationClient,
       this.skillsClient,
+      this.suggestionsClient,
     ]);
     type UnimplementedApis = Exclude<keyof GlicBrowserHost, keyof typeof proxy>;
     assertNever<UnimplementedApis>();
@@ -409,6 +402,9 @@
     this.experimentalTriggeringClient.initialize(
         this.router, response.experimentalTriggeringReceiver, this.webClient,
         this.clientRemote);
+    this.suggestionsClient.initialize(
+        response.initialState, response.zeroStateSuggestionsRemote);
+
     const state = response.initialState;
     this.geminiEnterpriseSettings.assignAndSignal(
         state.geminiEnterpriseSettings ?? undefined);
@@ -469,7 +465,6 @@
 
     if (!state.enableZeroStateSuggestions) {
       this.getZeroStateSuggestionsForFocusedTab = undefined;
-      this.getZeroStateSuggestions = undefined;
     }
 
     if (!state.enableDefaultTabContextSettingFeature) {
@@ -949,36 +944,6 @@
     return zeroStateResult.suggestions;
   }
 
-  private async zeroStateActiveSubscriptionStateChanged(
-      options: ZeroStateSuggestionsOptions, hasActiveSubscription: boolean) {
-    if (options !== this.currentZeroStateSuggestionOptions) {
-      // Dont send out of date updates.
-      return;
-    }
-    const zeroStateResult = await this.clientRemote.requestWithResponse(
-        'getZeroStateSuggestionsAndSubscribe', {
-          hasActiveSubscription: hasActiveSubscription,
-          options: options,
-        });
-    if (zeroStateResult.suggestions) {
-      this.currentZeroStateObserver?.assignAndSignal(
-          zeroStateResult.suggestions);
-    }
-  }
-
-  getZeroStateSuggestions?(options?: ZeroStateSuggestionsOptions):
-      ObservableValueImpl<ZeroStateSuggestionsV2> {
-    options = options ?? {
-      isFirstRun: false,
-      supportedTools: [],
-    };
-    this.currentZeroStateSuggestionOptions = options;
-    this.currentZeroStateObserver =
-        ObservableValueImpl.withNoValue<ZeroStateSuggestionsV2>(
-            this.zeroStateActiveSubscriptionStateChanged.bind(this, options));
-    return this.currentZeroStateObserver;
-  }
-
 
   maybeRefreshUserStatus?(): void {
     this.cachedUserProfile = undefined;
diff --git a/chrome/browser/resources/glic/glic_api_impl/host/glic_api_host.ts b/chrome/browser/resources/glic/glic_api_impl/host/glic_api_host.ts
index 30a03be..67856a45 100644
--- a/chrome/browser/resources/glic/glic_api_impl/host/glic_api_host.ts
+++ b/chrome/browser/resources/glic/glic_api_impl/host/glic_api_host.ts
@@ -9,7 +9,7 @@
 import {loadTimeData} from '//resources/js/load_time_data.js';
 
 import type {BrowserProxy} from '../../browser_proxy.js';
-import {ActorClientReceiver, ActorHandlerRemote, AnnotationHandlerRemote, ExperimentalTriggeringClientReceiver, SkillsClientReceiver, SkillsHandlerRemote, WebClientHandlerRemote} from '../../glic.mojom-webui.js';
+import {ActorClientReceiver, ActorHandlerRemote, AnnotationHandlerRemote, ExperimentalTriggeringClientReceiver, SkillsClientReceiver, SkillsHandlerRemote, WebClientHandlerRemote, ZeroStateSuggestionsHandlerRemote} from '../../glic.mojom-webui.js';
 import type {ExperimentalTriggeringUpdatesHandlerRemote, WebClientInitialState} from '../../glic.mojom-webui.js';
 import type {ClientCapabilities} from '../../glic_api/glic_api.js';
 import {ObservableValue} from '../../observable.js';
@@ -28,9 +28,11 @@
 import type {ResponseExtras} from '../transport/messaging.js';
 import type {InterfaceDef, PendingReceiver, PendingRemote, PostMessageHandler, PostMessageLifecycleObserver, PostMessageReceiver, PostMessageRemote, PostMessageRequestReceiver, PostMessageRequestSender, PostMessageRouter} from '../transport/post_message_transport.js';
 import {createBidirectionalPostMessageTransport} from '../transport/post_message_transport.js';
+import {ZeroStateSuggestionsHostMessageHandler} from '../zero_state_suggestions/zero_state_suggestions_host.js';
+import {ZeroStateSuggestionsHostDef} from '../zero_state_suggestions/zero_state_suggestions_types.js';
 
 import {ERROR_CODEC, getHostRequestHistogramInfo, MAX_REQUEST_ID, WebClientDef, WebClientHostDef} from './../request_types.js';
-import type {ActorClient, ActorHost, SkillsClient, SkillsHost, WebClient, WebClientHost} from './../request_types.js';
+import type {ActorClient, ActorHost, SkillsClient, SkillsHost, WebClient, WebClientHost, ZeroStateSuggestionsHost} from './../request_types.js';
 import {urlFromClient} from './conversions.js';
 import {HostMessageHandler} from './host_from_client.js';
 import type {CaptureRegionObserverImpl, PinCandidatesObserverImpl} from './host_from_client.js';
@@ -221,6 +223,7 @@
   annotationHandler?: AnnotationHandlerRemote;
   skillsHandler?: SkillsHandlerRemote;
 
+  zeroStateSuggestionsHandler?: ZeroStateSuggestionsHandlerRemote;
   private isSubscribedToZoomLevel = false;
   private experimentalTriggeringUpdatesHandler =
       new Map<number, ExperimentalTriggeringUpdatesHandlerRemote>();
@@ -285,6 +288,7 @@
     skillsReceiver?: PendingReceiver<SkillsClient>,
     experimentalTriggeringReceiver?: PendingReceiver<
                                       ExperimentalTriggeringClient>,
+    zeroStateSuggestionsRemote?: PendingRemote<ZeroStateSuggestionsHost>,
   } {
     this.panelIsActive = initialState.panelIsActive;
 
@@ -337,12 +341,30 @@
     this.handler.createExperimentalTriggeringClient(
         experimentalTriggeringClientReceiver.$.bindNewPipeAndPassRemote());
 
+    let zeroStateSuggestionsRemote: PendingRemote<ZeroStateSuggestionsHost>|
+        undefined;
+    if (initialState.enableZeroStateSuggestions) {
+      this.zeroStateSuggestionsHandler =
+          new ZeroStateSuggestionsHandlerRemote();
+      this.handler.createZeroStateSuggestionsHandler(
+          this.zeroStateSuggestionsHandler.$.bindNewPipeAndPassReceiver());
+      const zeroStateSuggestionsHostMessageHandler =
+          new ZeroStateSuggestionsHostMessageHandler(
+              this.zeroStateSuggestionsHandler, this.communicator.router);
+      const {remote: zeroStateSuggestionsRemoteVal} =
+          this.communicator.router.newPipeWithReceiver(
+              zeroStateSuggestionsHostMessageHandler,
+              ZeroStateSuggestionsHostDef);
+      zeroStateSuggestionsRemote = zeroStateSuggestionsRemoteVal;
+    }
+
     return {
       actorRemote,
       actorReceiver,
       skillsRemote,
       skillsReceiver,
       experimentalTriggeringReceiver,
+      zeroStateSuggestionsRemote,
     };
   }
 
diff --git a/chrome/browser/resources/glic/glic_api_impl/host/host_from_client.ts b/chrome/browser/resources/glic/glic_api_impl/host/host_from_client.ts
index 35480a31..9df41a2 100644
--- a/chrome/browser/resources/glic/glic_api_impl/host/host_from_client.ts
+++ b/chrome/browser/resources/glic/glic_api_impl/host/host_from_client.ts
@@ -14,15 +14,15 @@
 import {CaptureRegionObserverReceiver, ClientErrorDialogType as ClientErrorDialogTypeMojo, PinCandidatesObserverReceiver, ResponseStopCause as ResponseStopCauseMojo, SettingsPageField as SettingsPageFieldMojo, TabDataHandlerReceiver, TabFaviconHandlerReceiver, WebClientReceiver} from '../../glic.mojom-webui.js';
 import type {CaptureRegionErrorReason as CaptureRegionErrorReasonMojo, CaptureRegionObserver, CaptureRegionResult as CaptureRegionResultMojo, OpenSettingsOptions as OpenSettingsOptionsMojo, PinCandidate as PinCandidateMojo, PinCandidatesObserver, TabDataHandlerInterface, TabDataMojoType, TabFaviconHandlerInterface, WebClientHandlerInterface} from '../../glic.mojom-webui.js';
 import {CaptureScreenshotErrorReason, ClientCapabilities, ResponseStopCause} from '../../glic_api/glic_api.js';
-import type {CaptureRegionParams, ClientErrorDialogType, ConversationInfo, CounterAbuseVerdict, ExperimentalTriggeringUpdate, GetPinCandidatesOptions, MicrophoneStatus, OnResponseStoppedDetails, OpenSettingsOptions, PinTabsOptions, Screenshot, TabContextOptions, UnpinTabsOptions, WebClientMode, ZeroStateSuggestions, ZeroStateSuggestionsOptions, ZeroStateSuggestionsV2} from '../../glic_api/glic_api.js';
+import type {CaptureRegionParams, ClientErrorDialogType, ConversationInfo, CounterAbuseVerdict, ExperimentalTriggeringUpdate, GetPinCandidatesOptions, MicrophoneStatus, OnResponseStoppedDetails, OpenSettingsOptions, PinTabsOptions, Screenshot, TabContextOptions, UnpinTabsOptions, WebClientMode, ZeroStateSuggestions} from '../../glic_api/glic_api.js';
 import {replaceProperties} from '../conversions.js';
 import type {ExperimentalTriggeringClient} from '../experimental_triggering/experimental_triggering_types.js';
-import type {ActorClient, ActorHost, AnnotationHost, GlicException, ImageBytesResultPrivate, RgbaImage, SkillsClient, SkillsHost, TabContextResultPrivate, WebClientHost, WebClientInitialStatePrivate, WebClientPinCandidatesObserver, WebClientRegionCapture, WebClientTabDataObserver, WebClientTabFaviconObserver} from '../request_types.js';
+import type {ActorClient, ActorHost, AnnotationHost, GlicException, ImageBytesResultPrivate, RgbaImage, SkillsClient, SkillsHost, TabContextResultPrivate, WebClientHost, WebClientInitialStatePrivate, WebClientPinCandidatesObserver, WebClientRegionCapture, WebClientTabDataObserver, WebClientTabFaviconObserver, ZeroStateSuggestionsHost} from '../request_types.js';
 import {ErrorWithReasonImpl, exceptionFromTransferable, SubscriberObservationType} from '../request_types.js';
 import {ResponseExtras} from '../transport/messaging.js';
 import type {PendingReceiver, PendingRemote, PostMessageHandler, PostMessageRemote, PostMessageRouter} from '../transport/post_message_transport.js';
 
-import {bitmapN32ToRGBAImage, captureRegionResultToClient, conversationInfoFromClient, conversionSettings, counterAbuseVerdictFromClient, focusedTabDataToClient, getPinCandidatesOptionsFromClient, hostCapabilitiesToClient, idFromClient, idToClient, imageBytesResultToClient, microphoneStatusToMojo, optionalFromClient, optionalToClient, panelStateToClient, pinTabsOptionsToMojo, subscriberObservationTypeFromClient, tabContextOptionsFromClient, tabContextToClient, tabDataToClient, timeDeltaFromClient, unpinTabsOptionsToMojo, urlFromClient, urlToClient, webClientModeToMojo, zeroStateSuggestionsToClient} from './conversions.js';
+import {bitmapN32ToRGBAImage, captureRegionResultToClient, conversationInfoFromClient, conversionSettings, counterAbuseVerdictFromClient, focusedTabDataToClient, getPinCandidatesOptionsFromClient, hostCapabilitiesToClient, idFromClient, idToClient, imageBytesResultToClient, microphoneStatusToMojo, optionalFromClient, optionalToClient, panelStateToClient, pinTabsOptionsToMojo, subscriberObservationTypeFromClient, tabContextOptionsFromClient, tabContextToClient, tabDataToClient, timeDeltaFromClient, unpinTabsOptionsToMojo, urlFromClient, urlToClient, webClientModeToMojo} from './conversions.js';
 import type {ApiHostEmbedder, GlicApiHost} from './glic_api_host.js';
 import {DetailedWebClientState} from './glic_api_host.js';
 import {WebClientImpl} from './host_to_client.js';
@@ -64,6 +64,7 @@
     skillsReceiver?: PendingReceiver<SkillsClient>,
     experimentalTriggeringReceiver?: PendingReceiver<
                                       ExperimentalTriggeringClient>,
+    zeroStateSuggestionsRemote?: PendingRemote<ZeroStateSuggestionsHost>,
   }> {
     if (this.receiver) {
       throw new Error('web client already created');
@@ -119,6 +120,7 @@
       skillsReceiver: initialPipes.skillsReceiver,
       experimentalTriggeringReceiver:
           initialPipes.experimentalTriggeringReceiver,
+      zeroStateSuggestionsRemote: initialPipes.zeroStateSuggestionsRemote,
     };
   }
 
@@ -638,24 +640,6 @@
     }
   }
 
-  async getZeroStateSuggestionsAndSubscribe(request: {
-    hasActiveSubscription: boolean,
-    options: ZeroStateSuggestionsOptions,
-  }): Promise<{suggestions?: ZeroStateSuggestionsV2}> {
-    const zeroStateResult =
-        await this.handler.getZeroStateSuggestionsAndSubscribe(
-            request.hasActiveSubscription, {
-              isFirstRun: request.options.isFirstRun ?? false,
-              supportedTools: request.options.supportedTools ?? [],
-            });
-    const zeroStateData = zeroStateResult.zeroStateSuggestions;
-    if (!zeroStateData) {
-      return {};
-    } else {
-      return {suggestions: zeroStateSuggestionsToClient(zeroStateData)};
-    }
-  }
-
   maybeRefreshUserStatus(): void {
     this.handler.maybeRefreshUserStatus();
   }
diff --git a/chrome/browser/resources/glic/glic_api_impl/host/host_to_client.ts b/chrome/browser/resources/glic/glic_api_impl/host/host_to_client.ts
index 071776f4..81dde61 100644
--- a/chrome/browser/resources/glic/glic_api_impl/host/host_to_client.ts
+++ b/chrome/browser/resources/glic/glic_api_impl/host/host_to_client.ts
@@ -5,12 +5,12 @@
 // This file handles messages from the browser, sending messages to the client.
 
 import type {PageMetadata as PageMetadataMojo} from '../../ai_page_content_metadata.mojom-webui.js';
-import type {AdditionalContext as AdditionalContextMojo, FileUploadPolicyState, FocusedTabData as FocusedTabDataMojo, GeminiEnterpriseSettings as GeminiEnterpriseSettingsMojo, InvokeOptions as InvokeOptionsMojo, OpenPanelInfo as OpenPanelInfoMojo, PanelOpeningData as PanelOpeningDataMojo, PanelState as PanelStateMojo, TabData as TabDataMojo, WebClientInterface, ZeroStateSuggestionsOptions as ZeroStateSuggestionsOptionsMojo, ZeroStateSuggestionsV2 as ZeroStateSuggestionsV2Mojo} from '../../glic.mojom-webui.js';
+import type {AdditionalContext as AdditionalContextMojo, FileUploadPolicyState, FocusedTabData as FocusedTabDataMojo, GeminiEnterpriseSettings as GeminiEnterpriseSettingsMojo, InvokeOptions as InvokeOptionsMojo, OpenPanelInfo as OpenPanelInfoMojo, PanelOpeningData as PanelOpeningDataMojo, PanelState as PanelStateMojo, TabData as TabDataMojo, WebClientInterface} from '../../glic.mojom-webui.js';
 import type {WebClient} from '../request_types.js';
 import {ResponseExtras} from '../transport/messaging.js';
 import type {PostMessageRemote} from '../transport/post_message_transport.js';
 
-import {additionalContextToClient, fileUploadPolicyStateToClient, focusedTabDataToClient, idToClient, invokeOptionsToClient, pageMetadataToClient, panelOpeningDataToClient, panelStateToClient, tabDataToClient, timeDeltaFromClient, webClientModeToMojo, zeroStateSuggestionsToClient} from './conversions.js';
+import {additionalContextToClient, fileUploadPolicyStateToClient, focusedTabDataToClient, idToClient, invokeOptionsToClient, pageMetadataToClient, panelOpeningDataToClient, panelStateToClient, tabDataToClient, timeDeltaFromClient, webClientModeToMojo} from './conversions.js';
 import type {ApiHostEmbedder, GlicApiHost} from './glic_api_host.js';
 import {PanelOpenState} from './types.js';
 
@@ -207,15 +207,6 @@
   }
 
 
-  notifyZeroStateSuggestionsChanged(
-      suggestions: ZeroStateSuggestionsV2Mojo,
-      options: ZeroStateSuggestionsOptionsMojo): void {
-    this.sender.requestNoResponse('zeroStateSuggestionsChanged', {
-      suggestions: zeroStateSuggestionsToClient(suggestions),
-      options: options,
-    });
-  }
-
   notifyPageMetadataChanged(tabId: number, metadata: PageMetadataMojo|null):
       void {
     this.sender.requestNoResponse('pageMetadataChanged', {
diff --git a/chrome/browser/resources/glic/glic_api_impl/host/host_utils.ts b/chrome/browser/resources/glic/glic_api_impl/host/host_utils.ts
index a2195be..52947de1 100644
--- a/chrome/browser/resources/glic/glic_api_impl/host/host_utils.ts
+++ b/chrome/browser/resources/glic/glic_api_impl/host/host_utils.ts
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import type {InterfaceDef, PendingRemote, PostMessageRemote, PostMessageRouter} from '../transport/post_message_transport.js';
+
 export interface HasMojoConnection {
   onConnectionError: {addListener: (l: Function) => number};
-  $: {close(): void};
+  $: {close(): void, bindNewPipeAndPassRemote(): unknown};
 }
 export interface HasPostMessagePipe {
   addCloseHandler(f: Function): void;
@@ -41,3 +43,26 @@
     }
   }
 }
+
+// Automatically creates a postMessage remote from a PendingRemote, wraps it
+// in a Mojo receiver using the provided constructors, links their lifecycles,
+// and returns the bound Mojo receiver and Mojo remote.
+export function createForwardingMojoRemote<
+    MojoInterface, PostMessageInterface extends
+        InterfaceDef, MojoReceiverType extends HasMojoConnection>(
+    router: PostMessageRouter,
+    postMessagePipe: PendingRemote<PostMessageInterface>,
+    receiverConstructor: new (impl: MojoInterface) => MojoReceiverType,
+    implConstructor: new (remote: PostMessageRemote<PostMessageInterface>) =>
+        MojoInterface,
+    ): {
+  receiver: MojoReceiverType,
+  remote: ReturnType<MojoReceiverType['$']['bindNewPipeAndPassRemote']>,
+} {
+  const pmRemote = router.newRemote(postMessagePipe);
+  const mojoReceiver = new receiverConstructor(new implConstructor(pmRemote));
+  const mojoRemote = mojoReceiver.$.bindNewPipeAndPassRemote() as
+      ReturnType<MojoReceiverType['$']['bindNewPipeAndPassRemote']>;
+  linkPipeClosure(pmRemote, mojoReceiver);
+  return {receiver: mojoReceiver, remote: mojoRemote};
+}
diff --git a/chrome/browser/resources/glic/glic_api_impl/request_types.ts b/chrome/browser/resources/glic/glic_api_impl/request_types.ts
index e6517b4..4acbdf53 100644
--- a/chrome/browser/resources/glic/glic_api_impl/request_types.ts
+++ b/chrome/browser/resources/glic/glic_api_impl/request_types.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import type {WebClientInitialState} from '../glic.mojom-webui.js';
-import type {AdditionalContext, AdditionalContextPart, AnnotatedPageData, CaptureRegionErrorReason, CaptureRegionParams, CaptureRegionResult, ChromeVersion, ClientCapabilities, ClientErrorDialogType, ConversationInfo, CounterAbuseVerdict, ErrorReasonTypes, ErrorWithReason, ExperimentalTriggeringUpdate, FileUploadPolicyState, FocusedTabDataHasFocus, FocusedTabDataHasNoFocus, FormFactor, GeminiEnterpriseSettings, GetPinCandidatesOptions, HostCapability, InvokeOptions, MetricUserInputReactionType, MicrophoneStatus, OnResponseStoppedDetails, OpenPanelInfo, OpenSettingsOptions, PageMetadata, PanelOpeningData, PanelState, PdfDocumentData, PinCandidate, PinTabsOptions, Platform, ResumeActorTaskResult, Screenshot, TabContextOptions, TabContextResult, TabData, UnpinTabsOptions, UserProfileInfo, WebClientMode, ZeroStateSuggestions, ZeroStateSuggestionsOptions, ZeroStateSuggestionsV2} from '../glic_api/glic_api.js';
+import type {AdditionalContext, AdditionalContextPart, AnnotatedPageData, CaptureRegionErrorReason, CaptureRegionParams, CaptureRegionResult, ChromeVersion, ClientCapabilities, ClientErrorDialogType, ConversationInfo, CounterAbuseVerdict, ErrorReasonTypes, ErrorWithReason, ExperimentalTriggeringUpdate, FileUploadPolicyState, FocusedTabDataHasFocus, FocusedTabDataHasNoFocus, FormFactor, GeminiEnterpriseSettings, GetPinCandidatesOptions, HostCapability, InvokeOptions, MetricUserInputReactionType, MicrophoneStatus, OnResponseStoppedDetails, OpenPanelInfo, OpenSettingsOptions, PageMetadata, PanelOpeningData, PanelState, PdfDocumentData, PinCandidate, PinTabsOptions, Platform, ResumeActorTaskResult, Screenshot, TabContextOptions, TabContextResult, TabData, UnpinTabsOptions, UserProfileInfo, WebClientMode, ZeroStateSuggestions} from '../glic_api/glic_api.js';
 
 import type {ActorClient, ActorHost} from './actor/actor_types.js';
 import type {AnnotationClient, AnnotationHost} from './annotation/annotation_types.js';
@@ -12,6 +12,7 @@
 import type {InterfaceDef, InterfaceDefMethods, ReplaceProperties} from './transport/messaging.js';
 import {defInterface, defMessage} from './transport/messaging.js';
 import type {ErrorCodec, PendingReceiver, PendingRemote, TransferableException} from './transport/post_message_transport.js';
+import type {ZeroStateSuggestionsClient, ZeroStateSuggestionsHost} from './zero_state_suggestions/zero_state_suggestions_types.js';
 
 export type {
   ActorClient,
@@ -21,6 +22,8 @@
   ExperimentalTriggeringClient,
   SkillsClient,
   SkillsHost,
+  ZeroStateSuggestionsClient,
+  ZeroStateSuggestionsHost,
 };
 
 /*
@@ -48,6 +51,7 @@
         skillsReceiver?: PendingReceiver<SkillsClient>,
         experimentalTriggeringReceiver?: PendingReceiver<
                                           ExperimentalTriggeringClient>,
+        zeroStateSuggestionsRemote?: PendingRemote<ZeroStateSuggestionsHost>,
       }>(),
       histogram: {name: 'WebClientCreated', id: 1},
     },
@@ -451,17 +455,7 @@
       name: 'maybeRefreshUserStatus',
       histogram: {id: 58},
     },
-    {
-      name: 'getZeroStateSuggestionsAndSubscribe',
-      request: defMessage<{
-        hasActiveSubscription: boolean,
-        options: ZeroStateSuggestionsOptions,
-      }>(),
-      response: defMessage<{
-        suggestions?: ZeroStateSuggestionsV2,
-      }>(),
-      histogram: {id: 55},
-    },
+
     {
       name: 'subscribeToPageMetadata',
       request: defMessage<{
@@ -685,13 +679,7 @@
         tabData: TabDataPrivate,
       }>(),
     },
-    {
-      name: 'zeroStateSuggestionsChanged',
-      request: defMessage<{
-        suggestions: ZeroStateSuggestionsV2,
-        options: ZeroStateSuggestionsOptions,
-      }>(),
-    },
+
     {
       name: 'pageMetadataChanged',
       request: defMessage<{
@@ -872,7 +860,7 @@
   SubscribeToPinCandidates: 52,
   // Do not reuse deleted request ID: 53,
   GetZeroStateSuggestionsForFocusedTab: 54,
-  GetZeroStateSuggestionsAndSubscribe: 55,
+  // Do not reuse deleted request ID: 55,
   SetClosedCaptioningSetting: 56,
   DropScrollToHighlight: 57,
   MaybeRefreshUserStatus: 58,
diff --git a/chrome/browser/resources/glic/glic_api_impl/zero_state_suggestions/zero_state_suggestions_client.ts b/chrome/browser/resources/glic/glic_api_impl/zero_state_suggestions/zero_state_suggestions_client.ts
new file mode 100644
index 0000000..d857b5e
--- /dev/null
+++ b/chrome/browser/resources/glic/glic_api_impl/zero_state_suggestions/zero_state_suggestions_client.ts
@@ -0,0 +1,97 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import type {GlicBrowserHost, ObservableValue, ZeroStateSuggestionsOptions, ZeroStateSuggestionsV2} from '../../glic_api/glic_api.js';
+import {ObservableValue as ObservableValueImpl} from '../../observable.js';
+import type {GlicBrowserHostBaseContext} from '../client/glic_client_common.js';
+import type {WebClientInitialStatePrivate} from '../request_types.js';
+import type {PendingRemote, PostMessageHandler, PostMessageReceiver, PostMessageRemote} from '../transport/post_message_transport.js';
+
+import {ZeroStateSuggestionsClientDef} from './zero_state_suggestions_types.js';
+import type {ZeroStateSuggestionsClient, ZeroStateSuggestionsHost} from './zero_state_suggestions_types.js';
+
+class ZeroStateSuggestionsClientImpl implements
+    PostMessageHandler<ZeroStateSuggestionsClient> {
+  constructor(private host: {
+    currentZeroStateObserver?: ObservableValueImpl<ZeroStateSuggestionsV2>,
+  }) {}
+
+  zeroStateSuggestionsChanged(payload: {
+    suggestions: ZeroStateSuggestionsV2,
+    options: ZeroStateSuggestionsOptions,
+  }): void {
+    this.host.currentZeroStateObserver?.assignAndSignal(payload.suggestions);
+  }
+}
+
+export class GlicBrowserHostZeroStateSuggestions implements
+    Partial<GlicBrowserHost> {
+  zeroStateSuggestionsSender?: PostMessageRemote<ZeroStateSuggestionsHost>;
+  zeroStateSuggestionsReceiver: PostMessageReceiver|null = null;
+
+  currentZeroStateSuggestionOptions?: ZeroStateSuggestionsOptions;
+  currentZeroStateObserver?: ObservableValueImpl<ZeroStateSuggestionsV2>;
+
+  constructor(private host: GlicBrowserHostBaseContext) {}
+
+  initialize(
+      initialState: WebClientInitialStatePrivate,
+      zeroStateSuggestionsRemote: PendingRemote<ZeroStateSuggestionsHost>|
+      undefined) {
+    if (zeroStateSuggestionsRemote === undefined ||
+        !initialState.enableZeroStateSuggestions) {
+      this.getZeroStateSuggestions = undefined;
+      return;
+    }
+
+    this.zeroStateSuggestionsSender =
+        this.host.router.newRemote(zeroStateSuggestionsRemote);
+  }
+
+  getZeroStateSuggestions?(options?: ZeroStateSuggestionsOptions):
+      ObservableValue<ZeroStateSuggestionsV2> {
+    options = options ?? {
+      isFirstRun: false,
+      supportedTools: [],
+    };
+    this.currentZeroStateSuggestionOptions = options;
+    this.currentZeroStateObserver =
+        ObservableValueImpl.withNoValue<ZeroStateSuggestionsV2>(
+            this.zeroStateActiveSubscriptionStateChanged.bind(this, options));
+    return this.currentZeroStateObserver;
+  }
+
+  async zeroStateActiveSubscriptionStateChanged(
+      options: ZeroStateSuggestionsOptions, hasActiveSubscription: boolean) {
+    if (options !== this.currentZeroStateSuggestionOptions) {
+      // Dont send out of date updates.
+      return;
+    }
+
+    let suggestionsClientPipe = undefined;
+    if (hasActiveSubscription) {
+      this.zeroStateSuggestionsReceiver?.close();
+      const {remote, receiver} =
+          this.host.router.newPipeWithReceiver<ZeroStateSuggestionsClient>(
+              new ZeroStateSuggestionsClientImpl(this),
+              ZeroStateSuggestionsClientDef);
+      this.zeroStateSuggestionsReceiver = receiver;
+      suggestionsClientPipe = remote;
+    } else {
+      this.zeroStateSuggestionsReceiver?.close();
+      this.zeroStateSuggestionsReceiver = null;
+    }
+
+    const zeroStateResult =
+        await this.zeroStateSuggestionsSender!.requestWithResponse(
+            'getZeroStateSuggestionsAndSubscribe', {
+              pendingRemote: suggestionsClientPipe,
+              options: options,
+            });
+    if (zeroStateResult.suggestions) {
+      this.currentZeroStateObserver?.assignAndSignal(
+          zeroStateResult.suggestions);
+    }
+  }
+}
diff --git a/chrome/browser/resources/glic/glic_api_impl/zero_state_suggestions/zero_state_suggestions_host.ts b/chrome/browser/resources/glic/glic_api_impl/zero_state_suggestions/zero_state_suggestions_host.ts
new file mode 100644
index 0000000..bf1deb0
--- /dev/null
+++ b/chrome/browser/resources/glic/glic_api_impl/zero_state_suggestions/zero_state_suggestions_host.ts
@@ -0,0 +1,72 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {ZeroStateSuggestionsClientReceiver} from '../../glic.mojom-webui.js';
+import type {ZeroStateSuggestionsClientInterface, ZeroStateSuggestionsHandlerInterface, ZeroStateSuggestionsOptions as ZeroStateSuggestionsOptionsMojo, ZeroStateSuggestionsV2 as ZeroStateSuggestionsV2Mojo} from '../../glic.mojom-webui.js';
+import type {ZeroStateSuggestionsOptions, ZeroStateSuggestionsV2} from '../../glic_api/glic_api.js';
+import {zeroStateSuggestionsToClient} from '../host/conversions.js';
+import {createForwardingMojoRemote} from '../host/host_utils.js';
+import type {MessageHandlerInterface} from '../transport/messaging.js';
+import type {PendingRemote, PostMessageRemote, PostMessageRouter} from '../transport/post_message_transport.js';
+
+import type {ZeroStateSuggestionsClient, ZeroStateSuggestionsHost} from './zero_state_suggestions_types.js';
+
+export class ZeroStateSuggestionsHostMessageHandler implements
+    MessageHandlerInterface<ZeroStateSuggestionsHost> {
+  private clientReceiver: ZeroStateSuggestionsClientReceiver|null = null;
+
+  constructor(
+      private handler: ZeroStateSuggestionsHandlerInterface,
+      private router: PostMessageRouter,
+  ) {}
+
+  getZeroStateSuggestionsAndSubscribe(payload: {
+    pendingRemote?: PendingRemote<ZeroStateSuggestionsClient>,
+                 options: ZeroStateSuggestionsOptions,
+  }): Promise<{suggestions?: ZeroStateSuggestionsV2}> {
+    const {pendingRemote, options} = payload;
+    const mojoOptions = {
+      isFirstRun: options.isFirstRun ?? false,
+      supportedTools: options.supportedTools ?? [],
+    };
+
+    this.clientReceiver?.$.close();
+    this.clientReceiver = null;
+    let mojoClientRemote = null;
+    if (pendingRemote !== undefined) {
+      const {receiver, remote} = createForwardingMojoRemote(
+          this.router, pendingRemote, ZeroStateSuggestionsClientReceiver,
+          ZeroStateSuggestionsClientImpl);
+      this.clientReceiver = receiver;
+      mojoClientRemote = remote;
+    }
+    return this.handler
+        .getZeroStateSuggestionsAndSubscribe(mojoClientRemote, mojoOptions)
+        .then((response: {
+                zeroStateSuggestions: ZeroStateSuggestionsV2Mojo|null,
+              }) => {
+          if (!response.zeroStateSuggestions) {
+            return {};
+          }
+          return {
+            suggestions:
+                zeroStateSuggestionsToClient(response.zeroStateSuggestions),
+          };
+        });
+  }
+}
+
+export class ZeroStateSuggestionsClientImpl implements
+    ZeroStateSuggestionsClientInterface {
+  constructor(private sender: PostMessageRemote<ZeroStateSuggestionsClient>) {}
+
+  notifyZeroStateSuggestionsChanged(
+      suggestions: ZeroStateSuggestionsV2Mojo,
+      options: ZeroStateSuggestionsOptionsMojo): void {
+    this.sender.requestNoResponse('zeroStateSuggestionsChanged', {
+      suggestions: zeroStateSuggestionsToClient(suggestions),
+      options,
+    });
+  }
+}
diff --git a/chrome/browser/resources/glic/glic_api_impl/zero_state_suggestions/zero_state_suggestions_types.ts b/chrome/browser/resources/glic/glic_api_impl/zero_state_suggestions/zero_state_suggestions_types.ts
new file mode 100644
index 0000000..e095d20
--- /dev/null
+++ b/chrome/browser/resources/glic/glic_api_impl/zero_state_suggestions/zero_state_suggestions_types.ts
@@ -0,0 +1,41 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import type {ZeroStateSuggestionsOptions, ZeroStateSuggestionsV2} from '../../glic_api/glic_api.js';
+import {defInterface, defMessage} from '../transport/messaging.js';
+import type {PendingRemote} from '../transport/post_message_transport.js';
+
+// Defines the message exchange between the client suggestions component and
+// the host.
+export const ZeroStateSuggestionsHostDef = defInterface({
+  name: 'ZeroStateSuggestionsHost',
+  methods: [
+    {
+      name: 'getZeroStateSuggestionsAndSubscribe',
+      request: defMessage<{
+        pendingRemote?: PendingRemote<ZeroStateSuggestionsClient>,
+                     options: ZeroStateSuggestionsOptions,
+      }>(),
+      response: defMessage<{
+        suggestions?: ZeroStateSuggestionsV2,
+      }>(),
+      histogram: {id: 55},
+    },
+  ],
+});
+export type ZeroStateSuggestionsHost = typeof ZeroStateSuggestionsHostDef;
+
+export const ZeroStateSuggestionsClientDef = defInterface({
+  name: 'ZeroStateSuggestionsClient',
+  methods: [
+    {
+      name: 'zeroStateSuggestionsChanged',
+      request: defMessage<{
+        suggestions: ZeroStateSuggestionsV2,
+        options: ZeroStateSuggestionsOptions,
+      }>(),
+    },
+  ],
+});
+export type ZeroStateSuggestionsClient = typeof ZeroStateSuggestionsClientDef;
diff --git a/chrome/browser/resources/new_tab_page/app.ts b/chrome/browser/resources/new_tab_page/app.ts
index 3bc4121d..55179487 100644
--- a/chrome/browser/resources/new_tab_page/app.ts
+++ b/chrome/browser/resources/new_tab_page/app.ts
@@ -49,6 +49,7 @@
 import {CustomizeButtonsProxy} from './customize_buttons_proxy.js';
 import {CustomizeChromeSection} from './customize_chrome.mojom-webui.js';
 import {CustomizeDialogPage} from './customize_dialog_types.js';
+import type {FuseboxAction} from './fusebox_action.mojom-webui.js';
 import type {IframeElement} from './iframe.js';
 import type {LogoElement} from './logo.js';
 import {recordBoolean, recordDuration, recordEnumeration, recordLinearValue, recordLoadDuration, recordSparseValueWithPersistentHash} from './metrics_utils.js';
@@ -984,12 +985,24 @@
       model: detail.fuseboxAction?.preselectedModel,
       suggestInventory: detail.fuseboxAction?.preferredInventory,
     } as ComposeboxState);
+    this.handleFuseboxAction_(detail.fuseboxAction);
   }
 
   protected onOpenComposebox_(e: CustomEvent<ComposeboxState>) {
     this.openComposebox_(e.detail);
   }
 
+  protected async handleFuseboxAction_(action?: FuseboxAction|null) {
+    if (action) {
+      await this.updateComplete;
+      const composebox =
+          this.shadowRoot?.querySelector<NtpComposeboxElement>('#composebox');
+      if (composebox) {
+        composebox.handleFuseboxAction(action);
+      }
+    }
+  }
+
   protected onContextMenuEntrypointClick_() {
     this.pageHandler_.onContextualSearchIPHEngaged();
   }
diff --git a/chrome/browser/resources/new_tab_page/new_tab_page.ts b/chrome/browser/resources/new_tab_page/new_tab_page.ts
index 29829e73..0c710ab 100644
--- a/chrome/browser/resources/new_tab_page/new_tab_page.ts
+++ b/chrome/browser/resources/new_tab_page/new_tab_page.ts
@@ -43,6 +43,8 @@
 export * from './new_tab_page.mojom-webui.js';
 export type {ActionChip, PageRemote as ActionChipsPageRemote, TabInfo} from './action_chips.mojom-webui.js';
 export {ActionChipsHandlerRemote, IconType, PageCallbackRouter as ActionChipsPageCallbackRouter} from './action_chips.mojom-webui.js';
+export type {FuseboxAction} from './fusebox_action.mojom-webui.js';
+export {InputSource} from './fusebox_action.mojom-webui.js';
 export type {CalendarEvent, Attachment} from './calendar_data.mojom-webui.js';
 export type {Promo as NtpPromo, NtpPromoClientRemote, NtpPromoHandlerInterface} from './ntp_promo.mojom-webui.js';
 export {NtpPromoClientCallbackRouter} from './ntp_promo.mojom-webui.js';
diff --git a/chrome/browser/resources/new_tab_page/ntp_composebox.ts b/chrome/browser/resources/new_tab_page/ntp_composebox.ts
index 3d03e4e8..a2e44c5e 100644
--- a/chrome/browser/resources/new_tab_page/ntp_composebox.ts
+++ b/chrome/browser/resources/new_tab_page/ntp_composebox.ts
@@ -34,6 +34,8 @@
 import type {PageCallbackRouter as SearchboxPageCallbackRouter, PageHandlerRemote as SearchboxPageHandlerRemote} from '//resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js';
 import type {UnguessableToken} from '//resources/mojo/mojo/public/mojom/base/unguessable_token.mojom-webui.js';
 
+import {InputSource} from './fusebox_action.mojom-webui.js';
+import type {FuseboxAction} from './fusebox_action.mojom-webui.js';
 import {getCss} from './ntp_composebox.css.js';
 import {getHtml} from './ntp_composebox.html.js';
 
@@ -186,6 +188,25 @@
     }
     return file;
   }
+
+  handleFuseboxAction(action: FuseboxAction) {
+    if (action.preselectedInputSource) {
+      switch (action.preselectedInputSource) {
+        case InputSource.kInputSourceGallery:
+          this.$.fileInputs.shadowRoot
+              ?.querySelector<HTMLInputElement>('#imageInput')
+              ?.click();
+          break;
+        case InputSource.kInputSourceFilePicker:
+          this.$.fileInputs.shadowRoot
+              ?.querySelector<HTMLInputElement>('#fileInput')
+              ?.click();
+          break;
+        default:
+          break;
+      }
+    }
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/omnibox_popup/omnibox_popup_searchbox.css b/chrome/browser/resources/omnibox_popup/omnibox_popup_searchbox.css
index 3894c1c4..794535b 100644
--- a/chrome/browser/resources/omnibox_popup/omnibox_popup_searchbox.css
+++ b/chrome/browser/resources/omnibox_popup/omnibox_popup_searchbox.css
@@ -77,12 +77,15 @@
   margin-bottom: var(--alignment-inset-top);
 }
 
-:host(:not([dropdown-is-visible])) #inputWrapper:hover:not(:focus-within) {
+:host(:not([dropdown-is-visible]):not([has-input-selection_]):not([is-focused_])) #inputWrapper:hover:not(:focus-within) {
   cursor: text;
   background-color: var(--color-omnibox-results-background-hovered);
 }
 
-:host(:not([dropdown-is-visible])) #inputWrapper:focus-within {
+:host(:not([dropdown-is-visible])) #inputWrapper:focus-within,
+:host([is-focused_]:not([dropdown-is-visible])) #inputWrapper,
+:host([is-focused_][has-input-selection_]) #inputWrapper {
+  --cr-searchbox-shadow: none;
   box-shadow: 0 0 0 2px var(--cr-focus-outline-color);
 }
 
@@ -140,7 +143,10 @@
   padding-top: 0;
 }
 
-cr-searchbox-input::part(searchbox-input)::selection {
+/* When the window is inactive (top container has focus), the selection should
+   still be the same color. */
+cr-searchbox-input::part(searchbox-input)::selection,
+cr-searchbox-input::part(searchbox-input):window-inactive::selection {
   background-color: var(--color-omnibox-selection-background);
   color: var(--color-omnibox-selection-foreground);
 }
diff --git a/chrome/browser/resources/omnibox_popup/omnibox_popup_searchbox.ts b/chrome/browser/resources/omnibox_popup/omnibox_popup_searchbox.ts
index 80b1651..1ab35989 100644
--- a/chrome/browser/resources/omnibox_popup/omnibox_popup_searchbox.ts
+++ b/chrome/browser/resources/omnibox_popup/omnibox_popup_searchbox.ts
@@ -22,7 +22,7 @@
 import type {PropertyValues} from '//resources/lit/v3_0/lit.rollup.js';
 import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
 import {SelectionLineState} from '//resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js';
-import type {AutocompleteResult, PageCallbackRouter as SearchboxPageCallbackRouter, PageHandlerInterface as SearchboxPageHandlerInterface} from '//resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js';
+import type {PageCallbackRouter as SearchboxPageCallbackRouter, PageHandlerInterface as SearchboxPageHandlerInterface} from '//resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js';
 import type {Url} from '//resources/mojo/url/mojom/url.mojom-webui.js';
 
 import {browserProxyFactory, OmniboxEscapeAction} from './omnibox_popup.mojom-webui.js';
@@ -152,12 +152,30 @@
         type: Boolean,
         reflect: true,
       },
+      /**
+       * Whether the input has selection or not. Used when the widget loses
+       * activation but should still show the selection,
+       */
+      hasInputSelection_: {
+        type: Boolean,
+        reflect: true,
+      },
+      /**
+       * Whether the input has focus or not. Used when the widget loses
+       * activation but should still show the focus ring.
+       */
+      isLogicallyFocused_: {
+        type: Boolean,
+        reflect: true,
+      },
     };
   }
 
   accessor canShowSecondarySide: boolean =
       canShowSecondarySideMediaQueryList.matches;
   accessor hasSecondarySide: boolean = false;
+  accessor isLogicallyFocused_: boolean = false;
+  accessor hasInputSelection_: boolean = false;
   accessor searchboxChromeRefreshTheming: boolean =
       loadTimeData.getBoolean('searchboxCr23Theming');
   accessor searchboxSteadyStateShadow: boolean =
@@ -260,6 +278,8 @@
           this.onSetInputState_.bind(this)),
       this.popupCallbackRouter_.setFocus.addListener(
           this.onSetFocus_.bind(this)),
+      this.popupCallbackRouter_.clearAutocompleteMatches.addListener(
+          this.clearAutocompleteMatches.bind(this)),
     ];
     this.eventTracker_.add(
         document, 'selectionchange', this.onSelectionChanged_.bind(this));
@@ -436,10 +456,6 @@
     return true;
   }
 
-  override isAutocompleteResultStale(result: AutocompleteResult): boolean {
-    return super.isAutocompleteResultStale(result) || !result.matches.length;
-  }
-
   //========================================================================
   // Event handlers
   //========================================================================
@@ -568,6 +584,7 @@
    */
   private onSelectionChanged_() {
     const input = this.$.input.inputElement;
+    this.hasInputSelection_ = input.selectionStart !== input.selectionEnd;
     // Suppress selection updates during active IME text composition.
     if (this.shadowRoot.activeElement !== this.$.input || this.isComposing_) {
       return;
@@ -599,6 +616,8 @@
       this.dropdownIsVisible = false;
     }
 
+    this.isLogicallyFocused_ = state.isFocused;
+
     if (state.isFocused) {
       if (document.visibilityState === 'visible') {
         this.deferredFocusAction_ = null;
@@ -616,6 +635,8 @@
       this.fullUrlShown_ = false;
     }
 
+    this.hasInputSelection_ = state.selection.start !== state.selection.end;
+
     this.selectRange(state.selection);
     this.getDropdownElement().unselect();
     // If zero-prefix suggestions are requested by the new state, initiate
@@ -638,11 +659,11 @@
    * If hidden, defers the action until `visibilitychange`.
    */
   private onSetFocus_(isFocused: boolean) {
+    this.isLogicallyFocused_ = isFocused;
     if (isFocused) {
       if (document.visibilityState === 'visible') {
         this.deferredFocusAction_ = null;
         this.$.input.focus();
-        this.getInputElement().select();
       } else {
         // Defer focusing and selecting text if the document is currently
         // hidden, as DOM focus calls on hidden documents may be ignored.
@@ -685,7 +706,8 @@
       this.maybeShowFullUrl_();
     }
 
-    if (isFullSelection) {
+    const isLogicallyFocused = this.shadowRoot?.activeElement === this.$.input;
+    if (isFullSelection && isLogicallyFocused) {
       this.getInputElement().select();
     } else {
       this.$.input.setSelectionRange(
diff --git a/chrome/browser/resources/organizer_panel/BUILD.gn b/chrome/browser/resources/organizer_panel/BUILD.gn
index 3b2069d0..3b46fa3 100644
--- a/chrome/browser/resources/organizer_panel/BUILD.gn
+++ b/chrome/browser/resources/organizer_panel/BUILD.gn
@@ -23,6 +23,7 @@
 
   ts_deps = [
     "//third_party/lit/v3_0:build_ts",
+    "//ui/webui/resources/cr_elements:build_ts",
     "//ui/webui/resources/js:build_ts",
   ]
 }
diff --git a/chrome/browser/resources/organizer_panel/app.css b/chrome/browser/resources/organizer_panel/app.css
index 7a52dcd..f319416 100644
--- a/chrome/browser/resources/organizer_panel/app.css
+++ b/chrome/browser/resources/organizer_panel/app.css
@@ -7,6 +7,13 @@
  * #scheme=relative
  * #css_wrapper_metadata_end */
 
-* {
-  background-color: transparent;
+#searchField {
+  --cr-toolbar-search-field-background: var(--color-toolbar);
+  margin: 8px 12px;
+  width: auto;
+}
+
+#shortcut {
+  color: var(--cr-secondary-text-color);
+  margin-inline-end: 12px;
 }
diff --git a/chrome/browser/resources/organizer_panel/app.html.ts b/chrome/browser/resources/organizer_panel/app.html.ts
index 2682209..5ebaae6 100644
--- a/chrome/browser/resources/organizer_panel/app.html.ts
+++ b/chrome/browser/resources/organizer_panel/app.html.ts
@@ -8,5 +8,10 @@
 
 export function getHtml(this: OrganizerPanelAppElement) {
   return html`<!--_html_template_start_-->
+<cr-toolbar-search-field id="searchField"
+    label="$i18n{searchTabs}"
+    clear-label="$i18n{clearSearch}">
+  <div id="shortcut" slot="suffixElement">$i18n{shortcutText}</div>
+</cr-toolbar-search-field>
 <!--_html_template_end_-->`;
 }
diff --git a/chrome/browser/resources/organizer_panel/app.ts b/chrome/browser/resources/organizer_panel/app.ts
index 73b6934..b6ba35a 100644
--- a/chrome/browser/resources/organizer_panel/app.ts
+++ b/chrome/browser/resources/organizer_panel/app.ts
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js';
 import '/strings.m.js';
 
 import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
diff --git a/chrome/browser/resources/settings/glic_page/glic_subpage.html b/chrome/browser/resources/settings/glic_page/glic_subpage.html
index 69e1656..3acf99fd 100644
--- a/chrome/browser/resources/settings/glic_page/glic_subpage.html
+++ b/chrome/browser/resources/settings/glic_page/glic_subpage.html
@@ -189,6 +189,14 @@
           on-settings-boolean-control-change=
               "onKeepSidepanelOpenOnNewTabsToggleChange_">
       </settings-toggle-button>
+      <settings-toggle-button class="hr"
+          id="shakeTriggerToggle"
+          hidden="[[!showGlicShakeTrigger_]]"
+          pref="{{prefs.glic.shake_trigger_enabled}}"
+          label="$i18n{glicShakeTriggerToggle}"
+          sub-label="$i18n{glicShakeTriggerToggleSublabel}"
+          on-settings-boolean-control-change="onShakeTriggerToggleChange_">
+      </settings-toggle-button>
     </template>
   </div>
   <div class="section">
@@ -406,6 +414,14 @@
             on-sub-label-link-clicked="onMediaUnderstandingToggleLearnMoreClick_">
         </settings-toggle-button>
       </template>
+      <settings-toggle-button class="hr"
+          id="shakeTriggerToggle"
+          pref="{{prefs.glic.shake_trigger_enabled}}"
+          hidden="[[!showGlicShakeTrigger_]]"
+          label="$i18n{glicShakeTriggerToggle}"
+          sub-label="$i18n{glicShakeTriggerToggleSublabel}"
+          on-settings-boolean-control-change="onShakeTriggerToggleChange_">
+      </settings-toggle-button>
       <template is="dom-if" if="[[actorLoginFederatedLoginSupportEnabled_]]"
           restamp>
         <cr-link-row id="actorLoginPermissionsButton" class="hr"
diff --git a/chrome/browser/resources/settings/glic_page/glic_subpage.ts b/chrome/browser/resources/settings/glic_page/glic_subpage.ts
index e2a0365..229bc52 100644
--- a/chrome/browser/resources/settings/glic_page/glic_subpage.ts
+++ b/chrome/browser/resources/settings/glic_page/glic_subpage.ts
@@ -57,6 +57,7 @@
   EXPERIMENTAL_TRIGGERING_ENABLED = 'glic.experimental_triggering_enabled',
   KEEP_SIDEPANEL_OPEN_ON_NEW_TABS_ENABLED =
       'glic.keep_sidepanel_open_on_new_tabs_enabled',
+  SHAKE_TRIGGER_ENABLED = 'glic.shake_trigger_enabled',
   HOTKEY_GLOBAL_SCOPE_ENABLED = 'glic.hotkey_global_scope_enabled',
 }
 
@@ -200,6 +201,11 @@
             'showGlicKeepSidepanelOpenOnNewTabsSetting'),
       },
 
+      showGlicShakeTrigger_: {
+        type: Boolean,
+        value: () => loadTimeData.getBoolean('showGlicShakeTrigger'),
+      },
+
       locationSubLabel_: {
         type: String,
         computed: `computeLocationSubLabel_(prefs.${
@@ -380,6 +386,7 @@
   declare private showGlicPersonalContextLink_: boolean;
   declare private showGlicInstructionLink_: boolean;
   declare private showGlicKeepSidepanelOpenOnNewTabsSetting_: boolean;
+  declare private showGlicShakeTrigger_: boolean;
   declare private locationSubLabel_: string;
   declare private locationLearnMoreUrl_: string;
   declare private microphoneSubLabel_: string;
@@ -615,6 +622,14 @@
         (enabled ? '.Enabled' : '.Disabled'));
   }
 
+  private onShakeTriggerToggleChange_(event: CustomEvent) {
+    const target = event.target as SettingsToggleButtonElement;
+    const enabled = target.checked;
+    this.metricsBrowserProxy_.recordAction(
+        'Glic.Settings.ShakeTrigger' +
+        (enabled ? '.Enabled' : '.Disabled'));
+  }
+
   private onWebActuationEnabledChanged_(enabled: boolean) {
     if (this.isWebActuationDisabledForEnterprise_) {
       this.webActuationEnabledExpanded_ = false;
diff --git a/chrome/browser/resources/settings/languages_page/languages.ts b/chrome/browser/resources/settings/languages_page/languages.ts
index bbb4999..2c3dd55 100644
--- a/chrome/browser/resources/settings/languages_page/languages.ts
+++ b/chrome/browser/resources/settings/languages_page/languages.ts
@@ -10,13 +10,11 @@
  * this class via the LanguageHelper singleton instance.
  */
 
-import '/shared/settings/prefs/prefs.js';
-
 import {assert} from '//resources/js/assert.js';
 import {PromiseResolver} from '//resources/js/promise_resolver.js';
 import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {PrefsMixin} from '/shared/settings/prefs/prefs_mixin.js';
-import {CrSettingsPrefs} from '/shared/settings/prefs/prefs_types.js';
+import {PrefService} from '/shared/settings/prefs2/pref_service.js';
+import {PrefServiceObserverMixin} from '/shared/settings/prefs2/pref_service_observer_mixin.js';
 
 import type {LanguagesBrowserProxy} from './languages_browser_proxy.js';
 import {LanguagesBrowserProxyImpl} from './languages_browser_proxy.js';
@@ -58,7 +56,7 @@
  * updates it whenever Chrome's pref store and other settings change.
  */
 
-const SettingsLanguagesElementBase = PrefsMixin(PolymerElement);
+const SettingsLanguagesElementBase = PrefServiceObserverMixin(PolymerElement);
 
 class SettingsLanguagesElement extends SettingsLanguagesElementBase implements
     LanguageHelper {
@@ -72,42 +70,67 @@
         type: Object,
         notify: true,
       },
+
+      intlAcceptLanguagesPref_: Object,
+      intlAppLocalePref_: Object,
+      intlForcedLanguagesPref_: Object,
+      spellcheckBlockedDictionariesPref_: Object,
+      spellcheckDictionariesPref_: Object,
+      spellcheckForcedDictionariesPref_: Object,
+      translateAllowlistsPref_: Object,
+      translateBlockedLanguagesPref_: Object,
+      translateRecentTargetPref_: Object,
+      translateSiteBlocklistPref_: Object,
     };
   }
 
   static get observers() {
     return [
-      // All observers wait for the model to be populated by including the
-      // |languages| property.
       'alwaysTranslateLanguagesPrefChanged_(' +
-          'prefs.translate_allowlists.value.*, languages)',
+          'translateAllowlistsPref_, languages)',
       'neverTranslateLanguagesPrefChanged_(' +
-          'prefs.translate_blocked_languages.value.*, languages)',
-      'neverTranslateSitesPrefChanged_(' +
-          'prefs.translate_site_blocklist_with_time.value.*, languages)',
+          'translateBlockedLanguagesPref_, languages)',
+      'neverTranslateSitesPrefChanged_(translateSiteBlocklistPref_, languages)',
       // <if expr="is_win">
-      'prospectiveUiLanguageChanged_(prefs.intl.app_locale.value, languages)',
+      'prospectiveUiLanguageChanged_(intlAppLocalePref_, languages)',
       // </if>
       'preferredLanguagesPrefChanged_(' +
-          'prefs.intl.accept_languages.value, languages)',
-      'preferredLanguagesPrefChanged_(' +
-          'prefs.intl.forced_languages.value.*, languages)',
+          'intlAcceptLanguagesPref_, intlForcedLanguagesPref_, languages)',
       'spellCheckDictionariesPrefChanged_(' +
-          'prefs.spellcheck.dictionaries.value.*, ' +
-          'prefs.spellcheck.forced_dictionaries.value.*, ' +
-          'prefs.spellcheck.blocked_dictionaries.value.*, languages)',
+          'spellcheckDictionariesPref_, ' +
+          'spellcheckForcedDictionariesPref_, ' +
+          'spellcheckBlockedDictionariesPref_, languages)',
       'translateLanguagesPrefChanged_(' +
-          'prefs.translate_blocked_languages.value.*, languages)',
-      'translateTargetPrefChanged_(' +
-          'prefs.translate_recent_target.value, languages)',
+          'translateBlockedLanguagesPref_, languages)',
+      'translateTargetPrefChanged_(translateRecentTargetPref_, languages)',
       'updateRemovableLanguages_(' +
-          'prefs.intl.app_locale.value, languages.enabled)',
-      'updateRemovableLanguages_(' +
-          'prefs.translate_blocked_languages.value.*)',
+          'intlAppLocalePref_, translateBlockedLanguagesPref_, ' +
+          'languages.enabled)',
     ];
   }
 
-  declare languages?: LanguagesModel|undefined;
+  declare languages: LanguagesModel|undefined;
+
+  declare private intlAcceptLanguagesPref_:
+      chrome.settingsPrivate.PrefObject<string>|undefined;
+  declare private intlAppLocalePref_: chrome.settingsPrivate.PrefObject<string>|
+      undefined;
+  declare private intlForcedLanguagesPref_:
+      chrome.settingsPrivate.PrefObject<string[]>|undefined;
+  declare private spellcheckBlockedDictionariesPref_:
+      chrome.settingsPrivate.PrefObject<string[]>|undefined;
+  declare private spellcheckDictionariesPref_:
+      chrome.settingsPrivate.PrefObject<string[]>|undefined;
+  declare private spellcheckForcedDictionariesPref_:
+      chrome.settingsPrivate.PrefObject<string[]>|undefined;
+  declare private translateAllowlistsPref_:
+      chrome.settingsPrivate.PrefObject<Record<string, string>>|undefined;
+  declare private translateBlockedLanguagesPref_:
+      chrome.settingsPrivate.PrefObject<string[]>|undefined;
+  declare private translateRecentTargetPref_:
+      chrome.settingsPrivate.PrefObject<string>|undefined;
+  declare private translateSiteBlocklistPref_:
+      chrome.settingsPrivate.PrefObject<Record<string, string>>|undefined;
 
   private resolver_: PromiseResolver<void> = new PromiseResolver();
   private supportedLanguageMap_:
@@ -142,6 +165,19 @@
     assert(!instance);
     instance = this;
 
+    this.mirrorPrefs({
+      'intl.accept_languages': 'intlAcceptLanguagesPref_',
+      'intl.app_locale': 'intlAppLocalePref_',
+      'intl.forced_languages': 'intlForcedLanguagesPref_',
+      'spellcheck.blocked_dictionaries': 'spellcheckBlockedDictionariesPref_',
+      'spellcheck.dictionaries': 'spellcheckDictionariesPref_',
+      'spellcheck.forced_dictionaries': 'spellcheckForcedDictionariesPref_',
+      'translate_allowlists': 'translateAllowlistsPref_',
+      'translate_blocked_languages': 'translateBlockedLanguagesPref_',
+      'translate_recent_target': 'translateRecentTargetPref_',
+      'translate_site_blocklist_with_time': 'translateSiteBlocklistPref_',
+    });
+
     const promises: Array<Promise<void>> = [];
 
     /**
@@ -163,7 +199,7 @@
 
     // Wait until prefs are initialized before creating the model, so we can
     // include information about enabled languages.
-    promises.push(CrSettingsPrefs.initialized);
+    promises.push(PrefService.getInstance().whenInitialized());
 
     // Get the language list.
     promises.push(
@@ -243,10 +279,13 @@
   /**
    * Updates the prospective UI language based on the new pref value.
    */
-  private prospectiveUiLanguageChanged_(prospectiveUILanguage: string) {
+  private prospectiveUiLanguageChanged_() {
+    if (this.intlAppLocalePref_ === undefined || this.languages === undefined) {
+      return;
+    }
     this.set(
         'languages.prospectiveUILanguage',
-        prospectiveUILanguage || this.originalProspectiveUILanguage_);
+        this.intlAppLocalePref_.value || this.originalProspectiveUILanguage_);
   }
   // </if>
 
@@ -254,7 +293,9 @@
    * Updates the list of enabled languages from the preferred languages pref.
    */
   private preferredLanguagesPrefChanged_() {
-    if (this.prefs === undefined || this.languages === undefined) {
+    if (this.intlAcceptLanguagesPref_ === undefined ||
+        this.intlForcedLanguagesPref_ === undefined ||
+        this.languages === undefined) {
       return;
     }
 
@@ -286,16 +327,19 @@
    * Updates the spellCheckEnabled state of each enabled language.
    */
   private spellCheckDictionariesPrefChanged_() {
-    if (this.prefs === undefined || this.languages === undefined) {
+    if (this.spellcheckDictionariesPref_ === undefined ||
+        this.spellcheckForcedDictionariesPref_ === undefined ||
+        this.spellcheckBlockedDictionariesPref_ === undefined ||
+        this.languages === undefined) {
       return;
     }
 
-    const spellCheckSet = this.makeSetFromArray_(
-        this.getPref<string[]>('spellcheck.dictionaries').value);
-    const spellCheckForcedSet = this.makeSetFromArray_(
-        this.getPref<string[]>('spellcheck.forced_dictionaries').value);
-    const spellCheckBlockedSet = this.makeSetFromArray_(
-        this.getPref<string[]>('spellcheck.blocked_dictionaries').value);
+    const spellCheckSet =
+        this.makeSetFromArray_(this.spellcheckDictionariesPref_.value);
+    const spellCheckForcedSet =
+        this.makeSetFromArray_(this.spellcheckForcedDictionariesPref_.value);
+    const spellCheckBlockedSet =
+        this.makeSetFromArray_(this.spellcheckBlockedDictionariesPref_.value);
 
     for (let i = 0; i < this.languages.enabled.length; i++) {
       const languageState = this.languages.enabled[i];
@@ -333,18 +377,23 @@
      * Gets the list of language codes indicated by the preference name, and
      * de-duplicates it with all other language codes.
      */
-    const getPrefAndDedupe = (prefName: string): string[] => {
-      const result =
-          this.getPref<string[]>(prefName).value.filter(x => !seenCodes.has(x));
-      result.forEach((code: string) => seenCodes.add(code));
-      return result;
-    };
+    const getPrefAndDedupe =
+        (pref: chrome.settingsPrivate.PrefObject<string[]>): string[] => {
+          const result = pref.value.filter(x => !seenCodes.has(x));
+          result.forEach((code: string) => seenCodes.add(code));
+          return result;
+        };
 
-    const forcedCodes = getPrefAndDedupe('spellcheck.forced_dictionaries');
+    assert(this.spellcheckForcedDictionariesPref_);
+    const forcedCodes =
+        getPrefAndDedupe(this.spellcheckForcedDictionariesPref_);
     const forcedCodesSet = new Set(forcedCodes);
-    const blockedCodes = getPrefAndDedupe('spellcheck.blocked_dictionaries');
+    assert(this.spellcheckBlockedDictionariesPref_);
+    const blockedCodes =
+        getPrefAndDedupe(this.spellcheckBlockedDictionariesPref_);
     const blockedCodesSet = new Set(blockedCodes);
-    const enabledCodes = getPrefAndDedupe('spellcheck.dictionaries');
+    assert(this.spellcheckDictionariesPref_);
+    const enabledCodes = getPrefAndDedupe(this.spellcheckDictionariesPref_);
 
     const on: SpellCheckLanguageState[] = [];
     // We want to add newly enabled languages to the end of the "on" list, so we
@@ -401,11 +450,12 @@
    * Updates the list of always translate languages from translate prefs.
    */
   private alwaysTranslateLanguagesPrefChanged_() {
-    if (this.prefs === undefined || this.languages === undefined) {
+    if (this.translateAllowlistsPref_ === undefined ||
+        this.languages === undefined) {
       return;
     }
-    const alwaysTranslateCodes = Object.keys(
-        this.getPref<Record<string, unknown>>('translate_allowlists').value);
+    const alwaysTranslateCodes =
+        Object.keys(this.translateAllowlistsPref_.value);
     const alwaysTranslateLanguages =
         alwaysTranslateCodes.map((code: string) => this.getLanguage(code));
     this.set('languages.alwaysTranslate', alwaysTranslateLanguages);
@@ -415,11 +465,11 @@
    * Updates the list of never translate languages from translate prefs.
    */
   private neverTranslateLanguagesPrefChanged_() {
-    if (this.prefs === undefined || this.languages === undefined) {
+    if (this.translateBlockedLanguagesPref_ === undefined ||
+        this.languages === undefined) {
       return;
     }
-    const neverTranslateCodes =
-        this.getPref<string[]>('translate_blocked_languages').value;
+    const neverTranslateCodes = this.translateBlockedLanguagesPref_.value;
     const neverTranslateLanguages =
         neverTranslateCodes.map(code => this.getLanguage(code));
     this.set('languages.neverTranslate', neverTranslateLanguages);
@@ -429,23 +479,22 @@
    * Updates the list of never translate sites from translate prefs.
    */
   private neverTranslateSitesPrefChanged_() {
-    if (this.prefs === undefined || this.languages === undefined) {
+    if (this.translateSiteBlocklistPref_ === undefined ||
+        this.languages === undefined) {
       return;
     }
     const neverTranslateSites =
-        Object.keys(this.getPref<Record<string, unknown>>(
-                            'translate_site_blocklist_with_time')
-                        .value);
+        Object.keys(this.translateSiteBlocklistPref_.value);
     this.set('languages.neverTranslateSites', neverTranslateSites);
   }
 
   private translateLanguagesPrefChanged_() {
-    if (this.prefs === undefined || this.languages === undefined) {
+    if (this.translateBlockedLanguagesPref_ === undefined ||
+        this.languages === undefined) {
       return;
     }
 
-    const translateBlockedPrefValue =
-        this.getPref<string[]>('translate_blocked_languages').value;
+    const translateBlockedPrefValue = this.translateBlockedLanguagesPref_.value;
     const translateBlockedSet =
         this.makeSetFromArray_(translateBlockedPrefValue);
 
@@ -460,12 +509,12 @@
   }
 
   private translateTargetPrefChanged_() {
-    if (this.prefs === undefined || this.languages === undefined) {
+    if (this.translateRecentTargetPref_ === undefined ||
+        this.languages === undefined) {
       return;
     }
     this.set(
-        'languages.translateTarget',
-        this.getPref('translate_recent_target').value);
+        'languages.translateTarget', this.translateRecentTargetPref_.value);
   }
 
   /**
@@ -486,8 +535,8 @@
     let prospectiveUILanguage;
     // <if expr="is_win">
     // eslint-disable-next-line prefer-const
-    prospectiveUILanguage = this.getPref<string>('intl.app_locale').value ||
-        this.originalProspectiveUILanguage_;
+    prospectiveUILanguage =
+        this.intlAppLocalePref_?.value || this.originalProspectiveUILanguage_;
     // </if>
 
     // Create a list of enabled languages from the supported languages.
@@ -536,26 +585,20 @@
   private getEnabledLanguageStates_(
       translateTarget: string,
       prospectiveUILanguage: string|undefined): LanguageState[] {
-    assert(CrSettingsPrefs.isInitialized);
-
-    const pref = this.getPref<string>('intl.accept_languages');
-    const enabledLanguageCodes = pref.value.split(',');
-    const languagesForcedPref = this.getPref<string[]>('intl.forced_languages');
-    const spellCheckPref = this.getPref<string[]>('spellcheck.dictionaries');
-    const spellCheckForcedPref =
-        this.getPref<string[]>('spellcheck.forced_dictionaries');
-    const spellCheckBlockedPref =
-        this.getPref<string[]>('spellcheck.blocked_dictionaries');
-    const languageForcedSet = this.makeSetFromArray_(languagesForcedPref.value);
+    const enabledLanguageCodes =
+        (this.intlAcceptLanguagesPref_?.value || '').split(',');
+    const languageForcedSet =
+        this.makeSetFromArray_(this.intlForcedLanguagesPref_?.value || []);
     const spellCheckSet = this.makeSetFromArray_(
-        spellCheckPref.value.concat(spellCheckForcedPref.value));
-    const spellCheckForcedSet =
-        this.makeSetFromArray_(spellCheckForcedPref.value);
-    const spellCheckBlockedSet =
-        this.makeSetFromArray_(spellCheckBlockedPref.value);
+        (this.spellcheckDictionariesPref_?.value ||
+         []).concat(this.spellcheckForcedDictionariesPref_?.value || []));
+    const spellCheckForcedSet = this.makeSetFromArray_(
+        this.spellcheckForcedDictionariesPref_?.value || []);
+    const spellCheckBlockedSet = this.makeSetFromArray_(
+        this.spellcheckBlockedDictionariesPref_?.value || []);
 
     const translateBlockedPrefValue =
-        this.getPref<string[]>('translate_blocked_languages').value;
+        this.translateBlockedLanguagesPref_?.value || [];
     const translateBlockedSet =
         this.makeSetFromArray_(translateBlockedPrefValue);
 
@@ -621,12 +664,11 @@
       statusMap.set(status.languageCode, status);
     });
 
-    const collectionNames =
-        ['enabled', 'spellCheckOnLanguages', 'spellCheckOffLanguages'];
-    const languages = this.languages as unknown as
-        {[k: string]: Array<LanguageState|SpellCheckLanguageState>};
+    const collectionNames:
+        Array<'enabled'|'spellCheckOnLanguages'|'spellCheckOffLanguages'> =
+            ['enabled', 'spellCheckOnLanguages', 'spellCheckOffLanguages'];
     collectionNames.forEach(collectionName => {
-      languages[collectionName].forEach((languageState, index) => {
+      this.languages![collectionName].forEach((languageState, index) => {
         const status = statusMap.get(languageState.language.code);
         if (!status) {
           return;
@@ -655,7 +697,9 @@
    * on what other languages and input methods are enabled.
    */
   private updateRemovableLanguages_() {
-    if (this.prefs === undefined || this.languages === undefined) {
+    if (this.intlAppLocalePref_ === undefined ||
+        this.translateBlockedLanguagesPref_ === undefined ||
+        this.languages === undefined) {
       return;
     }
 
@@ -715,10 +759,6 @@
    * Enables the language, making it available for spell check and input.
    */
   enableLanguage(languageCode: string) {
-    if (!CrSettingsPrefs.isInitialized) {
-      return;
-    }
-
     this.languageSettingsPrivate_.enableLanguage(languageCode);
   }
 
@@ -726,12 +766,15 @@
    * Disables the language.
    */
   disableLanguage(languageCode: string) {
-    if (!CrSettingsPrefs.isInitialized) {
-      return;
-    }
-
     // Remove the language from spell check.
-    this.deletePrefListItem('spellcheck.dictionaries', languageCode);
+    const pref = this.spellcheckDictionariesPref_!;
+    const index = pref.value.indexOf(languageCode);
+    if (index !== -1) {
+      const updated = [...pref.value];
+      updated.splice(index, 1);
+      PrefService.getInstance().setPrefValue<string[]>(
+          'spellcheck.dictionaries', updated);
+    }
 
     // Remove the language from preferred languages.
     this.languageSettingsPrivate_.disableLanguage(languageCode);
@@ -778,10 +821,6 @@
    *     down
    */
   moveLanguage(languageCode: string, upDirection: boolean) {
-    if (!CrSettingsPrefs.isInitialized) {
-      return;
-    }
-
     if (upDirection) {
       this.languageSettingsPrivate_.moveLanguage(languageCode, MoveType.UP);
     } else {
@@ -793,10 +832,6 @@
    * Moves the language directly to the front of the list of enabled languages.
    */
   moveLanguageToFront(languageCode: string) {
-    if (!CrSettingsPrefs.isInitialized) {
-      return;
-    }
-
     this.languageSettingsPrivate_.moveLanguage(languageCode, MoveType.TOP);
   }
 
@@ -833,11 +868,18 @@
       return;
     }
 
+    const pref = this.spellcheckDictionariesPref_!;
     if (enable) {
-      this.getPref('spellcheck.dictionaries');
-      this.appendPrefListItem('spellcheck.dictionaries', languageCode);
+      PrefService.getInstance().appendPrefListItem<string>(
+          'spellcheck.dictionaries', languageCode);
     } else {
-      this.deletePrefListItem('spellcheck.dictionaries', languageCode);
+      const index = pref.value.indexOf(languageCode);
+      if (index !== -1) {
+        const updated = [...pref.value];
+        updated.splice(index, 1);
+        PrefService.getInstance().setPrefValue<string[]>(
+            'spellcheck.dictionaries', updated);
+      }
     }
   }
 
diff --git a/chrome/browser/resources/settings/settings_main/settings_main.html b/chrome/browser/resources/settings/settings_main/settings_main.html
index 2689096..a899694 100644
--- a/chrome/browser/resources/settings/settings_main/settings_main.html
+++ b/chrome/browser/resources/settings/settings_main/settings_main.html
@@ -44,8 +44,7 @@
 
 <if expr="not is_chromeos">
   <template is="dom-if" if="[[showPage_(pageVisibility_.languages)]]">
-    <settings-languages languages="{{languages_}}" prefs="{{prefs}}">
-    </settings-languages>
+    <settings-languages languages="{{languages_}}"></settings-languages>
   </template>
 </if>
 
diff --git a/chrome/browser/resources/settings_shared/prefs2/pref_service.ts b/chrome/browser/resources/settings_shared/prefs2/pref_service.ts
index 8dcb259..c75e33b 100644
--- a/chrome/browser/resources/settings_shared/prefs2/pref_service.ts
+++ b/chrome/browser/resources/settings_shared/prefs2/pref_service.ts
@@ -108,6 +108,20 @@
     return this.setPrefValue(prefKey, dict);
   }
 
+  /**
+   * @param prefKey The key of the PrefObject to be updated. Must be of
+   *     PrefType.LIST otherwise an assertion error will be thrown.
+   * @param value The value to append to the list pref if not already present.
+   */
+  async appendPrefListItem<T>(prefKey: string, value: T): Promise<boolean> {
+    const pref = this.getPref<T[]>(prefKey);
+    assert(pref.type === chrome.settingsPrivate.PrefType.LIST);
+    if (pref.value.includes(value)) {
+      return Promise.resolve(true);
+    }
+    return this.setPrefValue(prefKey, [...pref.value, value]);
+  }
+
   getPref<T>(key: string): Readonly<PrefObject<T>> {
     assert(
         this.isInitialized_,
diff --git a/chrome/browser/resources/side_panel/read_anything/app/app.html.ts b/chrome/browser/resources/side_panel/read_anything/app/app.html.ts
index 3141792..e109442 100644
--- a/chrome/browser/resources/side_panel/read_anything/app/app.html.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app/app.html.ts
@@ -37,6 +37,7 @@
         .lineFocusEnabled="${this.lineFocusEnabled_}"
         .lineFocusMovement="${this.lineFocusMovement_}"
         .isLineFocusShowing="${this.computeIsLineFocusShowing_()}"
+        .showLineFocusNewBadge="${this.showLineFocusNewBadge_}"
         @select-voice="${this.onSelectVoice_}"
         @voice-language-toggle="${this.onVoiceLanguageToggle_}"
         @preview-voice="${this.onPreviewVoice_}"
diff --git a/chrome/browser/resources/side_panel/read_anything/app/app.ts b/chrome/browser/resources/side_panel/read_anything/app/app.ts
index 0061e82..c3cd011 100644
--- a/chrome/browser/resources/side_panel/read_anything/app/app.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app/app.ts
@@ -92,6 +92,7 @@
       lineFocusMovement_: {type: Number},
       isDocsLoadMoreButtonVisible_: {type: Boolean},
       hasValidSelection_: {type: Boolean},
+      showLineFocusNewBadge_: {type: Boolean},
     };
   }
 
@@ -152,6 +153,9 @@
 
   protected accessor isSpeechActive_: boolean = false;
   protected accessor isAudioCurrentlyPlaying_: boolean = false;
+  // TODO(crbug.com/543113387): Remove this when the WebUI new badge supports
+  // auto-disappearing logic itself.
+  protected accessor showLineFocusNewBadge_: boolean = false;
 
   protected accessor presentationState_: number = 0;
 
@@ -298,6 +302,10 @@
       this.restoreSettingsFromPrefs_();
     };
 
+    chrome.readingMode.onShouldShowLineFocusNewBadgeResponse = (show) => {
+      this.showLineFocusNewBadge_ = show;
+    };
+
     chrome.readingMode.languageChanged = () => {
       this.languageChanged();
     };
diff --git a/chrome/browser/resources/side_panel/read_anything/app/read_anything.d.ts b/chrome/browser/resources/side_panel/read_anything/app/read_anything.d.ts
index f74dffa..8994a83 100644
--- a/chrome/browser/resources/side_panel/read_anything/app/read_anything.d.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app/read_anything.d.ts
@@ -151,9 +151,6 @@
     // should only be used when baseLanguageForSpeech is unavailable.
     let defaultLanguageForSpeech: string;
 
-    // If the current platform is ChromeOS Ash.
-    let isChromeOsAsh: boolean;
-
     // If distillations have been queued up.
     let requiresDistillation: boolean;
 
@@ -213,10 +210,6 @@
     // contained within the selection.
     function getChildren(nodeId: number): number[];
 
-    // Returns content of "data-font-css" html attribute. This is needed for
-    // rendering content from annotated canvas in Google Docs.
-    function getDataFontCss(nodeId: number): string;
-
     // Returns the HTML tag of the AXNode for the provided AXNodeID.
     function getHtmlTag(nodeId: number): string;
 
@@ -399,12 +392,6 @@
     function setAnchorsForTesting(
         snapshotLite: Object, contentNodeIds: number[]): void;
 
-    // Set the theme. Used by tests only.
-    function setThemeForTesting(
-        fontName: string, fontSize: number, linksEnabled: boolean,
-        foregroundColor: number, backgroundColor: number, lineSpacing: number,
-        letterSpacing: number): void;
-
     // Sets the page language. Used by tests only.
     function setLanguageForTesting(code: string): void;
 
@@ -436,6 +423,19 @@
     // Called by the Read Anything app to toggle between presentation modes.
     function togglePresentation(): void;
 
+    // The following 3 functions are for handling the auto-disappearing logic
+    // for the line focus new badge.
+    // TODO(crbug.com/543113387): Remove these when the WebUI new badge supports
+    // auto-disappearing logic itself.
+    // Called to request whether to show the new badge for the line focus menu.
+    function requestShouldShowLineFocusNewBadge(): void;
+
+    // Called with the answer to the above requestShouldShowLineFocusNewBadge.
+    function onShouldShowLineFocusNewBadgeResponse(show: boolean): void;
+
+    // Called when the line focus feature is used.
+    function onLineFocusFeatureUsed(): void;
+
     // Whether the Google Docs load more button is visible.
     let isDocsLoadMoreButtonVisible: boolean;
 
@@ -613,9 +613,9 @@
 
     // Called when the main frame undergoes a same document navigation (such as
     // a fragment navigation).
-    let onMainFrameSameDocumentNavigation: (url: string) => void;
+    function onMainFrameSameDocumentNavigation(url: string): void;
 
     // Called to inform the web ui to play read aloud on open.
-    let setPlayOnOpen: (playOnOpen: boolean) => void;
+    function setPlayOnOpen(playOnOpen: boolean): void;
   }
 }
diff --git a/chrome/browser/resources/side_panel/read_anything/app/read_anything_toolbar.html.ts b/chrome/browser/resources/side_panel/read_anything/app/read_anything_toolbar.html.ts
index 99ec16a..f135ae9 100644
--- a/chrome/browser/resources/side_panel/read_anything/app/read_anything_toolbar.html.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app/read_anything_toolbar.html.ts
@@ -157,6 +157,7 @@
       .isImmersiveMode="${this.isImmersiveMode}"
       .isReadAnythingPinned="${this.isReadAnythingPinned}"
       .isSpeechActive="${this.isSpeechActive}"
+      .showLineFocusNewBadge="${this.showLineFocusNewBadge}"
       @close-submenu-requested="${this.onCloseSubmenuRequested_}"
       @close-all-menus="${this.onCloseAllMenus_}"
       @open-settings-submenu="${this.onOpenSettingsSubmenu_}"
diff --git a/chrome/browser/resources/side_panel/read_anything/app/read_anything_toolbar.ts b/chrome/browser/resources/side_panel/read_anything/app/read_anything_toolbar.ts
index 71049c1c5..29f3059 100644
--- a/chrome/browser/resources/side_panel/read_anything/app/read_anything_toolbar.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app/read_anything_toolbar.ts
@@ -177,6 +177,7 @@
       lineFocusStyle: {type: Object},
       lineFocusEnabled: {type: Boolean},
       lineFocusMovement: {type: Number},
+      showLineFocusNewBadge: {type: Boolean},
       webuiRoundedIconsEnabled_: {type: Boolean},
     };
   }
@@ -207,6 +208,9 @@
   accessor lineFocusStyle: LineFocusStyle|null = null;
   accessor lineFocusEnabled: boolean = false;
   accessor lineFocusMovement: LineFocusMovement|null = null;
+  // TODO(crbug.com/543113387): Remove this when the WebUI new badge supports
+  // auto-disappearing logic itself.
+  accessor showLineFocusNewBadge: boolean = false;
   protected accessor hideSpinner_: boolean = true;
   protected accessor isImmersiveEnabled_: boolean = false;
   // Overflow buttons on the toolbar that open a menu of options.
diff --git a/chrome/browser/resources/side_panel/read_anything/content/line_focus_move_mode.ts b/chrome/browser/resources/side_panel/read_anything/content/line_focus_move_mode.ts
index 1464ed0..9daf817 100644
--- a/chrome/browser/resources/side_panel/read_anything/content/line_focus_move_mode.ts
+++ b/chrome/browser/resources/side_panel/read_anything/content/line_focus_move_mode.ts
@@ -209,7 +209,7 @@
   }
 
   protected getCenterY(): number {
-    return (this.model_.getMaxY()) / 2;
+    return this.styleMode_.getCenterY();
   }
 
   protected notifyScrollToTop(): void {
diff --git a/chrome/browser/resources/side_panel/read_anything/content/line_focus_style_mode.ts b/chrome/browser/resources/side_panel/read_anything/content/line_focus_style_mode.ts
index edfb0d3..37f611a7 100644
--- a/chrome/browser/resources/side_panel/read_anything/content/line_focus_style_mode.ts
+++ b/chrome/browser/resources/side_panel/read_anything/content/line_focus_style_mode.ts
@@ -21,6 +21,9 @@
     return this.style_;
   }
 
+  // Returns the vertical midpoint of the visible reading area.
+  abstract getCenterY(): number;
+
   // Returns how far from the center the current focal point is if it is outside
   // the viewport. Returns 0 otherwise.
   getOffScreenDiff(targetIndex: number): number {
@@ -30,7 +33,7 @@
     if (bottomRect.bottom > this.model_.getMaxY() ||
         topRect.top < this.model_.getMinY()) {
       const center = this.getFocalPoint(topRect, bottomRect);
-      return center - (this.model_.getMaxY() / 2);
+      return center - this.getCenterY();
     }
     return 0;
   }
@@ -41,7 +44,7 @@
     const {topRect, bottomRect} =
         this.getFocusWindowBounds(textBounds, targetIndex);
     const center = this.getFocalPoint(topRect, bottomRect);
-    return center - (this.model_.getMaxY() / 2);
+    return center - this.getCenterY();
   }
 
   // Returns where the center of the focus element should be in the focus area
@@ -86,6 +89,16 @@
     super(style, model);
   }
 
+  override getCenterY(): number {
+    // Because underline style is rendered directly under a line of text,
+    // offsetting by the toolbar / minY can sometimes cause the underline
+    // to be placed directly through the center of the text when scrolling
+    // automatically, so the vertical midpoint should only be calculated using
+    // maxY.
+    return this.model_.getMaxY() / 2;
+  }
+
+
   updateFocusBounds(): void {
     this.model_.setTop(this.model_.getFocalPoint());
     this.model_.setWindowHeight(0);
@@ -131,6 +144,14 @@
     super(style, model);
   }
 
+  override getCenterY(): number {
+    // Because window style is rendered centered around a line of text,
+    // the vertical midpoint should be offset by the toolbar (minY) in
+    // order to ensure the window is properly centered around the current
+    // line(s) when scrolling automatically.
+    return (this.model_.getMinY() + this.model_.getMaxY()) / 2;
+  }
+
   updateFocusBounds(): void {
     const bounds = this.model_.getTextBounds();
     if (bounds.length === 0) {
@@ -140,7 +161,7 @@
     // Use a set height to calculate a multi-line window if the window should
     // not adapt to the line heights.
     if (!this.shouldAdaptToTextBounds()) {
-      const center = this.model_.getMaxY() / 2;
+      const center = this.getCenterY();
       const mostCommonPitch = getMostCommonPitch(bounds);
       const windowHeight = mostCommonPitch * this.style_.lines;
       this.model_.setTop(center - (windowHeight / 2));
@@ -268,6 +289,11 @@
     return false;
   }
 
+  override getCenterY(): number {
+    // The center Y isn't needed when line focus is disabled.
+    return 0;
+  }
+
   protected getFocusWindowBounds(_lines: DOMRect[], _targetIndex: number):
       {topRect: DOMRect, bottomRect: DOMRect} {
     // The focus area should never be drawn when line focus is disabled.
diff --git a/chrome/browser/resources/side_panel/read_anything/menus/line_focus_menu.ts b/chrome/browser/resources/side_panel/read_anything/menus/line_focus_menu.ts
index 576ab7c..7c8bd32a 100644
--- a/chrome/browser/resources/side_panel/read_anything/menus/line_focus_menu.ts
+++ b/chrome/browser/resources/side_panel/read_anything/menus/line_focus_menu.ts
@@ -152,6 +152,9 @@
 
   close() {
     this.$.menu.close();
+    if (this.lineFocusEnabled) {
+      chrome.readingMode.onLineFocusFeatureUsed();
+    }
   }
 
   protected onLineFocusStyleChange_() {
diff --git a/chrome/browser/resources/side_panel/read_anything/menus/menu_util.ts b/chrome/browser/resources/side_panel/read_anything/menus/menu_util.ts
index 70e6349..3b4c453 100644
--- a/chrome/browser/resources/side_panel/read_anything/menus/menu_util.ts
+++ b/chrome/browser/resources/side_panel/read_anything/menus/menu_util.ts
@@ -26,6 +26,8 @@
   // Needed when the aria label should be different from the title
   ariaLabel?: string;
   showSeparator?: boolean;
+  // Whether to show the "NEW" badge for this item.
+  showBadge?: boolean;
 }
 
 // Represents a single menu item in a dropown menu in the toolbar.
diff --git a/chrome/browser/resources/side_panel/read_anything/menus/rate_menu.ts b/chrome/browser/resources/side_panel/read_anything/menus/rate_menu.ts
index 5c7d826..1aeef6a 100644
--- a/chrome/browser/resources/side_panel/read_anything/menus/rate_menu.ts
+++ b/chrome/browser/resources/side_panel/read_anything/menus/rate_menu.ts
@@ -26,9 +26,18 @@
 
 // 3x and 4x speeds are hidden on non-ChromeOS because natural voices on
 // non-ChromeOS do not currently support 3x and 4x speeds.
-export const RATE_OPTIONS: number[] = chrome.readingMode.isChromeOsAsh ?
-    [0.5, 0.8, 1, 1.2, 1.5, 2, 3, 4] :
-    [0.5, 0.8, 1, 1.2, 1.5, 2];
+export const RATE_OPTIONS: number[] = [
+  0.5,
+  0.8,
+  1,
+  1.2,
+  1.5,
+  2,
+  // <if expr="is_chromeos">
+  3,
+  4,
+  // </if>
+];
 
 const RateMenuElementBase = WebUiListenerMixinLit(CrLitElement);
 
diff --git a/chrome/browser/resources/side_panel/read_anything/menus/settings_menu.css b/chrome/browser/resources/side_panel/read_anything/menus/settings_menu.css
index ec57eac0..0462f16 100644
--- a/chrome/browser/resources/side_panel/read_anything/menus/settings_menu.css
+++ b/chrome/browser/resources/side_panel/read_anything/menus/settings_menu.css
@@ -54,10 +54,18 @@
 }
 
 .menu-row .label {
+  align-items: center;
+  display: flex;
   flex: 1;
+  gap: 8px;
   font-weight: 400;
 }
 
+new-badge {
+  --color-badge-background: var(--color-sys-primary);
+  color: var(--color-sys-on-primary);
+}
+
 .menu-row .end-icon {
   --cr-icon-color: var(--cr-secondary-text-color);
 }
diff --git a/chrome/browser/resources/side_panel/read_anything/menus/settings_menu.html.ts b/chrome/browser/resources/side_panel/read_anything/menus/settings_menu.html.ts
index 75a1332..0308a976 100644
--- a/chrome/browser/resources/side_panel/read_anything/menus/settings_menu.html.ts
+++ b/chrome/browser/resources/side_panel/read_anything/menus/settings_menu.html.ts
@@ -33,7 +33,10 @@
             <cr-icon class="start-icon" icon="${item.icon}"></cr-icon>
           ` : ''}
 
-          <div class="label">${item.title}</div>
+          <div class="label">
+            ${item.title}
+            ${item.showBadge ? html`<new-badge></new-badge>` : ''}
+          </div>
         </div>
 
         ${item.itemType === SettingsItemType.TOGGLE ? html`
diff --git a/chrome/browser/resources/side_panel/read_anything/menus/settings_menu.ts b/chrome/browser/resources/side_panel/read_anything/menus/settings_menu.ts
index 94f617b..a77cb7a0 100644
--- a/chrome/browser/resources/side_panel/read_anything/menus/settings_menu.ts
+++ b/chrome/browser/resources/side_panel/read_anything/menus/settings_menu.ts
@@ -7,6 +7,7 @@
 import '//resources/cr_elements/cr_icon/cr_icon.js';
 import '//resources/cr_elements/cr_toggle/cr_toggle.js';
 import '//resources/cr_elements/cr_lazy_render/cr_lazy_render_lit.js';
+import '//resources/cr_components/help_bubble/new_badge.js';
 
 import type {CrActionMenuElement} from '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
 import type {CrLazyRenderLitElement} from '//resources/cr_elements/cr_lazy_render/cr_lazy_render_lit.js';
@@ -112,6 +113,7 @@
         'read-anything:line-focus-old',
     title: 'lineFocusLabel',
     itemType: SettingsItemType.MENU,
+    showBadge: false,
   },
   [SettingsOption.PINNED_TO_TOOLBAR]: {
     id: SettingsOption.PINNED_TO_TOOLBAR,
@@ -189,6 +191,7 @@
       isImmersiveMode: {type: Boolean},
       isReadAnythingPinned: {type: Boolean},
       isSpeechActive: {type: Boolean},
+      showLineFocusNewBadge: {type: Boolean},
       settingsPrefs: {type: Object},
       currentOpenId_: {
         state: true,
@@ -201,6 +204,9 @@
   accessor isImmersiveMode: boolean = false;
   accessor isReadAnythingPinned: boolean = false;
   accessor isSpeechActive: boolean = false;
+  // TODO(crbug.com/543113387): Remove this when the WebUI new badge supports
+  // auto-disappearing logic itself.
+  accessor showLineFocusNewBadge: boolean = false;
   accessor settingsPrefs: SettingsPrefs = DEFAULT_SETTINGS;
 
   protected accessor options_: SettingsItem[] = [];
@@ -233,7 +239,8 @@
     if (changedProperties.has('settingsPrefs') ||
         changedProperties.has('isImmersiveMode') ||
         changedProperties.has('isReadAnythingPinned') ||
-        changedProperties.has('isSpeechActive')) {
+        changedProperties.has('isSpeechActive') ||
+        changedProperties.has('showLineFocusNewBadge')) {
       this.initializeMenuOptions_();
     }
   }
@@ -328,6 +335,8 @@
         disabled = this.isSpeechActive;
       }
 
+      const showBadge =
+          (id === SettingsOption.LINE_FOCUS) && this.showLineFocusNewBadge;
       if (id === SettingsOption.PINNED_TO_TOOLBAR) {
         checked = this.isReadAnythingPinned;
         ariaLabel = this.getPinItemLabels();
@@ -340,6 +349,7 @@
         ariaLabel,
         checked,
         disabled,
+        showBadge,
       };
     });
 
@@ -575,6 +585,9 @@
   }
 
   open(anchor: HTMLElement) {
+    if (chrome.readingMode.isLineFocusEnabled) {
+      chrome.readingMode.requestShouldShowLineFocusNewBadge();
+    }
     openMenu(this.$.lazyMenu.get(), anchor);
     window.addEventListener('keydown', this.keyDownCallback_, {capture: true});
     this.interceptedEvents_.forEach(eventType => {
diff --git a/chrome/browser/resources/side_panel/read_anything/read_aloud/tts_voice_filtering.ts b/chrome/browser/resources/side_panel/read_anything/read_aloud/tts_voice_filtering.ts
index d87c08b..b0889db 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_aloud/tts_voice_filtering.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_aloud/tts_voice_filtering.ts
@@ -2,7 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {AVAILABLE_GOOGLE_TTS_LOCALES, convertLangOrLocaleToExactVoicePackLocale, defaultIsGoogle, hasEspeakIdentifier} from './voice_language_conversions.js';
+import {convertLangOrLocaleToExactVoicePackLocale} from './voice_language_conversions.js';
+// clang-format off
+// <if expr="is_chromeos">
+import {hasEspeakIdentifier} from './voice_language_conversions.js';
+// </if>
+// <if expr="not is_chromeos">
+import {AVAILABLE_GOOGLE_TTS_LOCALES, defaultIsGoogle} from './voice_language_conversions.js';
+// </if>
+// clang-format on
 
 // Helper for filtering the voice list.
 export function getFilteredVoiceList(possibleVoices: SpeechSynthesisVoice[]):
@@ -17,43 +25,43 @@
   // doesn't filter them out. Since they can cause unexpected behavior
   // in Read Aloud, go ahead and filter them out. To avoid causing any
   // unexpected behavior outside of ChromeOS, just filter them on ChromeOS.
-  if (chrome.readingMode.isChromeOsAsh) {
-    availableVoices = availableVoices.filter(
-        ({name}) => !name.toLowerCase().includes('android'));
-    // Filter out espeak voices if there exists a Google voice in the same
-    // locale.
-    availableVoices = availableVoices.filter(
-        voice => !hasEspeakIdentifier(voice) ||
-            convertLangOrLocaleToExactVoicePackLocale(voice.lang) ===
-                undefined);
-  } else {
-    // Group non-Google voices by language and select a default voice for each
-    // language. This represents the system voice for each language.
-    const languageToNonGoogleVoices =
-        availableVoices.filter(voice => !defaultIsGoogle(voice))
-            .reduce((map, voice) => {
-              map[voice.lang] = map[voice.lang] || [];
-              map[voice.lang]!.push(voice);
-              return map;
-            }, {} as {[language: string]: SpeechSynthesisVoice[]});
-    // Only keep system voices that exactly match Google TTS supported locales,
-    // or for languages for which there are no Google TTS supported locales.
-    const systemVoices =
-        Object.values(languageToNonGoogleVoices)
-            .map((voices) => {
-              const defaultVoice = voices.find(voice => voice.default);
-              return defaultVoice || voices[0];
-            })
-            .filter(
-                systemVoice => AVAILABLE_GOOGLE_TTS_LOCALES.has(
-                                   systemVoice!.lang.toLowerCase()) ||
-                    convertLangOrLocaleToExactVoicePackLocale(
-                        systemVoice!.lang.toLowerCase()) === undefined);
+  // <if expr="is_chromeos">
+  availableVoices = availableVoices.filter(
+      ({name}) => !name.toLowerCase().includes('android'));
+  // Filter out espeak voices if there exists a Google voice in the same
+  // locale.
+  availableVoices = availableVoices.filter(
+      voice => !hasEspeakIdentifier(voice) ||
+          convertLangOrLocaleToExactVoicePackLocale(voice.lang) === undefined);
+  // </if>
+  // <if expr="not is_chromeos">
+  // Group non-Google voices by language and select a default voice for each
+  // language. This represents the system voice for each language.
+  const languageToNonGoogleVoices =
+      availableVoices.filter(voice => !defaultIsGoogle(voice))
+          .reduce((map, voice) => {
+            map[voice.lang] = map[voice.lang] || [];
+            map[voice.lang]!.push(voice);
+            return map;
+          }, {} as {[language: string]: SpeechSynthesisVoice[]});
+  // Only keep system voices that exactly match Google TTS supported locales,
+  // or for languages for which there are no Google TTS supported locales.
+  const systemVoices =
+      Object.values(languageToNonGoogleVoices)
+          .map((voices) => {
+            const defaultVoice = voices.find(voice => voice.default);
+            return defaultVoice || voices[0];
+          })
+          .filter(
+              systemVoice => AVAILABLE_GOOGLE_TTS_LOCALES.has(
+                                 systemVoice!.lang.toLowerCase()) ||
+                  convertLangOrLocaleToExactVoicePackLocale(
+                      systemVoice!.lang.toLowerCase()) === undefined);
 
-    // Keep all Google voices and one system voice per language.
-    availableVoices = availableVoices.filter(
-        voice => defaultIsGoogle(voice) || systemVoices.includes(voice));
-  }
+  // Keep all Google voices and one system voice per language.
+  availableVoices = availableVoices.filter(
+      voice => defaultIsGoogle(voice) || systemVoices.includes(voice));
+  // </if>
 
   return availableVoices;
 }
diff --git a/chrome/browser/resources/skills/discover_skills_page.ts b/chrome/browser/resources/skills/discover_skills_page.ts
index 257d49f8..93c9de2 100644
--- a/chrome/browser/resources/skills/discover_skills_page.ts
+++ b/chrome/browser/resources/skills/discover_skills_page.ts
@@ -20,7 +20,7 @@
 import {getCss} from './discover_skills_page.css.js';
 import {getHtml} from './discover_skills_page.html.js';
 import type {Skill, TopicInfo} from './skill.mojom-webui.js';
-import {SkillsDialogType} from './skill.mojom-webui.js';
+import {SkillsDialogType, SkillSource} from './skill.mojom-webui.js';
 import {SkillsManagementAction, SkillsManagementPage} from './skill_metrics.mojom-webui.js';
 import type {BrowseSkillsInitialState} from './skills.mojom-webui.js';
 import {SkillsPageBrowserProxy} from './skills_page_browser_proxy.js';
@@ -99,6 +99,15 @@
     this.proxy_.handler.recordSkillsManagementAction(
         SkillsManagementPage.kBrowseSkills,
         SkillsManagementAction.kClickedAddSkill);
+
+    // Enterprise skills are fully defined and downloaded on policy update and
+    // do not require a separate prompt download request or verification via
+    // maybeSaveSkill(). Open the save dialog immediately.
+    if (savedSkill.source === SkillSource.kEnterprise) {
+      this.proxy_.handler.openSkillsDialog(SkillsDialogType.kAdd, savedSkill);
+      return;
+    }
+
     this.is1PSkillSaving_ = true;
     this.proxy_.handler.maybeSave1PSkill(savedSkill.id).then(({success}) => {
       if (success) {
@@ -157,6 +166,8 @@
         return loadTimeData.getBoolean('webuiRoundedIconsEnabled') ?
             'skills:ink-pen' :
             'skills:write-old';
+      case 'From your organization':
+        return 'cr:domain';
       default:
         return 'cr:add';
     }
diff --git a/chrome/browser/resources/user_education_internals/user_education_internals.html.ts b/chrome/browser/resources/user_education_internals/user_education_internals.html.ts
index d7613cc4..51733ef 100644
--- a/chrome/browser/resources/user_education_internals/user_education_internals.html.ts
+++ b/chrome/browser/resources/user_education_internals/user_education_internals.html.ts
@@ -79,7 +79,7 @@
         </p>
       </div>
       <cr-page-selector ?hidden="${!this.initialized_}"
-                        .selected="${this.selectedTabIndex_}" show-all="true"
+                        .selected="${this.selectedTabIndex}" show-all="true"
                         id="selector">
         <div id="iph" class="promo-list">
           <h2>Feature Promos</h2>
diff --git a/chrome/browser/resources/user_education_internals/user_education_internals.ts b/chrome/browser/resources/user_education_internals/user_education_internals.ts
index 4a0dfb5..5e7c1a8 100644
--- a/chrome/browser/resources/user_education_internals/user_education_internals.ts
+++ b/chrome/browser/resources/user_education_internals/user_education_internals.ts
@@ -28,6 +28,7 @@
 import type {CrPageSelectorElement} from 'chrome://resources/cr_elements/cr_page_selector/cr_page_selector.js';
 import type {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
+import {TrackedElementManager} from 'chrome://resources/js/tracked_element/tracked_element_manager.js';
 import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
@@ -72,6 +73,11 @@
        */
       filter: {type: String},
       /**
+       * Index of the selected "tab" determining which data to show. -1 means
+       * show all.
+       */
+      selectedTabIndex: {type: Number},
+      /**
        * List of tutorials and feature_promos that can be started.
        * Each tutorial has a string identifier.
        */
@@ -108,12 +114,12 @@
       ntpPromoPreferences_: {type: Array},
       sessionData_: {type: Array},
       currentChromeVersion_: {type: Number},
-      selectedTabIndex_: {type: Number},
       initialized_: {type: Boolean},
     };
   }
 
   accessor filter: string = '';
+  accessor selectedTabIndex = -1;
   protected accessor tutorials_: FeaturePromoDemoPageInfo[] = [];
   protected accessor featurePromos_: FeaturePromoDemoPageInfo[] = [];
   protected accessor newBadges_: FeaturePromoDemoPageInfo[] = [];
@@ -131,7 +137,6 @@
       loadTimeData.getInteger('whatsNewVersionToRequest');
   protected accessor currentChromeVersion_: number =
       loadTimeData.getInteger('currentChromeVersion');
-  protected accessor selectedTabIndex_ = -1;
   protected accessor initialized_ = false;
 
   private handler_: UserEducationInternalsPageHandlerInterface;
@@ -145,6 +150,18 @@
     ColorChangeUpdater.forDocument().start();
 
     this.checkInitializedAndReadAllData_();
+
+    // These are used in tests of the TrackedElementManager and its handler.
+    const manager = TrackedElementManager.getInstance();
+    manager.startTracking(
+        this.$.menu, 'UserEducationInternalsUI::kMenuElementId');
+    for (const child of this.$.menu.children) {
+      if (child instanceof HTMLElement && child.getAttribute('index')) {
+        manager.startTracking(
+            child, 'UserEducationInternalsUI::kMenuItemElementId',
+            {secondaryId: 'index:' + child.getAttribute('index')});
+      }
+    }
   }
 
   // If data is read from the backend before the Feature Engagement system is
@@ -432,7 +449,7 @@
     this.$.menu.selected = index;
     if (index >= 0) {
       this.$.selector.removeAttribute('show-all');
-      this.selectedTabIndex_ = index;
+      this.selectedTabIndex = index;
     } else {
       this.$.selector.setAttribute('show-all', 'true');
     }
diff --git a/chrome/browser/resources/webui_toolbar/content_setting_icon.css b/chrome/browser/resources/webui_toolbar/content_setting_icon.css
index f939d50..fb7114f 100644
--- a/chrome/browser/resources/webui_toolbar/content_setting_icon.css
+++ b/chrome/browser/resources/webui_toolbar/content_setting_icon.css
@@ -30,6 +30,8 @@
 }
 
 #icon {
+  height: var(--icon-size);
+  width: var(--icon-size);
   background-color: currentColor;
   mask-position: center;
   mask-repeat: no-repeat;
diff --git a/chrome/browser/resources/webui_toolbar/location_bar.css b/chrome/browser/resources/webui_toolbar/location_bar.css
index 06b8ea2..199d01e 100644
--- a/chrome/browser/resources/webui_toolbar/location_bar.css
+++ b/chrome/browser/resources/webui_toolbar/location_bar.css
@@ -27,7 +27,8 @@
    * anyway. We may need to compensate in chip positioning (or change the
    * approach once they exist
    */
-  padding-inline: var(--border-radius);
+  padding-inline-start: var(--border-radius);
+  padding-inline-end: var(--location-bar-trailing-decoration-edge-padding);
   transition-property: background-color;
   transition-duration: 200ms;
   transition-timing-function: linear;
diff --git a/chrome/browser/resources/webui_toolbar/page_action_icon.css b/chrome/browser/resources/webui_toolbar/page_action_icon.css
index 1049754..e11ec3c 100644
--- a/chrome/browser/resources/webui_toolbar/page_action_icon.css
+++ b/chrome/browser/resources/webui_toolbar/page_action_icon.css
@@ -4,6 +4,7 @@
 
 /* #css_wrapper_metadata_start
  * #type=style-lit
+ * #import=/shared/webui_toolbar_shared_vars.css.js
  * #scheme=relative
  * #css_wrapper_metadata_end */
 
@@ -11,15 +12,52 @@
   display: flex;
   align-items: center;
   width: auto;
+
+  /* Matches the C++ slide duration in PageActionView. */
+  /* TODO(crbug.com/526706879): Support reduced motion. */
+  --chip-slide-duration: 600ms;
 }
 
-:host(.force-focus-ring) cr-icon-button {
+/* Target the toolbar-chip-button directly to apply layout variables.
+ * These must be applied to the child element from the outside rather than
+ * on :host, because toolbar-chip-button's own :host block defines default
+ * values that would override any values inherited from page-action-icon. */
+#button {
+  /* TODO(crbug.com/501449027): Add a different background color when expanded
+   * as a chip to match C++ views (e.g. for PWA install icons). */
+  --toolbar-chip-height: calc(
+      var(--location-bar-trailing-icon-size) +
+      var(--location-bar-page-action-icon-padding-top) +
+      var(--location-bar-page-action-icon-padding-bottom));
+  --toolbar-chip-icon-size: var(--location-bar-trailing-icon-size);
+  --toolbar-chip-padding-start:
+      var(--location-bar-page-action-icon-padding-start);
+  --toolbar-chip-padding-end:
+      var(--location-bar-page-action-icon-padding-end);
+  /* The 4px manually mirrors the SetExpandedLabelAdditionalInsets(4, 8) gap
+   * added dynamically in Native C++ PageActionView::PageActionView(). */
+  --toolbar-chip-icon-label-gap:
+      calc(var(--location-bar-child-internal-spacing) + 4px);
+  --toolbar-chip-expand-duration: var(--chip-slide-duration);
+}
+
+#text {
+  display: inline-block;
+  font-family: var(--omnibox-primary-body-3-emphasis-font-family);
+  font-size: var(--omnibox-primary-body-3-emphasis-font-size);
+  font-weight: var(--omnibox-primary-body-3-emphasis-font-weight);
+  line-height: var(--omnibox-primary-body-3-emphasis-line-height, inherit);
+  text-overflow: clip;
+  white-space: nowrap;
+}
+
+:host(.force-focus-ring) #button {
   box-shadow: inset 0 0 0 2px var(--cr-icon-button-focus-outline-color,
       var(--cr-focus-outline-color));
 }
 
 @media (forced-colors: active) {
-  :host(.force-focus-ring) cr-icon-button {
+  :host(.force-focus-ring) #button {
     /* Use outline instead of box-shadow (which does not work) in Windows
        HCM. */
     outline: var(--cr-focus-outline-hcm);
diff --git a/chrome/browser/resources/webui_toolbar/page_action_icon.html.ts b/chrome/browser/resources/webui_toolbar/page_action_icon.html.ts
index dac37b1..617569b36 100644
--- a/chrome/browser/resources/webui_toolbar/page_action_icon.html.ts
+++ b/chrome/browser/resources/webui_toolbar/page_action_icon.html.ts
@@ -2,21 +2,29 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {html, nothing} from '//resources/lit/v3_0/lit.rollup.js';
+import '/shared/icon_from_table.js';
+import {html} from '//resources/lit/v3_0/lit.rollup.js';
 
 import type {PageActionIconElement} from './page_action_icon.js';
 
 export function getHtml(this: PageActionIconElement) {
+  // clang-format off
   return html`<!--_html_template_start_-->
-<cr-icon-button id="button"
-    iron-icon="${this.getIronIcon_() ?? nothing}"
-    style="${this.getIconStyle_() ?? nothing}"
-    title="${this.state.tooltipText}"
-    aria-label="${this.getAriaLabel_()}"
+<toolbar-chip-button id="button"
+    ?animates-label="${this.shouldAnimate_()}"
+    ?has-label="${this.shouldShowLabel_()}"
+    .tooltip="${this.state.tooltipText}"
+    .ariaLabel="${this.getAriaLabel_()}"
     @click="${this.onClick_}"
     @pointerenter="${this.onPointerenter_}"
     @pointerleave="${this.onPointerleave_}"
     @pointercancel="${this.onPointercancel_}">
-</cr-icon-button>
+  <icon-from-table slot="prefix-icon" id="icon"
+      .iconHandle="${this.state.icon}"></icon-from-table>
+  <span id="text" ?visible="${this.shouldShowLabel_()}">
+    ${this.state.text || ''}
+  </span>
+</toolbar-chip-button>
 <!--_html_template_end_-->`;
+  // clang-format on
 }
diff --git a/chrome/browser/resources/webui_toolbar/page_action_icon.ts b/chrome/browser/resources/webui_toolbar/page_action_icon.ts
index c7a9b29..af22272 100644
--- a/chrome/browser/resources/webui_toolbar/page_action_icon.ts
+++ b/chrome/browser/resources/webui_toolbar/page_action_icon.ts
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import '/shared/icon_from_table.js';
+import './toolbar_chip_button.js';
 
-import type {CrIconButtonElement} from '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import {ensureTransitionEndEvent} from '//resources/js/util.js';
 import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
 import type {PropertyValues} from '//resources/lit/v3_0/lit.rollup.js';
 import {MenuSourceType} from '//resources/mojo/ui/base/mojom/menu_source_type.mojom-webui.js';
-import {IconTable} from '/shared/icon_table.js';
 import type {PageActionState} from '/shared/toolbar_ui_api_data_model.mojom-webui.js';
 import {PageActionId, PageActionTrigger} from '/shared/toolbar_ui_api_data_model.mojom-webui.js';
 
@@ -17,10 +17,11 @@
 import {getCss} from './page_action_icon.css.js';
 import {getHtml} from './page_action_icon.html.js';
 import {getClickSourceType} from './toolbar_button.js';
+import type {ToolbarChipButtonElement} from './toolbar_chip_button.js';
 
 export interface PageActionIconElement {
   $: {
-    button: CrIconButtonElement,
+    button: ToolbarChipButtonElement,
   };
 }
 
@@ -51,36 +52,42 @@
     accessibleName: '',
     tooltipText: '',
     icon: {handleId: 0n},
+    text: '',
+    shouldShowChip: false,
+    shouldAnimateChipIn: false,
+    shouldAnimateChipOut: false,
   };
 
   accessor forceFocusRing: boolean = false;
 
+  private browserProxy_: BrowserProxy = BrowserProxyImpl.getInstance();
+
+  protected shouldShowLabel_(): boolean {
+    return this.state.shouldShowChip && !!this.state.text;
+  }
+
+  protected shouldAnimate_(): boolean {
+    return this.state.shouldShowChip ? this.state.shouldAnimateChipIn :
+                                       this.state.shouldAnimateChipOut;
+  }
+
   override updated(changedProperties: PropertyValues<this>): void {
     super.updated(changedProperties);
     if (changedProperties.has('forceFocusRing')) {
       this.classList.toggle('force-focus-ring', this.forceFocusRing);
     }
-  }
-
-  private browserProxy_: BrowserProxy = BrowserProxyImpl.getInstance();
-  private iconTable_: IconTable = IconTable.getInstance();
-
-  protected getIronIcon_(): string|undefined {
-    return this.iconTable_.getIconName(this.state.icon);
-  }
-
-  protected getIconStyle_(): string|undefined {
-    const providedIconUrl = this.iconTable_.getIconMaskUrl(this.state.icon);
-    const providedIconColor = this.iconTable_.getIconColor(this.state.icon);
-    let style = '';
-
-    if (providedIconUrl) {
-      style += `--cr-icon-image: url(${providedIconUrl});`;
+    if (changedProperties.has('state')) {
+      const oldState = changedProperties.get('state');
+      if (!oldState || oldState.shouldShowChip !== this.state.shouldShowChip) {
+        const button = this.$.button;
+        const fireIpc = () => {
+          this.browserProxy_.toolbarUIHandler.onPageActionChipShowingChanged(
+              this.state.pageActionId);
+        };
+        button.addEventListener('transitionend', fireIpc, {once: true});
+        ensureTransitionEndEvent(button);
+      }
     }
-    if (providedIconColor) {
-      style += `--cr-icon-button-fill-color: ${providedIconColor};`;
-    }
-    return style.length > 0 ? style : undefined;
   }
 
   protected getAriaLabel_(): string {
diff --git a/chrome/browser/resources/webui_toolbar/toolbar_button.ts b/chrome/browser/resources/webui_toolbar/toolbar_button.ts
index 90d07ef..e4ccc6f 100644
--- a/chrome/browser/resources/webui_toolbar/toolbar_button.ts
+++ b/chrome/browser/resources/webui_toolbar/toolbar_button.ts
@@ -357,6 +357,9 @@
   // KeyboardEvent. Need to check e.detail instead, which is 0 for
   // KeyboardEvent. Note: Keyboard activations on `<cr-icon-button>` (Space or
   // Enter) programmatically synthesize standard `click` events with `detail` 0.
+  // TODO(crbug.com/543376027): Handle pointer events correctly. Physical
+  // pointer events can have detail === 0, which incorrectly classifies them as
+  // keyboard events.
   if (e instanceof MouseEvent && e.detail === 0) {
     return MenuSourceType.kKeyboard;
   }
diff --git a/chrome/browser/resources/webui_toolbar/toolbar_chip_button.css b/chrome/browser/resources/webui_toolbar/toolbar_chip_button.css
index 4366204..b99f7ed1 100644
--- a/chrome/browser/resources/webui_toolbar/toolbar_chip_button.css
+++ b/chrome/browser/resources/webui_toolbar/toolbar_chip_button.css
@@ -40,10 +40,11 @@
   padding-inline-start: var(--toolbar-chip-leading-margin, 0);
 
   /* Animation durations matching Native Views parity. */
-  /* (see views::kHighlightFadeOutOnHoverChangeDuration in views/animation/ink_drop_host.cc) */
+  /* (see views::kHighlightFadeOutOnHoverChangeDuration in
+   * views/animation/ink_drop_host.cc) */
   --toolbar-chip-highlight-fade-duration: 250ms;
   /* (see views::FloodFillInkDropRipple::ACTION_TRIGGERED_FADE_OUT in
-   *  views/animation/flood_fill_ink_drop_ripple.cc) */
+   * views/animation/flood_fill_ink_drop_ripple.cc) */
   --toolbar-chip-pressed-fade-duration: 300ms;
 }
 
@@ -77,7 +78,8 @@
 
 /* --- HIGHLIGHT ENGINE ---
  * Mimics views::InkDrop highlight and pressed states for 1:1 parity.
- * We use ::before for the hover highlight and ::after for the pressed highlight.
+ * We use ::before for the hover highlight and ::after for the pressed
+ * highlight.
  * Slotted elements are positioned (position: relative; z-index: 1) to paint
  * cleanly on top of these absolute background highlights. */
 
@@ -104,14 +106,16 @@
 }
 
 /* HOVER STATE: Highlight layer fade-in.
- * SHOW_ON_RIPPLE compositing: Hover layer stays visible during active/menu states. */
+ * SHOW_ON_RIPPLE compositing: Hover layer stays visible during active/menu
+ * states. */
 :host(:hover) #button::before,
 :host([is-menu-open]) #button::before {
   opacity: 1;
 }
 
 /* --- PRESSED/MENU STATE ---
- * Pressed highlight layer fade-in. This layer composites on top of the hover layer. */
+ * Pressed highlight layer fade-in. This layer composites on top of the hover
+ * layer. */
 :host(:active) #button::after,
 :host([is-menu-open]) #button::after {
   opacity: 1;
@@ -141,7 +145,8 @@
   flex-shrink: 0;
   font-size: var(--toolbar-chip-font-size);
   font-weight: var(--toolbar-chip-font-weight);
-  position: relative; /* Stacks above absolute background pseudo-element highlights */
+  /* Stacks above absolute background pseudo-element highlights */
+  position: relative;
   white-space: nowrap;
   z-index: 1;
 }
@@ -149,8 +154,9 @@
 #button ::slotted(cr-icon),
 #button ::slotted([slot='prefix-icon']),
 #button ::slotted([slot='suffix-icon']) {
-  height: var(--toolbar-chip-icon-size);
-  width: var(--toolbar-chip-icon-size);
+  --icon-size: var(--toolbar-chip-icon-size);
+  --iron-icon-width: var(--toolbar-chip-icon-size);
+  --iron-icon-height: var(--toolbar-chip-icon-size);
 }
 
 :host([has-label]) {
@@ -184,6 +190,60 @@
   padding-inline-end: var(--toolbar-chip-padding-end);
 }
 
+/* --- SLIDING LABEL ANIMATION ---
+ * Natively handles sliding expansion/collapse animations for the slotted label
+ * text when the [has-label] attribute is toggled.
+ *
+ * When to use [animates-label]:
+ * - Provide this attribute on chips that should slide open/closed (e.g.,
+ *   page_action_icon, permission_chip).
+ * - Omit this attribute on chips with static labels, or chips that should
+ *   instantly appear/disappear without sliding.
+ */
+:host([animates-label]) {
+  interpolate-size: allow-keywords;
+}
+
+/* Attach transition properties to the base button so they are always active.
+ * This ensures properties animate smoothly in both directions (expanding and
+ * collapsing) whenever the [has-label] state is toggled. */
+:host([animates-label]) #button {
+  transition:
+      gap var(--toolbar-chip-expand-duration, 600ms)
+          var(--ease-out-cubic-bezier),
+      padding-inline-start var(--toolbar-chip-expand-duration, 600ms)
+          var(--ease-out-cubic-bezier),
+      padding-inline-end var(--toolbar-chip-expand-duration, 600ms)
+          var(--ease-out-cubic-bezier),
+      background-color var(--toolbar-chip-expand-duration, 600ms)
+          var(--ease-out-cubic-bezier);
+}
+
+/* When the label is hidden, collapse the flex gap between the icon and the
+ * text so there is no lingering empty space inside the circular button. */
+:host([animates-label]:not([has-label])) #button {
+  gap: 0;
+}
+
+/* Target the label text (the slotted element that does not have a specific
+ * "slot" attribute). When the chip expands, this transitions the text from 0
+ * width and opacity up to its intrinsic max-content width and full opacity. */
+:host([animates-label]) ::slotted(:not([slot])) {
+  max-width: max-content;
+  overflow: hidden;
+  transition:
+      max-width var(--toolbar-chip-expand-duration, 600ms)
+          var(--ease-out-cubic-bezier),
+      opacity var(--toolbar-chip-expand-duration, 600ms)
+          var(--ease-out-cubic-bezier);
+}
+
+/* When the label is hidden, crush its width and opacity to 0. */
+:host([animates-label]:not([has-label])) ::slotted(:not([slot])) {
+  max-width: 0;
+  opacity: 0;
+}
+
 #button:disabled {
   opacity: var(--cr-disabled-opacity, 0.38);
   pointer-events: none;
diff --git a/chrome/browser/resources/webui_toolbar/toolbar_chip_button.ts b/chrome/browser/resources/webui_toolbar/toolbar_chip_button.ts
index 891dc22..6c7277c 100644
--- a/chrome/browser/resources/webui_toolbar/toolbar_chip_button.ts
+++ b/chrome/browser/resources/webui_toolbar/toolbar_chip_button.ts
@@ -28,30 +28,20 @@
 
   static override get properties() {
     return {
-      ariaLabel: {
-        type: String,
-        attribute: 'aria-label',
-      },
-      ariaDescription: {
-        type: String,
-        attribute: 'aria-description',
-      },
-      tooltip: {
-        type: String,
-        attribute: 'title',
-      },
-      ariaHasPopup: {
-        type: String,
-        attribute: 'aria-haspopup',
-      },
-      ariaExpanded: {
-        type: String,
-        attribute: 'aria-expanded',
-      },
+      ariaLabel: {type: String},
+      ariaDescription: {type: String},
+      tooltip: {type: String},
+      ariaHasPopup: {type: String},
+      ariaExpanded: {type: String},
       disabled: {
         type: Boolean,
         reflect: true,
       },
+      animatesLabel: {
+        type: Boolean,
+        reflect: true,
+        attribute: 'animates-label',
+      },
     };
   }
 
@@ -61,6 +51,7 @@
   override accessor ariaHasPopup: string|null = null;
   override accessor ariaExpanded: string|null = null;
   accessor disabled: boolean = false;
+  accessor animatesLabel: boolean = false;
 
   override focus() {
     this.$.button.focus();
diff --git a/chrome/browser/resources/webui_toolbar_shared/icon_from_table.css b/chrome/browser/resources/webui_toolbar_shared/icon_from_table.css
index eb080fbf..7fd11652 100644
--- a/chrome/browser/resources/webui_toolbar_shared/icon_from_table.css
+++ b/chrome/browser/resources/webui_toolbar_shared/icon_from_table.css
@@ -8,13 +8,19 @@
  * #css_wrapper_metadata_end */
 
 :host {
-  display: flex;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  /* Default intrinsic size. Layout containers (like toolbar-chip-button)
+     can override height/width to forcefully constrain this element. */
+  height: var(--icon-size);
+  width: var(--icon-size);
 }
 
 #container {
   display: inline-block;
-  height: var(--icon-size);
-  width: var(--icon-size);
+  height: 100%;
+  width: 100%;
 }
 
 #maskIconContainer,
@@ -43,5 +49,7 @@
 cr-icon {
   --iron-icon-width: var(--icon-size);
   --iron-icon-height: var(--icon-size);
+  width: 100%;
+  height: 100%;
   display: block;
 }
diff --git a/chrome/browser/resources/webui_toolbar_shared/icon_from_table.ts b/chrome/browser/resources/webui_toolbar_shared/icon_from_table.ts
index d46e2b7..4fd2f6cd 100644
--- a/chrome/browser/resources/webui_toolbar_shared/icon_from_table.ts
+++ b/chrome/browser/resources/webui_toolbar_shared/icon_from_table.ts
@@ -11,7 +11,6 @@
 import {getCss} from './icon_from_table.css.js';
 import {getHtml} from './icon_from_table.html.js';
 import type {IconHandle} from './icon_handle.mojom-webui.js';
-import {IconType} from './icon_handle.mojom-webui.js';
 import {IconTable} from './icon_table.js';
 import type {IconInfo} from './icon_table.js';
 
@@ -70,13 +69,11 @@
   // Will be undefined if the icon doesn't request to be rendered in a specific
   // color (including when it's multicolor).
   protected getIconColorCss_(): string|undefined {
-    const propName =
-        (this.iconInfo_ && this.iconInfo_.type === IconType.kIconSet) ?
-        '--cr-icon-button-fill-color' :
-        'color';
-    return this.iconInfo_ && this.iconInfo_.color ?
-        `${propName}: ${skColorToRgba(this.iconInfo_.color)}` :
-        undefined;
+    if (!this.iconInfo_ || !this.iconInfo_.color) {
+      return undefined;
+    }
+    const rgba = skColorToRgba(this.iconInfo_.color);
+    return `color: ${rgba}; --iron-icon-fill-color: ${rgba};`;
   }
 }
 
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/MagicStackBridgeTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/MagicStackBridgeTest.java
index a4b752366..7bb32aa 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/MagicStackBridgeTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/MagicStackBridgeTest.java
@@ -18,12 +18,10 @@
 import org.mockito.junit.MockitoRule;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.profiles.Profile;
 
 /** Tests for the Safety Hub Magic Stack bridge. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class MagicStackBridgeTest {
     private static final String DESCRIPTION = "description";
 
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsDataSourceTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsDataSourceTest.java
index 588b45d..1f333d2 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsDataSourceTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsDataSourceTest.java
@@ -27,7 +27,6 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.preferences.Pref;
@@ -43,7 +42,6 @@
 
 /** Robolectric tests for {@link SafetyHubAccountPasswordsDataSource}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class SafetyHubAccountPasswordsDataSourceTest {
     private static class SafetyHubAccountPasswordsDataSourceObserverTest
             implements SafetyHubAccountPasswordsDataSource.Observer {
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsModuleMediatorTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsModuleMediatorTest.java
index 8a16496..1e212ea8 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsModuleMediatorTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsModuleMediatorTest.java
@@ -26,7 +26,6 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.safety_hub.SafetyHubAccountPasswordsDataSource.ModuleType;
@@ -34,7 +33,6 @@
 
 /** Robolectric tests for {@link SafetyHubAccountPasswordsModuleMediator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class SafetyHubAccountPasswordsModuleMediatorTest {
     private static final @DrawableRes int SAFE_ICON = R.drawable.material_ic_check_24dp;
     private static final @DrawableRes int INFO_ICON = R.drawable.btn_info;
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubBrowserStateModuleMediatorTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubBrowserStateModuleMediatorTest.java
index 9eeb430..7b357299 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubBrowserStateModuleMediatorTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubBrowserStateModuleMediatorTest.java
@@ -18,7 +18,6 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.safety_hub.SafetyHubModuleMediator.ModuleOption;
 import org.chromium.chrome.browser.safety_hub.SafetyHubModuleMediator.ModuleState;
 import org.chromium.components.browser_ui.settings.CardPreference;
@@ -29,7 +28,6 @@
 
 /** Robolectric tests for {@link SafetyHubBrowserStateModuleMediator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class SafetyHubBrowserStateModuleMediatorTest {
     static class SafetyHubModuleMediatorImpl implements SafetyHubModuleMediator {
         @ModuleState int mModuleState;
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubFetchServiceTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubFetchServiceTest.java
index a3b75aed..501e1b3b 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubFetchServiceTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubFetchServiceTest.java
@@ -25,7 +25,6 @@
 import org.mockito.junit.MockitoRule;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.password_manager.FakePasswordCheckupClientHelper;
@@ -42,7 +41,6 @@
 
 /** Unit tests for SafetyHubFetchService. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class SafetyHubFetchServiceTest {
     private static final int ONE_DAY_IN_MILLISECONDS = (int) TimeUnit.DAYS.toMillis(1);
 
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsDataSourceTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsDataSourceTest.java
index dda71c4..8e9f9fb0 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsDataSourceTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsDataSourceTest.java
@@ -26,7 +26,6 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.password_manager.PasswordStoreBridge;
@@ -37,7 +36,6 @@
 
 /** Robolectric tests for {@link SafetyHubLocalPasswordsDataSource}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Features.EnableFeatures({
     ChromeFeatureList.SAFETY_HUB_WEAK_AND_REUSED_PASSWORDS,
     ChromeFeatureList.SAFETY_HUB_LOCAL_PASSWORDS_MODULE
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsModuleMediatorTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsModuleMediatorTest.java
index bec2ba8..75d215c 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsModuleMediatorTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsModuleMediatorTest.java
@@ -26,7 +26,6 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.safety_hub.SafetyHubLocalPasswordsDataSource.ModuleType;
@@ -34,7 +33,6 @@
 
 /** Robolectric tests for {@link SafetyHubLocalPasswordsModuleMediator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Features.EnableFeatures({
     ChromeFeatureList.SAFETY_HUB_WEAK_AND_REUSED_PASSWORDS,
     ChromeFeatureList.SAFETY_HUB_LOCAL_PASSWORDS_MODULE
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubMagicStackMediatorTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubMagicStackMediatorTest.java
index fb545f5..ea5aad3d 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubMagicStackMediatorTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubMagicStackMediatorTest.java
@@ -31,7 +31,6 @@
 import org.robolectric.RuntimeEnvironment;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.magic_stack.ModuleDelegate;
 import org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType;
 import org.chromium.chrome.browser.preferences.Pref;
@@ -50,7 +49,6 @@
 
 /** Tests for the Safety Hub Magic Stack mediator. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class SafetyHubMagicStackMediatorTest {
     private static final String DESCRIPTION = "description";
 
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubMagicStackViewBinderTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubMagicStackViewBinderTest.java
index 901727cb..f815359 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubMagicStackViewBinderTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubMagicStackViewBinderTest.java
@@ -27,7 +27,6 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.ui.base.TestActivity;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
@@ -37,7 +36,6 @@
 
 /** Test relating to binding for the Safety Hub Magic Stack view. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class SafetyHubMagicStackViewBinderTest {
     private static final String HEADER_STRING = "header";
     private static final String TITLE_STRING = "title";
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubModuleDelegateTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubModuleDelegateTest.java
index a17fd42..5c093ba 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubModuleDelegateTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubModuleDelegateTest.java
@@ -35,7 +35,6 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -58,7 +57,6 @@
 
 /** Tests {@link SafetyHubModuleDelegate} */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.PER_CLASS)
 @EnableFeatures({
     SigninFeatures.ENABLE_SEAMLESS_SIGNIN,
     SigninFeatures.ENABLE_ACTIVITYLESS_SIGNIN_ALL_ENTRY_POINT
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubModuleMediatorTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubModuleMediatorTest.java
index 0bcb879..c5d3d47 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubModuleMediatorTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubModuleMediatorTest.java
@@ -23,7 +23,6 @@
 
 import org.chromium.base.test.BaseRobolectricTestRule;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.safety_hub.SafetyHubModuleMediator.ModuleOption;
 import org.chromium.chrome.browser.safety_hub.SafetyHubModuleMediator.ModuleState;
 import org.chromium.ui.base.TestActivity;
@@ -83,7 +82,6 @@
     }
 
     @RunWith(ParameterizedRobolectricTestRunner.class)
-    @Batch(Batch.UNIT_TESTS)
     public static class SafetyHubModuleMediatorParamTests {
         @Parameters
         public static Collection<Object> data() {
@@ -225,7 +223,6 @@
     }
 
     @RunWith(BaseRobolectricTestRunner.class)
-    @Batch(Batch.UNIT_TESTS)
     public static class SafetyHubModuleMediatorSingleTests {
         @Test
         public void order_mixedStates() {
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubNotificationsModuleMediatorTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubNotificationsModuleMediatorTest.java
index 277bd86..de28b10 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubNotificationsModuleMediatorTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubNotificationsModuleMediatorTest.java
@@ -25,14 +25,12 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.ui.base.TestActivity;
 
 import java.util.List;
 
 /** Robolectric tests for {@link SafetyHubNotificationsModuleMediator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class SafetyHubNotificationsModuleMediatorTest {
     private static final @DrawableRes int SAFE_ICON = R.drawable.material_ic_check_24dp;
     private static final @DrawableRes int INFO_ICON = R.drawable.btn_info;
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsFetchServiceTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsFetchServiceTest.java
index bb92605d..9f6528e 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsFetchServiceTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsFetchServiceTest.java
@@ -30,7 +30,6 @@
 import org.chromium.base.TimeUtils;
 import org.chromium.base.test.BaseRobolectricTestRule;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.password_manager.FakePasswordCheckupClientHelper;
@@ -50,7 +49,6 @@
 public class SafetyHubPasswordsFetchServiceTest {
 
     @RunWith(ParameterizedRobolectricTestRunner.class)
-    @Batch(Batch.UNIT_TESTS)
     @Features.EnableFeatures({ChromeFeatureList.SAFETY_HUB_WEAK_AND_REUSED_PASSWORDS})
     public static class SafetyHubPasswordsFetchServiceParamTests {
         @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -275,7 +273,6 @@
     }
 
     @RunWith(BaseRobolectricTestRunner.class)
-    @Batch(Batch.UNIT_TESTS)
     @Features.EnableFeatures({ChromeFeatureList.SAFETY_HUB_WEAK_AND_REUSED_PASSWORDS})
     public static class SafetyHubPasswordsFetchServiceSingleTests {
         @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediatorTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediatorTest.java
index 7c2e7f5..87348135 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediatorTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediatorTest.java
@@ -26,14 +26,12 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.ui.base.TestActivity;
 
 /** Robolectric tests for {@link SafetyHubPasswordsModuleMediator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Features.EnableFeatures({
     ChromeFeatureList.SAFETY_HUB_WEAK_AND_REUSED_PASSWORDS,
     ChromeFeatureList.SAFETY_HUB_LOCAL_PASSWORDS_MODULE,
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPermissionsRevocationModuleMediatorTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPermissionsRevocationModuleMediatorTest.java
index 2181bc0f..b68567c0 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPermissionsRevocationModuleMediatorTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPermissionsRevocationModuleMediatorTest.java
@@ -25,13 +25,11 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.ui.base.TestActivity;
 
 /** Robolectric tests for {@link SafetyHubPermissionsRevocationModuleMediator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class SafetyHubPermissionsRevocationModuleMediatorTest {
     private static final @DrawableRes int SAFE_ICON = R.drawable.material_ic_check_24dp;
     private static final @DrawableRes int INFO_ICON = R.drawable.btn_info;
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubSafeBrowsingModuleMediatorTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubSafeBrowsingModuleMediatorTest.java
index a04d99e..372ea32 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubSafeBrowsingModuleMediatorTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubSafeBrowsingModuleMediatorTest.java
@@ -23,7 +23,6 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.safe_browsing.SafeBrowsingBridge;
 import org.chromium.chrome.browser.safe_browsing.SafeBrowsingBridgeJni;
@@ -32,7 +31,6 @@
 
 /** Robolectric tests for {@link SafetyHubSafeBrowsingModuleMediator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class SafetyHubSafeBrowsingModuleMediatorTest {
     private static final @DrawableRes int SAFE_ICON = R.drawable.material_ic_check_24dp;
     private static final @DrawableRes int MANAGED_ICON = R.drawable.ic_domain;
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubUpdateCheckModuleMediatorTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubUpdateCheckModuleMediatorTest.java
index b176314..e69cfa1 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubUpdateCheckModuleMediatorTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubUpdateCheckModuleMediatorTest.java
@@ -25,13 +25,11 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.omaha.UpdateStatusProvider;
 import org.chromium.ui.base.TestActivity;
 
 /** Robolectric tests for {@link SafetyHubUpdateCheckModuleMediator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class SafetyHubUpdateCheckModuleMediatorTest {
     private static final @DrawableRes int SAFE_ICON = R.drawable.material_ic_check_24dp;
     private static final @DrawableRes int INFO_ICON = R.drawable.btn_info;
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackDelegateTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackDelegateTest.java
index d6262da..41ce5e1 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackDelegateTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureCallbackDelegateTest.java
@@ -44,7 +44,6 @@
 import org.chromium.base.MemoryPressureLevel;
 import org.chromium.base.memory.MemoryPressureMonitor;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
@@ -63,7 +62,6 @@
 
 /** Tests for the {@link ScrollCaptureCallbackDelegate} */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class ScrollCaptureCallbackDelegateTest {
     @Mock private Tab mTab;
     @Mock private WebContentsImpl mWebContents;
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureManagerTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureManagerTest.java
index 51122920..f85c5f5 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureManagerTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/scroll_capture/ScrollCaptureManagerTest.java
@@ -27,12 +27,10 @@
 import org.chromium.base.supplier.ObservableSuppliers;
 import org.chromium.base.supplier.SettableNullableObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.tab.Tab;
 
 /** Tests for the ScreenshotBoundsManager */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class ScrollCaptureManagerTest {
     @Mock private Tab mTab;
     @Mock private ScrollCaptureManagerDelegate mScrollCaptureManagerDelegateMock;
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/OtherDevicesShortcutControllerTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/OtherDevicesShortcutControllerTest.java
index c6d6b21..9be2c2e 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/OtherDevicesShortcutControllerTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/OtherDevicesShortcutControllerTest.java
@@ -34,7 +34,6 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.RobolectricUtil;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -51,7 +50,6 @@
 /** Unit tests for OtherDevicesShortcutController. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE, sdk = Build.VERSION_CODES.R)
-@Batch(Batch.UNIT_TESTS)
 @EnableFeatures(ChromeFeatureList.SEND_TAB_TO_SELF_DYNAMIC_SHORTCUTS)
 public class OtherDevicesShortcutControllerTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler_browsertest.cc b/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler_browsertest.cc
index 5a02ccf6..1e35aaa 100644
--- a/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler_browsertest.cc
+++ b/chrome/browser/sharing/glic_experimental_triggering/glic_experimental_triggering_message_handler_browsertest.cc
@@ -12,9 +12,7 @@
 #include "base/base64.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
-#include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
-#include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_future.h"
 #include "base/time/time.h"
@@ -32,12 +30,10 @@
 #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
 #include "chrome/common/chrome_features.h"
 #include "components/policy/core/common/management/scoped_management_service_override_for_testing.h"
-#include "components/prefs/pref_service.h"
 #include "components/sharing_message/mock_sharing_message_sender.h"
 #include "components/sharing_message/proto/sharing_message.pb.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
-#include "crypto/keypair.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -688,178 +684,7 @@
             kDefaultSequenceNumber);
 }
 
-IN_PROC_BROWSER_TEST_F(GlicExperimentalTriggeringMessageHandlerBrowserTest,
-                       HandlesMultipleConcurrentDeviceOptInRequests) {
-  auto message1 = CreateTriggeringMessage(42, "test_config_1");
-  message1.mutable_glic_experimental_triggering()
-      ->mutable_request()
-      ->mutable_device_opt_in_request()
-      ->set_triggering_source("ChromeOS");
 
-  auto message2 = CreateTriggeringMessage(43, "test_config_2");
-  message2.mutable_glic_experimental_triggering()
-      ->mutable_request()
-      ->mutable_device_opt_in_request()
-      ->set_triggering_source("ChromeOS");
-
-  base::test::TestFuture<components_sharing_message::ServerChannelConfiguration,
-                         components_sharing_message::SharingMessage>
-      future1;
-  base::test::TestFuture<components_sharing_message::ServerChannelConfiguration,
-                         components_sharing_message::SharingMessage>
-      future2;
-
-  EXPECT_CALL(mock_sharing_message_sender_,
-              SendMessageToServerTarget(_, _, _, _))
-      .WillOnce(
-          [&](const components_sharing_message::ServerChannelConfiguration&
-                  server_channel,
-              base::TimeDelta timeout,
-              components_sharing_message::SharingMessage message,
-              SharingMessageSender::ResponseCallback callback) {
-            future1.SetValue(server_channel, std::move(message));
-            return base::OnceClosure();
-          })
-      .WillOnce(
-          [&](const components_sharing_message::ServerChannelConfiguration&
-                  server_channel,
-              base::TimeDelta timeout,
-              components_sharing_message::SharingMessage message,
-              SharingMessageSender::ResponseCallback callback) {
-            future2.SetValue(server_channel, std::move(message));
-            return base::OnceClosure();
-          });
-
-  SendMessageAndWait(std::move(message1));
-  SendMessageAndWait(std::move(message2));
-
-  auto* glic_service = glic::GlicKeyedService::Get(GetProfile());
-  ASSERT_TRUE(glic_service);
-  glic_service->opt_in_controller().CloseDialog(/*accepted=*/true);
-
-  auto [server_channel1, received_message1] = future1.Take();
-  EXPECT_EQ(server_channel1.configuration(), "test_config_1");
-  EXPECT_TRUE(received_message1.has_glic_experimental_triggering());
-  EXPECT_TRUE(received_message1.glic_experimental_triggering().has_response());
-  EXPECT_EQ(received_message1.glic_experimental_triggering()
-                .response()
-                .device_opt_in_result(),
-            components_sharing_message::GlicExperimentalTriggering::
-                ExperimentalTriggeringResponse::ACCEPTED);
-  EXPECT_EQ(received_message1.glic_experimental_triggering()
-                .task_metadata()
-                .sender_sequence_number(),
-            0);
-  EXPECT_EQ(received_message1.glic_experimental_triggering()
-                .task_metadata()
-                .last_seen_sequence_number(),
-            42);
-
-  auto [server_channel2, received_message2] = future2.Take();
-  EXPECT_EQ(server_channel2.configuration(), "test_config_2");
-  EXPECT_TRUE(received_message2.has_glic_experimental_triggering());
-  EXPECT_TRUE(received_message2.glic_experimental_triggering().has_response());
-  EXPECT_EQ(received_message2.glic_experimental_triggering()
-                .response()
-                .device_opt_in_result(),
-            components_sharing_message::GlicExperimentalTriggering::
-                ExperimentalTriggeringResponse::ACCEPTED);
-  EXPECT_EQ(received_message2.glic_experimental_triggering()
-                .task_metadata()
-                .sender_sequence_number(),
-            0);
-  EXPECT_EQ(received_message2.glic_experimental_triggering()
-                .task_metadata()
-                .last_seen_sequence_number(),
-            43);
-}
-
-IN_PROC_BROWSER_TEST_F(
-    GlicExperimentalTriggeringMessageHandlerBrowserTest,
-    HandlesMultipleConcurrentDeviceOptInRequestsDeclinedOnTeardown) {
-  auto message1 = CreateTriggeringMessage(42, "test_config_1");
-  message1.mutable_glic_experimental_triggering()
-      ->mutable_request()
-      ->mutable_device_opt_in_request()
-      ->set_triggering_source("ChromeOS");
-
-  auto message2 = CreateTriggeringMessage(43, "test_config_2");
-  message2.mutable_glic_experimental_triggering()
-      ->mutable_request()
-      ->mutable_device_opt_in_request()
-      ->set_triggering_source("ChromeOS");
-
-  base::test::TestFuture<components_sharing_message::ServerChannelConfiguration,
-                         components_sharing_message::SharingMessage>
-      future1;
-  base::test::TestFuture<components_sharing_message::ServerChannelConfiguration,
-                         components_sharing_message::SharingMessage>
-      future2;
-
-  EXPECT_CALL(mock_sharing_message_sender_,
-              SendMessageToServerTarget(_, _, _, _))
-      .WillOnce(
-          [&](const components_sharing_message::ServerChannelConfiguration&
-                  server_channel,
-              base::TimeDelta timeout,
-              components_sharing_message::SharingMessage message,
-              SharingMessageSender::ResponseCallback callback) {
-            future1.SetValue(server_channel, std::move(message));
-            return base::OnceClosure();
-          })
-      .WillOnce(
-          [&](const components_sharing_message::ServerChannelConfiguration&
-                  server_channel,
-              base::TimeDelta timeout,
-              components_sharing_message::SharingMessage message,
-              SharingMessageSender::ResponseCallback callback) {
-            future2.SetValue(server_channel, std::move(message));
-            return base::OnceClosure();
-          });
-
-  SendMessageAndWait(std::move(message1));
-  SendMessageAndWait(std::move(message2));
-
-  // Close the active tab to trigger dialog teardown/rejection.
-  browser()->tab_strip_model()->CloseWebContentsAt(
-      0, TabCloseTypes::CLOSE_USER_GESTURE);
-
-  auto [server_channel1, received_message1] = future1.Take();
-  EXPECT_EQ(server_channel1.configuration(), "test_config_1");
-  EXPECT_TRUE(received_message1.has_glic_experimental_triggering());
-  EXPECT_TRUE(received_message1.glic_experimental_triggering().has_response());
-  EXPECT_EQ(received_message1.glic_experimental_triggering()
-                .response()
-                .device_opt_in_result(),
-            components_sharing_message::GlicExperimentalTriggering::
-                ExperimentalTriggeringResponse::DECLINED);
-  EXPECT_EQ(received_message1.glic_experimental_triggering()
-                .task_metadata()
-                .sender_sequence_number(),
-            0);
-  EXPECT_EQ(received_message1.glic_experimental_triggering()
-                .task_metadata()
-                .last_seen_sequence_number(),
-            42);
-
-  auto [server_channel2, received_message2] = future2.Take();
-  EXPECT_EQ(server_channel2.configuration(), "test_config_2");
-  EXPECT_TRUE(received_message2.has_glic_experimental_triggering());
-  EXPECT_TRUE(received_message2.glic_experimental_triggering().has_response());
-  EXPECT_EQ(received_message2.glic_experimental_triggering()
-                .response()
-                .device_opt_in_result(),
-            components_sharing_message::GlicExperimentalTriggering::
-                ExperimentalTriggeringResponse::DECLINED);
-  EXPECT_EQ(received_message2.glic_experimental_triggering()
-                .task_metadata()
-                .sender_sequence_number(),
-            0);
-  EXPECT_EQ(received_message2.glic_experimental_triggering()
-                .task_metadata()
-                .last_seen_sequence_number(),
-            43);
-}
 #endif
 
 IN_PROC_BROWSER_TEST_F(GlicExperimentalTriggeringMessageHandlerBrowserTest,
@@ -1072,60 +897,7 @@
             2);
 }
 
-IN_PROC_BROWSER_TEST_F(GlicExperimentalTriggeringMessageHandlerBrowserTest,
-                       RejectRequestWhenNotOptedIn) {
-  // Ensure we are NOT opted in.
-  auto* glic_service = glic::GlicKeyedService::Get(GetProfile());
-  base::HistogramTester histogram_tester;
-  ASSERT_TRUE(glic_service);
-  glic_service->enabling().SetCompletedFre(glic::prefs::FreStatus::kNotStarted);
-  glic_service->enabling().SetUserEnabledActuationOnWeb(false);
-  glic_service->enabling().SetExperimentalTriggeringEnabled(false);
 
-  auto message = CreateTriggeringMessage();
-  message.mutable_glic_experimental_triggering()
-      ->mutable_request()
-      ->mutable_trigger_actuation_request();
-
-  EXPECT_CALL(mock_sharing_message_sender_,
-              SendMessageToServerTarget(_, _, _, _))
-      .Times(0);
-
-  auto response = SendMessageAndWait(std::move(message));
-
-  histogram_tester.ExpectUniqueSample(
-      "Glic.ExperimentalTriggering.StateOnActuationRequest",
-      syncer::DeviceInfo::GlicExperimentalTriggeringState::kNeedsOptIn, 1);
-
-  // Verify that Glic was NOT invoked (no new tabs created).
-  EXPECT_EQ(GetTabListInterface()->GetTabCount(), 1);
-
-  // Verify that a FAILED response was sent back with the correct error message.
-  ASSERT_TRUE(response);
-  EXPECT_TRUE(response->has_glic_experimental_triggering());
-  EXPECT_FALSE(response->glic_experimental_triggering().context_id().empty());
-  EXPECT_EQ(
-      response->glic_experimental_triggering().response().task_update().state(),
-      components_sharing_message::GlicExperimentalTriggering::
-          ExperimentalTriggeringResponse::TaskUpdate::FAILED);
-  EXPECT_EQ(response->glic_experimental_triggering()
-                .response()
-                .task_update()
-                .data_type(),
-            components_sharing_message::GlicExperimentalTriggering::
-                ExperimentalTriggeringResponse::TaskUpdate::ERROR_MESSAGE);
-  EXPECT_EQ(
-      response->glic_experimental_triggering().response().task_update().data(),
-      "User is not opted in to experimental triggering.");
-  EXPECT_EQ(response->glic_experimental_triggering()
-                .task_metadata()
-                .sender_sequence_number(),
-            0);
-  EXPECT_EQ(response->glic_experimental_triggering()
-                .task_metadata()
-                .last_seen_sequence_number(),
-            kDefaultSequenceNumber);
-}
 
 IN_PROC_BROWSER_TEST_F(GlicExperimentalTriggeringMessageHandlerBrowserTest,
                        CleansUpUpdatesHandlerOnPayloadNotSet) {
@@ -1139,44 +911,7 @@
   EXPECT_EQ(handler_->GetUpdatesHandlerMapSizeForTesting(), 0u);
 }
 
-IN_PROC_BROWSER_TEST_F(GlicExperimentalTriggeringMessageHandlerBrowserTest,
-                       CleansUpUpdatesHandlerOnOptInRejection) {
-  // Ensure we are NOT opted in.
-  auto* glic_service = glic::GlicKeyedService::Get(GetProfile());
-  ASSERT_TRUE(glic_service);
-  glic_service->enabling().SetCompletedFre(glic::prefs::FreStatus::kNotStarted);
-  glic_service->enabling().SetUserEnabledActuationOnWeb(false);
-  glic_service->enabling().SetExperimentalTriggeringEnabled(false);
 
-  auto message = CreateTriggeringMessage();
-  message.mutable_glic_experimental_triggering()
-      ->mutable_request()
-      ->mutable_trigger_actuation_request();
-
-  EXPECT_CALL(mock_sharing_message_sender_,
-              SendMessageToServerTarget(_, _, _, _))
-      .Times(0);
-
-  auto response = SendMessageAndWait(std::move(message));
-  ASSERT_TRUE(response);
-  EXPECT_TRUE(response->has_glic_experimental_triggering());
-  EXPECT_FALSE(response->glic_experimental_triggering().context_id().empty());
-  EXPECT_EQ(
-      response->glic_experimental_triggering().response().task_update().state(),
-      components_sharing_message::GlicExperimentalTriggering::
-          ExperimentalTriggeringResponse::TaskUpdate::FAILED);
-  EXPECT_EQ(response->glic_experimental_triggering()
-                .task_metadata()
-                .sender_sequence_number(),
-            0);
-  EXPECT_EQ(response->glic_experimental_triggering()
-                .task_metadata()
-                .last_seen_sequence_number(),
-            kDefaultSequenceNumber);
-
-  // Response sent, and handler should be cleaned up immediately.
-  EXPECT_EQ(handler_->GetUpdatesHandlerMapSizeForTesting(), 0u);
-}
 
 #if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(GlicExperimentalTriggeringMessageHandlerBrowserTest,
@@ -1218,83 +953,6 @@
 #endif
 
 IN_PROC_BROWSER_TEST_F(GlicExperimentalTriggeringMessageHandlerBrowserTest,
-                       CleansUpUpdatesHandlerOnStopActuation) {
-  OptIn();
-  // Start Actuation
-  auto start_message = CreateTriggeringMessage();
-  auto* start_triggering = start_message.mutable_glic_experimental_triggering();
-  start_triggering->set_context_id("test-context-id");
-  start_triggering->mutable_request()->mutable_trigger_actuation_request();
-
-  int initial_tab_count = GetTabListInterface()->GetTabCount();
-
-  base::test::TestFuture<components_sharing_message::SharingMessage> future;
-  EXPECT_CALL(mock_sharing_message_sender_,
-              SendMessageToServerTarget(_, _, _, _))
-      .WillRepeatedly(
-          [&](const components_sharing_message::ServerChannelConfiguration&,
-              base::TimeDelta,
-              components_sharing_message::SharingMessage message,
-              SharingMessageSender::ResponseCallback) {
-            future.SetValue(std::move(message));
-            return base::OnceClosure();
-          });
-
-  auto start_response = SendMessageAndWait(std::move(start_message));
-  ASSERT_TRUE(start_response);
-  EXPECT_TRUE(start_response->has_glic_experimental_triggering());
-  EXPECT_EQ(start_response->glic_experimental_triggering()
-                .response()
-                .task_update()
-                .state(),
-            components_sharing_message::GlicExperimentalTriggering::
-                ExperimentalTriggeringResponse::TaskUpdate::STARTING);
-  EXPECT_FALSE(start_response->glic_experimental_triggering()
-                   .response()
-                   .task_update()
-                   .has_data_type());
-
-  // Active triggering handler should exist.
-  EXPECT_EQ(handler_->GetUpdatesHandlerMapSizeForTesting(), 1u);
-
-  // Verify the instance is bound.
-  auto* new_tab = GetTabListInterface()->GetTab(initial_tab_count);
-  ASSERT_TRUE(new_tab);
-  ASSERT_OK(WaitForGlicInstanceBoundToTab(new_tab));
-
-  // Stop Actuation
-  auto stop_message = CreateTriggeringMessage(kDefaultSequenceNumber + 1);
-  auto* stop_triggering = stop_message.mutable_glic_experimental_triggering();
-  stop_triggering->set_context_id("test-context-id");
-  stop_triggering->mutable_request()
-      ->mutable_stop_actuation_request()
-      ->set_stop_reason("STOPPED_BY_USER");
-
-  auto response = SendMessageAndWait(std::move(stop_message));
-  ASSERT_TRUE(response);
-  EXPECT_TRUE(response->has_glic_experimental_triggering());
-  EXPECT_EQ(
-      response->glic_experimental_triggering().response().task_update().state(),
-      components_sharing_message::GlicExperimentalTriggering::
-          ExperimentalTriggeringResponse::TaskUpdate::STOPPED);
-  EXPECT_FALSE(response->glic_experimental_triggering()
-                   .response()
-                   .task_update()
-                   .has_data_type());
-  EXPECT_EQ(response->glic_experimental_triggering()
-                .task_metadata()
-                .sender_sequence_number(),
-            1);
-  EXPECT_EQ(response->glic_experimental_triggering()
-                .task_metadata()
-                .last_seen_sequence_number(),
-            kDefaultSequenceNumber + 1);
-
-  // The stop request completes, and the handler should be cleaned up.
-  EXPECT_EQ(handler_->GetUpdatesHandlerMapSizeForTesting(), 0u);
-}
-
-IN_PROC_BROWSER_TEST_F(GlicExperimentalTriggeringMessageHandlerBrowserTest,
                        testRelaysParentConversationMetadataUpdated) {
   OptIn();
   auto start_message = CreateTriggeringMessage();
diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc
index c10b3130..e0c12fc 100644
--- a/chrome/browser/spellchecker/spellcheck_service.cc
+++ b/chrome/browser/spellchecker/spellcheck_service.cc
@@ -840,8 +840,14 @@
   for (auto it = content::RenderProcessHost::AllHostsIterator(); !it.IsAtEnd();
        it.Advance()) {
     content::RenderProcessHost* process = it.GetCurrentValue();
-    if (process && process->GetProcess().Handle())
+    // Do not test GetProcess().Handle() here: a renderer that has been
+    // initialized but whose process is still launching has no handle yet, and
+    // skipping it drops the notification for good. Its mojo channel already
+    // exists, so messages queue up and are delivered once the process is up.
+    // This matches OnCustomDictionaryChanged() below.
+    if (process && process->IsInitializedAndNotDead()) {
       InitForRenderer(process);
+    }
   }
 }
 
diff --git a/chrome/browser/spellchecker/spellcheck_service_unittest.cc b/chrome/browser/spellchecker/spellcheck_service_unittest.cc
index 90738f3..47b627e 100644
--- a/chrome/browser/spellchecker/spellcheck_service_unittest.cc
+++ b/chrome/browser/spellchecker/spellcheck_service_unittest.cc
@@ -9,10 +9,14 @@
 #include <ostream>
 
 #include "base/command_line.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
+#include "base/process/kill.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/supports_user_data.h"
+#include "base/test/run_until.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/spellchecker/spellcheck_factory.h"
@@ -23,9 +27,13 @@
 #include "components/prefs/testing_pref_service.h"
 #include "components/spellcheck/browser/pref_names.h"
 #include "components/spellcheck/browser/spellcheck_platform.h"
+#include "components/spellcheck/common/spellcheck.mojom.h"
 #include "components/spellcheck/common/spellcheck_features.h"
 #include "components/user_prefs/user_prefs.h"
 #include "content/public/test/browser_task_environment.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 struct TestCase {
@@ -173,6 +181,93 @@
   EXPECT_EQ(GetParam().expected_dictionaries, dictionaries);
 }
 
+// Observes the SpellChecker interface for a single renderer. A unit test has no
+// real renderers, so the MockRenderProcessHost below is the only host
+// InitForAllRenderers() can find, which makes an Initialize() call
+// unambiguously attributable to it.
+class SpellcheckServiceRendererInitUnitTest
+    : public SpellcheckServiceUnitTestBase,
+      public spellcheck::mojom::SpellChecker {
+ public:
+  SpellcheckServiceRendererInitUnitTest() = default;
+
+  SpellcheckServiceRendererInitUnitTest(
+      const SpellcheckServiceRendererInitUnitTest&) = delete;
+  SpellcheckServiceRendererInitUnitTest& operator=(
+      const SpellcheckServiceRendererInitUnitTest&) = delete;
+
+ protected:
+  void SetUp() override {
+    SpellcheckServiceUnitTestBase::SetUp();
+    SpellcheckService::OverrideBinderForTesting(base::BindRepeating(
+        &SpellcheckServiceRendererInitUnitTest::Bind, base::Unretained(this)));
+    renderer_ = std::make_unique<content::MockRenderProcessHost>(&profile_);
+    renderer_->Init();
+  }
+
+  void TearDown() override {
+    receivers_.Clear();
+    renderer_.reset();
+    SpellcheckService::OverrideBinderForTesting(base::NullCallback());
+  }
+
+  content::MockRenderProcessHost* renderer() { return renderer_.get(); }
+
+  // Waits until at least `count` Initialize() calls have arrived. Returns
+  // false on timeout.
+  [[nodiscard]] bool WaitForInitializeCount(int count) {
+    return base::test::RunUntil(
+        [this, count]() { return initialize_count_ >= count; });
+  }
+
+ private:
+  void Bind(mojo::PendingReceiver<spellcheck::mojom::SpellChecker> receiver) {
+    // A ReceiverSet, not a single Receiver: InitForAllRenderers() may reach
+    // several hosts, and rebinding a single Receiver would close the earlier
+    // pipe before its Initialize() call was delivered, silently losing it.
+    receivers_.Add(this, std::move(receiver));
+  }
+
+  // spellcheck::mojom::SpellChecker:
+  void Initialize(
+      std::vector<spellcheck::mojom::SpellCheckBDictLanguagePtr> dictionaries,
+      const std::vector<std::string>& custom_words,
+      bool enable) override {
+    ++initialize_count_;
+  }
+  void CustomDictionaryChanged(
+      const std::vector<std::string>& words_added,
+      const std::vector<std::string>& words_removed) override {}
+
+  std::unique_ptr<content::MockRenderProcessHost> renderer_;
+  mojo::ReceiverSet<spellcheck::mojom::SpellChecker> receivers_;
+  int initialize_count_ = 0;
+
+#if BUILDFLAG(IS_WIN)
+  // This test assumes the Hunspell code path.
+  spellcheck::ScopedDisableBrowserSpellCheckerForTesting
+      disable_browser_spell_checker_;
+#endif  // BUILDFLAG(IS_WIN)
+};
+
+// A renderer that has been initialized but whose process is still launching has
+// no process handle yet. It must still be initialized, because
+// InitForAllRenderers() is the only path that delivers spellcheck state to a
+// renderer that was contacted before its dictionaries were ready.
+TEST_F(SpellcheckServiceRendererInitUnitTest, ReachesStillLaunchingRenderer) {
+  // Reproduce the window between RenderProcessHostImpl::Init() and
+  // OnProcessLaunched().
+  renderer()->SimulateProcessStillLaunchingForTesting(true);
+  ASSERT_FALSE(renderer()->GetProcess().Handle());
+  ASSERT_TRUE(renderer()->IsInitializedAndNotDead());
+
+  // Flipping the pref runs InitForAllRenderers().
+  prefs()->SetBoolean(spellcheck::prefs::kSpellCheckEnable, false);
+
+  EXPECT_TRUE(WaitForInitializeCount(1))
+      << "a still-launching renderer was never initialized";
+}
+
 #if BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
 class SpellcheckServiceHybridUnitTestBase
     : public SpellcheckServiceUnitTestBase {
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabAttributeKeys.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabAttributeKeys.java
index 8bbdad87..feb6d036 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabAttributeKeys.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabAttributeKeys.java
@@ -24,6 +24,10 @@
 @Retention(RetentionPolicy.SOURCE)
 @NullMarked
 public @interface TabAttributeKeys {
+    // Number of string keys declared in this interface. Used to preallocate
+    // TabAttributes.mAttributes.
+    int NUM_ENTRIES = 7;
+
     /** Whether the tab should be grouped with its parent tab. True by default. */
     String GROUPED_WITH_PARENT = "isTabGroupedWithParent";
 
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabAttributes.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabAttributes.java
index 8ee17f7..8b53da3 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabAttributes.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabAttributes.java
@@ -4,12 +4,13 @@
 
 package org.chromium.chrome.browser.tab;
 
+import android.util.ArrayMap;
+
 import org.chromium.base.UserData;
 import org.chromium.base.UserDataHost;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 
-import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -20,7 +21,7 @@
 public class TabAttributes implements UserData {
     private static final Class<TabAttributes> USER_DATA_KEY = TabAttributes.class;
 
-    private final Map<String, Object> mAttributes = new HashMap<>();
+    private final Map<String, Object> mAttributes = new ArrayMap<>(TabAttributeKeys.NUM_ENTRIES);
 
     // Null object used to differentiate the uninitialized attributes from those explicitly
     // set to |null|.
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributesRegistry.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributesRegistry.java
index 3540857..aa62287 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributesRegistry.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributesRegistry.java
@@ -4,19 +4,21 @@
 
 package org.chromium.chrome.browser.tab;
 
+import android.util.ArrayMap;
+
 import org.chromium.base.UserDataHost;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.content_public.browser.WebContents;
 
-import java.util.HashMap;
 import java.util.Map;
 
 /** Registry class to hold multiple TabStateAttributes mapped by Class key. */
 @NullMarked
 public class TabStateAttributesRegistry extends TabWebContentsUserData {
+    // There should be at most 2 instances of TabStateAttributes in the registry.
     private final Map<Class<? extends TabStateAttributes.StoreKey>, TabStateAttributes>
-            mAttributesMap = new HashMap<>();
+            mAttributesMap = new ArrayMap<>(2);
 
     public TabStateAttributesRegistry(Tab tab) {
         super(tab);
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelObserver.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelObserver.java
index d6b39d13..1cb614be 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelObserver.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelObserver.java
@@ -23,6 +23,32 @@
  */
 @NullMarked
 public interface TabModelObserver {
+    // Tab Addition
+
+    /**
+     * Called before a tab will be added to the {@link TabModel}.
+     *
+     * @param tab The tab about to be added.
+     * @param type The type of tab launch.
+     */
+    default void willAddTab(Tab tab, @TabLaunchType int type) {}
+
+    /**
+     * Called after a tab has been added to the {@link TabModel}.
+     *
+     * @param tab The newly added tab.
+     * @param type The type of tab launch.
+     * @param creationState How the tab was created.
+     * @param markedForSelection Indicates whether the added tab will be selected.
+     */
+    default void didAddTab(
+            Tab tab,
+            @TabLaunchType int type,
+            @TabCreationState int creationState,
+            boolean markedForSelection) {}
+
+    // Tab Selection
+
     /**
      * Called when a tab is selected. This may not be called in some cases if this model is not the
      * active model. If observing the current tab in this model is desired consider using {@link
@@ -35,6 +61,45 @@
      */
     default void didSelectTab(Tab tab, @TabSelectionType int type, int lastId) {}
 
+    /** Called when the set of multi-selected tabs has changed. */
+    default void onTabsSelectionChanged() {}
+
+    // Tab Movement and Attributes
+
+    /**
+     * Called after a tab has been moved from one position in the {@link TabModel} to another.
+     *
+     * @param tab The tab which has been moved.
+     * @param newIndex The new index of the tab in the model.
+     * @param curIndex The old index of the tab in the model.
+     */
+    default void didMoveTab(Tab tab, int newIndex, int curIndex) {}
+
+    /**
+     * Called when a tab's pin state is about to change.
+     *
+     * @param tab The tab whose pin state is about to change.
+     */
+    default void willChangePinState(Tab tab) {}
+
+    /**
+     * Called when a tab's pin state has changed.
+     *
+     * @param tab The tab whose pin state has changed.
+     */
+    default void didChangePinState(Tab tab) {}
+
+    // Tab Removal
+
+    /**
+     * Called after a tab has been removed. At this point, the tab is no longer in the tab model.
+     *
+     * @param tab The tab that has been removed.
+     */
+    default void tabRemoved(Tab tab) {}
+
+    // Tab Closure
+
     /**
      * Called when a tab starts closing.
      *
@@ -45,6 +110,24 @@
     default void willCloseTab(Tab tab, boolean didCloseAlone) {}
 
     /**
+     * Called when multiple tabs closure will happen. If "all tabs" are closed at once, @{@link
+     * TabModelObserver#willCloseAllTabs(boolean)} is invoked.
+     *
+     * @param allowUndo If undo is allowed on the tab closure.
+     * @param tabs being closed.
+     */
+    default void willCloseMultipleTabs(boolean allowUndo, List<Tab> tabs) {}
+
+    /**
+     * Called when an "all tabs" closure will happen. If multiple tabs are closed, {@link
+     * #willCloseMultipleTabs(boolean, List)} is invoked
+     */
+    default void willCloseAllTabs(boolean incognito) {}
+
+    /** Called when tabs are being closed, and none will be left in the {@link TabModel}. */
+    default void allTabsAreClosing() {}
+
+    /**
      * Called after the tab has been removed from the tab model for tab closure. This is called
      * regardless of whether the tab closure is undoable or not and will always be called before a
      * tab closure is finalized.
@@ -62,6 +145,69 @@
     default void didRemoveTabForClosure(Tab tab) {}
 
     /**
+     * Called right before when tabs are pending closure, i.e. the user has just closed them, but it
+     * can still be undone.
+     *
+     * @param tabs The list of {@link Tab}s that are pending closure.
+     * @param isAllTabs Whether tabs are all the tabs.
+     * @param closingSource The tab closing source, e.g. the tablet tab strip.
+     */
+    default void onTabClosePending(
+            List<Tab> tabs, boolean isAllTabs, @TabClosingSource int closingSource) {}
+
+    /**
+     * Called before the {@code tabs} have been reinserted into the model by an undo action.
+     *
+     * @param tabs The list of {@link Tab}s that has been reopened.
+     * @param isAllTabs Whether tabs are all the tabs.
+     */
+    default void willUndoTabClosure(List<Tab> tabs, boolean isAllTabs) {}
+
+    /**
+     * Called when a tab closure is undone.
+     *
+     * @param tab The tab that has been reopened.
+     */
+    default void tabClosureUndone(Tab tab) {}
+
+    /**
+     * Called after the {@code tabs} have been reinserted into the model by an undo action, but
+     * before the restoration is fully complete. Some updates, such as setting the index, if
+     * applicable, may still be in-flight
+     *
+     * @param tabs The list of {@link Tab}s that has been reopened.
+     * @param isAllTabs Whether tabs are all the tabs.
+     */
+    default void onTabCloseUndone(List<Tab> tabs, boolean isAllTabs) {}
+
+    /**
+     * Called right before {@code tabs} closure is committed permanently and cannot be undone.
+     * This is called for both synchronous immediate closures (e.g. non-undoable, incognito)
+     * and deferred commits after the pending undo window expires.
+     *
+     * <p>TODO(crbug.com/381471263): Method in development. For non-undoable closures (allowUndo =
+     * false), this replaces initial pre-close callbacks ({@link #willCloseTab}, {@link
+     * #willCloseMultipleTabs}, {@link #willCloseAllTabs}, and {@link #allTabsAreClosing}). For
+     * finalized closures, this replaces {@link #onFinishingTabClosure}, {@link
+     * #onFinishingMultipleTabClosure}, {@link #tabClosureCommitted}, and {@link
+     * #allTabsClosureCommitted}.
+     *
+     * @param tabs The list of {@link Tab}s that are closed.
+     * @param isAllTabs Whether tabs are all the tabs.
+     */
+    default void onTabCloseCommitted(List<Tab> tabs, boolean isAllTabs) {}
+
+    /**
+     * Called when a tab closure is committed and can't be undone anymore.
+     *
+     * @param tab The tab that has been closed.
+     */
+    default void tabClosureCommitted(Tab tab) {}
+
+    /** Called when an "all tabs" closure has been committed and can't be undone anymore. */
+    default void allTabsClosureCommitted(boolean isIncognito) {}
+
+    /**
      * Called right before {@code tab} will be destroyed. Called for each tab.
      *
      * @param tab The {@link Tab} that was closed.
@@ -87,150 +233,7 @@
      */
     default void onFinishingMultipleTabClosure(List<Tab> tabs, boolean canRestore) {}
 
-    /**
-     * Called before a tab will be added to the {@link TabModel}.
-     *
-     * @param tab The tab about to be added.
-     * @param type The type of tab launch.
-     */
-    default void willAddTab(Tab tab, @TabLaunchType int type) {}
-
-    /**
-     * Called after a tab has been added to the {@link TabModel}.
-     *
-     * @param tab The newly added tab.
-     * @param type The type of tab launch.
-     * @param creationState How the tab was created.
-     * @param markedForSelection Indicates whether the added tab will be selected.
-     */
-    default void didAddTab(
-            Tab tab,
-            @TabLaunchType int type,
-            @TabCreationState int creationState,
-            boolean markedForSelection) {}
-
-    /**
-     * Called after a tab has been moved from one position in the {@link TabModel} to another.
-     *
-     * @param tab The tab which has been moved.
-     * @param newIndex The new index of the tab in the model.
-     * @param curIndex The old index of the tab in the model.
-     */
-    default void didMoveTab(Tab tab, int newIndex, int curIndex) {}
-
-    /**
-     * Called when a tab's pin state is about to change.
-     *
-     * @param tab The tab whose pin state is about to change.
-     */
-    default void willChangePinState(Tab tab) {}
-
-    /**
-     * Called when a tab's pin state has changed.
-     *
-     * @param tab The tab whose pin state has changed.
-     */
-    default void didChangePinState(Tab tab) {}
-
-    /**
-     * Called right before when tabs are pending closure, i.e. the user has just closed them, but it
-     * can still be undone.
-     *
-     * @param tabs The list of {@link Tab}s that are pending closure.
-     * @param isAllTabs Whether tabs are all the tabs.
-     * @param closingSource The tab closing source, e.g. the tablet tab strip.
-     */
-    default void onTabClosePending(
-            List<Tab> tabs, boolean isAllTabs, @TabClosingSource int closingSource) {}
-
-    /**
-     * Called when a tab closure is undone.
-     *
-     * @param tab The tab that has been reopened.
-     */
-    default void tabClosureUndone(Tab tab) {}
-
-    /**
-     * Called when a tab closure is committed and can't be undone anymore.
-     *
-     * @param tab The tab that has been closed.
-     */
-    default void tabClosureCommitted(Tab tab) {}
-
-    /**
-     * Called when an "all tabs" closure will happen. If multiple tabs are closed, {@link
-     * #willCloseMultipleTabs(boolean, List)} is invoked
-     */
-    default void willCloseAllTabs(boolean incognito) {}
-
-    /**
-     * Called when multiple tabs closure will happen. If "all tabs" are closed at once, @{@link
-     * TabModelObserver#willCloseAllTabs(boolean)} is invoked.
-     *
-     * @param allowUndo If undo is allowed on the tab closure.
-     * @param tabs being closed.
-     */
-    default void willCloseMultipleTabs(boolean allowUndo, List<Tab> tabs) {}
-
-    /** Called when an "all tabs" closure has been committed and can't be undone anymore. */
-    default void allTabsClosureCommitted(boolean isIncognito) {}
-
-    /** Called when tabs are being closed, and none will be left in the {@link TabModel}. */
-    default void allTabsAreClosing() {}
-
-    /**
-     * Called after a tab has been removed. At this point, the tab is no longer in the tab model.
-     *
-     * @param tab The tab that has been removed.
-     */
-    default void tabRemoved(Tab tab) {}
-
-    /**
-     * Called after all {@link org.chromium.chrome.browser.tab.TabState}s within {@link TabModel}
-     * are loaded from storage.
-     */
-    default void restoreCompleted() {}
-
-    //  TODO(crbug.com/381471263): The following methods are still in development and will
-    //  replace the existing tab closure events in the near future. Methods being replaced are
-    //  tabClosureUndone, allTabsClosureUndone, tabClosureCommitted, willCloseAllTabs,
-    //  willCloseMultipleTabs and allTabsClosureCommitted.
-    /**
-     * Called right before {@code tabs} will be destroyed.
-     *
-     * @param tabs The list of {@link Tab}s that will be closed.
-     * @param isAllTabs Whether tabs are all the tabs.
-     */
-    default void onTabCloseImmediate(List<Tab> tabs, boolean isAllTabs) {}
-
-    /**
-     * Called right before {@code tabs} closure is committed permanently and cannot be undone.
-     *
-     * @param tabs The list of {@link Tab}s that are closed.
-     * @param isAllTabs Whether tabs are all the tabs.
-     */
-    default void onTabCloseCommitted(List<Tab> tabs, boolean isAllTabs) {}
-
-    /**
-     * Called before the {@code tabs} have been reinserted into the model by an undo action.
-     *
-     * @param tabs The list of {@link Tab}s that has been reopened.
-     * @param isAllTabs Whether tabs are all the tabs.
-     */
-    default void willUndoTabClosure(List<Tab> tabs, boolean isAllTabs) {}
-
-    /**
-     * Called after the {@code tabs} have been reinserted into the model by an undo action, but
-     * before the restoration is fully complete. Some updates, such as setting the index, if
-     * applicable, may still be in-flight.
-     *
-     * @param tabs The list of {@link Tab}s that has been reopened.
-     * @param isAllTabs Whether tabs are all the tabs.
-     */
-    default void onTabCloseUndone(List<Tab> tabs, boolean isAllTabs) {}
-
-    /** Called when the set of multi-selected tabs has changed. */
-    default void onTabsSelectionChanged() {}
+    // Tab Group
 
     /**
      * Called after a tab group is created. Note that new code should prefer using {@link
@@ -265,6 +268,14 @@
      */
     default void onTabGroupVisualsChanged(Token groupId) {}
 
+    // Misc
+
+    /**
+     * Called after all {@link org.chromium.chrome.browser.tab.TabState}s within {@link TabModel}
+     * are loaded from storage.
+     */
+    default void restoreCompleted() {}
+
     /**
      * Called when the TabModel is destroyed. Note that for the incognito tab model this may be
      * called multiple times as the observer is registered to the outer tab model, but the inner
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/autofill/TouchToFillAutofillControllerRobolectricTest.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/autofill/TouchToFillAutofillControllerRobolectricTest.java
index f9ba1785..6cfe92b 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/autofill/TouchToFillAutofillControllerRobolectricTest.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/autofill/TouchToFillAutofillControllerRobolectricTest.java
@@ -26,7 +26,6 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.touch_to_fill.common.BottomSheetFocusHelper;
 import org.chromium.chrome.browser.touch_to_fill.payments.R;
@@ -35,7 +34,6 @@
 
 /** Tests for {@link TouchToFillAutofillCoordinator} and {@link TouchToFillAutofillMediator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.PER_CLASS)
 public class TouchToFillAutofillControllerRobolectricTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerRobolectricTest.java b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerRobolectricTest.java
index 5141aa2..eeeee68e 100644
--- a/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerRobolectricTest.java
+++ b/chrome/browser/touch_to_fill/autofill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillPaymentMethodControllerRobolectricTest.java
@@ -181,7 +181,6 @@
 import org.chromium.base.ServiceLoaderUtil;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
@@ -241,7 +240,6 @@
  * TouchToFillPaymentMethodMediator}
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.PER_CLASS)
 @EnableFeatures({AutofillFeatures.AUTOFILL_ENABLE_NEW_FOP_DISPLAY_ANDROID})
 @DisableFeatures({
     AutofillFeatures.AUTOFILL_ENABLE_SECURITY_TOUCH_EVENT_FILTERING_ANDROID,
diff --git a/chrome/browser/touch_to_fill/common/android/java/src/org/chromium/chrome/browser/touch_to_fill/common/BottomSheetFocusHelperRoboelectricTest.java b/chrome/browser/touch_to_fill/common/android/java/src/org/chromium/chrome/browser/touch_to_fill/common/BottomSheetFocusHelperRoboelectricTest.java
index 18dc7a7..d2aaa9dc 100644
--- a/chrome/browser/touch_to_fill/common/android/java/src/org/chromium/chrome/browser/touch_to_fill/common/BottomSheetFocusHelperRoboelectricTest.java
+++ b/chrome/browser/touch_to_fill/common/android/java/src/org/chromium/chrome/browser/touch_to_fill/common/BottomSheetFocusHelperRoboelectricTest.java
@@ -17,7 +17,6 @@
 import org.mockito.junit.MockitoRule;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorSupplier;
@@ -29,7 +28,6 @@
 
 /** Tests for {@link BottomSheetFocusHelper}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.PER_CLASS)
 public class BottomSheetFocusHelperRoboelectricTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
     private BottomSheetFocusHelper mBottomSheetFocusHelper;
diff --git a/chrome/browser/touch_to_fill/password_manager/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetModuleTest.java b/chrome/browser/touch_to_fill/password_manager/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetModuleTest.java
index a13c54b..0ab3965 100644
--- a/chrome/browser/touch_to_fill/password_manager/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetModuleTest.java
+++ b/chrome/browser/touch_to_fill/password_manager/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetModuleTest.java
@@ -36,7 +36,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 
 import java.lang.ref.WeakReference;
@@ -44,7 +43,6 @@
 /** Tests for {@link NoPasskeysBottomSheetBridge} */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 public class NoPasskeysBottomSheetModuleTest {
     private static final long TEST_NATIVE = 42069;
     private static final String TEST_ORIGIN = "origin.com";
diff --git a/chrome/browser/touch_to_fill/password_manager/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationBridgeTest.java b/chrome/browser/touch_to_fill/password_manager/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationBridgeTest.java
index f8c8fc4..5ae2df0 100644
--- a/chrome/browser/touch_to_fill/password_manager/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationBridgeTest.java
+++ b/chrome/browser/touch_to_fill/password_manager/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationBridgeTest.java
@@ -17,7 +17,6 @@
 import org.mockito.junit.MockitoRule;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.prefs.PrefService;
 import org.chromium.content_public.browser.WebContents;
@@ -26,7 +25,6 @@
 
 /** Tests for {@link TouchToFillPasswordGenerationBridge} */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.PER_CLASS)
 public class TouchToFillPasswordGenerationBridgeTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/touch_to_fill/password_manager/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationModuleTest.java b/chrome/browser/touch_to_fill/password_manager/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationModuleTest.java
index dabbb13b..e43b479 100644
--- a/chrome/browser/touch_to_fill/password_manager/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationModuleTest.java
+++ b/chrome/browser/touch_to_fill/password_manager/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationModuleTest.java
@@ -42,7 +42,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
@@ -59,7 +58,6 @@
 /** Tests for {@link TouchToFillPasswordGenerationBridge} */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 @CommandLineFlags.Add({
     ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
     ChromeSwitches.DISABLE_NATIVE_INITIALIZATION
diff --git a/chrome/browser/ttc/OWNERS b/chrome/browser/ttc/OWNERS
index 57642475..caa13c4 100644
--- a/chrome/browser/ttc/OWNERS
+++ b/chrome/browser/ttc/OWNERS
@@ -1,6 +1,8 @@
+adithyas@chromium.org
 bokan@chromium.org
 gklassen@chromium.org
 mcnee@chromium.org
+migueloruiz@chromium.org
 vollick@chromium.org
 
 file://chrome/browser/ai/OWNERS
diff --git a/chrome/browser/ttc/resources/BUILD.gn b/chrome/browser/ttc/resources/BUILD.gn
index 98cec809..0f7ce8d 100644
--- a/chrome/browser/ttc/resources/BUILD.gn
+++ b/chrome/browser/ttc/resources/BUILD.gn
@@ -64,10 +64,12 @@
   mojo_files_deps = [
     ":generate_tool_definitions",
     "//chrome/browser/ui/webui/ai_overlay_dialog:mojo_bindings_ts__generator",
+    "//third_party/blink/public/mojom:dom_node_id_ts__generator",
   ]
   mojo_files = [
     "$root_gen_dir/chrome/browser/ui/webui/ai_overlay_dialog/ai_overlay_dialog.mojom-webui.ts",
     "$root_gen_dir/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.mojom-webui.ts",
+    "$root_gen_dir/third_party/blink/public/mojom/dom/dom_node_id.mojom-webui.ts",
     "$target_gen_dir/generated_tool_definitions.ts",
   ]
 
diff --git a/chrome/browser/ttc/resources/OWNERS b/chrome/browser/ttc/resources/OWNERS
index 4949f075..f2a6275d 100644
--- a/chrome/browser/ttc/resources/OWNERS
+++ b/chrome/browser/ttc/resources/OWNERS
@@ -1,6 +1,8 @@
+adithyas@chromium.org
 bokan@chromium.org
 gklassen@chromium.org
 mcnee@chromium.org
+migueloruiz@chromium.org
 vollick@chromium.org
 
 
diff --git a/chrome/browser/ttc/resources/tools/generate_tool_definitions.py b/chrome/browser/ttc/resources/tools/generate_tool_definitions.py
index 0c4ca74..bea891c 100644
--- a/chrome/browser/ttc/resources/tools/generate_tool_definitions.py
+++ b/chrome/browser/ttc/resources/tools/generate_tool_definitions.py
@@ -65,7 +65,7 @@
         return 'STRING'
     if 'bool' in mojom_type:
         return 'BOOLEAN'
-    if 'int' in mojom_type:
+    if 'int' in mojom_type or 'DOMNodeId' in mojom_type:
         return 'INTEGER'
     if 'double' in mojom_type or 'float' in mojom_type: return 'NUMBER'
     return 'STRING'
diff --git a/chrome/browser/ui/android/default_browser_promo/java/src/org/chromium/chrome/browser/ui/default_browser_promo/DefaultBrowserPromoManagerTest.java b/chrome/browser/ui/android/default_browser_promo/java/src/org/chromium/chrome/browser/ui/default_browser_promo/DefaultBrowserPromoManagerTest.java
index f7d7739..f6fa0fb 100644
--- a/chrome/browser/ui/android/default_browser_promo/java/src/org/chromium/chrome/browser/ui/default_browser_promo/DefaultBrowserPromoManagerTest.java
+++ b/chrome/browser/ui/android/default_browser_promo/java/src/org/chromium/chrome/browser/ui/default_browser_promo/DefaultBrowserPromoManagerTest.java
@@ -27,7 +27,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.build.annotations.Nullable;
@@ -40,7 +39,6 @@
 
 /** Test whether metrics are correctly recorded by {@link DefaultBrowserPromoManager}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class DefaultBrowserPromoManagerTest {
 
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/TopControlsLockCoordinator.java b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/TopControlsLockCoordinator.java
index a4a82bda..883524c 100644
--- a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/TopControlsLockCoordinator.java
+++ b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/TopControlsLockCoordinator.java
@@ -77,9 +77,8 @@
 
     /** Set the {@link SideUiStateProvider} instance. */
     public void setSideUiStateProvider(SideUiStateProvider sideUiStateProvider) {
-        mSideUiStateProvider = sideUiStateProvider;
-        mSideUiStateProvider.addObserver(mSideUiObserver);
-        updateLock();
+        // TODO(crbug.com/542055382): Remove the rest of SideUiState code. The corresponding logic
+        // was implemented using BrowserStateBrowserControlsVisibilityDelegate in Vertical Tabs.
     }
 
     /** Get the token holder used to block scrolling updates. */
diff --git a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/TopControlsLockCoordinatorTest.java b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/TopControlsLockCoordinatorTest.java
index 9c977a4..acb60e8e 100644
--- a/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/TopControlsLockCoordinatorTest.java
+++ b/chrome/browser/ui/android/desktop_windowing/java/src/org/chromium/chrome/browser/ui/desktop_windowing/TopControlsLockCoordinatorTest.java
@@ -234,7 +234,7 @@
         when(mSideUiStateProvider.getCurrentSideUiSpecs()).thenReturn(specs);
         coordinator.setSideUiStateProvider(mSideUiStateProvider);
 
-        verify(mTopControlsStacker).setScrollingDisabled(true);
-        verify(mTopControlsStacker).requestLayerUpdatePost(false);
+        verify(mTopControlsStacker).setScrollingDisabled(false);
+        verify(mTopControlsStacker, never()).requestLayerUpdatePost(anyBoolean());
     }
 }
diff --git a/chrome/browser/ui/android/google_bottom_bar/java/src/org/chromium/chrome/browser/ui/google_bottom_bar/BottomBarConfigCreatorTest.java b/chrome/browser/ui/android/google_bottom_bar/java/src/org/chromium/chrome/browser/ui/google_bottom_bar/BottomBarConfigCreatorTest.java
index 52bad71..6d038a5 100644
--- a/chrome/browser/ui/android/google_bottom_bar/java/src/org/chromium/chrome/browser/ui/google_bottom_bar/BottomBarConfigCreatorTest.java
+++ b/chrome/browser/ui/android/google_bottom_bar/java/src/org/chromium/chrome/browser/ui/google_bottom_bar/BottomBarConfigCreatorTest.java
@@ -49,7 +49,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.browserservices.intents.CustomButtonParams;
@@ -70,7 +69,6 @@
 
 /** Unit tests for {@link BottomBarConfig}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class BottomBarConfigCreatorTest {
 
diff --git a/chrome/browser/ui/android/omnibox/AGENTS.md b/chrome/browser/ui/android/omnibox/AGENTS.md
index 3edb788..cb484ac 100644
--- a/chrome/browser/ui/android/omnibox/AGENTS.md
+++ b/chrome/browser/ui/android/omnibox/AGENTS.md
@@ -33,6 +33,7 @@
 *   **View Inflation**: Prefer using `AsyncViewInflation` where possible to keep the Main Thread free and reduce startup latency.
 *   **Imports**: Use `import` statements whenever possible instead of using fully qualified class names within the code.
 *   **Reuse & Pre-research**: Research relevant existing libraries, utilities, and methods before implementing something new. Follow existing patterns in the codebase when applicable.
+*   **Resource & Type Annotations**: Always annotate integer resource IDs and typed values with appropriate AndroidX annotations (e.g., `@ColorInt`, `@ColorRes`, `@DrawableRes`, `@StringRes`, `@Px`).
 *   **Constants over Magic Numbers**: Do not create or use magic numbers directly in the code. Define and use descriptive constants instead.
 *   **Method Signatures**: Avoid creating constructors or methods that accept too many boolean parameters, as this degrades readability.
 *   **Complexity & Early Returns**: Prefer early return statements over deeply nested conditional statements. Keep the cyclomatic complexity of methods low.
@@ -41,7 +42,8 @@
 
 ## Testing
 
-*   **Mocking with Annotations**: All mock objects (`@Mock`) and argument captors (`@Captor`) must be created using Mockito annotations. Manual mock creation using `Mockito.mock(...)` or `ArgumentCaptor.forClass(...)` is discouraged.
+*   **Mocking with Annotations**: Declare mock objects (`@Mock`) and argument captors (`@Captor`) as class fields using Mockito annotations. Avoid creating inline mocks in test methods using `mock(...)` or `ArgumentCaptor.forClass(...)`.
+*   **Annotation Placement**: Field-level test annotations (`@Rule`, `@Mock`, `@Spy`, `@Captor`) must precede access modifiers (e.g., `@Mock private Foo mFoo;`). `@Rule` fields must be declared `public final` (e.g., `@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();`).
 *   **Naming Conventions**: To clearly distinguish unit tests from integration/instrumentation/render tests, unit test files must be named `*UnitTest.java` (e.g., AutocompleteMediatorUnitTest).
 *   **Test Length**: Unit tests should be kept concise.
     *   The ideal test case is **10 ± 5 lines of code**.
diff --git a/chrome/browser/ui/android/omnibox/BUILD.gn b/chrome/browser/ui/android/omnibox/BUILD.gn
index 17e7899..722a448 100644
--- a/chrome/browser/ui/android/omnibox/BUILD.gn
+++ b/chrome/browser/ui/android/omnibox/BUILD.gn
@@ -441,8 +441,8 @@
     "java/res/layout/url_action_container.xml",
     "java/res/values-ldrtl/drawables.xml",
     "java/res/values-night/drawables.xml",
+    "java/res/values-sw600dp-w600dp/dimens.xml",
     "java/res/values-sw600dp/dimens.xml",
-    "java/res/values-w600dp/dimens.xml",
     "java/res/values/colors.xml",
     "java/res/values/dimens.xml",
     "java/res/values/drawables.xml",
@@ -516,7 +516,7 @@
     "java/src/org/chromium/chrome/browser/omnibox/DeferredIMEWindowInsetApplicationCallbackTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/FuseboxSessionStateUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/GlifStrokeDrawableTest.java",
-    "java/src/org/chromium/chrome/browser/omnibox/HintTextUpdaterTest.java",
+    "java/src/org/chromium/chrome/browser/omnibox/HintTextUpdaterUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/KeyboardHideHelperUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/LocationBarBackgroundDrawableUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorUnitTest.java",
diff --git a/chrome/browser/ui/android/omnibox/java/res/values-w600dp/dimens.xml b/chrome/browser/ui/android/omnibox/java/res/values-sw600dp-w600dp/dimens.xml
similarity index 100%
rename from chrome/browser/ui/android/omnibox/java/res/values-w600dp/dimens.xml
rename to chrome/browser/ui/android/omnibox/java/res/values-sw600dp-w600dp/dimens.xml
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/HintTextUpdater.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/HintTextUpdater.java
index 0260c46..e310cf0 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/HintTextUpdater.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/HintTextUpdater.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.omnibox;
 
-import android.content.Context;
 import android.text.TextUtils;
 
 import org.chromium.base.Callback;
@@ -38,7 +37,7 @@
  */
 @NullMarked
 public class HintTextUpdater implements LocationBarDataProvider.Observer {
-    private final Context mContext;
+    private final OmniboxResourceProvider mResourceProvider;
     private final LocationBarDataProvider mLocationBarDataProvider;
     private final LocationBarEmbedderUiOverrides mEmbedderUiOverrides;
     private final Callback<String> mUpdateHintTextCallback;
@@ -66,14 +65,14 @@
     private boolean mAimHintShownThisSession;
 
     public HintTextUpdater(
-            Context context,
+            OmniboxResourceProvider resourceProvider,
             LocationBarDataProvider locationBarDataProvider,
             LocationBarEmbedderUiOverrides embedderUiOverrides,
             MonotonicObservableSupplier<SearchEngineService> searchEngineServiceSupplier,
             FuseboxCoordinator fuseboxCoordinator,
             MonotonicObservableSupplier<Profile> profileSupplier,
             Callback<String> updateHintTextCallback) {
-        mContext = context;
+        mResourceProvider = resourceProvider;
         mLocationBarDataProvider = locationBarDataProvider;
         mEmbedderUiOverrides = embedderUiOverrides;
         mSearchEngineServiceSupplier = searchEngineServiceSupplier;
@@ -176,10 +175,9 @@
         if (useAimActivationOrEmptyHint()) {
             if (triggerOrAlreadyShowingActivationHint()) {
                 mUpdateHintTextCallback.onResult(
-                        OmniboxResourceProvider.getString(
-                                mContext,
+                        mResourceProvider.getString(
                                 R.string.ai_mode_omnibox_placeholder,
-                                mContext.getString(R.string.ai_mode_entrypoint_label)));
+                                mResourceProvider.getString(R.string.ai_mode_entrypoint_label)));
             } else {
                 mUpdateHintTextCallback.onResult("");
             }
@@ -208,7 +206,7 @@
         assert mSearchEngineService != null;
         String searchEngineName = mSearchEngineService.getSearchEngineName();
         if (TextUtils.isEmpty(searchEngineName)) {
-            return OmniboxResourceProvider.getString(mContext, R.string.omnibox_empty_hint);
+            return mResourceProvider.getString(R.string.omnibox_empty_hint);
         }
 
         if (OmniboxFeatures.sShowModelPicker.getValue()
@@ -221,15 +219,11 @@
 
         switch (requestType) {
             case AutocompleteRequestType.AI_MODE:
-                return OmniboxResourceProvider.getString(
-                        mContext,
-                        R.string.omnibox_ai_mode_scope_placeholder_text,
-                        searchEngineName);
+                return mResourceProvider.getString(
+                        R.string.omnibox_ai_mode_scope_placeholder_text, searchEngineName);
             case AutocompleteRequestType.IMAGE_GENERATION:
-                return OmniboxResourceProvider.getString(
-                        mContext,
-                        R.string.omnibox_empty_hint_for_image_generation,
-                        searchEngineName);
+                return mResourceProvider.getString(
+                        R.string.omnibox_empty_hint_for_image_generation, searchEngineName);
         }
         return mSearchEngineService.getOmniboxHintString();
     }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/HintTextUpdaterTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/HintTextUpdaterUnitTest.java
similarity index 96%
rename from chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/HintTextUpdaterTest.java
rename to chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/HintTextUpdaterUnitTest.java
index 3d0889f..54afba9 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/HintTextUpdaterTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/HintTextUpdaterUnitTest.java
@@ -38,7 +38,9 @@
 import org.chromium.chrome.browser.omnibox.fusebox.FuseboxCoordinator;
 import org.chromium.chrome.browser.omnibox.fusebox.FuseboxCoordinator.FuseboxLayoutMode;
 import org.chromium.chrome.browser.omnibox.fusebox.FuseboxCoordinator.FuseboxState;
+import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.components.contextual_search.InputState;
 import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.components.feature_engagement.Tracker;
@@ -53,7 +55,7 @@
 
 /** Unit tests for {@link HintTextUpdater}. */
 @RunWith(BaseRobolectricTestRunner.class)
-public class HintTextUpdaterTest {
+public class HintTextUpdaterUnitTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Mock private LocationBarDataProvider mLocationBarDataProvider;
@@ -92,6 +94,7 @@
 
     private HintTextUpdater mUpdater;
     private SearchEngineNameObserver mSearchEngineNameObserver;
+    private OmniboxResourceProvider mResourceProvider;
 
     @Before
     public void setUp() {
@@ -120,9 +123,12 @@
         mProfileSupplier.set(mProfile);
         TrackerFactory.setTrackerForTests(mTracker);
 
+        var context = ApplicationProvider.getApplicationContext();
+        context.setTheme(R.style.Theme_BrowserUI_DayNight);
+        mResourceProvider = new OmniboxResourceProvider(context, BrandedColorScheme.APP_DEFAULT);
         mUpdater =
                 new HintTextUpdater(
-                        ApplicationProvider.getApplicationContext(),
+                        mResourceProvider,
                         mLocationBarDataProvider,
                         mEmbedderUiOverrides,
                         mSearchEngineServiceSupplier,
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
index 7362123..a8ab96c8 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
@@ -439,7 +439,7 @@
         mOmniboxChipManager = omniboxChipManager;
         mHintTextUpdater =
                 new HintTextUpdater(
-                        mContext,
+                        mResourceProvider,
                         mLocationBarDataProvider,
                         mEmbedderUiOverrides,
                         mSearchEngineServiceSupplier,
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
index 53fba84..34ca40b 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
@@ -254,7 +254,6 @@
     @Mock private View mMicButton;
     @Mock private View mNavigateButton;
     @Mock private View mPlusButton;
-    @Mock private OmniboxResourceProvider mOmniboxResourceProvider;
 
     @Captor private ArgumentCaptor<Runnable> mRunnableCaptor;
     @Captor private ArgumentCaptor<LoadUrlParams> mLoadUrlParamsCaptor;
@@ -262,6 +261,7 @@
     @Captor private ArgumentCaptor<Callback<Boolean>> mOnInteractionCompletedCallbackCaptor;
     private Callback<Boolean> mOnInteractionCompletedCallback;
     private Context mContext;
+    private OmniboxResourceProvider mOmniboxResourceProvider;
     private SettableNonNullObservableSupplier<TabModelSelector> mTabModelSelectorSupplier;
     private final SettableMonotonicObservableSupplier<Profile> mProfileSupplier =
             ObservableSuppliers.createMonotonic();
@@ -307,6 +307,9 @@
                         ApplicationProvider.getApplicationContext(),
                         R.style.Theme_BrowserUI_DayNight);
 
+        mOmniboxResourceProvider =
+                new OmniboxResourceProvider(mContext, BrandedColorScheme.APP_DEFAULT);
+
         UserPrefs.setPrefServiceForTesting(mPrefService);
         lenient().doReturn(mProfile).when(mProfile).getOriginalProfile();
         lenient().doReturn(true).when(mPrefService).getBoolean(Pref.SHOW_AI_MODE_OMNIBOX_BUTTON);
@@ -1679,8 +1682,9 @@
         verify(mStatusCoordinator).setBrandedColorScheme(BrandedColorScheme.LIGHT_BRANDED_THEME);
         verify(mAutocompleteCoordinator)
                 .updateVisualsForState(BrandedColorScheme.LIGHT_BRANDED_THEME);
-        verify(mOmniboxResourceProvider)
-                .setBrandedColorScheme(BrandedColorScheme.LIGHT_BRANDED_THEME);
+        assertEquals(
+                BrandedColorScheme.LIGHT_BRANDED_THEME,
+                mOmniboxResourceProvider.getBrandedColorScheme());
     }
 
     @Test
@@ -1693,8 +1697,9 @@
         verify(mStatusCoordinator).setBrandedColorScheme(BrandedColorScheme.DARK_BRANDED_THEME);
         verify(mAutocompleteCoordinator)
                 .updateVisualsForState(BrandedColorScheme.DARK_BRANDED_THEME);
-        verify(mOmniboxResourceProvider)
-                .setBrandedColorScheme(BrandedColorScheme.DARK_BRANDED_THEME);
+        assertEquals(
+                BrandedColorScheme.DARK_BRANDED_THEME,
+                mOmniboxResourceProvider.getBrandedColorScheme());
     }
 
     @Test
@@ -1709,7 +1714,8 @@
         verify(mLocationBarLayout).setDeleteButtonTint(any(ColorStateList.class));
         verify(mStatusCoordinator).setBrandedColorScheme(BrandedColorScheme.INCOGNITO);
         verify(mAutocompleteCoordinator).updateVisualsForState(BrandedColorScheme.INCOGNITO);
-        verify(mOmniboxResourceProvider).setBrandedColorScheme(BrandedColorScheme.INCOGNITO);
+        assertEquals(
+                BrandedColorScheme.INCOGNITO, mOmniboxResourceProvider.getBrandedColorScheme());
     }
 
     @Test
@@ -1719,7 +1725,8 @@
         verify(mLocationBarLayout).setDeleteButtonTint(any(ColorStateList.class));
         verify(mStatusCoordinator).setBrandedColorScheme(BrandedColorScheme.APP_DEFAULT);
         verify(mAutocompleteCoordinator).updateVisualsForState(BrandedColorScheme.APP_DEFAULT);
-        verify(mOmniboxResourceProvider).setBrandedColorScheme(BrandedColorScheme.APP_DEFAULT);
+        assertEquals(
+                BrandedColorScheme.APP_DEFAULT, mOmniboxResourceProvider.getBrandedColorScheme());
     }
 
     @Test
@@ -1735,8 +1742,9 @@
         verify(mStatusCoordinator).setBrandedColorScheme(BrandedColorScheme.DARK_BRANDED_THEME);
         verify(mAutocompleteCoordinator)
                 .updateVisualsForState(BrandedColorScheme.DARK_BRANDED_THEME);
-        verify(mOmniboxResourceProvider)
-                .setBrandedColorScheme(BrandedColorScheme.DARK_BRANDED_THEME);
+        assertEquals(
+                BrandedColorScheme.DARK_BRANDED_THEME,
+                mOmniboxResourceProvider.getBrandedColorScheme());
     }
 
     @Test
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
index 0889f74..9931ed7 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
@@ -4,8 +4,6 @@
 
 package org.chromium.chrome.browser.omnibox;
 
-import static org.chromium.build.NullUtil.assumeNonNull;
-
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.Resources;
@@ -25,7 +23,6 @@
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.Px;
-import androidx.appcompat.content.res.AppCompatResources;
 
 import org.chromium.build.annotations.Initializer;
 import org.chromium.build.annotations.NullMarked;
@@ -126,11 +123,9 @@
         mMicButtonWidth = locationBarIconWidth;
         mLensButtonWidth = locationBarIconWidth;
         mFocusedPopupDrawable =
-                (LayerDrawable)
-                        assumeNonNull(
-                                context.getDrawable(
-                                        R.drawable
-                                                .modern_toolbar_tablet_text_box_background_focused_popup));
+                OmniboxResourceProvider.getDrawable(
+                        context,
+                        R.drawable.modern_toolbar_tablet_text_box_background_focused_popup);
         mFocusedPopupDrawable.mutate();
         mOuterRect =
                 (GradientDrawable)
@@ -155,11 +150,8 @@
                 resources.getDimensionPixelSize(R.dimen.location_bar_desktop_popover_margin_end);
 
         mHoverDrawable =
-                (LayerDrawable)
-                        assumeNonNull(
-                                AppCompatResources.getDrawable(
-                                        getContext(),
-                                        R.drawable.modern_toolbar_text_box_background_highlight));
+                OmniboxResourceProvider.getDrawable(
+                        getContext(), R.drawable.modern_toolbar_text_box_background_highlight);
         mHoverDrawable.mutate();
 
         @Px float strokeWidth = resources.getDimension(R.dimen.fusebox_glif_stroke_width);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarUnitTest.java
index 9f46838..dfa58e7 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarUnitTest.java
@@ -71,7 +71,6 @@
 import org.chromium.base.MathUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.RobolectricUtil;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -95,7 +94,6 @@
 /** Unit tests for {@link UrlBar}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(qualifiers = "w100dp-h50dp")
-@Batch(Batch.UNIT_TESTS)
 public class UrlBarUnitTest {
     // UrlBar has 4 px of padding on the left and right. Set this to url bar width + padding so
     // getVisibleMeasuredViewportWidth() returns 100. This ensures NUMBER_OF_VISIBLE_CHARACTERS
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java
index 4fa433b..a317311 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java
@@ -525,13 +525,13 @@
     @VisibleForTesting
     void onContextPopupDismissed() {
         if (mViewHolder == null || mViewHolder.plusButton == null) return;
-        boolean actionTaken = mMediator != null && mMediator.wasActionTaken();
-        if (!actionTaken) {
+        boolean popupItemSelected = mMediator != null && mMediator.wasPopupItemSelected();
+        if (!popupItemSelected) {
             mViewHolder.plusButton.requestFocus();
             mViewHolder.plusButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
         }
         if (mOnInteractionCompletedCallback != null) {
-            mOnInteractionCompletedCallback.onResult(actionTaken);
+            mOnInteractionCompletedCallback.onResult(popupItemSelected);
         }
     }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinatorUnitTest.java
index 1fa0a70..3f586053 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinatorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinatorUnitTest.java
@@ -377,11 +377,11 @@
 
     @Test
     @EnableFeatures(OmniboxFeatureList.OMNIBOX_MULTIMODAL_INPUT)
-    public void testPopupDismissed_noActionTaken_plusButtonFocused() {
+    public void testPopupDismissed_noPopupItemSelected_plusButtonFocused() {
         mCoordinator.beginInput(createSession());
         RobolectricUtil.runAllBackgroundAndUiIncludingDelayed();
         mCoordinator.setMediatorForTesting(mMediator);
-        doReturn(false).when(mMediator).wasActionTaken();
+        doReturn(false).when(mMediator).wasPopupItemSelected();
 
         mCoordinator.setOnInteractionCompletedCallback(mOnInteractionCompletedCallback);
 
@@ -395,11 +395,11 @@
 
     @Test
     @EnableFeatures(OmniboxFeatureList.OMNIBOX_MULTIMODAL_INPUT)
-    public void testPopupDismissed_actionTaken_plusButtonNotFocused() {
+    public void testPopupDismissed_popupItemSelected_plusButtonNotFocused() {
         mCoordinator.beginInput(createSession());
         RobolectricUtil.runAllBackgroundAndUiIncludingDelayed();
         mCoordinator.setMediatorForTesting(mMediator);
-        doReturn(true).when(mMediator).wasActionTaken();
+        doReturn(true).when(mMediator).wasPopupItemSelected();
 
         mCoordinator.setOnInteractionCompletedCallback(mOnInteractionCompletedCallback);
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediator.java
index 12ad70f..9ebab8b 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediator.java
@@ -150,7 +150,7 @@
     private @Nullable ComposeboxQueryControllerBridge mComposeboxQueryControllerBridge;
     private @Nullable FuseboxMetrics mMetrics;
     private @Nullable PropertyModel mScrimModel;
-    private boolean mActionTaken;
+    private boolean mPopupItemSelected;
     private @Nullable Runnable mOnFirstPickerInteractionCanceledCallback;
     private boolean mNeedUnfocusOnCancel;
     @VisibleForTesting /* package */ @Nullable PrefChangeRegistrar mPrefChangeRegistrar;
@@ -256,8 +256,8 @@
         mWindowHasFocusSupplier.removeObserver(mOnWindowFocusChanged);
     }
 
-    public boolean wasActionTaken() {
-        return mActionTaken;
+    public boolean wasPopupItemSelected() {
+        return mPopupItemSelected;
     }
 
     public void setOnFirstPickerInteractionCanceledCallback(Runnable callback) {
@@ -369,7 +369,7 @@
      *     through the endInput() (valid -> valid). This is the case for tab switching.
      */
     /* package */ void beginInput(FuseboxSessionState session) {
-        mActionTaken = false;
+        mPopupItemSelected = false;
         mMetrics = session.getMetrics();
         mProfile = assertNonNull(session.getProfile());
         if (mPrefChangeRegistrar != null) {
@@ -862,7 +862,7 @@
     private void onTabPickerClicked() {
         if (!isInInputSession()) return;
 
-        mActionTaken = true;
+        mPopupItemSelected = true;
         hidePopup();
         mMetrics.notifyAttachmentButtonUsed(FuseboxAttachmentButtonType.TAB_PICKER);
 
@@ -998,7 +998,7 @@
     private void onCameraClicked() {
         if (!isInInputSession()) return;
 
-        mActionTaken = true;
+        mPopupItemSelected = true;
         hidePopup();
         mMetrics.notifyAttachmentButtonUsed(FuseboxAttachmentButtonType.CAMERA);
 
@@ -1203,7 +1203,7 @@
     private void onImagePickerClicked() {
         if (!isInInputSession()) return;
 
-        mActionTaken = true;
+        mPopupItemSelected = true;
         hidePopup();
         mMetrics.notifyAttachmentButtonUsed(FuseboxAttachmentButtonType.GALLERY);
 
@@ -1254,7 +1254,7 @@
     private void onFilePickerClicked() {
         if (!isInInputSession()) return;
 
-        mActionTaken = true;
+        mPopupItemSelected = true;
         hidePopup();
         mMetrics.notifyAttachmentButtonUsed(FuseboxAttachmentButtonType.FILES);
 
@@ -1483,7 +1483,7 @@
     }
 
     private void onDynamicButtonClicked(PopupButtonData data) {
-        mActionTaken = true;
+        mPopupItemSelected = true;
         mNeedUnfocusOnCancel = false;
         if (data.type == PopupButtonType.MODEL) {
             FuseboxMetrics.notifyModelButtonSelected(data.protoId);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediatorUnitTest.java
index 32f9770..e2b39ac 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediatorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediatorUnitTest.java
@@ -1247,7 +1247,7 @@
     @Config(sdk = Build.VERSION_CODES.S_V2)
     public void testGalleryIntent_extraAllowMultiple() {
         mModel.get(FuseboxProperties.POPUP_ATTACH_GALLERY_CLICKED).run();
-        assertTrue(mMediator.wasActionTaken());
+        assertTrue(mMediator.wasPopupItemSelected());
         verify(mWindowAndroid).showCancelableIntent(mIntentCaptor.capture(), any(), any());
         Intent intent = mIntentCaptor.getValue();
         assertTrue(intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, /* defaultValue= */ false));
@@ -1452,7 +1452,7 @@
 
         List<PopupButtonData> models = mModel.get(FuseboxProperties.POPUP_MODEL_BUTTON_DATA_LIST);
         models.get(0).onClicked.run();
-        assertTrue(mMediator.wasActionTaken());
+        assertTrue(mMediator.wasPopupItemSelected());
 
         histogramWatcher.assertExpected();
     }
@@ -1812,7 +1812,7 @@
     @Test
     public void onTabPickerClicked_launchesTabPickerActivity() {
         mModel.get(FuseboxProperties.POPUP_ATTACH_TAB_PICKER_CLICKED).run();
-        assertTrue(mMediator.wasActionTaken());
+        assertTrue(mMediator.wasPopupItemSelected());
 
         assertEquals(PopupState.HIDDEN, (int) mModel.get(FuseboxProperties.POPUP_STATE));
         verify(mWindowAndroid).showCancelableIntent(mIntentCaptor.capture(), any(), any());
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxPopup.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxPopup.java
index b782369..0e6d1712 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxPopup.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxPopup.java
@@ -23,6 +23,7 @@
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.omnibox.R;
 import org.chromium.chrome.browser.omnibox.fusebox.FuseboxCoordinator.PopupState;
+import org.chromium.components.omnibox.OmniboxCapabilities;
 import org.chromium.ui.base.LocalizationUtils;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.insets.InsetObserver;
@@ -160,12 +161,16 @@
                 R.string.accessibility_omnibox_add_camera_picture);
         initializeItem(
                 mGalleryButton,
-                R.string.omnibox_navattach_gallery,
+                OmniboxCapabilities.isDesktopPlatform()
+                        ? R.string.omnibox_navattach_add_images
+                        : R.string.omnibox_navattach_gallery,
                 R.drawable.add_photo_alternate_24dp,
                 R.string.accessibility_omnibox_add_images);
         initializeItem(
                 mFileButton,
-                R.string.omnibox_navattach_files,
+                OmniboxCapabilities.isDesktopPlatform()
+                        ? R.string.omnibox_navattach_add_files
+                        : R.string.omnibox_navattach_files,
                 R.drawable.ic_attach_file_24dp,
                 R.string.accessibility_omnibox_add_files);
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
index dd6cdf20..136a51b 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
@@ -421,13 +421,12 @@
                         && UrlUtilities.isNtpUrl(url)
                         && !mLocationBarDataProvider.isIncognitoBranded();
 
+        int pageClassification =
+                mLocationBarDataProvider.getPageClassification(/* prefetch= */ false);
         mModel.set(
                 StatusProperties.USE_WIDE_STATUS_ICON,
-                mUrlHasFocus
-                        || isRegularNtpUrl
-                        || PageClassificationUtils.isHubOrTabSearch(
-                                mLocationBarDataProvider.getPageClassification(
-                                        /* prefetch= */ false)));
+                pageClassification != PageClassification.ANDROID_TAB_SEARCH_OVERLAY_VALUE
+                        && (mUrlHasFocus || isRegularNtpUrl));
     }
 
     public void setUseSmallWidget(boolean useSmallWidget) {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
index f85b1dc2..e9db7a57 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
@@ -596,13 +596,13 @@
 
     @Test
     @SmallTest
-    public void testWideIconTrue_tabSearchOverlay() {
+    public void testWideIconFalse_tabSearchOverlay() {
         doReturn(PageClassification.ANDROID_TAB_SEARCH_OVERLAY_VALUE)
                 .when(mLocationBarDataProvider)
                 .getPageClassification(/* prefetch= */ false);
 
         mMediator.beginInput(mFuseboxSessionState);
-        assertTrue(mModel.get(StatusProperties.USE_WIDE_STATUS_ICON));
+        assertFalse(mModel.get(StatusProperties.USE_WIDE_STATUS_ICON));
     }
 
     @Test
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java
index bcbbb7e5..fa46ba346 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java
@@ -193,9 +193,17 @@
         return LayoutSize.PHONE;
     }
 
-    /** As {@link #getDrawable(Context, int)} but uses the instance context and cache. */
-    public Drawable getDrawable(@DrawableRes int res) {
-        return mCache.getDrawable(res);
+    /**
+     * Resolves a drawable resource using the instance context and cache, automatically casting the
+     * result to the target {@link Drawable} subtype.
+     *
+     * @param <T> Target {@link Drawable} subtype to cast to.
+     * @param res Drawable resource ID to retrieve.
+     * @return Resolved {@link Drawable} cast to the target type {@code T}.
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends Drawable> T getDrawable(@DrawableRes int res) {
+        return (T) mCache.getDrawable(res);
     }
 
     /**
@@ -702,20 +710,25 @@
     }
 
     /**
-     * As {@link androidx.appcompat.content.res.AppCompatResources#getDrawable(Context, int)} but
-     * potentially augmented with caching. If caching is enabled, there is a single, unbounded cache
-     * of ConstantState shared by all contexts.
+     * Resolves a drawable resource and caches its constant state for reuse across all contexts,
+     * automatically casting the result to the target {@link Drawable} subtype.
+     *
+     * @param <T> Target {@link Drawable} subtype to cast to.
+     * @param context Current context used to resolve the drawable resource.
+     * @param res Drawable resource ID to retrieve and cache.
+     * @return Resolved {@link Drawable} cast to the target type {@code T}.
      */
-    public static Drawable getDrawable(Context context, @DrawableRes int res) {
+    @SuppressWarnings("unchecked")
+    public static <T extends Drawable> T getDrawable(Context context, @DrawableRes int res) {
         ThreadUtils.assertOnUiThread();
         ConstantState constantState = sDrawableCache.get(res, null);
         if (constantState != null) {
-            return constantState.newDrawable(context.getResources());
+            return (T) constantState.newDrawable(context.getResources());
         }
 
         Drawable drawable = AppCompatResources.getDrawable(context, res);
         sDrawableCache.put(res, drawable.getConstantState());
-        return drawable;
+        return (T) drawable;
     }
 
     /**
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/action/OmniboxActionInSuggestUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/action/OmniboxActionInSuggestUnitTest.java
index d88c0e3..8b421e7 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/action/OmniboxActionInSuggestUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/action/OmniboxActionInSuggestUnitTest.java
@@ -33,7 +33,7 @@
 import org.chromium.chrome.browser.omnibox.OmniboxMetrics;
 import org.chromium.chrome.browser.omnibox.R;
 import org.chromium.components.embedder_support.util.UrlConstants;
-import org.chromium.components.omnibox.SuggestTemplateInfoProto.SuggestTemplateInfo;
+import org.chromium.components.omnibox.SuggestTemplateInfoProto.SuggestTemplateInfo.TemplateAction.ActionType;
 import org.chromium.components.omnibox.action.ActionPresentationMode;
 import org.chromium.components.omnibox.action.OmniboxAction;
 import org.chromium.components.omnibox.action.OmniboxActionDelegate;
@@ -48,13 +48,11 @@
 public class OmniboxActionInSuggestUnitTest {
     private static final List<Integer> sKnownActionTypes =
             List.of(
-                    SuggestTemplateInfo.TemplateAction.ActionType.CALL_VALUE,
-                    SuggestTemplateInfo.TemplateAction.ActionType.DIRECTIONS_VALUE,
-                    SuggestTemplateInfo.TemplateAction.ActionType.REVIEWS_VALUE,
-                    SuggestTemplateInfo.TemplateAction.ActionType.CHROME_AIM_VALUE,
-                    SuggestTemplateInfo.TemplateAction.ActionType.CHROME_TAB_SWITCH_VALUE);
-    private static final SuggestTemplateInfo.TemplateAction EMPTY_INFO =
-            SuggestTemplateInfo.TemplateAction.getDefaultInstance();
+                    ActionType.CALL_VALUE,
+                    ActionType.DIRECTIONS_VALUE,
+                    ActionType.REVIEWS_VALUE,
+                    ActionType.CHROME_AIM_VALUE,
+                    ActionType.CHROME_TAB_SWITCH_VALUE);
 
     public @Rule MockitoRule mockitoRule = MockitoJUnit.rule();
     private @Mock OmniboxActionDelegate mDelegate;
@@ -63,13 +61,13 @@
 
     @Test
     public void creation_usesCustomIconForKnownActionTypes() {
-        for (var kesemActionType : sKnownActionTypes) {
+        for (var entitySuggestionActionType : sKnownActionTypes) {
             var action =
                     new OmniboxActionInSuggest(
                             0,
                             "hint",
                             "accessibility",
-                            kesemActionType,
+                            entitySuggestionActionType,
                             "",
                             /* tabId= */ 0,
                             ActionPresentationMode.CHIP);
@@ -79,14 +77,14 @@
 
     @Test
     public void creation_usesFallbackIconForUnknownActionTypes() {
-        for (var kesemActionType : SuggestTemplateInfo.TemplateAction.ActionType.values()) {
-            if (sKnownActionTypes.contains(kesemActionType.getNumber())) continue;
+        for (var entitySuggestionActionType : ActionType.values()) {
+            if (sKnownActionTypes.contains(entitySuggestionActionType.getNumber())) continue;
             var action =
                     new OmniboxActionInSuggest(
                             0,
                             "hint",
                             "accessibility",
-                            kesemActionType.getNumber(),
+                            entitySuggestionActionType.getNumber(),
                             "",
                             /* tabId= */ 0,
                             ActionPresentationMode.CHIP);
@@ -103,7 +101,7 @@
                                 0,
                                 null,
                                 "",
-                                SuggestTemplateInfo.TemplateAction.ActionType.CALL_VALUE,
+                                ActionType.CALL_VALUE,
                                 "",
                                 /* tabId= */ 0,
                                 ActionPresentationMode.CHIP));
@@ -118,7 +116,7 @@
                                 0,
                                 "",
                                 "",
-                                SuggestTemplateInfo.TemplateAction.ActionType.CALL_VALUE,
+                                ActionType.CALL_VALUE,
                                 "",
                                 /* tabId= */ 0,
                                 ActionPresentationMode.CHIP));
@@ -156,15 +154,14 @@
                         0,
                         "hint",
                         "accessibility",
-                        SuggestTemplateInfo.TemplateAction.ActionType.REVIEWS_VALUE,
+                        ActionType.REVIEWS_VALUE,
                         "",
                         /* tabId= */ 0,
                         ActionPresentationMode.CHIP));
     }
 
     /** Create Action in Suggest with a supplied definition. */
-    private OmniboxAction buildActionInSuggest(
-            SuggestTemplateInfo.TemplateAction.ActionType type, Intent intent) {
+    private OmniboxAction buildActionInSuggest(ActionType type, Intent intent) {
         var uri = intent.toUri(Intent.URI_INTENT_SCHEME);
         return new OmniboxActionInSuggest(
                 0,
@@ -181,9 +178,7 @@
         doReturn(false).when(mDelegate).isIncognito();
         doReturn(true).when(mDelegate).startActivity(any());
 
-        buildActionInSuggest(
-                        SuggestTemplateInfo.TemplateAction.ActionType.DIRECTIONS,
-                        new Intent("Magic Intent Action"))
+        buildActionInSuggest(ActionType.DIRECTIONS, new Intent("Magic Intent Action"))
                 .execute(mDelegate);
 
         verify(mDelegate, times(1)).isIncognito();
@@ -207,8 +202,7 @@
         var intent = new Intent(Intent.ACTION_VIEW);
         intent.setData(Uri.parse(UrlConstants.CHROME_DINO_URL));
 
-        buildActionInSuggest(SuggestTemplateInfo.TemplateAction.ActionType.DIRECTIONS, intent)
-                .execute(mDelegate);
+        buildActionInSuggest(ActionType.DIRECTIONS, intent).execute(mDelegate);
 
         verify(mDelegate, times(1)).isIncognito();
 
@@ -234,8 +228,7 @@
         var intent = new Intent(Intent.ACTION_VIEW);
         intent.setData(Uri.parse(UrlConstants.CHROME_DINO_URL));
 
-        buildActionInSuggest(SuggestTemplateInfo.TemplateAction.ActionType.DIRECTIONS, intent)
-                .execute(mDelegate);
+        buildActionInSuggest(ActionType.DIRECTIONS, intent).execute(mDelegate);
 
         verify(mDelegate, times(1)).isIncognito();
 
@@ -259,9 +252,7 @@
         doReturn(false).when(mDelegate).isIncognito();
         doReturn(true).when(mDelegate).startActivity(any());
 
-        buildActionInSuggest(
-                        SuggestTemplateInfo.TemplateAction.ActionType.CALL, new Intent(Intent.ACTION_CALL))
-                .execute(mDelegate);
+        buildActionInSuggest(ActionType.CALL, new Intent(Intent.ACTION_CALL)).execute(mDelegate);
 
         verify(mDelegate, times(1)).isIncognito();
         verify(mDelegate, times(1)).startActivity(mIntentCaptor.capture());
@@ -287,7 +278,7 @@
         var intent = new Intent(Intent.ACTION_VIEW);
         intent.setData(Uri.parse(UrlConstants.CHROME_DINO_URL));
 
-        buildActionInSuggest(SuggestTemplateInfo.TemplateAction.ActionType.CALL, intent).execute(mDelegate);
+        buildActionInSuggest(ActionType.CALL, intent).execute(mDelegate);
 
         verify(mDelegate, times(1)).isIncognito();
         verify(mDelegate, times(1)).startActivity(any());
@@ -305,8 +296,7 @@
         var intent = new Intent(Intent.ACTION_VIEW);
         intent.setData(Uri.parse(UrlConstants.CHROME_DINO_URL));
 
-        buildActionInSuggest(SuggestTemplateInfo.TemplateAction.ActionType.REVIEWS, intent)
-                .execute(mDelegate);
+        buildActionInSuggest(ActionType.REVIEWS, intent).execute(mDelegate);
 
         verify(mDelegate, times(1)).isIncognito();
 
@@ -338,8 +328,7 @@
                         "Android.Omnibox.ActionInSuggest.IntentResult",
                         OmniboxMetrics.ActionInSuggestIntentResult.SUCCESS);
 
-        buildActionInSuggest(SuggestTemplateInfo.TemplateAction.ActionType.CHROME_AIM, intent)
-                .execute(mDelegate);
+        buildActionInSuggest(ActionType.CHROME_AIM, intent).execute(mDelegate);
 
         verify(mDelegate, times(1)).isIncognito();
 
@@ -366,9 +355,7 @@
                             /* tabId= */ 0,
                             ActionPresentationMode.CHIP);
             assertEquals(
-                    actionType
-                                    == SuggestTemplateInfo.TemplateAction.ActionType
-                                            .CHROME_TAB_SWITCH_VALUE
+                    actionType == ActionType.CHROME_TAB_SWITCH_VALUE
                             ? WindowOpenDisposition.SWITCH_TO_TAB
                             : WindowOpenDisposition.CURRENT_TAB,
                     action.disposition);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinatorUnitTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinatorUnitTest.java
index 5af3565..ad088b86 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinatorUnitTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinatorUnitTest.java
@@ -40,7 +40,6 @@
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.base.supplier.SettableNonNullObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsVisibilityManager;
 import org.chromium.chrome.browser.browser_controls.BrowserStateBrowserControlsVisibilityDelegate;
 import org.chromium.chrome.browser.browser_controls.TopControlsStacker;
@@ -83,7 +82,6 @@
 
 /** Unit tests for {@link TopToolbarCoordinator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class TopToolbarCoordinatorUnitTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/ui/extensions/extension_settings_overridden_dialog.h b/chrome/browser/ui/extensions/extension_settings_overridden_dialog.h
index 64f4f5e..e375667 100644
--- a/chrome/browser/ui/extensions/extension_settings_overridden_dialog.h
+++ b/chrome/browser/ui/extensions/extension_settings_overridden_dialog.h
@@ -107,7 +107,8 @@
   void HandleDialogResult(DialogResult result) override;
 
   // Sets a callback to be invoked when the dialog result is handled.
-  using DialogResultCallback = base::OnceCallback<void(DialogResult result)>;
+  using DialogResultCallback =
+      base::OnceCallback<void(std::optional<DialogResult> result)>;
   void SetDialogResultCallback(DialogResultCallback callback);
 
   // Potentially triggers a HaTS survey, for the trigger value supplied in
diff --git a/chrome/browser/ui/extensions/settings_overridden_params_providers.cc b/chrome/browser/ui/extensions/settings_overridden_params_providers.cc
index c39cdf2..f02cfaa98 100644
--- a/chrome/browser/ui/extensions/settings_overridden_params_providers.cc
+++ b/chrome/browser/ui/extensions/settings_overridden_params_providers.cc
@@ -477,6 +477,43 @@
 }
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+bool ExtensionSearchOverrideMatchesExistingEngine(Profile* profile) {
+  if (!profile) {
+    return false;
+  }
+
+  if (!extensions::GetExtensionOverridingSearchEngine(profile)) {
+    return false;
+  }
+
+  TemplateURLService* template_url_service =
+      TemplateURLServiceFactory::GetForProfile(profile);
+  CHECK(template_url_service);
+  if (!template_url_service->IsExtensionControlledDefaultSearch()) {
+    return false;
+  }
+
+  const TemplateURL* default_search =
+      template_url_service->GetDefaultSearchProvider();
+  if (!default_search) {
+    return false;
+  }
+
+  const GURL search_url(default_search->url());
+  if (!search_url.is_valid()) {
+    return false;
+  }
+
+  // NOTE: Normally, we wouldn't want to use direct equality comparison of
+  // GURL::GetOrigin() because of edge cases like inner URLs with filesystem,
+  // etc. This is okay here, because if the origins don't match we fall through
+  // to showing the dialog. That's likely good if any extension is doing
+  // something as crazy as using filesystem: URLs as a search engine.
+  const SecondarySearchInfo secondary_search = GetSecondarySearchInfo(profile);
+  return !secondary_search.origin.is_empty() &&
+         secondary_search.origin == search_url.DeprecatedGetOriginAsURL();
+}
+
 void GetSearchOverriddenParamsThenRun(
     content::WebContents* web_contents,
     base::OnceCallback<
@@ -519,21 +556,16 @@
   GURL search_url(default_search->url());
   DCHECK(search_url.is_valid()) << default_search->url();
 
-  // Check whether the secondary search is the same search the extension set.
-  // This can happen if the user set a search engine, and then installed an
-  // extension that set the same one.
-  SecondarySearchInfo secondary_search = GetSecondarySearchInfo(profile);
-  // NOTE: Normally, we wouldn't want to use direct equality comparison of
-  // GURL::GetOrigin() because of edge cases like inner URLs with filesystem,
-  // etc. This okay here, because if the origins don't match, we'll show the
-  // dialog to the user. That's likely good if any extension is doing something
-  // as crazy as using filesystem: URLs as a search engine.
-  if (!secondary_search.origin.is_empty() &&
-      secondary_search.origin == search_url.DeprecatedGetOriginAsURL()) {
+  // Nothing was actually overridden; there is nothing to confirm. Callers check
+  // this synchronously before withholding a navigation; re-check here in case
+  // extension state changed in between.
+  if (ExtensionSearchOverrideMatchesExistingEngine(profile)) {
     std::move(done_callback).Run(nullptr);
     return;
   }
 
+  SecondarySearchInfo secondary_search = GetSecondarySearchInfo(profile);
+
   constexpr char kGenericDialogHistogramName[] =
       "Extensions.SettingsOverridden.GenericSearchOverriddenDialogResult";
   constexpr char kBackToOtherHistogramName[] =
diff --git a/chrome/browser/ui/extensions/settings_overridden_params_providers.h b/chrome/browser/ui/extensions/settings_overridden_params_providers.h
index 77ea304..441bf657 100644
--- a/chrome/browser/ui/extensions/settings_overridden_params_providers.h
+++ b/chrome/browser/ui/extensions/settings_overridden_params_providers.h
@@ -34,6 +34,17 @@
     Profile* profile);
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+// Returns true if the extension controlling the default search engine set the
+// *same* engine the user was already using, in which case nothing was actually
+// overridden and there is nothing to confirm. This happens when a user selects
+// a search engine and then installs an extension that provides the same one.
+//
+// This is synchronous and must stay that way: callers use it to decide whether
+// a navigation needs to be held for confirmation, and that decision has to be
+// complete before the navigation is abandoned. See
+// https://crbug.com/540532980 for what happens when it is not.
+bool ExtensionSearchOverrideMatchesExistingEngine(Profile* profile);
+
 // Retrieves the params for displaying the dialog indicating that the default
 // search engine has been overridden, if there is a controlling extension, and
 // asynchronously passes them to the supplied callback. Otherwise, the callback
diff --git a/chrome/browser/ui/login/login_handler.cc b/chrome/browser/ui/login/login_handler.cc
index cb2a7b7..abca7d4e 100644
--- a/chrome/browser/ui/login/login_handler.cc
+++ b/chrome/browser/ui/login/login_handler.cc
@@ -330,8 +330,8 @@
     dialog_form.scheme = PasswordForm::Scheme::kOther;
   }
   dialog_form.url = auth_info.challenger.GetURL();
-  DCHECK(auth_info.is_proxy ||
-         auth_info.challenger == url::SchemeHostPort(request_url));
+  CHECK(auth_info.is_proxy ||
+        auth_info.challenger == url::SchemeHostPort(request_url));
   dialog_form.signon_realm = GetSignonRealm(dialog_form.url, auth_info);
   return dialog_form;
 }
diff --git a/chrome/browser/ui/login/login_handler.h b/chrome/browser/ui/login/login_handler.h
index a52974f..873d2f607 100644
--- a/chrome/browser/ui/login/login_handler.h
+++ b/chrome/browser/ui/login/login_handler.h
@@ -108,6 +108,7 @@
 
  private:
   FRIEND_TEST_ALL_PREFIXES(LoginHandlerTest, DialogStringsAndRealm);
+  FRIEND_TEST_ALL_PREFIXES(LoginHandlerTest, MakeInputForPasswordManager);
 
   // When any handler finishes, called on every other handler. |username| and
   // |password| are only valid if |supplied| is true. If |supplied| is false
diff --git a/chrome/browser/ui/login/login_handler_unittest.cc b/chrome/browser/ui/login/login_handler_unittest.cc
index a9ba34a..cf8fe74 100644
--- a/chrome/browser/ui/login/login_handler_unittest.cc
+++ b/chrome/browser/ui/login/login_handler_unittest.cc
@@ -177,3 +177,80 @@
       helper->WillProcessMainFrameUnauthorizedResponse(&handle);
   EXPECT_EQ(content::NavigationThrottle::CANCEL, result.action());
 }
+
+// Tests that WillProcessMainFrameUnauthorizedResponse() cancels the navigation
+// to show a login prompt when the auth challenge's challenger matches the
+// navigation URL.
+TEST_F(LoginHandlerWithWebContentsTest, MatchingChallengerCancelsForPrompt) {
+  LoginTabHelper::CreateForWebContents(web_contents());
+  LoginTabHelper* helper = LoginTabHelper::FromWebContents(web_contents());
+
+  net::AuthChallengeInfo challenge;
+  challenge.is_proxy = false;
+  challenge.challenger = url::SchemeHostPort(GURL("https://requested.test"));
+
+  content::MockNavigationHandle handle;
+  handle.set_url(GURL("https://requested.test/page"));
+  handle.SetAuthChallengeInfo(challenge);
+  handle.set_global_request_id({network::OriginatingProcessId::browser(), 1});
+
+  content::NavigationThrottle::ThrottleCheckResult result =
+      helper->WillProcessMainFrameUnauthorizedResponse(&handle);
+  EXPECT_EQ(content::NavigationThrottle::CANCEL, result.action());
+}
+
+// Tests that WillProcessMainFrameUnauthorizedResponse() does not cancel the
+// navigation to show a login prompt when the auth challenge's challenger does
+// not match the navigation URL. This can occur when a service worker responds
+// to a navigation with a 401 response that it fetched from a different origin.
+TEST_F(LoginHandlerWithWebContentsTest, MismatchedChallengerDoesNotShowPrompt) {
+  LoginTabHelper::CreateForWebContents(web_contents());
+  LoginTabHelper* helper = LoginTabHelper::FromWebContents(web_contents());
+
+  net::AuthChallengeInfo challenge;
+  challenge.is_proxy = false;
+  challenge.challenger = url::SchemeHostPort(GURL("https://other.test"));
+
+  content::MockNavigationHandle handle;
+  handle.set_url(GURL("https://requested.test/page"));
+  handle.SetAuthChallengeInfo(challenge);
+  handle.set_global_request_id({network::OriginatingProcessId::browser(), 1});
+
+  content::NavigationThrottle::ThrottleCheckResult result =
+      helper->WillProcessMainFrameUnauthorizedResponse(&handle);
+  EXPECT_EQ(content::NavigationThrottle::PROCEED, result.action());
+}
+
+// Tests that proxy authentication challenges are processed for prompts even
+// when the proxy challenger host differs from the navigation URL.
+TEST_F(LoginHandlerWithWebContentsTest, ProxyChallengerCancelsForPrompt) {
+  LoginTabHelper::CreateForWebContents(web_contents());
+  LoginTabHelper* helper = LoginTabHelper::FromWebContents(web_contents());
+
+  net::AuthChallengeInfo challenge;
+  challenge.is_proxy = true;
+  challenge.challenger = url::SchemeHostPort(GURL("http://proxy.test:8080"));
+
+  content::MockNavigationHandle handle;
+  handle.set_url(GURL("https://requested.test/page"));
+  handle.SetAuthChallengeInfo(challenge);
+  handle.set_global_request_id({network::OriginatingProcessId::browser(), 1});
+
+  content::NavigationThrottle::ThrottleCheckResult result =
+      helper->WillProcessMainFrameUnauthorizedResponse(&handle);
+  EXPECT_EQ(content::NavigationThrottle::CANCEL, result.action());
+}
+
+TEST(LoginHandlerTest, MakeInputForPasswordManager) {
+  GURL request_url("https://requested.test/page");
+  net::AuthChallengeInfo auth_info;
+  auth_info.is_proxy = false;
+  auth_info.challenger = url::SchemeHostPort(request_url);
+  auth_info.scheme = "basic";
+  auth_info.realm = "realm";
+
+  password_manager::PasswordForm form =
+      LoginHandler::MakeInputForPasswordManager(request_url, auth_info);
+  EXPECT_EQ(form.url, GURL("https://requested.test"));
+  EXPECT_EQ(form.scheme, password_manager::PasswordForm::Scheme::kBasic);
+}
diff --git a/chrome/browser/ui/login/login_tab_helper.cc b/chrome/browser/ui/login/login_tab_helper.cc
index 12e2c2c8..fe68c72 100644
--- a/chrome/browser/ui/login/login_tab_helper.cc
+++ b/chrome/browser/ui/login/login_tab_helper.cc
@@ -18,6 +18,20 @@
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
 #include "services/network/public/mojom/network_context.mojom.h"
+#include "url/scheme_host_port.h"
+
+namespace {
+
+// Returns true if `challenge` is consistent with a navigation to `url`. The
+// login prompt displays the navigation URL as the origin asking for
+// credentials, so a server challenge for a different origin must not be shown
+// to the user or used to populate the auth cache.
+bool ChallengeMatchesNavigation(const net::AuthChallengeInfo& challenge,
+                                const GURL& url) {
+  return challenge.is_proxy || challenge.challenger == url::SchemeHostPort(url);
+}
+
+}  // namespace
 
 LoginTabHelper::~LoginTabHelper() = default;
 
@@ -73,7 +87,10 @@
     return;
   }
 
-  if (!navigation_handle->GetAuthChallengeInfo()) {
+  if (!navigation_handle->GetAuthChallengeInfo() ||
+      !ChallengeMatchesNavigation(
+          navigation_handle->GetAuthChallengeInfo().value(),
+          navigation_handle->GetURL())) {
     return;
   }
 
@@ -155,10 +172,14 @@
     return content::NavigationThrottle::PROCEED;
   }
 
-  // Do not cancel the navigation if there is no auth challenge. We only want to
-  // cancel the navigation below to show a blank page if there is an auth
-  // challenge for which to show a login prompt.
-  if (!navigation_handle->GetAuthChallengeInfo()) {
+  // Do not cancel the navigation if there is no auth challenge, or if the
+  // challenge does not match the navigation URL. We only want to cancel the
+  // navigation below to show a blank page if there is an auth challenge for
+  // which to show a login prompt.
+  if (!navigation_handle->GetAuthChallengeInfo() ||
+      !ChallengeMatchesNavigation(
+          navigation_handle->GetAuthChallengeInfo().value(),
+          navigation_handle->GetURL())) {
     return content::NavigationThrottle::PROCEED;
   }
 
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
index 03432ed..b264630 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
@@ -8,6 +8,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <optional>
 #include <utility>
 
 #include "base/functional/bind.h"
@@ -16,6 +17,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
+#include "base/notreached.h"
 #include "base/rand_util.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
@@ -151,17 +153,67 @@
 }
 
 #if BUILDFLAG(ENABLE_EXTENSIONS) && (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC))
+// Guards a withheld navigation against a continuation that is never invoked.
+//
+// OpenMatch() abandons a pending navigation while the extension-DSE
+// confirmation is resolved, and relies on a callback to resume it. That
+// callback crosses an asynchronous boundary that includes a network image
+// fetch gated by a BarrierClosure with no timeout, so "the callback is simply
+// never run" is a reachable state, not a hypothetical one. If it happens, the
+// user clicks a suggestion and nothing occurs, forever, with no feedback.
+//
+// Holding the continuation here makes the safe outcome the default: if this
+// object is destroyed without Run() having been called, the navigation is
+// resumed as if no dialog had been shown. Only an explicit user decision can
+// suppress it. See https://crbug.com/540532980.
+class PendingNavigationGuard {
+ public:
+  using Callback =
+      base::OnceCallback<void(OmniboxClient::ExtensionControlledDialogResult)>;
+
+  explicit PendingNavigationGuard(Callback callback)
+      : callback_(std::move(callback)) {}
+
+  PendingNavigationGuard(const PendingNavigationGuard&) = delete;
+  PendingNavigationGuard& operator=(const PendingNavigationGuard&) = delete;
+
+  ~PendingNavigationGuard() {
+    if (callback_) {
+      std::move(callback_).Run(
+          OmniboxClient::ExtensionControlledDialogResult::kNoDialogShown);
+    }
+  }
+
+  void Run(OmniboxClient::ExtensionControlledDialogResult result) {
+    if (callback_) {
+      std::move(callback_).Run(result);
+    }
+  }
+
+ private:
+  Callback callback_;
+};
+
 ExtensionControlledDialogResult SettingDialogResultToExtensionDialogResult(
-    SettingsOverriddenDialogController::DialogResult result) {
+    std::optional<SettingsOverriddenDialogController::DialogResult> result) {
   using DialogResult = SettingsOverriddenDialogController::DialogResult;
-  switch (result) {
+  // No dialog was shown, so the user was never asked. Resume the navigation.
+  if (!result.has_value()) {
+    return ExtensionControlledDialogResult::kNoDialogShown;
+  }
+  // Enumerate every case explicitly: a `default:` here is what allowed
+  // "no dialog was shown" to masquerade as "the user cancelled", silently
+  // dropping the navigation (http://crbug.com/540532980).
+  switch (*result) {
     case DialogResult::kKeepNewSettings:
       return ExtensionControlledDialogResult::kAccept;
     case DialogResult::kChangeSettingsBack:
       return ExtensionControlledDialogResult::kReject;
-    default:
+    case DialogResult::kDialogDismissed:
+    case DialogResult::kDialogClosedWithoutUserAction:
       return ExtensionControlledDialogResult::kCancel;
   }
+  NOTREACHED();
 }
 #endif
 
@@ -278,16 +330,21 @@
     return false;
   }
 
+  auto guarded_callback = base::BindOnce(
+      &PendingNavigationGuard::Run,
+      std::make_unique<PendingNavigationGuard>(std::move(callback)));
+
   controller->ShowConfirmationDialog(
       *web_contents,
       base::BindOnce(
           [](base::OnceCallback<void(ExtensionControlledDialogResult)>
                  client_callback,
-             SettingsOverriddenDialogController::DialogResult result) {
+             std::optional<SettingsOverriddenDialogController::DialogResult>
+                 result) {
             std::move(client_callback)
                 .Run(SettingDialogResultToExtensionDialogResult(result));
           },
-          std::move(callback)));
+          std::move(guarded_callback)));
 
   return true;
 #else
diff --git a/chrome/browser/ui/omnibox/omnibox_edit_model.cc b/chrome/browser/ui/omnibox/omnibox_edit_model.cc
index 0e5abed..905f614 100644
--- a/chrome/browser/ui/omnibox/omnibox_edit_model.cc
+++ b/chrome/browser/ui/omnibox/omnibox_edit_model.cc
@@ -2634,7 +2634,13 @@
   //
   // When `proceed` is kCancel, the dialog was closed without making a choice,
   // and we don't do a search.
-  if (proceed == OmniboxClient::ExtensionControlledDialogResult::kAccept) {
+  //
+  // kNoDialogShown means the confirmation turned out to be unnecessary and the
+  // user was never asked anything. The navigation we withheld must proceed
+  // exactly as originally requested.
+  if (proceed == OmniboxClient::ExtensionControlledDialogResult::kAccept ||
+      proceed ==
+          OmniboxClient::ExtensionControlledDialogResult::kNoDialogShown) {
     OpenMatch(selection, match, disposition, alternate_nav_url, pasted_text,
               match_selection_timestamp);
   } else if (proceed ==
diff --git a/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_controller.cc b/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_controller.cc
index fc7ca7a1..8c486e86 100644
--- a/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_controller.cc
+++ b/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_controller.cc
@@ -163,7 +163,6 @@
 
 void OmniboxEverywhereController::OnKeyPressed(
     const ui::Accelerator& accelerator) {
-  ui_manager_->SetIsNavigating(false);
   InvokeForActiveBrowserProfile(InvocationSource::kGlobalHotkey);
 }
 
diff --git a/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_interactive_uitest.cc b/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_interactive_uitest.cc
index 90749ce..eacc640 100644
--- a/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_interactive_uitest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_interactive_uitest.cc
@@ -120,6 +120,20 @@
         WaitForStateChange(web_contents_id, search_input_loaded));
   }
 
+  // Simulates dragging the mouse from `start_point` to `end_point` within
+  // `view`. Crosses the drag threshold to ensure window drag handlers are
+  // triggered.
+  auto DragMouseInView(ElementSpecifier view,
+                       gfx::Point start_point,
+                       gfx::Point end_point) {
+    const gfx::Point threshold_point = start_point + gfx::Vector2d(15, 15);
+    return Steps(MoveMouseInView(view, start_point),
+                 ClickMouse(ui_controls::LEFT, /*release=*/false),
+                 MoveMouseInView(view, threshold_point),
+                 MoveMouseInView(view, end_point),
+                 ReleaseMouse(ui_controls::LEFT));
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
 };
@@ -145,11 +159,13 @@
   EXPECT_FALSE(delegate->CanMinimize());
   EXPECT_FALSE(delegate->CanResize());
 
-  // Close the widget and wait for destruction.
-  views::test::WidgetDestroyedWaiter waiter(widget);
+  // Close (hide) the widget.
   ui_manager.Close();
-  waiter.Wait();
+  EXPECT_FALSE(widget->IsVisible());
+  EXPECT_TRUE(ui_manager.widget());
 
+  // Shutdown destroys the widget.
+  ui_manager.Shutdown();
   EXPECT_FALSE(ui_manager.widget());
 }
 
@@ -165,12 +181,18 @@
   views::test::WaitForWidgetActive(widget, true);
   EXPECT_TRUE(widget->IsActive());
 
-  views::test::WidgetDestroyedWaiter waiter(widget);
   ui_manager.Close();
-  waiter.Wait();
+  EXPECT_FALSE(widget->IsVisible());
+  ui_manager.Shutdown();
 }
 
-IN_PROC_BROWSER_TEST_F(OmniboxEverywhereBrowserTest, ShowAndDismissViaHotkey) {
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_ShowAndDismissViaHotkey DISABLED_ShowAndDismissViaHotkey
+#else
+#define MAYBE_ShowAndDismissViaHotkey ShowAndDismissViaHotkey
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxEverywhereBrowserTest,
+                       MAYBE_ShowAndDismissViaHotkey) {
   OmniboxEverywhereController* controller =
       g_browser_process->GetFeatures()->omnibox_everywhere_controller();
   ASSERT_TRUE(controller);
@@ -252,30 +274,22 @@
   const char kContentsView[] = "OmniboxContentsView";
   DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOmniboxWebContentsId);
 
-  const gfx::Point initial_point(10, 10);
-  const gfx::Point threshold_point(25, 25);
-  ASSERT_TRUE(
-      views::View::ExceededDragThreshold(threshold_point - initial_point));
-
-  RunTestSequence(InvokeViaHotkey(), CheckWidgetVisible(true),
-                  WaitForOmniboxWebUIReady(kOmniboxWebContentsId), Do([&]() {
-                    initial_origin = controller->ui_manager()
-                                         ->widget()
-                                         ->GetWindowBoundsInScreen()
-                                         .origin();
-                  }),
-                  NameOmniboxContentsView(kContentsView),
-                  // Drag background area (draggable region at 10, 10).
-                  MoveMouseInView(kContentsView, initial_point),
-                  ClickMouse(ui_controls::LEFT, /*release=*/false),
-                  MoveMouseInView(kContentsView, threshold_point),
-                  MoveMouseInView(kContentsView, gfx::Point(110, 110)),
-                  ReleaseMouse(ui_controls::LEFT), Check([&]() {
-                    views::Widget* widget = controller->ui_manager()->widget();
-                    return widget->GetWindowBoundsInScreen().origin() !=
-                           initial_origin;
-                  }),
-                  InvokeViaHotkey(), CheckWidgetVisible(false));
+  RunTestSequence(
+      InvokeViaHotkey(), CheckWidgetVisible(true),
+      WaitForOmniboxWebUIReady(kOmniboxWebContentsId), Do([&]() {
+        initial_origin = controller->ui_manager()
+                             ->widget()
+                             ->GetWindowBoundsInScreen()
+                             .origin();
+      }),
+      NameOmniboxContentsView(kContentsView),
+      // Drag background area (draggable region at 10, 10).
+      DragMouseInView(kContentsView, gfx::Point(10, 10), gfx::Point(110, 110)),
+      Check([&]() {
+        views::Widget* widget = controller->ui_manager()->widget();
+        return widget->GetWindowBoundsInScreen().origin() != initial_origin;
+      }),
+      InvokeViaHotkey(), CheckWidgetVisible(false));
 }
 
 IN_PROC_BROWSER_TEST_F(OmniboxEverywhereBrowserTest, VoicePermissionState) {
@@ -294,9 +308,114 @@
 #endif
   EXPECT_TRUE(permissions::PermissionRequestManager::FromWebContents(contents));
 
-  views::test::WidgetDestroyedWaiter waiter(ui_manager.widget());
   ui_manager.Close();
-  waiter.Wait();
+  EXPECT_FALSE(ui_manager.widget()->IsVisible());
+  ui_manager.Shutdown();
+  EXPECT_FALSE(ui_manager.widget());
+}
+
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_RetainPositionOnReinvoke DISABLED_RetainPositionOnReinvoke
+#else
+#define MAYBE_RetainPositionOnReinvoke RetainPositionOnReinvoke
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxEverywhereBrowserTest,
+                       MAYBE_RetainPositionOnReinvoke) {
+#if BUILDFLAG(IS_OZONE)
+  if (ui::OzonePlatform::RunningOnWaylandForTest()) {
+    GTEST_SKIP()
+        << "Window drag is not supported under Wayland test compositor.";
+  }
+#endif
+
+  OmniboxEverywhereController* controller =
+      g_browser_process->GetFeatures()->omnibox_everywhere_controller();
+  ASSERT_TRUE(controller);
+
+  gfx::Point dragged_origin;
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOmniboxWebContentsId);
+
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+  // TODO(crbug.com/40249472): Modal drag loops in tests aren't supported on
+  // MacOS and Windows. Manually set widget bounds to test position retention
+  // across re-invocations.
+  auto reposition_step = Steps(Do([&]() {
+    views::Widget* widget = controller->ui_manager()->widget();
+    gfx::Rect target_bounds = widget->GetWindowBoundsInScreen();
+    target_bounds.Offset(100, 100);
+    widget->SetBounds(target_bounds);
+    dragged_origin = target_bounds.origin();
+  }));
+#else
+  const char kContentsView[] = "OmniboxContentsView";
+  auto reposition_step = Steps(
+      NameOmniboxContentsView(kContentsView),
+      DragMouseInView(kContentsView, gfx::Point(10, 10), gfx::Point(110, 110)),
+      Do([&]() {
+        dragged_origin = controller->ui_manager()
+                             ->widget()
+                             ->GetWindowBoundsInScreen()
+                             .origin();
+      }));
+#endif
+
+  RunTestSequence(InvokeViaHotkey(), CheckWidgetVisible(true),
+                  WaitForOmniboxWebUIReady(kOmniboxWebContentsId),
+                  std::move(reposition_step),
+                  // Dismiss via hotkey.
+                  InvokeViaHotkey(), CheckWidgetVisible(false),
+                  // Re-invoke via hotkey.
+                  InvokeViaHotkey(), CheckWidgetVisible(true), Check([&]() {
+                    return controller->ui_manager()
+                               ->widget()
+                               ->GetWindowBoundsInScreen()
+                               .origin() == dragged_origin;
+                  }),
+                  InvokeViaHotkey(), CheckWidgetVisible(false));
+}
+
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_RetainInputTextOnReinvoke DISABLED_RetainInputTextOnReinvoke
+#else
+#define MAYBE_RetainInputTextOnReinvoke RetainInputTextOnReinvoke
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxEverywhereBrowserTest,
+                       MAYBE_RetainInputTextOnReinvoke) {
+  OmniboxEverywhereController* controller =
+      g_browser_process->GetFeatures()->omnibox_everywhere_controller();
+  ASSERT_TRUE(controller);
+
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOmniboxWebContentsId);
+
+  const DeepQuery kSearchInputQuery{
+      "omnibox-everywhere-app",
+      "omnibox-everywhere-omnibox",
+      "cr-searchbox-input",
+  };
+  const DeepQuery kInputElementQuery{
+      "omnibox-everywhere-app",
+      "omnibox-everywhere-omnibox",
+      "cr-searchbox-input",
+      "input",
+  };
+
+  RunTestSequence(
+      InvokeViaHotkey(), CheckWidgetVisible(true),
+      WaitForOmniboxWebUIReady(kOmniboxWebContentsId),
+      ExecuteJsAt(
+          kOmniboxWebContentsId, kSearchInputQuery,
+          "el => { "
+          "el.setInputText('hello wor'); "
+          "el.dispatchEvent(new CustomEvent('searchbox-input-text-updated', "
+          "{ detail: { value: 'hello wor', isComposing: false }, bubbles: "
+          "true, composed: true })); }"),
+      // Dismiss via hotkey.
+      InvokeViaHotkey(), CheckWidgetVisible(false),
+      // Re-invoke via hotkey.
+      InvokeViaHotkey(), CheckWidgetVisible(true),
+      CheckJsResultAt(kOmniboxWebContentsId, kInputElementQuery,
+                      "el => el.value", "hello wor"),
+      InvokeViaHotkey(), CheckWidgetVisible(false));
 }
 
 }  // namespace omnibox_everywhere
diff --git a/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_ui_manager.cc b/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_ui_manager.cc
index d925df5..47c6c1a2 100644
--- a/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_ui_manager.cc
+++ b/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_ui_manager.cc
@@ -158,14 +158,13 @@
 
 void OmniboxEverywhereUIManager::ShowForProfile(Profile* profile,
                                                 gfx::NativeWindow context) {
-  if (widget_ && profile_ == profile && widget_->IsVisible()) {
+  if (widget_ && profile_ == profile) {
     ActivateAndFocus();
     return;
   }
 
   if (widget_) {
-    // If a different profile (or a hidden/closing widget) is present, clean up
-    // first.
+    // If a different profile is present, clean up first.
     CleanUpWidget();
   }
 
@@ -175,7 +174,6 @@
     browser_collection_observation_.Reset();
   }
   profile_ = profile;
-  is_navigating_ = false;
 
   EnsureContentsWrapperInitialized(profile_);
   CreateAndInitWidget(context);
@@ -255,14 +253,13 @@
   display::Display target_display =
       display::Screen::Get()->GetDisplayNearestPoint(
           display::Screen::Get()->GetCursorScreenPoint());
-  gfx::Rect screen_bounds = target_display.bounds();
+  gfx::Rect work_area = target_display.work_area();
   constexpr gfx::Size kDefaultPopupSize(864, 632);
-  params.bounds =
-      gfx::Rect(screen_bounds.x() +
-                    (screen_bounds.width() - kDefaultPopupSize.width()) / 2,
-                screen_bounds.y() +
-                    (screen_bounds.height() - kDefaultPopupSize.height()) / 2,
-                kDefaultPopupSize.width(), kDefaultPopupSize.height());
+  params.bounds = gfx::Rect(
+      work_area.x() + (work_area.width() - kDefaultPopupSize.width()) / 2,
+      work_area.y() + (work_area.height() - kDefaultPopupSize.height()) / 2,
+      kDefaultPopupSize.width(), kDefaultPopupSize.height());
+
   auto web_view = std::make_unique<views::WebView>(profile_);
   web_view->SetProperty(views::kElementIdentifierKey,
                         kOmniboxEverywhereElementId);
@@ -315,7 +312,15 @@
 
 void OmniboxEverywhereUIManager::Close() {
   if (widget_) {
-    widget_->Close();
+    if (is_file_chooser_open_ || is_drive_picker_open_) {
+      CleanUpWidget();
+      return;
+    }
+    if (is_context_menu_open_ && context_menu_runner_) {
+      context_menu_runner_->Cancel();
+      is_context_menu_open_ = false;
+    }
+    widget_->Hide();
   }
 }
 
@@ -356,7 +361,6 @@
   is_file_chooser_open_ = false;
   is_drive_picker_open_ = false;
   is_context_menu_open_ = false;
-  is_navigating_ = false;
   draggable_region_.reset();
   browser_collection_observation_.Reset();
 }
diff --git a/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_ui_manager.h b/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_ui_manager.h
index 75304774..dc97a03 100644
--- a/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_ui_manager.h
+++ b/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_ui_manager.h
@@ -117,9 +117,6 @@
   void OnBrowserActivated(BrowserWindowInterface* browser) override;
   void OnBrowserDeactivated(BrowserWindowInterface* browser) override {}
 
-  void SetIsNavigating(bool is_navigating) { is_navigating_ = is_navigating; }
-  bool IsNavigating() const { return is_navigating_; }
-
   Profile* profile() { return profile_; }
   const Profile* profile() const { return profile_; }
   views::Widget* widget() { return widget_.get(); }
@@ -185,7 +182,6 @@
   bool is_file_chooser_open_ = false;
   bool is_drive_picker_open_ = false;
   bool is_context_menu_open_ = false;
-  bool is_navigating_ = false;
   std::optional<SkRegion> draggable_region_;
 
   std::unique_ptr<views::UnhandledKeyboardEventHandler>
diff --git a/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_ui_manager_unittest.cc b/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_ui_manager_unittest.cc
index dfdefae6..c7bbf3a2 100644
--- a/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_ui_manager_unittest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_everywhere/omnibox_everywhere_ui_manager_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
+#include "base/test/run_until.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h"
@@ -111,33 +112,36 @@
   ASSERT_TRUE(widget);
   EXPECT_TRUE(widget->IsVisible());
 
-  // Closing the UI manager should trigger widget closure.
-  views::test::WidgetDestroyedWaiter waiter(widget);
+  // Closing the UI manager should trigger hiding the widget.
   ui_manager->Close();
-  waiter.Wait();
+  EXPECT_FALSE(widget->IsVisible());
+  EXPECT_TRUE(ui_manager->widget());
 
+  // Shutdown should destroy the widget.
+  ui_manager->Shutdown();
   EXPECT_FALSE(ui_manager->widget());
 }
 
-TEST_F(OmniboxEverywhereUIManagerTest, ShowWhileWidgetIsClosing) {
+TEST_F(OmniboxEverywhereUIManagerTest, ShowWhileWidgetIsHidden) {
   auto ui_manager = CreateUIManager();
 
   ui_manager->ShowForProfile(&profile_, GetContext());
   views::Widget* first_widget = ui_manager->widget();
   ASSERT_TRUE(first_widget);
 
-  // Close the widget.
+  // Close (hide) the widget.
   ui_manager->Close();
+  EXPECT_FALSE(first_widget->IsVisible());
 
-  // Showing it again immediately should successfully clean up the closing
-  // widget and create a new visible widget.
+  // Showing it again immediately should reactivate the existing hidden widget.
   ui_manager->ShowForProfile(&profile_, GetContext());
   views::Widget* second_widget = ui_manager->widget();
   ASSERT_TRUE(second_widget);
   EXPECT_TRUE(second_widget->IsVisible());
+  EXPECT_EQ(first_widget, second_widget);
 
   // Clean up.
-  ui_manager->Close();
+  ui_manager->Shutdown();
   EXPECT_FALSE(ui_manager->widget());
 }
 
@@ -160,11 +164,10 @@
   ASSERT_TRUE(widget);
   EXPECT_TRUE(widget->IsVisible());
 
-  // Simulating deactivation (active = false) should close the widget.
-  views::test::WidgetDestroyedWaiter waiter(widget);
+  // Simulating deactivation (active = false) should hide the widget.
   ui_manager->OnWidgetActivationChanged(widget, /*active=*/false);
-  waiter.Wait();
-  EXPECT_FALSE(ui_manager->widget());
+  EXPECT_TRUE(base::test::RunUntil([&]() { return !widget->IsVisible(); }));
+  EXPECT_TRUE(ui_manager->widget());
 }
 
 TEST_F(OmniboxEverywhereUIManagerTest, DismissBypassedDuringFileChooser) {
@@ -185,13 +188,11 @@
   EXPECT_TRUE(ui_manager->widget());
   EXPECT_TRUE(widget->IsVisible());
 
-  // Clean up: closing file chooser and triggering deactivation should close the
+  // Clean up: closing file chooser and triggering deactivation should hide the
   // widget.
-  views::test::WidgetDestroyedWaiter waiter2(widget);
   ui_manager->OnFileChooserClosed();
   ui_manager->OnWidgetActivationChanged(widget, /*active=*/false);
-  waiter2.Wait();
-  EXPECT_FALSE(ui_manager->widget());
+  EXPECT_TRUE(base::test::RunUntil([&]() { return !widget->IsVisible(); }));
 }
 
 TEST_F(OmniboxEverywhereUIManagerTest, MultiProfileSwapping) {
@@ -210,7 +211,7 @@
   EXPECT_TRUE(ui_manager->widget());
 
   // Clean up.
-  ui_manager->Close();
+  ui_manager->Shutdown();
   EXPECT_FALSE(ui_manager->widget());
 }
 
@@ -247,21 +248,6 @@
   EXPECT_FALSE(ui_manager->is_file_chooser_open_for_testing());
 }
 
-TEST_F(OmniboxEverywhereUIManagerTest, NavigationAndActivationStateTracking) {
-  auto ui_manager = CreateUIManager();
-
-  EXPECT_FALSE(ui_manager->IsNavigating());
-
-  ui_manager->ShowForProfile(&profile_, GetContext());
-  EXPECT_FALSE(ui_manager->IsNavigating());
-
-  ui_manager->SetIsNavigating(true);
-  EXPECT_TRUE(ui_manager->IsNavigating());
-
-  ui_manager->Close();
-  EXPECT_FALSE(ui_manager->IsNavigating());
-}
-
 #if BUILDFLAG(IS_CHROMEOS)
 #define MAYBE_ShowPositionsOnTargetDisplay DISABLED_ShowPositionsOnTargetDisplay
 #else
@@ -306,6 +292,49 @@
   display::Screen::SetScreenInstance(old_screen);
 }
 
+#if BUILDFLAG(IS_CHROMEOS)
+#define MAYBE_PreservePositionAcrossDisplaysOnReinvoke \
+  DISABLED_PreservePositionAcrossDisplaysOnReinvoke
+#else
+#define MAYBE_PreservePositionAcrossDisplaysOnReinvoke \
+  PreservePositionAcrossDisplaysOnReinvoke
+#endif
+TEST_F(OmniboxEverywhereUIManagerTest,
+       MAYBE_PreservePositionAcrossDisplaysOnReinvoke) {
+  display::test::TestScreen test_screen(/*create_display=*/false,
+                                        /*register_screen=*/false);
+  display::Screen* old_screen = display::Screen::SetScreenInstance(nullptr);
+  display::Screen::SetScreenInstance(&test_screen);
+
+  display::Display display1(1, gfx::Rect(0, 0, 800, 600));
+  display::Display display2(2, gfx::Rect(800, 0, 1024, 768));
+  test_screen.display_list().AddDisplay(display1,
+                                        display::DisplayList::Type::PRIMARY);
+  test_screen.display_list().AddDisplay(
+      display2, display::DisplayList::Type::NOT_PRIMARY);
+
+  // Set the fake cursor on the second display initially.
+  test_screen.set_cursor_screen_point(gfx::Point(1200, 300));
+
+  auto ui_manager = CreateUIManager();
+  ui_manager->ShowForProfile(&profile_, GetContext());
+  views::Widget* widget = ui_manager->widget();
+  ASSERT_TRUE(widget);
+
+  gfx::Rect initial_bounds = widget->GetWindowBoundsInScreen();
+
+  // Move the cursor to the first display and re-invoke ShowForProfile.
+  test_screen.set_cursor_screen_point(gfx::Point(100, 100));
+  ui_manager->ShowForProfile(&profile_, GetContext());
+
+  // The widget bounds should remain unchanged on the secondary display.
+  EXPECT_EQ(widget->GetWindowBoundsInScreen(), initial_bounds);
+
+  ui_manager->Close();
+  display::Screen::SetScreenInstance(nullptr);
+  display::Screen::SetScreenInstance(old_screen);
+}
+
 TEST_F(OmniboxEverywhereUIManagerTest, DrivePickerStateTracking) {
   auto ui_manager = CreateUIManager();
   EXPECT_FALSE(ui_manager->is_drive_picker_open_for_testing());
@@ -335,12 +364,26 @@
   EXPECT_TRUE(ui_manager->widget());
   EXPECT_TRUE(widget->IsVisible());
 
-  // Clean up: closing drive picker and triggering deactivation should close the
+  // Clean up: closing drive picker and triggering deactivation should hide the
   // widget.
-  views::test::WidgetDestroyedWaiter waiter(widget);
   ui_manager->OnDrivePickerClosed();
   ui_manager->OnWidgetActivationChanged(widget, /*active=*/false);
-  waiter.Wait();
+  EXPECT_TRUE(base::test::RunUntil([&]() { return !widget->IsVisible(); }));
+}
+
+TEST_F(OmniboxEverywhereUIManagerTest, CloseDestroysWidgetWhenChooserOpen) {
+  auto ui_manager = CreateUIManager();
+
+  ui_manager->ShowForProfile(&profile_, GetContext());
+  views::Widget* widget = ui_manager->widget();
+  ASSERT_TRUE(widget);
+
+  // Open file chooser.
+  ui_manager->OnFileChooserOpened();
+
+  // Close() while file chooser is open should destroy widget to prevent
+  // orphaned modals.
+  ui_manager->Close();
   EXPECT_FALSE(ui_manager->widget());
 }
 
@@ -433,11 +476,48 @@
 
   // Clean up: closing context menu and triggering deactivation should close the
   // widget.
-  views::test::WidgetDestroyedWaiter waiter(widget);
   ui_manager->OnContextMenuClosedForTesting();
   ui_manager->OnWidgetActivationChanged(widget, /*active=*/false);
-  waiter.Wait();
-  EXPECT_FALSE(ui_manager->widget());
+  EXPECT_TRUE(base::test::RunUntil([&]() { return !widget->IsVisible(); }));
+  EXPECT_TRUE(ui_manager->widget());
+}
+
+TEST_F(OmniboxEverywhereUIManagerTest, CloseCancelsOpenContextMenu) {
+  auto ui_manager = CreateUIManager();
+
+  ui_manager->ShowForProfile(&profile_, GetContext());
+  ASSERT_TRUE(ui_manager->widget());
+
+  auto* rfh = ui_manager->contents_wrapper_for_testing()
+                  ->web_contents()
+                  ->GetPrimaryMainFrame();
+  content::ContextMenuParams params;
+  params.is_editable = true;
+
+  bool menu_runner_created = false;
+  ui_manager->SetMenuRunnerFactoryForTesting(base::BindRepeating(
+      [](bool* created, ui::MenuModel* model,
+         base::RepeatingClosure on_closed) {
+        *created = true;
+        auto runner = std::make_unique<views::MenuRunner>(
+            model,
+            views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU,
+            on_closed);
+        views::test::MenuRunnerTestAPI(runner.get())
+            .SetMenuRunnerHandler(std::make_unique<TestMenuRunnerHandler>());
+        return runner;
+      },
+      &menu_runner_created));
+
+  ui_manager->HandleContextMenu(*rfh, params);
+  EXPECT_TRUE(menu_runner_created);
+  EXPECT_TRUE(ui_manager->is_context_menu_open_for_testing());
+
+  // Calling Close() while context menu is open should cancel the runner and
+  // reset state.
+  ui_manager->Close();
+  EXPECT_FALSE(ui_manager->is_context_menu_open_for_testing());
+  EXPECT_FALSE(ui_manager->widget()->IsVisible());
 }
 
 TEST_F(OmniboxEverywhereUIManagerTest, ContextMenuModelEditableElement) {
diff --git a/chrome/browser/ui/omnibox/omnibox_everywhere_service.cc b/chrome/browser/ui/omnibox/omnibox_everywhere_service.cc
index 9791983..e3f2f5d64 100644
--- a/chrome/browser/ui/omnibox/omnibox_everywhere_service.cc
+++ b/chrome/browser/ui/omnibox/omnibox_everywhere_service.cc
@@ -58,12 +58,6 @@
   }
 }
 
-void OmniboxEverywhereService::SetIsNavigating(bool is_navigating) {
-  if (controller() && controller()->ui_manager()) {
-    controller()->ui_manager()->SetIsNavigating(is_navigating);
-  }
-}
-
 void OmniboxEverywhereService::OnDrivePickerOpened() {
   if (controller() && controller()->ui_manager()) {
     controller()->ui_manager()->OnDrivePickerOpened();
@@ -79,7 +73,6 @@
 void OmniboxEverywhereService::OpenUrl(const GURL& url,
                                        WindowOpenDisposition disposition,
                                        ui::PageTransition transition) {
-  SetIsNavigating(true);
   HidePopup();
 
   auto* browser_collection = ProfileBrowserCollection::GetForProfile(profile_);
diff --git a/chrome/browser/ui/omnibox/omnibox_everywhere_service.h b/chrome/browser/ui/omnibox/omnibox_everywhere_service.h
index 63ea9cb..0c28b9b0 100644
--- a/chrome/browser/ui/omnibox/omnibox_everywhere_service.h
+++ b/chrome/browser/ui/omnibox/omnibox_everywhere_service.h
@@ -35,8 +35,6 @@
   // KeyedService:
   void Shutdown() override;
 
-  void SetIsNavigating(bool is_navigating);
-
   void OnDrivePickerOpened();
   void OnDrivePickerClosed();
 
diff --git a/chrome/browser/ui/omnibox/omnibox_popup_view.cc b/chrome/browser/ui/omnibox/omnibox_popup_view.cc
index d106bb4..fa03ae9 100644
--- a/chrome/browser/ui/omnibox/omnibox_popup_view.cc
+++ b/chrome/browser/ui/omnibox/omnibox_popup_view.cc
@@ -34,3 +34,9 @@
 OmniboxPopupViewBrowserView* OmniboxPopupView::AsOmniboxPopupViewBrowserView() {
   return nullptr;
 }
+
+bool OmniboxPopupView::IsReverting() const {
+  return false;
+}
+
+void OmniboxPopupView::SetIsReverting(bool reverting) {}
diff --git a/chrome/browser/ui/omnibox/omnibox_popup_view.h b/chrome/browser/ui/omnibox/omnibox_popup_view.h
index 95c7dc65..d1d16b18 100644
--- a/chrome/browser/ui/omnibox/omnibox_popup_view.h
+++ b/chrome/browser/ui/omnibox/omnibox_popup_view.h
@@ -87,12 +87,19 @@
   // Called when the omnibox gains focus.
   virtual void OnFocus(bool query_zps) {}
 
+  // Called when the omnibox loses focus.
+  virtual void OnBlur() {}
+
   // Called when the WebUI omnibox reports a manual blur.
   virtual void OnManualBlur() {}
 
   // Returns true if the popup controls its own selection state.
   virtual bool IsSelectionPopupControlled() const = 0;
 
+  // Returns true if the popup is currently processing a revert/Escape action.
+  virtual bool IsReverting() const;
+  virtual void SetIsReverting(bool reverting);
+
   // Safe downcasting to the BrowserView-embedded implementation.
   virtual OmniboxPopupViewBrowserView* AsOmniboxPopupViewBrowserView();
 
diff --git a/chrome/browser/ui/search_engines/default_search_extension_controlled_controller.cc b/chrome/browser/ui/search_engines/default_search_extension_controlled_controller.cc
index c31a10d..c766db3 100644
--- a/chrome/browser/ui/search_engines/default_search_extension_controlled_controller.cc
+++ b/chrome/browser/ui/search_engines/default_search_extension_controlled_controller.cc
@@ -147,10 +147,18 @@
     return false;
   }
 
+  // 7) Don't show if the extension set the same search engine the user was
+  // already using. Nothing was actually overridden, so there is nothing to
+  // confirm. See https://crbug.com/540532980.
+  if (settings_overridden_params::ExtensionSearchOverrideMatchesExistingEngine(
+          base::to_address(profile_))) {
+    return false;
+  }
+
   // TODO(crbug.com/463712739): Remove this check to show the Dialog for all
   // extensions.
   //
-  // 7) Don't show for "simple override" extensions.
+  // 8) Don't show for "simple override" extensions.
   if (simple_overrides::IsSimpleOverrideExtension(*extension)) {
     return ExtensionSettingsOverriddenDialog::
         ShouldShowForSimpleOverrideExtension(*profile_, *extension);
@@ -163,21 +171,24 @@
 void DefaultSearchExtensionControlledController::ShowConfirmationDialog(
     content::WebContents& web_contents,
     ConfirmationCallback callback) {
-  confirmation_callback_ = std::move(callback);
   SetDialogCurrentlyShowing(weak_factory_.GetWeakPtr());
 
   settings_overridden_params::GetSearchOverriddenParamsThenRun(
       &web_contents,
       base::BindOnce(
           &DefaultSearchExtensionControlledController::OnParamsLoaded,
-          weak_factory_.GetWeakPtr()));
+          weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void DefaultSearchExtensionControlledController::OnParamsLoaded(
+    ConfirmationCallback callback,
     std::unique_ptr<ExtensionSettingsOverriddenDialog::Params> params) {
+  confirmation_callback_ = std::move(callback);
   if (!params) {
-    DialogResolved(SettingsOverriddenDialogController::DialogResult::
-                       kDialogClosedWithoutUserAction);
+    // Confirmation turned out to be unnecessary, or the extension state
+    // changed while the parameters were loading. No dialog was shown. See
+    // https://crbug.com/540532980.
+    DialogResolved(std::nullopt);
     return;
   }
 
@@ -185,6 +196,7 @@
       std::move(*params), *profile_);
   CHECK(dialog->ShouldShow());
 
+  // A dialog is being shown, so every outcome from here on is a user decision.
   dialog->SetDialogResultCallback(base::BindOnce(
       &DefaultSearchExtensionControlledController::DialogResolved,
       weak_factory_.GetWeakPtr()));
@@ -198,7 +210,8 @@
 }
 
 void DefaultSearchExtensionControlledController::DialogResolved(
-    SettingsOverriddenDialogController::DialogResult dialog_result) {
+    std::optional<SettingsOverriddenDialogController::DialogResult>
+        dialog_result) {
   if (GetDialogCurrentlyShowing().get() == this) {
     SetDialogCurrentlyShowing(nullptr);
   }
diff --git a/chrome/browser/ui/search_engines/default_search_extension_controlled_controller.h b/chrome/browser/ui/search_engines/default_search_extension_controlled_controller.h
index 2bc804e..35618e1 100644
--- a/chrome/browser/ui/search_engines/default_search_extension_controlled_controller.h
+++ b/chrome/browser/ui/search_engines/default_search_extension_controlled_controller.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_SEARCH_ENGINES_DEFAULT_SEARCH_EXTENSION_CONTROLLED_CONTROLLER_H_
 #define CHROME_BROWSER_UI_SEARCH_ENGINES_DEFAULT_SEARCH_EXTENSION_CONTROLLED_CONTROLLER_H_
 
+#include <optional>
+
 #include "base/functional/callback.h"
 #include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
@@ -29,8 +31,13 @@
   static DefaultSearchExtensionControlledController* From(
       BrowserWindowInterface* browser);
 
+  // Run when the confirmation request completes. `std::nullopt` means no
+  // dialog was ever shown. The confirmation turned out to be unnecessary, or
+  // the extension state changed before the dialog could be built. Callers that
+  // deferred work pending confirmation must resume it in that case; a dialog
+  // the user never saw must never be treated as a decision the user made.
   using ConfirmationCallback = base::OnceCallback<void(
-      SettingsOverriddenDialogController::DialogResult result)>;
+      std::optional<SettingsOverriddenDialogController::DialogResult> result)>;
 
   explicit DefaultSearchExtensionControlledController(
       BrowserWindowInterface& browser_window_interface,
@@ -46,16 +53,18 @@
   // already acknowledged).
   bool ShouldRequestConfirmationForExtensionDse(const GURL& url) const;
 
-  // Shows the confirmation dialog. The callback is run when the user makes a
-  // decision.
+  // Shows the confirmation dialog. The callback is run when the request
+  // completes -- see ConfirmationCallback for the no-dialog-shown case.
   void ShowConfirmationDialog(content::WebContents& web_contents,
                               ConfirmationCallback callback);
 
   void DialogResolved(
-      SettingsOverriddenDialogController::DialogResult dialog_result);
+      std::optional<SettingsOverriddenDialogController::DialogResult>
+          dialog_result);
 
  private:
   void OnParamsLoaded(
+      ConfirmationCallback callback,
       std::unique_ptr<ExtensionSettingsOverriddenDialog::Params> params);
 
   // Tracks the active controller instance currently showing (or preparing to
diff --git a/chrome/browser/ui/search_engines/default_search_extension_controlled_controller_unittest.cc b/chrome/browser/ui/search_engines/default_search_extension_controlled_controller_unittest.cc
index c1a848287..4f2d111 100644
--- a/chrome/browser/ui/search_engines/default_search_extension_controlled_controller_unittest.cc
+++ b/chrome/browser/ui/search_engines/default_search_extension_controlled_controller_unittest.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h"
 #include "chrome/browser/ui/extensions/controlled_home_dialog_controller.h"
 #include "chrome/browser/ui/extensions/extension_settings_overridden_dialog.h"
+#include "chrome/browser/ui/extensions/settings_overridden_params_providers.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/search_engines/search_engines_test_util.h"
 #include "components/search_engines/template_url.h"
@@ -251,6 +252,95 @@
 }
 
 TEST_F(DefaultSearchExtensionControlledControllerTest,
+       ShouldRequestConfirmationFalseWhenExtensionDseMatchesExistingEngine) {
+  // Set up an existing user-selected search engine (the "secondary search"
+  // that would be restored if the extension were disabled).
+  TemplateURLData user_dse_data;
+  user_dse_data.SetShortName(u"Existing Engine");
+  user_dse_data.SetKeyword(u"existing");
+  user_dse_data.SetURL("https://www.example.com/?q={searchTerms}");
+  TemplateURL* user_dse = template_url_service_->Add(
+      std::make_unique<TemplateURL>(user_dse_data, TemplateURL::NORMAL));
+  ASSERT_TRUE(user_dse);
+  template_url_service_->SetUserSelectedDefaultSearchProvider(user_dse);
+
+  // Install an extension that points at the same origin.
+  auto extension =
+      AddEnabledExtension(&profile_, "Test Extension", kExtensionId);
+  InstallExtensionControlledDse(profile_, *template_url_service_, *extension);
+
+  DefaultSearchExtensionControlledController owned_controller(browser_window_,
+                                                              profile_);
+
+  // A search URL for the extension engine must NOT request confirmation
+  // because nothing was actually overridden (crbug.com/540532980).
+  GURL search_url("https://www.example.com/?q=foo");
+  EXPECT_FALSE(
+      owned_controller.ShouldRequestConfirmationForExtensionDse(search_url));
+}
+
+TEST_F(DefaultSearchExtensionControlledControllerTest,
+       ExtensionSearchOverrideMatchesExistingEngineFalseWhenOriginsDiffer) {
+  // User had Google as their search engine.
+  TemplateURLData user_dse_data;
+  user_dse_data.SetShortName(u"Google");
+  user_dse_data.SetKeyword(u"google");
+  user_dse_data.SetURL("https://www.google.com/search?q={searchTerms}");
+  TemplateURL* user_dse = template_url_service_->Add(
+      std::make_unique<TemplateURL>(user_dse_data, TemplateURL::NORMAL));
+  ASSERT_TRUE(user_dse);
+  template_url_service_->SetUserSelectedDefaultSearchProvider(user_dse);
+
+  // Extension sets example.com.
+  auto extension =
+      AddEnabledExtension(&profile_, "Test Extension", kExtensionId);
+  InstallExtensionControlledDse(profile_, *template_url_service_, *extension);
+
+  EXPECT_FALSE(
+      settings_overridden_params::ExtensionSearchOverrideMatchesExistingEngine(
+          &profile_));
+}
+
+TEST_F(DefaultSearchExtensionControlledControllerTest,
+       ExtensionSearchOverrideMatchesExistingEngineTrueWhenOriginsMatch) {
+  // User had example.com as their search engine.
+  TemplateURLData user_dse_data;
+  user_dse_data.SetShortName(u"Example");
+  user_dse_data.SetKeyword(u"example");
+  user_dse_data.SetURL("https://www.example.com/search?q={searchTerms}");
+  TemplateURL* user_dse = template_url_service_->Add(
+      std::make_unique<TemplateURL>(user_dse_data, TemplateURL::NORMAL));
+  ASSERT_TRUE(user_dse);
+  template_url_service_->SetUserSelectedDefaultSearchProvider(user_dse);
+
+  // Extension also sets example.com (different path, same origin).
+  auto extension =
+      AddEnabledExtension(&profile_, "Test Extension", kExtensionId);
+  InstallExtensionControlledDse(profile_, *template_url_service_, *extension);
+
+  EXPECT_TRUE(
+      settings_overridden_params::ExtensionSearchOverrideMatchesExistingEngine(
+          &profile_));
+}
+
+TEST_F(
+    DefaultSearchExtensionControlledControllerTest,
+    ExtensionSearchOverrideMatchesExistingEngineFalseWhenNoExtensionControlsSearch) {
+  TemplateURLData user_dse_data;
+  user_dse_data.SetShortName(u"Google");
+  user_dse_data.SetKeyword(u"google");
+  user_dse_data.SetURL("https://www.google.com/search?q={searchTerms}");
+  TemplateURL* user_dse = template_url_service_->Add(
+      std::make_unique<TemplateURL>(user_dse_data, TemplateURL::NORMAL));
+  ASSERT_TRUE(user_dse);
+  template_url_service_->SetUserSelectedDefaultSearchProvider(user_dse);
+
+  EXPECT_FALSE(
+      settings_overridden_params::ExtensionSearchOverrideMatchesExistingEngine(
+          &profile_));
+}
+
+TEST_F(DefaultSearchExtensionControlledControllerTest,
        ShouldRequestConfirmationFalseForSimpleOverrideExtension) {
   // Use the Simple extension helper (no extra permissions).
   auto extension =
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index 1e2c297..bc71931f 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -2151,6 +2151,14 @@
                                                  accelerator);
   }
 
+  // Reading mode uses different command IDs for different ways of opening
+  // it, so adjust to use the command ID for the keyboard shortcut to grab
+  // the proper accelerator.
+  if (command_id == IDC_SHOW_READING_MODE_SIDE_PANEL) {
+    return provider_->GetAcceleratorForCommandId(IDC_SHOW_READING_MODE_KEYBOARD,
+                                                 accelerator);
+  }
+
   if (command_id == IDC_NEW_INCOGNITO_WINDOW) {
     if (!IncognitoModePrefs::IsIncognitoAllowed(browser_->GetProfile())) {
       return false;
diff --git a/chrome/browser/ui/views/dictation/dictation_bubble_ui.cc b/chrome/browser/ui/views/dictation/dictation_bubble_ui.cc
index 76fe05d..4bd901d2 100644
--- a/chrome/browser/ui/views/dictation/dictation_bubble_ui.cc
+++ b/chrome/browser/ui/views/dictation/dictation_bubble_ui.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/dictation/dictation_bubble_ui.h"
 
 #include "base/memory/raw_ptr.h"
+#include "base/task/single_thread_task_runner.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/chrome_typography.h"
 #include "chrome/browser/ui/views/dictation/waveform_view.h"
@@ -206,7 +207,16 @@
   set_close_on_deactivate(false);
   SetContentsView(std::make_unique<DictationToastView>(
       std::move(close_callback), std::move(toggle_active_stream_callback)));
+
+  // Make this not activatable during creation, so that it does not steal focus
+  // from the page.
   SetCanActivate(false);
+  // After creation, we need to make this activatable again. Otherwise, we would
+  // discard mouse activation messages from Windows and the buttons in this
+  // bubble wouldn't be clickable. See https://crbug.com/542199776
+  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE, base::BindOnce(&DictationBubbleUi::SetCanActivate,
+                                weak_ptr_factory_.GetWeakPtr(), true));
 
   // TODO(crbug.com/509983464): Update this to call an undeprecated factory
   // function when this bug is fixed.
diff --git a/chrome/browser/ui/views/dictation/dictation_bubble_ui.h b/chrome/browser/ui/views/dictation/dictation_bubble_ui.h
index 8f62c1a..72f9d40 100644
--- a/chrome/browser/ui/views/dictation/dictation_bubble_ui.h
+++ b/chrome/browser/ui/views/dictation/dictation_bubble_ui.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_VIEWS_DICTATION_DICTATION_BUBBLE_UI_H_
 
 #include "base/functional/callback.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/views/dictation/ui_state.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/gfx/animation/linear_animation.h"
@@ -44,6 +45,7 @@
  private:
   std::unique_ptr<views::Widget> widget_;
   UiState state_ = UiState::kInactive;
+  base::WeakPtrFactory<DictationBubbleUi> weak_ptr_factory_{this};
 };
 
 }  // namespace dictation
diff --git a/chrome/browser/ui/views/extensions/BUILD.gn b/chrome/browser/ui/views/extensions/BUILD.gn
index 02824e3..cf86e12 100644
--- a/chrome/browser/ui/views/extensions/BUILD.gn
+++ b/chrome/browser/ui/views/extensions/BUILD.gn
@@ -92,6 +92,7 @@
     "extensions_menu_item_unittest.cc",
     "extensions_menu_main_page_view_unittest.cc",
     "extensions_menu_site_permissions_page_view_unittest.cc",
+    "extensions_menu_view_unittest.cc",
     "extensions_toolbar_button_unittest.cc",
     "extensions_toolbar_desktop_unittest.cc",
     "extensions_toolbar_unittest.cc",
@@ -145,7 +146,6 @@
       "extension_install_dialog_view_browsertest.cc",
       "extension_install_dialog_view_supervised_browsertest.cc",
       "extension_post_install_dialog_view_utils_browsertest.cc",
-      "extensions_menu_view_browsertest.cc",
       "extensions_request_access_hover_card_coordinator_browsertest.cc",
       "generic_mime_handler_chip_browsertest.cc",
       "security_dialog_tracker_browsertest.cc",
@@ -170,7 +170,6 @@
       "//extensions/browser",
       "//extensions/browser:webstore_installer",
       "//extensions/common",
-      "//testing/gmock",
       "//testing/gtest",
       "//ui/events:test_support",
       "//ui/views",
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc b/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc
deleted file mode 100644
index 89eef6b..0000000
--- a/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc
+++ /dev/null
@@ -1,504 +0,0 @@
-// Copyright 2026 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/extensions/extensions_menu_view.h"
-
-#include <algorithm>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/auto_reset.h"
-#include "base/containers/flat_set.h"
-#include "base/containers/to_vector.h"
-#include "base/memory/raw_ptr.h"
-#include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/metrics/user_action_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "build/build_config.h"
-#include "chrome/browser/extensions/chrome_test_extension_loader.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/toolbar/toolbar_action_view_model.h"
-#include "chrome/browser/ui/ui_features.h"
-#include "chrome/browser/ui/views/extensions/extensions_menu_button.h"
-#include "chrome/browser/ui/views/extensions/extensions_menu_item_view.h"
-#include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
-#include "chrome/browser/ui/views/extensions/extensions_toolbar_desktop.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/toolbar/toolbar_action_view.h"
-#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/test_utils.h"
-#include "extensions/browser/disable_reason.h"
-#include "extensions/browser/extension_registrar.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/browser/permissions/site_permissions_helper.h"
-#include "extensions/browser/test_extension_registry_observer.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/extension_features.h"
-#include "extensions/test/test_extension_dir.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "ui/events/base_event_utils.h"
-#include "ui/views/layout/animating_layout_manager_test_util.h"
-#include "ui/views/test/ax_event_counter.h"
-#include "ui/views/view_utils.h"
-#include "ui/views/widget/widget.h"
-
-class ExtensionsMenuViewBrowserTest : public InProcessBrowserTest {
- public:
-  ExtensionsMenuViewBrowserTest()
-      : allow_extension_menu_instances_(
-            ExtensionsMenuView::AllowInstancesForTesting()) {
-    scoped_feature_list_.InitWithFeatures(
-        {}, {extensions_features::kExtensionsMenuAccessControl,
-             extensions_features::kExtensionDisableUnsupportedDeveloper,
-             features::kExtensionsPinnedByDefault});
-  }
-
-  ExtensionsMenuViewBrowserTest(const ExtensionsMenuViewBrowserTest&) = delete;
-  ExtensionsMenuViewBrowserTest& operator=(
-      const ExtensionsMenuViewBrowserTest&) = delete;
-
-  ~ExtensionsMenuViewBrowserTest() override = default;
-
-  void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-
-    // Shorten delay on animations so tests run faster.
-    views::test::ReduceAnimationDuration(extensions_container());
-
-    ExtensionsMenuView::ShowBubble(
-        extensions_container()->GetExtensionsButton(), browser(),
-        extensions_container()->GetToolbarViewModel(), extensions_container());
-    if (extensions_menu()) {
-      extensions_menu()->set_close_on_deactivate(false);
-    }
-  }
-
-  void TearDownOnMainThread() override {
-    if (ExtensionsMenuView::IsShowing()) {
-      ExtensionsMenuView::GetExtensionsMenuViewForTesting()
-          ->GetWidget()
-          ->CloseNow();
-    }
-    InProcessBrowserTest::TearDownOnMainThread();
-  }
-
-  Profile* profile() { return browser()->GetProfile(); }
-
-  extensions::ExtensionRegistrar* extension_registrar() {
-    return extensions::ExtensionRegistrar::Get(profile());
-  }
-
-  ExtensionsToolbarDesktop* extensions_container() {
-    return BrowserView::GetBrowserViewForBrowser(browser())
-        ->toolbar()
-        ->extensions_container();
-  }
-
-  scoped_refptr<const extensions::Extension> InstallExtension(
-      const std::string& name) {
-    auto extension_dir = std::make_unique<extensions::TestExtensionDir>();
-    constexpr char kManifestTemplate[] = R"({
-      "name": "%s",
-      "version": "1",
-      "manifest_version": 3
-    })";
-    extension_dir->WriteManifest(
-        base::StringPrintf(kManifestTemplate, name.c_str()));
-    extensions::ChromeTestExtensionLoader loader(profile());
-    scoped_refptr<const extensions::Extension> extension =
-        loader.LoadExtension(extension_dir->UnpackedPath());
-    test_extension_dirs_.push_back(std::move(extension_dir));
-    LayoutContainerIfNecessary();
-    LayoutMenuIfNecessary();
-    return extension;
-  }
-
-  void DisableExtension(const extensions::ExtensionId& extension_id) {
-    extension_registrar()->DisableExtension(
-        extension_id, {extensions::disable_reason::DISABLE_USER_ACTION});
-  }
-
-  void EnableExtension(const extensions::ExtensionId& extension_id) {
-    extension_registrar()->EnableExtension(extension_id);
-  }
-
-  void ReloadExtension(const extensions::ExtensionId& extension_id) {
-    extension_registrar()->ReloadExtension(extension_id);
-  }
-
-  void ClickButton(views::Button* button) const {
-    ui::MouseEvent press_event(ui::EventType::kMousePressed, gfx::Point(),
-                               gfx::Point(), ui::EventTimeForNow(),
-                               ui::EF_LEFT_MOUSE_BUTTON, 0);
-    button->OnMousePressed(press_event);
-    ui::MouseEvent release_event(ui::EventType::kMouseReleased, gfx::Point(),
-                                 gfx::Point(), ui::EventTimeForNow(),
-                                 ui::EF_LEFT_MOUSE_BUTTON, 0);
-    button->OnMouseReleased(release_event);
-  }
-
-  void ClickPinButton(ExtensionMenuItemView* menu_item) const {
-    ClickButton(menu_item->pin_button_for_testing());
-  }
-
-  void WaitForAnimation() {
-#if BUILDFLAG(IS_MAC)
-    // Animation is not reliable in tests on Mac or we avoid using animations.
-#else
-    views::test::WaitForAnimatingLayoutManager(extensions_container());
-#endif
-    LayoutContainerIfNecessary();
-    LayoutMenuIfNecessary();
-  }
-
-  void LayoutContainerIfNecessary() {
-    extensions_container()->GetWidget()->LayoutRootViewIfNecessary();
-  }
-
-  void LayoutMenuIfNecessary() {
-    if (extensions_menu() && extensions_menu()->GetWidget()) {
-      extensions_menu()->GetWidget()->LayoutRootViewIfNecessary();
-    }
-  }
-
-  ExtensionsMenuView* extensions_menu() {
-    return ExtensionsMenuView::GetExtensionsMenuViewForTesting();
-  }
-
-  // Asserts there is exactly 1 menu item and then returns it.
-  ExtensionMenuItemView* GetOnlyMenuItem() {
-    base::flat_set<raw_ptr<ExtensionMenuItemView, CtnExperimental>> menu_items =
-        extensions_menu()->extensions_menu_items_for_testing();
-    if (menu_items.size() != 1u) {
-      ADD_FAILURE() << "Not exactly one item; size is: " << menu_items.size();
-      return nullptr;
-    }
-    return *menu_items.begin();
-  }
-
-  std::vector<ToolbarActionView*> GetPinnedExtensionViews() {
-    std::vector<ToolbarActionView*> result;
-    for (views::View* child : extensions_container()->children()) {
-      // Ensure we don't downcast the ExtensionsToolbarButton.
-      if (views::IsViewClass<ToolbarActionView>(child)) {
-        ToolbarActionView* const action =
-            static_cast<ToolbarActionView*>(child);
-#if BUILDFLAG(IS_MAC)
-        const bool is_visible =
-            extensions_container()->IsActionVisibleOnToolbar(
-                action->view_model()->GetId());
-#else
-        const bool is_visible = action->GetVisible();
-#endif
-        if (is_visible) {
-          result.push_back(action);
-        }
-      }
-    }
-    return result;
-  }
-
-  std::vector<std::string> GetPinnedExtensionNames() {
-    return base::ToVector(
-        GetPinnedExtensionViews(), [](ToolbarActionView* view) {
-          return base::UTF16ToUTF8(view->view_model()->GetActionName());
-        });
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-  base::AutoReset<bool> allow_extension_menu_instances_;
-  std::vector<std::unique_ptr<extensions::TestExtensionDir>>
-      test_extension_dirs_;
-};
-
-IN_PROC_BROWSER_TEST_F(ExtensionsMenuViewBrowserTest,
-                       ExtensionsAreShownInTheMenu) {
-  // To start, there should be no extensions in the menu.
-  EXPECT_EQ(0u, extensions_menu()->extensions_menu_items_for_testing().size());
-
-  // Add an extension, and verify that it's added to the menu.
-  constexpr char kExtensionName[] = "Test 1";
-  InstallExtension(kExtensionName);
-
-  {
-    base::flat_set<raw_ptr<ExtensionMenuItemView, CtnExperimental>> menu_items =
-        extensions_menu()->extensions_menu_items_for_testing();
-    ASSERT_EQ(1u, menu_items.size());
-    EXPECT_EQ(kExtensionName,
-              base::UTF16ToUTF8((*menu_items.begin())
-                                    ->primary_action_button_for_testing()
-                                    ->label_text_for_testing()));
-  }
-}
-
-IN_PROC_BROWSER_TEST_F(ExtensionsMenuViewBrowserTest,
-                       ExtensionsAreSortedInTheMenu) {
-  constexpr char kExtensionZName[] = "Z Extension";
-  InstallExtension(kExtensionZName);
-  constexpr char kExtensionAName[] = "A Extension";
-  InstallExtension(kExtensionAName);
-  constexpr char kExtensionBName[] = "b Extension";
-  InstallExtension(kExtensionBName);
-  constexpr char kExtensionCName[] = "C Extension";
-  InstallExtension(kExtensionCName);
-
-  std::vector<ExtensionMenuItemView*> menu_items =
-      ExtensionsMenuView::GetSortedItemsForSectionForTesting(
-          extensions::SitePermissionsHelper::SiteInteraction::kNone);
-  ASSERT_EQ(4u, menu_items.size());
-
-  std::vector<std::string> item_names;
-  for (auto* menu_item : menu_items) {
-    item_names.push_back(
-        base::UTF16ToUTF8(menu_item->primary_action_button_for_testing()
-                              ->label_text_for_testing()));
-  }
-
-  // Basic std::sort would do A,C,Z,b however we want A,b,C,Z
-  EXPECT_THAT(item_names,
-              testing::ElementsAre(kExtensionAName, kExtensionBName,
-                                   kExtensionCName, kExtensionZName));
-}
-
-IN_PROC_BROWSER_TEST_F(ExtensionsMenuViewBrowserTest,
-                       PinnedExtensionAppearsInToolbar) {
-  constexpr char kName[] = "Test Name";
-  const extensions::ExtensionId extension_id =
-      InstallExtension(kName)->id();
-
-  ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
-  ASSERT_TRUE(menu_item);
-  EXPECT_FALSE(extensions_container()->IsActionVisibleOnToolbar(extension_id));
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::IsEmpty());
-
-  ClickPinButton(menu_item);
-  WaitForAnimation();
-
-  EXPECT_TRUE(extensions_container()->IsActionVisibleOnToolbar(extension_id));
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::ElementsAre(kName));
-
-  ClickPinButton(menu_item);  // Unpin.
-  WaitForAnimation();
-
-  EXPECT_FALSE(extensions_container()->IsActionVisibleOnToolbar(extension_id));
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::IsEmpty());
-}
-
-IN_PROC_BROWSER_TEST_F(ExtensionsMenuViewBrowserTest,
-                       PinnedExtensionAppearsInAnotherWindow) {
-  const extensions::ExtensionId extension_id =
-      InstallExtension("Test Name")->id();
-  const auto is_action_visible_on_toolbar = [&extension_id](Browser* browser) {
-    return BrowserView::GetBrowserViewForBrowser(browser)
-        ->toolbar()
-        ->extensions_container()
-        ->IsActionVisibleOnToolbar(extension_id);
-  };
-
-  Browser* browser2 = CreateBrowser(profile());
-  views::test::ReduceAnimationDuration(
-      BrowserView::GetBrowserViewForBrowser(browser2)
-          ->toolbar()
-          ->extensions_container());
-
-  ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
-  ASSERT_TRUE(menu_item);
-  ClickPinButton(menu_item);
-  WaitForAnimation();
-#if !BUILDFLAG(IS_MAC)
-  views::test::WaitForAnimatingLayoutManager(
-      BrowserView::GetBrowserViewForBrowser(browser2)
-          ->toolbar()
-          ->extensions_container());
-#endif
-
-  // Window that was already open gets the pinned extension.
-  EXPECT_TRUE(is_action_visible_on_toolbar(browser2));
-
-  Browser* browser3 = CreateBrowser(profile());
-  views::test::ReduceAnimationDuration(
-      BrowserView::GetBrowserViewForBrowser(browser3)
-          ->toolbar()
-          ->extensions_container());
-#if !BUILDFLAG(IS_MAC)
-  views::test::WaitForAnimatingLayoutManager(
-      BrowserView::GetBrowserViewForBrowser(browser3)
-          ->toolbar()
-          ->extensions_container());
-#endif
-
-  // Brand-new window also gets the pinned extension.
-  EXPECT_TRUE(is_action_visible_on_toolbar(browser3));
-}
-
-IN_PROC_BROWSER_TEST_F(ExtensionsMenuViewBrowserTest,
-                       PinnedExtensionRemovedWhenDisabled) {
-  constexpr char kName[] = "Test Name";
-  const extensions::ExtensionId id = InstallExtension(kName)->id();
-
-  {
-    ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
-    ASSERT_TRUE(menu_item);
-    ClickPinButton(menu_item);
-  }
-
-  DisableExtension(id);
-  WaitForAnimation();
-
-  ASSERT_EQ(0u, extensions_menu()->extensions_menu_items_for_testing().size());
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::IsEmpty());
-
-  EnableExtension(id);
-  WaitForAnimation();
-
-  ASSERT_EQ(1u, extensions_menu()->extensions_menu_items_for_testing().size());
-  EXPECT_THAT(GetPinnedExtensionNames(), testing::ElementsAre(kName));
-}
-
-IN_PROC_BROWSER_TEST_F(ExtensionsMenuViewBrowserTest, PinnedExtensionLayout) {
-  for (int i = 0; i < 3; i++) {
-    InstallExtension(base::StringPrintf("Test %d", i));
-  }
-  for (ExtensionMenuItemView* menu_item :
-       extensions_menu()->extensions_menu_items_for_testing()) {
-    ClickPinButton(menu_item);
-  }
-  WaitForAnimation();
-
-  std::vector<ToolbarActionView*> action_views = GetPinnedExtensionViews();
-  ASSERT_EQ(3u, action_views.size());
-  ExtensionsToolbarButton* menu_button =
-      extensions_container()->GetExtensionsButton();
-
-  // All views should be lined up horizontally with the menu button.
-  EXPECT_EQ(action_views[0]->y(), action_views[1]->y());
-  EXPECT_EQ(action_views[1]->y(), action_views[2]->y());
-  EXPECT_EQ(action_views[2]->y(), menu_button->y());
-
-  // Views are ordered left-to-right (in LTR mode).
-  EXPECT_LE(action_views[0]->x() + action_views[0]->width(),
-            action_views[1]->x());
-  EXPECT_LE(action_views[1]->x() + action_views[1]->width(),
-            action_views[2]->x());
-  EXPECT_LE(action_views[2]->x() + action_views[2]->width(), menu_button->x());
-}
-
-// Tests that when an extension is reloaded it remains visible in the toolbar
-// and extensions menu.
-IN_PROC_BROWSER_TEST_F(ExtensionsMenuViewBrowserTest, ReloadExtension) {
-  // The extension must have a manifest to be reloaded.
-  extensions::TestExtensionDir extension_directory;
-  constexpr char kManifest[] = R"({
-        "name": "Test",
-        "version": "1",
-        "manifest_version": 3
-      })";
-  extension_directory.WriteManifest(kManifest);
-  extensions::ChromeTestExtensionLoader loader(profile());
-  scoped_refptr<const extensions::Extension> extension =
-      loader.LoadExtension(extension_directory.UnpackedPath());
-  // Force the menu to re-layout, since a new item was added.
-  LayoutMenuIfNecessary();
-  ASSERT_EQ(1u, extensions_menu()->extensions_menu_items_for_testing().size());
-
-  {
-    ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
-    ClickPinButton(menu_item);
-    EXPECT_TRUE(
-        extensions_container()->IsActionVisibleOnToolbar(extension->id()));
-    // |menu_item| will not be valid after the extension reloads.
-  }
-
-  extensions::TestExtensionRegistryObserver registry_observer(
-      extensions::ExtensionRegistry::Get(profile()));
-  ReloadExtension(extension->id());
-  ASSERT_TRUE(registry_observer.WaitForExtensionLoaded());
-  LayoutMenuIfNecessary();
-
-  ASSERT_EQ(1u, extensions_menu()->extensions_menu_items_for_testing().size());
-  EXPECT_TRUE(
-      extensions_container()->IsActionVisibleOnToolbar(extension->id()));
-}
-
-// Tests that a when an extension is reloaded with manifest errors, and
-// therefore fails to be loaded into Chrome, it's removed from the toolbar and
-// extensions menu.
-IN_PROC_BROWSER_TEST_F(ExtensionsMenuViewBrowserTest, ReloadExtensionFailed) {
-  extensions::TestExtensionDir extension_directory;
-  constexpr char kManifest[] = R"({
-        "name": "Test",
-        "version": "1",
-        "manifest_version": 3
-      })";
-  extension_directory.WriteManifest(kManifest);
-  extensions::ChromeTestExtensionLoader loader(profile());
-  scoped_refptr<const extensions::Extension> extension =
-      loader.LoadExtension(extension_directory.UnpackedPath());
-  LayoutMenuIfNecessary();
-  ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
-  ASSERT_TRUE(menu_item);
-  ClickPinButton(menu_item);
-  WaitForAnimation();
-
-  // Replace the extension's valid manifest with one containing errors. In this
-  // case, the error is that the version key is invalid.
-  constexpr char kManifestWithErrors[] = R"({
-        "name": "Test",
-        "version": 1,
-        "manifest_version": 3
-      })";
-  extension_directory.WriteManifest(kManifestWithErrors);
-
-  extensions::TestExtensionRegistryObserver registry_observer(
-      extensions::ExtensionRegistry::Get(profile()), extension->id());
-  // Reload the extension. It should fail due to the manifest errors.
-  extension_registrar()->ReloadExtensionWithQuietFailure(extension->id());
-  ASSERT_TRUE(registry_observer.WaitForExtensionUnloaded());
-  WaitForAnimation();
-  LayoutMenuIfNecessary();
-
-  // Since the extension is removed it's no longer visible on the toolbar or in
-  // the menu.
-  for (views::View* child : extensions_container()->children()) {
-    EXPECT_FALSE(views::IsViewClass<ToolbarActionView>(child));
-  }
-  EXPECT_EQ(0u, extensions_menu()->extensions_menu_items_for_testing().size());
-}
-
-IN_PROC_BROWSER_TEST_F(ExtensionsMenuViewBrowserTest,
-                       PinButtonUserActionWithAccessibility) {
-  base::UserActionTester user_action_tester;
-  InstallExtension("Test Extension");
-  ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
-  ASSERT_NE(nullptr, menu_item);
-  views::test::AXEventCounter counter(views::AXUpdateNotifier::Get());
-  constexpr char kPinButtonUserAction[] = "Extensions.Toolbar.PinButtonPressed";
-
-  // Verify behavior before pin, after pin, and after unpin.
-  for (int i = 0; i < 3; i++) {
-    EXPECT_EQ(i, user_action_tester.GetActionCount(kPinButtonUserAction));
-#if BUILDFLAG(IS_MAC)
-    // TODO(crbug.com/40670141): No Mac animations in unit tests cause errors.
-#else
-    EXPECT_EQ(i, counter.GetCount(ax::mojom::Event::kAlert));
-    EXPECT_EQ(i, counter.GetCount(ax::mojom::Event::kTextChanged));
-#endif
-    ClickPinButton(menu_item);
-  }
-}
-
-IN_PROC_BROWSER_TEST_F(ExtensionsMenuViewBrowserTest, WindowTitle) {
-  InstallExtension("Test Extension");
-
-  ExtensionsMenuView* const menu_view = extensions_menu();
-  EXPECT_FALSE(menu_view->GetWindowTitle().empty());
-  EXPECT_TRUE(menu_view->GetAccessibleWindowTitle().empty());
-}
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view_unittest.cc b/chrome/browser/ui/views/extensions/extensions_menu_view_unittest.cc
new file mode 100644
index 0000000..91595f7
--- /dev/null
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view_unittest.cc
@@ -0,0 +1,418 @@
+// Copyright 2019 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/extensions/extensions_menu_view.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/containers/to_vector.h"
+#include "base/memory/raw_ptr.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/user_action_tester.h"
+#include "build/build_config.h"
+#include "chrome/browser/extensions/chrome_test_extension_loader.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/toolbar/toolbar_action_view_model.h"
+#include "chrome/browser/ui/views/extensions/extensions_menu_button.h"
+#include "chrome/browser/ui/views/extensions/extensions_menu_item_view.h"
+#include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
+#include "chrome/browser/ui/views/extensions/extensions_toolbar_desktop.h"
+#include "chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
+#include "extensions/browser/test_extension_registry_observer.h"
+#include "extensions/common/extension.h"
+#include "extensions/test/test_extension_dir.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/views/layout/animating_layout_manager_test_util.h"
+#include "ui/views/test/ax_event_counter.h"
+#include "ui/views/view_utils.h"
+#include "ui/views/widget/widget.h"
+
+class ExtensionsMenuViewUnitTest : public ExtensionsToolbarUnitTest {
+ public:
+  ExtensionsMenuViewUnitTest()
+      : allow_extension_menu_instances_(
+            ExtensionsMenuView::AllowInstancesForTesting()) {}
+
+  ExtensionsMenuViewUnitTest(const ExtensionsMenuViewUnitTest&) = delete;
+  ExtensionsMenuViewUnitTest& operator=(const ExtensionsMenuViewUnitTest&) =
+      delete;
+
+  ~ExtensionsMenuViewUnitTest() override = default;
+
+  // ExtensionsToolbarUnitTest:
+  void SetUp() override;
+
+  scoped_refptr<const extensions::Extension> InstallExtensionAndLayout(
+      const std::string& name);
+
+  ExtensionsMenuView* extensions_menu() {
+    return ExtensionsMenuView::GetExtensionsMenuViewForTesting();
+  }
+
+  // Asserts there is exactly 1 menu item and then returns it.
+  ExtensionMenuItemView* GetOnlyMenuItem();
+
+  void ClickPinButton(ExtensionMenuItemView* menu_item) const;
+  void ClickContextMenuButton(ExtensionMenuItemView* menu_item) const;
+
+  std::vector<ToolbarActionView*> GetPinnedExtensionViews();
+
+  ExtensionMenuItemView* GetExtensionMenuItemView(const std::string& name);
+
+  // Returns a list of the names of the currently pinned extensions, in order
+  // from left to right.
+  std::vector<std::string> GetPinnedExtensionNames();
+
+  // Since this is a unittest (and doesn't have as much "real" rendering),
+  // the ExtensionsMenuView sometimes needs a nudge to re-layout the views.
+  void LayoutMenuIfNecessary();
+
+ private:
+  base::AutoReset<bool> allow_extension_menu_instances_;
+};
+
+void ExtensionsMenuViewUnitTest::SetUp() {
+  ExtensionsToolbarUnitTest::SetUp();
+
+  ExtensionsMenuView::ShowBubble(
+      extensions_container()->GetExtensionsButton(), browser(),
+      extensions_container()->GetToolbarViewModel(), extensions_container());
+}
+
+scoped_refptr<const extensions::Extension>
+ExtensionsMenuViewUnitTest::InstallExtensionAndLayout(const std::string& name) {
+  scoped_refptr<const extensions::Extension> extension = InstallExtension(name);
+  LayoutMenuIfNecessary();
+  return extension;
+}
+
+ExtensionMenuItemView* ExtensionsMenuViewUnitTest::GetOnlyMenuItem() {
+  base::flat_set<raw_ptr<ExtensionMenuItemView, CtnExperimental>> menu_items =
+      extensions_menu()->extensions_menu_items_for_testing();
+  if (menu_items.size() != 1u) {
+    ADD_FAILURE() << "Not exactly one item; size is: " << menu_items.size();
+    return nullptr;
+  }
+  return *menu_items.begin();
+}
+
+void ExtensionsMenuViewUnitTest::ClickPinButton(
+    ExtensionMenuItemView* menu_item) const {
+  ClickButton(menu_item->pin_button_for_testing());
+}
+
+void ExtensionsMenuViewUnitTest::ClickContextMenuButton(
+    ExtensionMenuItemView* menu_item) const {
+  ClickButton(menu_item->context_menu_button_for_testing());
+}
+
+std::vector<ToolbarActionView*>
+ExtensionsMenuViewUnitTest::GetPinnedExtensionViews() {
+  std::vector<ToolbarActionView*> result;
+  for (views::View* child : extensions_container()->children()) {
+    // Ensure we don't downcast the ExtensionsToolbarButton.
+    if (views::IsViewClass<ToolbarActionView>(child)) {
+      ToolbarActionView* const action = static_cast<ToolbarActionView*>(child);
+#if BUILDFLAG(IS_MAC)
+      // TODO(crbug.com/40670141): Use IsActionVisibleOnToolbar() because it
+      // queries the underlying model and not GetVisible(), as that relies on an
+      // animation running, which is not reliable in unit tests on Mac.
+      const bool is_visible = extensions_container()->IsActionVisibleOnToolbar(
+          action->view_model()->GetId());
+#else
+      const bool is_visible = action->GetVisible();
+#endif
+      if (is_visible) {
+        result.push_back(action);
+      }
+    }
+  }
+  return result;
+}
+
+ExtensionMenuItemView* ExtensionsMenuViewUnitTest::GetExtensionMenuItemView(
+    const std::string& name) {
+  base::flat_set<raw_ptr<ExtensionMenuItemView, CtnExperimental>> menu_items =
+      extensions_menu()->extensions_menu_items_for_testing();
+  auto iter =
+      std::ranges::find(menu_items, name, [](ExtensionMenuItemView* item) {
+        return base::UTF16ToUTF8(item->view_model()->GetActionName());
+      });
+  return iter == menu_items.end() ? nullptr : *iter;
+}
+
+std::vector<std::string> ExtensionsMenuViewUnitTest::GetPinnedExtensionNames() {
+  return base::ToVector(GetPinnedExtensionViews(), [](ToolbarActionView* view) {
+    return base::UTF16ToUTF8(view->view_model()->GetActionName());
+  });
+}
+
+void ExtensionsMenuViewUnitTest::LayoutMenuIfNecessary() {
+  extensions_menu()->GetWidget()->LayoutRootViewIfNecessary();
+}
+
+TEST_F(ExtensionsMenuViewUnitTest, ExtensionsAreShownInTheMenu) {
+  // To start, there should be no extensions in the menu.
+  EXPECT_EQ(0u, extensions_menu()->extensions_menu_items_for_testing().size());
+
+  // Add an extension, and verify that it's added to the menu.
+  constexpr char kExtensionName[] = "Test 1";
+  InstallExtensionAndLayout(kExtensionName);
+
+  {
+    base::flat_set<raw_ptr<ExtensionMenuItemView, CtnExperimental>> menu_items =
+        extensions_menu()->extensions_menu_items_for_testing();
+    ASSERT_EQ(1u, menu_items.size());
+    EXPECT_EQ(kExtensionName,
+              base::UTF16ToUTF8((*menu_items.begin())
+                                    ->primary_action_button_for_testing()
+                                    ->label_text_for_testing()));
+  }
+}
+
+TEST_F(ExtensionsMenuViewUnitTest, ExtensionsAreSortedInTheMenu) {
+  constexpr char kExtensionZName[] = "Z Extension";
+  InstallExtension(kExtensionZName);
+  constexpr char kExtensionAName[] = "A Extension";
+  InstallExtension(kExtensionAName);
+  constexpr char kExtensionBName[] = "b Extension";
+  InstallExtension(kExtensionBName);
+  constexpr char kExtensionCName[] = "C Extension";
+  InstallExtensionAndLayout(kExtensionCName);
+
+  std::vector<ExtensionMenuItemView*> menu_items =
+      ExtensionsMenuView::GetSortedItemsForSectionForTesting(
+          extensions::SitePermissionsHelper::SiteInteraction::kNone);
+  ASSERT_EQ(4u, menu_items.size());
+
+  std::vector<std::string> item_names;
+  for (auto* menu_item : menu_items) {
+    item_names.push_back(
+        base::UTF16ToUTF8(menu_item->primary_action_button_for_testing()
+                              ->label_text_for_testing()));
+  }
+
+  // Basic std::sort would do A,C,Z,b however we want A,b,C,Z
+  EXPECT_THAT(item_names,
+              testing::ElementsAre(kExtensionAName, kExtensionBName,
+                                   kExtensionCName, kExtensionZName));
+}
+
+TEST_F(ExtensionsMenuViewUnitTest, PinnedExtensionAppearsInToolbar) {
+  constexpr char kName[] = "Test Name";
+  const std::string& extension_id = InstallExtensionAndLayout(kName)->id();
+
+  ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
+  ASSERT_TRUE(menu_item);
+  EXPECT_FALSE(extensions_container()->IsActionVisibleOnToolbar(extension_id));
+  EXPECT_THAT(GetPinnedExtensionNames(), testing::IsEmpty());
+
+  ClickPinButton(menu_item);
+  WaitForAnimation();
+
+  EXPECT_TRUE(extensions_container()->IsActionVisibleOnToolbar(extension_id));
+  EXPECT_THAT(GetPinnedExtensionNames(), testing::ElementsAre(kName));
+
+  ClickPinButton(menu_item);  // Unpin.
+  WaitForAnimation();
+
+  EXPECT_FALSE(extensions_container()->IsActionVisibleOnToolbar(extension_id));
+  EXPECT_THAT(GetPinnedExtensionNames(), testing::IsEmpty());
+}
+
+TEST_F(ExtensionsMenuViewUnitTest, PinnedExtensionAppearsInAnotherWindow) {
+  const std::string& extension_id =
+      InstallExtensionAndLayout("Test Name")->id();
+  const auto is_action_visible_on_toolbar = [&extension_id](Browser* browser) {
+    return BrowserView::GetBrowserViewForBrowser(browser)
+        ->toolbar()
+        ->extensions_container()
+        ->IsActionVisibleOnToolbar(extension_id);
+  };
+
+  Browser* browser2 = CreateBrowserWithBrowserView(browser()->GetProfile(),
+                                                   browser()->GetType());
+
+  ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
+  ASSERT_TRUE(menu_item);
+  ClickPinButton(menu_item);
+
+  // Window that was already open gets the pinned extension.
+  EXPECT_TRUE(is_action_visible_on_toolbar(browser2));
+
+  Browser* browser3 = CreateBrowserWithBrowserView(browser()->GetProfile(),
+                                                   browser()->GetType());
+
+  // Brand-new window also gets the pinned extension.
+  EXPECT_TRUE(is_action_visible_on_toolbar(browser3));
+}
+
+TEST_F(ExtensionsMenuViewUnitTest, PinnedExtensionRemovedWhenDisabled) {
+  constexpr char kName[] = "Test Name";
+  const extensions::ExtensionId id = InstallExtensionAndLayout(kName)->id();
+
+  {
+    ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
+    ASSERT_TRUE(menu_item);
+    ClickPinButton(menu_item);
+  }
+
+  DisableExtension(id);
+  WaitForAnimation();
+
+  ASSERT_EQ(0u, extensions_menu()->extensions_menu_items_for_testing().size());
+  EXPECT_THAT(GetPinnedExtensionNames(), testing::IsEmpty());
+
+  EnableExtension(id);
+  WaitForAnimation();
+
+  ASSERT_EQ(1u, extensions_menu()->extensions_menu_items_for_testing().size());
+  EXPECT_THAT(GetPinnedExtensionNames(), testing::ElementsAre(kName));
+}
+
+TEST_F(ExtensionsMenuViewUnitTest, PinnedExtensionLayout) {
+  for (int i = 0; i < 3; i++) {
+    InstallExtensionAndLayout(base::StringPrintf("Test %d", i));
+  }
+  for (ExtensionMenuItemView* menu_item :
+       extensions_menu()->extensions_menu_items_for_testing()) {
+    ClickPinButton(menu_item);
+  }
+  WaitForAnimation();
+
+  std::vector<ToolbarActionView*> action_views = GetPinnedExtensionViews();
+  ASSERT_EQ(3u, action_views.size());
+  ExtensionsToolbarButton* menu_button =
+      extensions_container()->GetExtensionsButton();
+
+  // All views should be lined up horizontally with the menu button.
+  EXPECT_EQ(action_views[0]->y(), action_views[1]->y());
+  EXPECT_EQ(action_views[1]->y(), action_views[2]->y());
+  EXPECT_EQ(action_views[2]->y(), menu_button->y());
+
+  // Views are ordered left-to-right (in LTR mode).
+  EXPECT_LE(action_views[0]->x() + action_views[0]->width(),
+            action_views[1]->x());
+  EXPECT_LE(action_views[1]->x() + action_views[1]->width(),
+            action_views[2]->x());
+  EXPECT_LE(action_views[2]->x() + action_views[2]->width(), menu_button->x());
+}
+
+// Tests that when an extension is reloaded it remains visible in the toolbar
+// and extensions menu.
+TEST_F(ExtensionsMenuViewUnitTest, ReloadExtension) {
+  // The extension must have a manifest to be reloaded.
+  extensions::TestExtensionDir extension_directory;
+  constexpr char kManifest[] = R"({
+        "name": "Test",
+        "version": "1",
+        "manifest_version": 3
+      })";
+  extension_directory.WriteManifest(kManifest);
+  extensions::ChromeTestExtensionLoader loader(profile());
+  scoped_refptr<const extensions::Extension> extension =
+      loader.LoadExtension(extension_directory.UnpackedPath());
+  // Force the menu to re-layout, since a new item was added.
+  LayoutMenuIfNecessary();
+  ASSERT_EQ(1u, extensions_menu()->extensions_menu_items_for_testing().size());
+
+  {
+    ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
+    ClickPinButton(menu_item);
+    EXPECT_TRUE(
+        extensions_container()->IsActionVisibleOnToolbar(extension->id()));
+    // |menu_item| will not be valid after the extension reloads.
+  }
+
+  extensions::TestExtensionRegistryObserver registry_observer(
+      extensions::ExtensionRegistry::Get(profile()));
+  ReloadExtension(extension->id());
+  ASSERT_TRUE(registry_observer.WaitForExtensionLoaded());
+  LayoutMenuIfNecessary();
+
+  ASSERT_EQ(1u, extensions_menu()->extensions_menu_items_for_testing().size());
+  EXPECT_TRUE(
+      extensions_container()->IsActionVisibleOnToolbar(extension->id()));
+}
+
+// Tests that a when an extension is reloaded with manifest errors, and
+// therefore fails to be loaded into Chrome, it's removed from the toolbar and
+// extensions menu.
+TEST_F(ExtensionsMenuViewUnitTest, ReloadExtensionFailed) {
+  extensions::TestExtensionDir extension_directory;
+  constexpr char kManifest[] = R"({
+        "name": "Test",
+        "version": "1",
+        "manifest_version": 3
+      })";
+  extension_directory.WriteManifest(kManifest);
+  extensions::ChromeTestExtensionLoader loader(profile());
+  scoped_refptr<const extensions::Extension> extension =
+      loader.LoadExtension(extension_directory.UnpackedPath());
+  LayoutMenuIfNecessary();
+  ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
+  ASSERT_TRUE(menu_item);
+  ClickPinButton(menu_item);
+
+  // Replace the extension's valid manifest with one containing errors. In this
+  // case, the error is that the version key is invalid.
+  constexpr char kManifestWithErrors[] = R"({
+        "name": "Test",
+        "version": 1,
+        "manifest_version": 3
+      })";
+  extension_directory.WriteManifest(kManifestWithErrors);
+
+  // Reload the extension. It should fail due to the manifest errors.
+  extension_registrar()->ReloadExtensionWithQuietFailure(extension->id());
+  base::RunLoop().RunUntilIdle();
+
+  // Since the extension is removed it's no longer visible on the toolbar or in
+  // the menu.
+  for (views::View* child : extensions_container()->children()) {
+    EXPECT_FALSE(views::IsViewClass<ToolbarActionView>(child));
+  }
+  EXPECT_EQ(0u, extensions_menu()->extensions_menu_items_for_testing().size());
+}
+
+TEST_F(ExtensionsMenuViewUnitTest, PinButtonUserActionWithAccessibility) {
+  base::UserActionTester user_action_tester;
+  InstallExtensionAndLayout("Test Extension");
+  ExtensionMenuItemView* menu_item = GetOnlyMenuItem();
+  ASSERT_NE(nullptr, menu_item);
+  views::test::AXEventCounter counter(views::AXUpdateNotifier::Get());
+  constexpr char kPinButtonUserAction[] = "Extensions.Toolbar.PinButtonPressed";
+
+  // Verify behavior before pin, after pin, and after unpin.
+  for (int i = 0; i < 3; i++) {
+    EXPECT_EQ(i, user_action_tester.GetActionCount(kPinButtonUserAction));
+#if BUILDFLAG(IS_MAC)
+    // TODO(crbug.com/40670141): No Mac animations in unit tests cause errors.
+#else
+    EXPECT_EQ(i, counter.GetCount(ax::mojom::Event::kAlert));
+    EXPECT_EQ(i, counter.GetCount(ax::mojom::Event::kTextChanged));
+#endif
+    ClickPinButton(menu_item);
+  }
+}
+
+TEST_F(ExtensionsMenuViewUnitTest, WindowTitle) {
+  InstallExtensionAndLayout("Test Extension");
+
+  ExtensionsMenuView* const menu_view = extensions_menu();
+  EXPECT_FALSE(menu_view->GetWindowTitle().empty());
+  EXPECT_TRUE(menu_view->GetAccessibleWindowTitle().empty());
+}
+
+// TODO(crbug.com/40636292): When supported, add a test to verify the
+// ExtensionsToolbarDesktop shrinks when the window is too small to show all
+// pinned extensions.
+// TODO(crbug.com/40636292): When supported, add a test to verify an extension
+// is shown when a bubble pops up and needs to draw attention to it.
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index d29836e..07fdd70 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -847,9 +847,16 @@
     }
   };
 
+  // When the AIM page action is shown as the right-most page action in the
+  // location bar, it should be positioned flush against the right edge of the
+  // location bar.
+  constexpr int kTrailingEdgePaddingForAim = 5;
   add_trailing_decoration(page_action_container_,
                           /*intra_item_padding=*/0,
-                          /*edge_padding=*/trailing_decorations_edge_padding);
+                          /*edge_padding=*/
+                          GetPageActionInfo().is_aim_last_visible_page_action
+                              ? kTrailingEdgePaddingForAim
+                              : trailing_decorations_edge_padding);
   add_trailing_decoration(ai_mode_hint_label_, /*intra_item_padding=*/0,
                           /*edge_padding=*/trailing_decorations_edge_padding);
   for (ContentSettingImageView* view :
diff --git a/chrome/browser/ui/views/location_bar/webui_location_bar_browsertest.cc b/chrome/browser/ui/views/location_bar/webui_location_bar_browsertest.cc
index 7a8d90b..24963c9 100644
--- a/chrome/browser/ui/views/location_bar/webui_location_bar_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/webui_location_bar_browsertest.cc
@@ -289,26 +289,32 @@
               if (!icon.state || icon.state.pageActionId !== $1) {
                 return false;
               }
-              const button = icon.shadowRoot?.querySelector('cr-icon-button');
+              const button = icon.shadowRoot?.querySelector(
+                  'toolbar-chip-button');
               if (!button) {
                 return false;
               }
-              if (icon.state.icon.handleId == 0) {
-                return $2;
+              if ($2) {
+                return true;
               }
-              if (!button.hasAttribute('iron-icon')) {
+              const iconFromTable = icon.shadowRoot?.querySelector(
+                  'icon-from-table');
+              if (!iconFromTable) {
                 return false;
               }
-              const crIcons =
-                  button.shadowRoot?.querySelectorAll('#icon cr-icon') || [];
-              if (crIcons.length === 0) {
-                return false;
+              const container = iconFromTable.shadowRoot?.querySelector(
+                  '#container');
+              if (!container) return false;
+
+              // Ensure the icon properly resolved and rendered its SVG
+              // contents. If an SVG is missing from the WebUI iconset
+              // bundle, this will fail.
+              const crIcon = container.querySelector('cr-icon');
+              if (crIcon) {
+                return !!crIcon.shadowRoot?.querySelector('svg');
               }
-              for (const crIcon of crIcons) {
-                if (!crIcon.shadowRoot?.querySelector('svg')) {
-                  return false;
-                }
-              }
+
+              // Fallback for non-vector icons.
               return true;
             });
         )"}),
diff --git a/chrome/browser/ui/views/location_bar/webui_location_bar_interactive_uitest.cc b/chrome/browser/ui/views/location_bar/webui_location_bar_interactive_uitest.cc
index 6fcd4ae..23172c97 100644
--- a/chrome/browser/ui/views/location_bar/webui_location_bar_interactive_uitest.cc
+++ b/chrome/browser/ui/views/location_bar/webui_location_bar_interactive_uitest.cc
@@ -76,7 +76,7 @@
     "toolbar-app", "location-bar", "page-action-icons", "page-action-icon"};
 const WebContentsInteractionTestUtil::DeepQuery kAIMButton = {
     "toolbar-app", "location-bar", "page-action-icons", "page-action-icon",
-    "cr-icon-button"};
+    "toolbar-chip-button"};
 
 class ViewWidthObserver
     : public ui::test::
@@ -664,8 +664,9 @@
       // Clear it.
       SendKeyPress(kWebUIToolbarId, ui::VKEY_BACK), WaitTillOmniboxViewText(""),
       // Since text is empty, we should be able to see the AI mode button.
-      WaitForJsResultAt(kWebUIToolbarId, kAIMButton,
-                        "(el) => el.title === 'Ask AI Mode in Google Search'"),
+      WaitForJsResultAt(
+          kWebUIToolbarId, kAIMButton,
+          "(el) => el.tooltip === 'Ask AI Mode in Google Search'"),
       // Click it.
       ClickElement(kWebUIToolbarId, kAIMButton),
       // Should hide classic popup, show AIM one.
diff --git a/chrome/browser/ui/views/omnibox/omnibox_full_popup_webui_content.cc b/chrome/browser/ui/views/omnibox/omnibox_full_popup_webui_content.cc
index 18cbbca2..aa6a2e6 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_full_popup_webui_content.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_full_popup_webui_content.cc
@@ -47,10 +47,12 @@
 }
 
 void OmniboxFullPopupWebUIContent::CloseUI() {
+  // Call base class method first so that any focus operations downstream
+  // will be overridden by focusing the web contents.
+  OmniboxPopupWebUIBaseContent::CloseUI();
+
   controller()->client()->FocusWebContents();
   controller()->edit_model()->OnKillFocus();
-
-  OmniboxPopupWebUIBaseContent::CloseUI();
 }
 
 // TODO(b/504668887): If necessary, copy `OmniboxAimPopupWebUIContent::Clear()`
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_full_presenter.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_full_presenter.cc
index a6caf0f5..39b82cf 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_full_presenter.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_full_presenter.cc
@@ -18,11 +18,15 @@
 #include "chrome/browser/ui/omnibox/omnibox_popup_state_manager.h"
 #include "chrome/browser/ui/omnibox/omnibox_popup_view.h"
 #include "chrome/browser/ui/omnibox/omnibox_view.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/top_container_view.h"
+#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
 #include "chrome/browser/ui/views/omnibox/full_webui_omnibox_frame.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_full_popup_webui_content.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_popup_presenter_base.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_popup_presenter_delegate.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_popup_webui_base_content.h"
+#include "chrome/browser/ui/views/omnibox/omnibox_view_views.h"
 #include "chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.h"
 #include "chrome/browser/ui/webui/omnibox_popup/omnibox_popup_handler.h"
 #include "chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h"
@@ -30,7 +34,14 @@
 #include "chrome/browser/ui/webui/top_chrome/webui_contents_preload_manager.h"
 #include "chrome/browser/ui/webui/top_chrome/webui_contents_wrapper.h"
 #include "components/omnibox/common/omnibox_features.h"
+#include "components/permissions/permission_request_manager.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "ui/display/screen.h"
+#include "ui/events/event.h"
+#include "ui/views/controls/menu/menu_controller.h"
+#include "ui/views/event_monitor.h"
 #include "ui/views/focus/focus_manager.h"
+#include "ui/views/view_class_properties.h"
 #include "ui/views/view_utils.h"
 
 OmniboxPopupFullPresenter::OmniboxPopupFullPresenter(
@@ -89,14 +100,26 @@
     handler->SetAimButtonVisible(omnibox_view->AimButtonVisible());
   }
 
-  if (GetWidget() && !widget_observation_.IsObserving()) {
-    widget_observation_.Observe(GetWidget());
+  views::Widget* parent_widget = delegate().GetLocationBarWidget();
+  if (parent_widget && !parent_widget_observation_.IsObserving()) {
+    parent_widget_observation_.Observe(parent_widget);
+  }
+
+  if (GetWidget() && !popup_widget_observation_.IsObserving()) {
+    popup_widget_observation_.Observe(GetWidget());
+  }
+
+  if (parent_widget && !event_monitor_) {
+    event_monitor_ = views::EventMonitor::CreateApplicationMonitor(
+        this, parent_widget->GetNativeWindow(), {ui::EventType::kMousePressed});
   }
 }
 
 void OmniboxPopupFullPresenter::Hide() {
+  parent_widget_observation_.Reset();
+  event_monitor_.reset();
   forward_events_timer_.Stop();
-  widget_observation_.Reset();
+  popup_widget_observation_.Reset();
   OmniboxPopupPresenterBase::Hide();
 }
 
@@ -107,10 +130,9 @@
       if (GetUIContainer() && GetUIContainer()->GetWidget()) {
         if (auto* focus_manager =
                 GetUIContainer()->GetWidget()->GetFocusManager()) {
-          // Clear stored focus on the container widget so that activating the
-          // popup widget does not restore stale focus or steal focus back from
-          // the WebUI input.
-          focus_manager->SetStoredFocusView(nullptr);
+          // Set stored focus on the container widget to the WebUI content view
+          // so that activating the popup widget restores focus to it.
+          focus_manager->SetStoredFocusView(GetWebUIContent());
         }
       }
     }
@@ -150,6 +172,10 @@
   return true;
 }
 
+bool OmniboxPopupFullPresenter::IsDeactivating() const {
+  return is_deactivating_;
+}
+
 void OmniboxPopupFullPresenter::SynchronizePopupBounds() {
   if (!GetWidget()) {
     return;
@@ -201,57 +227,97 @@
 
 void OmniboxPopupFullPresenter::OnWidgetActivationChanged(views::Widget* widget,
                                                           bool active) {
-  // If omnibox has received focus, it has been told by permission prompt that
-  // permission prompt is closed and omnibox can behave normally again.
-  // Therefore, turn off the 'embedded-permission-showing' flag that forces
-  // omnibox to ignore focus-out events via function.
-  if (active) {
-    weak_factory_.InvalidateWeakPtrs();
-    OnWidgetActivated();
+  // If the widget that changed is the browser window.
+  if (widget == delegate().GetLocationBarWidget()) {
+    if (!active) {
+      // When the browser window deactivates (e.g. due to focusing the WebUI
+      // popup or opening a bubble), we must cache the Omnibox view
+      // as the stored focus view. This ensures that when the browser window
+      // reactivates, the FocusManager restores focus to the Omnibox.
+      //
+      // We must post this as a task because on macOS, native deactivation
+      // events run *before* the FocusManager processes
+      // `StoreFocusedView(false)`. If we set the stored focus view
+      // synchronously, it would immediately get overwritten and clobbered by
+      // the FocusManager caching `nullptr` or the `ContentsWebView` during the
+      // remainder of the deactivation cycle.
+      base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+          FROM_HERE,
+          base::BindOnce(
+              [](base::WeakPtr<OmniboxPopupFullPresenter> presenter) {
+                if (presenter) {
+                  if (auto* omnibox_view =
+                          presenter->location_bar()->GetOmniboxView()) {
+                    if (auto* omnibox_view_views =
+                            static_cast<OmniboxViewViews*>(omnibox_view)) {
+                      if (auto* focus_manager =
+                              omnibox_view_views->GetFocusManager()) {
+                        focus_manager->SetStoredFocusView(omnibox_view_views);
+                      }
+                    }
+                  }
+                }
+              },
+              weak_factory_.GetWeakPtr()));
+    }
     return;
   }
 
-  const bool is_esc = is_handling_escape_key_;
-  is_handling_escape_key_ = false;
-  const bool is_popup_open =
-      controller()->popup_state_manager()->popup_state() ==
-      OmniboxPopupState::kFull;
-  // If deactivation was triggered by an Escape key press while the popup is
-  // open, re-request focus asynchronously instead of closing the popup.
-  if (is_esc && is_popup_open) {
-    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, base::BindOnce(&OmniboxPopupFullPresenter::RequestFocus,
-                                  weak_factory_.GetWeakPtr()));
-    return;
-  }
+  if (widget == GetWidget()) {
+    if (active) {
+      OnWidgetActivated();
+      return;
+    }
 
-  // Close full omnibox popup if there is no directive (like permission prompt
-  // open) to ignore focus out events, and this is a focus out event.
-  if (!active &&
-      controller()->popup_state_manager()->popup_state() ==
-          OmniboxPopupState::kFull &&
-      !location_bar()->in_popup_state_transition() &&
-      !IsPermissionPromptPreventingClose()) {
-    // TODO(b/519724566): Look into using popup_closer here.
-    // Clear results here since `DeactivatePopupAndKillFocus` can happen
-    // after the new tab is opened and can pass on the stale results.
-    controller()->StopAutocomplete(/*clear_result=*/true);
+    const bool is_esc = is_handling_escape_key_;
+    is_handling_escape_key_ = false;
+    const bool is_popup_open =
+        controller()->popup_state_manager()->popup_state() ==
+        OmniboxPopupState::kFull;
+    // If deactivation was triggered by an Escape key press while the popup is
+    // open, re-request focus asynchronously instead of closing the popup.
+    if (is_esc && is_popup_open) {
+      base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+          FROM_HERE, base::BindOnce(&OmniboxPopupFullPresenter::RequestFocus,
+                                    weak_factory_.GetWeakPtr()));
+      return;
+    }
 
-    // Delay killing focus and deactivating popup so that the tab state can
-    // be saved before this operation.
-    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-        FROM_HERE, base::BindOnce(
-                       [](base::WeakPtr<OmniboxPopupFullPresenter> presenter) {
-                         if (presenter) {
-                           presenter->DeactivatePopupAndKillFocus();
-                         }
-                       },
-                       weak_factory_.GetWeakPtr()));
+#if BUILDFLAG(IS_MAC)
+    if (IsShown()) {
+      // When the suggestions popup widget loses activation on macOS, Cocoa
+      // deactivates the child window's view hierarchy, causing the underlying
+      // RenderWidgetHostViewMac to automatically set itself to inactive (hiding
+      // the selection/caret). Force it to remain active so that the WebUI
+      // content continues to paint and respond to events correctly (e.g.
+      // showing the caret after clicking the toolbar). On Aura (Windows/Linux),
+      // child widgets are managed under a unified focus controller and remain
+      // active automatically as long as the browser window is active, so this
+      // is only needed on macOS.
+      base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+          FROM_HERE,
+          base::BindOnce(
+              [](base::WeakPtr<OmniboxPopupFullPresenter> presenter) {
+                if (presenter && presenter->IsShown()) {
+                  auto* webui_content = presenter->GetWebUIContent();
+                  if (webui_content) {
+                    content::WebContents* wc =
+                        webui_content->GetWrappedWebContents();
+                    if (wc && wc->GetRenderWidgetHostView()) {
+                      wc->GetRenderWidgetHostView()->SetActive(true);
+                    }
+                  }
+                }
+              },
+              weak_factory_.GetWeakPtr()));
+    }
+#endif  // BUILDFLAG(IS_MAC)
   }
 }
 
 void OmniboxPopupFullPresenter::DeactivatePopupAndKillFocus() {
   ResetPermissionPromptShowingState();
+  is_deactivating_ = true;
   const bool user_input_in_progress =
       controller()->edit_model()->user_input_in_progress();
   const std::u16string& user_text = controller()->edit_model()->user_text();
@@ -275,30 +341,31 @@
     controller()->edit_model()->Revert();
   }
 
+  if (auto* popup_view = location_bar()->GetOmniboxPopupView()) {
+    popup_view->OnBlur();
+  }
+
+  views::Widget* parent_widget = delegate().GetLocationBarWidget();
+  if (parent_widget && parent_widget->GetFocusManager()) {
+    parent_widget->GetFocusManager()->ClearFocus();
+  }
+
   controller()->client()->FocusWebContents();
   controller()->edit_model()->OnKillFocus();
 
-  if (GetWebUIContent() && GetWebUIContent()->contents_wrapper()) {
-    if (auto* webui_controller =
-            GetWebUIContent()->contents_wrapper()->GetWebUIController()) {
-      if (auto* popup_ui = static_cast<OmniboxPopupUI*>(webui_controller)) {
-        if (auto* popup_handler = popup_ui->popup_handler()) {
-          popup_handler->SetFocus(false);
-        }
-      }
-    }
-  }
-
   // If the user is currently typing do not close the popup.
   if (!user_input_in_progress || user_text.empty()) {
     controller()->popup_state_manager()->SetPopupState(
         OmniboxPopupState::kNone);
   }
+
+  is_deactivating_ = false;
 }
 
 void OmniboxPopupFullPresenter::WidgetDestroyed() {
+  event_monitor_.reset();
   forward_events_timer_.Stop();
-  widget_observation_.Reset();
+  popup_widget_observation_.Reset();
   // Update the popup state manager if widget was destroyed externally, e.g., by
   // the OS. This ensures the popup state manager stays in sync.
   if (controller()->popup_state_manager()->popup_state() ==
@@ -316,3 +383,72 @@
     results_frame->SetForwardMouseEvents(false);
   }
 }
+
+void OmniboxPopupFullPresenter::OnEvent(const ui::Event& event) {
+  // TODO(b/543851644): Figure out how to handle touch events.
+  if (!event.IsMouseEvent()) {
+    return;
+  }
+  const ui::MouseEvent* mouse_event = event.AsMouseEvent();
+  if (mouse_event->type() != ui::EventType::kMousePressed) {
+    return;
+  }
+
+  // Right-clicks (context menu triggers) should never clear focus.
+  if (mouse_event->IsRightMouseButton()) {
+    return;
+  }
+
+  views::Widget* parent_widget = delegate().GetLocationBarWidget();
+  if (!parent_widget) {
+    return;
+  }
+
+  BrowserView* browser_view = BrowserView::GetBrowserViewForNativeWindow(
+      parent_widget->GetNativeWindow());
+  if (!browser_view) {
+    return;
+  }
+
+  gfx::Point cursor_point = display::Screen::Get()->GetCursorScreenPoint();
+  bool contains_top_container = false;
+
+  if (browser_view->top_container()) {
+    gfx::Rect top_container_bounds =
+        browser_view->top_container()->GetBoundsInScreen();
+    gfx::Rect window_bounds = parent_widget->GetWindowBoundsInScreen();
+    contains_top_container = cursor_point.x() >= window_bounds.x() &&
+                             cursor_point.x() < window_bounds.right() &&
+                             cursor_point.y() >= window_bounds.y() &&
+                             cursor_point.y() < top_container_bounds.bottom();
+  }
+
+  bool contains_popup = false;
+  if (IsShown()) {
+    contains_popup =
+        GetWidget()->GetWindowBoundsInScreen().Contains(cursor_point);
+  }
+
+  if (contains_popup) {
+    return;
+  }
+
+  // Clear autocomplete matches and reset activeQueryId_ on WebUI only if
+  // click is outside of the popup and the popup is shown.
+  if (IsShown() && GetWebUIContent() && GetWebUIContent()->contents_wrapper()) {
+    if (auto* webui_controller =
+            GetWebUIContent()->contents_wrapper()->GetWebUIController()) {
+      if (auto* popup_ui = static_cast<OmniboxPopupUI*>(webui_controller)) {
+        if (auto* popup_handler = popup_ui->popup_handler()) {
+          popup_handler->ClearAutocompleteMatches();
+        }
+      }
+    }
+  }
+
+  if (contains_top_container) {
+    return;
+  }
+
+  DeactivatePopupAndKillFocus();
+}
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_full_presenter.h b/chrome/browser/ui/views/omnibox/omnibox_popup_full_presenter.h
index c5284f7..f6b831d2 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_full_presenter.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_full_presenter.h
@@ -9,6 +9,9 @@
 #include "base/scoped_observation.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_popup_presenter_base.h"
+#include "ui/events/event_observer.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/views/event_monitor.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
 
@@ -19,7 +22,8 @@
 // Implements subclass of OmniboxPopupPresenterBase to present a single full
 // WebUI (input row + suggestions dropdown) into the Omnibox popup.
 class OmniboxPopupFullPresenter : public OmniboxPopupPresenterBase,
-                                  public views::WidgetObserver {
+                                  public views::WidgetObserver,
+                                  public ui::EventObserver {
  public:
   OmniboxPopupFullPresenter(LocationBar* location_bar,
                             OmniboxPopupPresenterDelegate& presenter_delegate,
@@ -44,6 +48,8 @@
       const override;
   bool ShouldDetachWebContentsOnHide() const override;
 
+  bool IsDeactivating() const override;
+
  protected:
   // OmniboxPopupPresenterBase:
   // Returns true so that explicit focus requests (`focus_requested_`) are
@@ -58,24 +64,38 @@
 
  private:
   // views::WidgetObserver:
-  // Used to close the popup and show the omnibox_view_views if the correct
-  // conditions are met.
+  // Handles window-wide focus shifts (e.g. clicking the webpage to activate
+  // the parent window or switching apps). We must use this to detect that
+  // focus has left the popup, since the `EventMonitor` is not notified if the
+  // `FocusManager` restores focus to the same native omnibox view upon window
+  // reactivation.
   void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
   void StopForwardingEvents();
 
+  // ui::EventObserver:
+  // Handles click events and determines if the popup should be deactivated.
+  void OnEvent(const ui::Event& event) override;
+
   void DeactivatePopupAndKillFocus();
 
   // Flag set when an ESC key event is intercepted before widget deactivation.
   bool is_handling_escape_key_ = false;
 
   base::ScopedObservation<views::Widget, views::WidgetObserver>
-      widget_observation_{this};
+      popup_widget_observation_{this};
+  base::ScopedObservation<views::Widget, views::WidgetObserver>
+      parent_widget_observation_{this};
+
+  // Used to determine where a click event happened to decide if the popup
+  // should be deactivated.
+  std::unique_ptr<views::EventMonitor> event_monitor_;
 
   // Timer to stop forwarding events after a short delay.
   base::OneShotTimer forward_events_timer_;
 
   // Whether the "first shown" metrics have been logged at least once.
   bool logged_first_shown_metric_ = false;
+  bool is_deactivating_ = false;
 
   base::WeakPtrFactory<OmniboxPopupFullPresenter> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_presenter_base.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_presenter_base.cc
index 67ac067..ded863e 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_presenter_base.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_presenter_base.cc
@@ -418,6 +418,10 @@
   return false;
 }
 
+bool OmniboxPopupPresenterBase::IsDeactivating() const {
+  return false;
+}
+
 void OmniboxPopupPresenterBase::OnWidgetClosed(
     views::Widget::ClosedReason closed_reason) {
   is_deferred_ = false;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_presenter_base.h b/chrome/browser/ui/views/omnibox/omnibox_popup_presenter_base.h
index 272d181..70c70ca 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_presenter_base.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_presenter_base.h
@@ -139,6 +139,8 @@
 
   views::Widget* get_widget_for_testing() { return widget_.get(); }
 
+  views::Widget* GetWidget() const { return widget_.get(); }
+
   void set_widget_for_testing(std::unique_ptr<views::Widget> widget) {
     widget_ = std::move(widget);
   }
@@ -163,6 +165,9 @@
   // which should prevent out-of-focus activation events from hiding the popup.
   bool IsPermissionPromptPreventingClose() const;
 
+  // Returns true if the presenter is currently deactivating.
+  virtual bool IsDeactivating() const;
+
  protected:
   inline static constexpr std::string_view kWebUIPopupMetricPrefix =
       "Omnibox.Popup.WebUI";
@@ -214,8 +219,6 @@
 
   LocationBar* location_bar() const { return location_bar_.get(); }
 
-  views::Widget* GetWidget() const { return widget_.get(); }
-
   OmniboxController* controller() const { return controller_.get(); }
 
   // permissions::PermissionRequestManager::Observer:
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_view_full_webui.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_view_full_webui.cc
index c107dca..0397b6c4 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_view_full_webui.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_view_full_webui.cc
@@ -11,7 +11,7 @@
 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
 #include "chrome/browser/ui/omnibox/omnibox_popup_state_manager.h"
 #include "chrome/browser/ui/omnibox/omnibox_tab_helper.h"
-#include "chrome/browser/ui/views/frame/contents_web_view.h"
+#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_full_popup_webui_content.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_popup_full_presenter.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_popup_presenter_base.h"
@@ -26,6 +26,7 @@
 #include "content/public/browser/web_contents.h"
 #include "ui/gfx/range/range.h"
 #include "ui/views/focus/focus_manager.h"
+#include "ui/views/view.h"
 #include "ui/views/view_class_properties.h"
 
 OmniboxPopupViewFullWebUI::OmniboxPopupViewFullWebUI(
@@ -44,9 +45,25 @@
 
 OmniboxPopupViewFullWebUI::~OmniboxPopupViewFullWebUI() = default;
 
+// This method acts solely as a visibility and focus guard gate. Avoid adding
+// extra feature logic here, as content updates are typically handled via
+// `SyncNativeStateToWebUI` from specific event hooks (such as focus or tab
+// switches).
 void OmniboxPopupViewFullWebUI::UpdatePopupAppearance() {
-  // Intentional no-op. Content updates are handled via `SyncNativeStateToWebUI`
-  // called directly from specific events (focus, tab switch).
+  // Show the suggestions popup and synchronize state if the browser window is
+  // active and the omnibox view holds keyboard focus. This prevents background
+  // updates or asynchronous tab-switch restorations from opening the popup
+  // when the user is interacting with other parts of the UI, while allowing
+  // typing into the WebUI omnibox after clicking the top container.
+  DCHECK(controller()->popup_state_manager()->popup_state() ==
+         OmniboxPopupState::kFull);
+  views::Widget* widget = presenter()->delegate().GetLocationBarWidget();
+  if (widget && widget->IsActive() && location_bar()->IsFocusWithin()) {
+    if (!IsReverting()) {
+      OnFocus(/*query_zps=*/false);
+      SyncNativeStateToWebUI(/*query_zps=*/true);
+    }
+  }
 }
 
 void OmniboxPopupViewFullWebUI::SyncNativeStateToWebUI(bool query_zps) {
@@ -105,33 +122,6 @@
 
   auto* edit_model = controller()->edit_model();
   bool logically_focused = edit_model->has_focus();
-  bool is_popup_open = controller()->popup_state_manager()->popup_state() ==
-                       OmniboxPopupState::kFull;
-
-  // Clicking a tab moves focus to the tab strip before `SaveStateToTab` runs,
-  // causing `window blur` and wiping `edit_model->has_focus()`. Unless the
-  // webpage (`ContentsWebView`) is focused, the user didn't click the page to
-  // blur the omnibox, so preserve logical focus across tab switches.
-  if (is_popup_open && !logically_focused) {
-    bool webpage_focused = false;
-    if (auto* omnibox_view_views =
-            static_cast<OmniboxViewViews*>(omnibox_view_)) {
-      if (auto* focus_manager = omnibox_view_views->GetFocusManager()) {
-        if (views::View* focused_view = focus_manager->GetFocusedView()) {
-          for (views::View* v = focused_view; v; v = v->parent()) {
-            if (v->GetProperty(views::kElementIdentifierKey) ==
-                ContentsWebView::kContentsWebViewElementId) {
-              webpage_focused = true;
-              break;
-            }
-          }
-        }
-      }
-    }
-    if (!webpage_focused) {
-      logically_focused = true;
-    }
-  }
 
   // The text input lives in WebUI, so the native view hierarchy does not
   // track selection. Fetch it from the WebUI handler.
@@ -297,6 +287,12 @@
   }
 }
 
+void OmniboxPopupViewFullWebUI::OnBlur() {
+  if (auto* popup_handler = GetPopupHandler()) {
+    popup_handler->SetFocus(false);
+  }
+}
+
 OmniboxPopupHandler* OmniboxPopupViewFullWebUI::GetPopupHandler() {
   if (!presenter() || !presenter()->GetWebUIContent()) {
     return nullptr;
@@ -307,3 +303,11 @@
   auto* popup_ui = static_cast<OmniboxPopupUI*>(webui_controller);
   return popup_ui ? popup_ui->popup_handler() : nullptr;
 }
+
+bool OmniboxPopupViewFullWebUI::IsReverting() const {
+  return is_reverting_;
+}
+
+void OmniboxPopupViewFullWebUI::SetIsReverting(bool reverting) {
+  is_reverting_ = reverting;
+}
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_view_full_webui.h b/chrome/browser/ui/views/omnibox/omnibox_popup_view_full_webui.h
index f639931..bbc4aa112 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_view_full_webui.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_view_full_webui.h
@@ -44,6 +44,12 @@
   // was already open, sends a dedicated `SetFocus(true)` Mojo IPC to ensure
   // DOM input focus in the WebUI is restored without resetting input state.
   void OnFocus(bool query_zps) override;
+  // Called when the native omnibox loses focus. Sends a dedicated
+  // `SetFocus(false)` Mojo IPC to ensure DOM input focus in the WebUI is
+  // cleared.
+  void OnBlur() override;
+  bool IsReverting() const override;
+  void SetIsReverting(bool reverting) override;
 
  private:
   // Gets the OmniboxPopupHandler associated with this view's WebUI.
@@ -54,6 +60,7 @@
   std::optional<std::u16string> last_sent_text_;
   // Caches the last focus state sent to the WebUI to detect focus transitions.
   std::optional<bool> last_sent_focus_;
+  bool is_reverting_ = false;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_OMNIBOX_OMNIBOX_POPUP_VIEW_FULL_WEBUI_H_
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui.h b/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui.h
index a4cd226..097ea82f 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui.h
@@ -62,6 +62,8 @@
   // OmniboxPopupView:
   bool IsOpen() const override;
 
+  LocationBar* location_bar() const { return location_bar_; }
+
  protected:
   OmniboxPopupViewWebUI(OmniboxView* omnibox_view,
                         OmniboxController* controller,
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui_interactive_uitest.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui_interactive_uitest.cc
index a0b0d7c..9fe0bd9 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui_interactive_uitest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui_interactive_uitest.cc
@@ -257,39 +257,6 @@
         {});
   }
 
-  std::unique_ptr<views::Widget> DeactivatePopupWidget() {
-    // Wait for the popup transition to complete (clearing
-    // in_popup_state_transition).
-    EXPECT_TRUE(base::test::RunUntil(
-        [&]() { return !location_bar()->in_popup_state_transition(); }));
-
-    // Since in background test runners the browser window is not natively
-    // active, focusing on the web contents will not activate the browser
-    // widget. Create a temporary dummy widget and activate it to force the main
-    // browser window (and the child popup widget) inactive.
-    auto dummy_widget = std::make_unique<views::Widget>();
-    views::Widget::InitParams params(
-        views::Widget::InitParams::CLIENT_OWNS_WIDGET,
-        views::Widget::InitParams::TYPE_WINDOW);
-    params.context = location_bar()->GetWidget()->GetNativeWindow();
-    params.bounds = gfx::Rect(0, 0, 100, 100);
-    dummy_widget->Init(std::move(params));
-    dummy_widget->Show();
-    dummy_widget->Activate();
-
-    // In background test runners, the OS blocks background applications from
-    // changing native window activation.
-    // Directly notify the presenter observer of focus loss.
-    auto* popup_view = static_cast<OmniboxPopupViewWebUI*>(
-        location_bar()->GetOmniboxPopupView());
-    auto* presenter =
-        static_cast<OmniboxPopupFullPresenter*>(popup_view->presenter());
-    static_cast<views::WidgetObserver*>(presenter)->OnWidgetActivationChanged(
-        presenter->get_widget_for_testing(), /*active=*/false);
-
-    return dummy_widget;
-  }
-
  private:
   base::test::ScopedFeatureList feature_list_full_v2_;
 };
@@ -403,81 +370,6 @@
   }));
 }
 
-// TODO(crbug.com/542637306): Re-enable this test on Mac.
-#if BUILDFLAG(IS_MAC)
-#define MAYBE_DeactivationClearsPopupState DISABLED_DeactivationClearsPopupState
-#else
-#define MAYBE_DeactivationClearsPopupState DeactivationClearsPopupState
-#endif
-IN_PROC_BROWSER_TEST_F(OmniboxPopupViewWebUIFullV2Test,
-                       MAYBE_DeactivationClearsPopupState) {
-  // Focus the location bar to ensure the Omnibox has active focus and the popup
-  // is open.
-  location_bar()->FocusLocation(/*is_user_initiated=*/true,
-                                /*clear_focus_if_failed=*/false);
-
-  // Verify that the popup state is initially kFull.
-  ASSERT_EQ(OmniboxPopupState::kFull,
-            controller()->popup_state_manager()->popup_state());
-
-  auto dummy_widget = DeactivatePopupWidget();
-
-  // Wait for the deferred deactivation task to run and verify that the popup
-  // state transitions to kNone.
-  EXPECT_TRUE(base::test::RunUntil([&]() {
-    return controller()->popup_state_manager()->popup_state() ==
-           OmniboxPopupState::kNone;
-  }));
-}
-
-// TODO(crbug.com/536046012): Re-enable this test.
-IN_PROC_BROWSER_TEST_F(OmniboxPopupViewWebUIFullV2Test,
-                       DISABLED_DeactivationWithTextDoesNotClearPopupState) {
-  // Focus the location bar to ensure the Omnibox has active focus and the popup
-  // is open.
-  location_bar()->FocusLocation(/*is_user_initiated=*/true,
-                                /*clear_focus_if_failed=*/false);
-
-  // Verify that the popup state is initially kFull.
-  ASSERT_EQ(OmniboxPopupState::kFull,
-            controller()->popup_state_manager()->popup_state());
-
-  omnibox_view()->SetUserText(u"test query");
-
-  auto dummy_widget = DeactivatePopupWidget();
-
-  // Wait for the deferred deactivation task to run and verify that the popup
-  // state transitions to kNone.
-  EXPECT_TRUE(base::test::RunUntil([&]() {
-    return controller()->popup_state_manager()->popup_state() !=
-           OmniboxPopupState::kNone;
-  }));
-}
-
-IN_PROC_BROWSER_TEST_F(OmniboxPopupViewWebUIFullV2Test,
-                       DeactivationWithClearedTextClearsPopupState) {
-  // Focus the location bar to ensure the Omnibox has active focus and the popup
-  // is open.
-  location_bar()->FocusLocation(/*is_user_initiated=*/true,
-                                /*clear_focus_if_failed=*/false);
-
-  // Verify that the popup state is initially kFull.
-  ASSERT_EQ(OmniboxPopupState::kFull,
-            controller()->popup_state_manager()->popup_state());
-
-  omnibox_view()->SetUserText(u"test query");
-  omnibox_view()->SetUserText(u"");
-
-  auto dummy_widget = DeactivatePopupWidget();
-
-  // Wait for the deferred deactivation task to run and verify that the popup
-  // state transitions to kNone.
-  EXPECT_TRUE(base::test::RunUntil([&]() {
-    return controller()->popup_state_manager()->popup_state() ==
-           OmniboxPopupState::kNone;
-  }));
-}
-
 class OmniboxPopupDimensionsTest : public OmniboxPopupViewWebUITest,
                                    public testing::WithParamInterface<bool> {
  public:
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index d56f870..4dcb13b0 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -57,10 +57,13 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/frame/contents_web_view.h"
 #include "chrome/browser/ui/views/location_bar/icon_label_bubble_view.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_placeholder_util.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_popup_closer.h"
+#include "chrome/browser/ui/views/omnibox/omnibox_popup_presenter_base.h"
+#include "chrome/browser/ui/views/omnibox/omnibox_popup_webui_base_content.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_text_view.h"
 #include "chrome/browser/ui/views/page_action/page_action_container_view.h"
 #include "chrome/browser/ui/views/page_action/page_action_view.h"
@@ -119,6 +122,7 @@
 #include "ui/base/models/image_model.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/compositor/layer.h"
+#include "ui/display/screen.h"
 #include "ui/events/event.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/font_list.h"
@@ -134,6 +138,7 @@
 #include "ui/views/button_drag_utils.h"
 #include "ui/views/controls/focus_ring.h"
 #include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/focus/focus_manager.h"
 #include "ui/views/view_class_properties.h"
 #include "ui/views/widget/widget.h"
 #include "url/gurl.h"
@@ -1606,9 +1611,6 @@
 void OmniboxViewViews::OnBlur() {
   views::Textfield::OnBlur();
 
-  // Save the user's existing selection to restore it later.
-  saved_selection_for_focus_change_ = GetSelectedRange();
-
   // If focus is transferring to a WebUI popup widget (e.g., Full Popup or AIM
   // Popup), treat this as a logical focus transfer rather than a true blur.
   // Keep the edit model's focus state active, and skip all reversion/blurring.
@@ -1619,6 +1621,9 @@
     return;
   }
 
+  // Save the user's existing selection to restore it later.
+  saved_selection_for_focus_change_ = GetSelectedRange();
+
   // If the view is showing text that's not user-text, revert the text to the
   // permanent display text. This usually occurs if Steady State Elisions is on
   // and the user has unelided, but not edited the URL.
diff --git a/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc b/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc
index 063dec31..acd863a 100644
--- a/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc
+++ b/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/layout_constants.h"
+#include "chrome/browser/ui/omnibox/omnibox_next_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_aim_popup_webui_content.h"
@@ -300,22 +301,22 @@
     return gfx::Insets::TLBR(6, 1, 5, 1);
   }
 #if BUILDFLAG(IS_MAC)
-  // On macOS, the popup is hosted in a separate native window. Converting
-  // sub-pixel Views layout coordinates of the location bar to integer screen
-  // coordinates for the OS window positioning introduces rounding discrepancies
-  // (up to 1px). Additionally, differences in visual border rendering thickness
-  // (1px CSS outline in WebUI vs 0.5px native retina border) require a slightly
-  // tighter fit.
+  // On macOS, the popup is hosted in a separate native window. Differences in
+  // visual border rendering thickness (1px CSS outline in WebUI vs 0.5px native
+  // retina border) require a slightly tighter horizontal fit (5px instead of
+  // 6px).
   //
-  // To avoid adding platform-specific 1px hacks or relative offsets in the
-  // shared WebUI CSS:
-  // - We set the vertical inset to 4px (1px smaller than default 5px). This
-  //   effectively offsets the widget top down by 1px, centering the 32px
-  //   WebUI searchbox inside the 34px native height.
-  // - We set the horizontal inset to 5px (1px smaller than default 6px). This
-  //   narrows the widget by 2px overall, aligning the searchbox's visual
-  //   boundaries with the native location bar's visual border.
-  return gfx::Insets::VH(4, 5);
+  // When WebUIOmniboxFullPopup is enabled:
+  // - Top inset is kept at 5px so the widget anchors flush with the bottom of
+  //   the tab line (y = LocationBar_y - 5 = 0).
+  // - Horizontal insets are 5px (1px tighter) to align visual boundaries with
+  //   the native location bar.
+  // - Bottom inset is 4px to accommodate the 32px WebUI searchbox.
+  // When disabled, we fall back to standard (5, 6) insets.
+  if (omnibox::IsWebUIOmniboxFullPopupEnabled()) {
+    return gfx::Insets::TLBR(5, 5, 4, 5);
+  }
+  return gfx::Insets::VH(5, 6);
 #else
   return gfx::Insets::VH(5, 6);
 #endif
diff --git a/chrome/browser/ui/views/page_action/webui_page_action_control.cc b/chrome/browser/ui/views/page_action/webui_page_action_control.cc
index 17251dd7..bc68fbc 100644
--- a/chrome/browser/ui/views/page_action/webui_page_action_control.cc
+++ b/chrome/browser/ui/views/page_action/webui_page_action_control.cc
@@ -237,6 +237,10 @@
   state->icon = cached_icon_ =
       owner_->webui_delegate_->GetIconTable().RegisterImageModelTryReuse(
           image_model, cached_icon_);
+  state->text = model->GetText();
+  state->should_show_chip = model->ShouldShowSuggestionChip();
+  state->should_animate_chip_in = model->GetShouldAnimateChipIn();
+  state->should_animate_chip_out = model->GetShouldAnimateChipOut();
   return state;
 }
 
diff --git a/chrome/browser/ui/views/renderer_context_menu/render_view_context_menu_views.cc b/chrome/browser/ui/views/renderer_context_menu/render_view_context_menu_views.cc
index 0eb9f31..d38a539 100644
--- a/chrome/browser/ui/views/renderer_context_menu/render_view_context_menu_views.cc
+++ b/chrome/browser/ui/views/renderer_context_menu/render_view_context_menu_views.cc
@@ -251,6 +251,19 @@
       *accel = ui::Accelerator(ui::VKEY_U, ui::EF_CONTROL_DOWN);
       return true;
 
+    case IDC_CONTENT_CONTEXT_OPEN_IN_READING_MODE: {
+      ui::AcceleratorProvider* accelerator_provider =
+          GetBrowserAcceleratorProvider();
+      if (!accelerator_provider) {
+        return false;
+      }
+      // Reading mode uses different command IDs for different ways of opening
+      // it, so adjust to use the command ID for the keyboard shortcut to grab
+      // the proper accelerator.
+      return accelerator_provider->GetAcceleratorForCommandId(
+          IDC_SHOW_READING_MODE_KEYBOARD, accel);
+    }
+
     case IDC_CONTENT_CONTEXT_EMOJI:
 #if BUILDFLAG(IS_WIN)
       *accel = ui::Accelerator(ui::VKEY_OEM_PERIOD, ui::EF_COMMAND_DOWN);
diff --git a/chrome/browser/ui/views/tabs/common/pinned_tab_container_view.cc b/chrome/browser/ui/views/tabs/common/pinned_tab_container_view.cc
index 94fe786..6c492fd1 100644
--- a/chrome/browser/ui/views/tabs/common/pinned_tab_container_view.cc
+++ b/chrome/browser/ui/views/tabs/common/pinned_tab_container_view.cc
@@ -47,7 +47,11 @@
                                         TabCollectionAnimatingLayoutManager>(
           std::make_unique<views::DelegatingLayoutManager>(this),
           *this,
-          TabCollectionAnimatingLayoutManager::AnimationAxis::kHorizontal))) {
+          collection_node && collection_node->orientation() ==
+                                 TabStripOrientation::kHorizontal
+              ? TabCollectionAnimatingLayoutManager::AnimationAxis::kHorizontal
+              : TabCollectionAnimatingLayoutManager::AnimationAxis::
+                    kHorizontalWrappingVertically))) {
   collection_node->set_remove_child_from_node(base::BindRepeating(
       &TabCollectionAnimatingLayoutManager::AnimateAndDestroyChildView,
       base::Unretained(base::to_address(layout_manager_))));
diff --git a/chrome/browser/ui/views/tabs/common/tab_collection_animating_layout_manager.cc b/chrome/browser/ui/views/tabs/common/tab_collection_animating_layout_manager.cc
index 0cf69f1..ef561af 100644
--- a/chrome/browser/ui/views/tabs/common/tab_collection_animating_layout_manager.cc
+++ b/chrome/browser/ui/views/tabs/common/tab_collection_animating_layout_manager.cc
@@ -115,7 +115,7 @@
       target_layout_manager_->GetPreferredSize(host);
   if (animate_host_size_ && animation_.is_animating() &&
       !delegate_->IsDragging()) {
-    if (animation_axis_ == AnimationAxis::kVertical) {
+    if (IsVerticalOrWrappingVertically()) {
       target_preferred_size.set_height(current_layout_content_size_);
     } else {
       target_preferred_size.set_width(current_layout_content_size_);
@@ -137,7 +137,7 @@
       target_layout_manager_->GetPreferredSize(host, available_size);
   if (animate_host_size_ && animation_.is_animating() &&
       !delegate_->IsDragging()) {
-    if (animation_axis_ == AnimationAxis::kVertical) {
+    if (IsVerticalOrWrappingVertically()) {
       target_preferred_size.set_height(current_layout_content_size_);
     } else {
       target_preferred_size.set_width(current_layout_content_size_);
@@ -292,11 +292,11 @@
     return false;
   }
 
-  // Calculate the target layout with unbounded height for vertical axis, or
-  // bounded by available parent width for horizontal axis, to support dynamic
-  // tab shrinking and scrolling.
+  // Calculate the target layout with unbounded height for vertical or
+  // horizontal-wrapping-vertically axes, or bounded by available parent width
+  // for horizontal axis, to support dynamic tab shrinking and scrolling.
   views::SizeBounds size_bounds;
-  if (animation_axis_ == AnimationAxis::kVertical) {
+  if (IsVerticalOrWrappingVertically()) {
     size_bounds = views::SizeBounds(std::max(host_view()->width(), 0), {});
   } else {
     const int available_width = host_view()->parent()
@@ -316,7 +316,7 @@
   // Animating bounds along the cross-axis is not supported and layout should
   // immediately snap to target for cross-axis bounds changes.
   const bool cross_axis_changed =
-      animation_axis_ == AnimationAxis::kVertical
+      IsVerticalOrWrappingVertically()
           ? current_layout_.host_size.width() != new_target.host_size.width()
           : current_layout_.host_size.height() != new_target.host_size.height();
   if (!current_layout_.host_size.IsEmpty() && cross_axis_changed) {
@@ -425,7 +425,7 @@
   // interpolated host size along the animation axis to prevent the layout from
   // shrinking momentarily when tabs are swapping positions (e.g., reordering
   // tabs).
-  if (animation_axis_ == AnimationAxis::kVertical) {
+  if (IsVerticalOrWrappingVertically()) {
     current_layout_content_size_ =
         gfx::Tween::IntValueBetween(value, starting_layout_.host_size.height(),
                                     target_layout_.host_size.height());
@@ -436,8 +436,7 @@
   }
 
   auto get_animation_axis_end = [this](const gfx::Rect& bounds) {
-    return animation_axis_ == AnimationAxis::kVertical ? bounds.bottom()
-                                                       : bounds.right();
+    return IsVerticalOrWrappingVertically() ? bounds.bottom() : bounds.right();
   };
 
   for (views::View* child_view : host_view()->children()) {
@@ -667,3 +666,9 @@
   view->ClearProperty(kPreviousCollectionBounds);
   view->ClearProperty(kSourceLayoutInfo);
 }
+
+bool TabCollectionAnimatingLayoutManager::IsVerticalOrWrappingVertically()
+    const {
+  return animation_axis_ == AnimationAxis::kVertical ||
+         animation_axis_ == AnimationAxis::kHorizontalWrappingVertically;
+}
diff --git a/chrome/browser/ui/views/tabs/common/tab_collection_animating_layout_manager.h b/chrome/browser/ui/views/tabs/common/tab_collection_animating_layout_manager.h
index 5594719..2652d0f7 100644
--- a/chrome/browser/ui/views/tabs/common/tab_collection_animating_layout_manager.h
+++ b/chrome/browser/ui/views/tabs/common/tab_collection_animating_layout_manager.h
@@ -27,7 +27,11 @@
  public:
   // Controls along which axis view bounds are animated during animate-in and
   // animate-out transitions.
-  enum class AnimationAxis { kVertical, kHorizontal };
+  enum class AnimationAxis {
+    kVertical,
+    kHorizontal,
+    kHorizontalWrappingVertically,
+  };
 
   // Represents how animations should progress along the animation axis.
   enum class AnimationDirection { kStartToEnd, kEndToStart };
@@ -137,6 +141,9 @@
   // current `animation_` value.
   views::ProposedLayout InterpolateLayout(double value) const;
 
+  // Returns true if the animation axis is vertical or wraps vertically.
+  bool IsVerticalOrWrappingVertically() const;
+
   // Removes and destroys any views marked for deletion that are no longer
   // needed for animated effects. This is called after a new layout has been
   // calculated.
diff --git a/chrome/browser/ui/views/tabs/common/tab_collection_animating_layout_manager_unittest.cc b/chrome/browser/ui/views/tabs/common/tab_collection_animating_layout_manager_unittest.cc
index 5adf3b4..5b7613f 100644
--- a/chrome/browser/ui/views/tabs/common/tab_collection_animating_layout_manager_unittest.cc
+++ b/chrome/browser/ui/views/tabs/common/tab_collection_animating_layout_manager_unittest.cc
@@ -58,6 +58,18 @@
 
       // Calculate the total height based on all child layouts.
       layout.host_size = gfx::Size(size_bounds.width().value_or(100), y);
+    } else if (axis_ == TabCollectionAnimatingLayoutManager::AnimationAxis::
+                            kHorizontalWrappingVertically) {
+      // Place children horizontally within a fixed-width parent.
+      int x = 0;
+      for (const auto& child : host_view()->children()) {
+        layout.child_layouts.emplace_back(child, child->GetVisible(),
+                                          gfx::Rect(x, 0, 20, 100));
+        x += 20;
+      }
+
+      // Calculate host size with fixed bounded width and total height.
+      layout.host_size = gfx::Size(size_bounds.width().value_or(100), 100);
     } else {
       // Place children at full height, starting from the left of the parent.
       int x = 0;
@@ -215,10 +227,6 @@
   gfx::ScopedAnimationDurationScaleMode normal_duration_mode(
       gfx::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
 
-  const bool is_vertical =
-      animation_axis() ==
-      TabCollectionAnimatingLayoutManager::AnimationAxis::kVertical;
-
   // Setup the layout manager and initial child views.
   SetLayoutManager(std::make_unique<TabCollectionAnimatingLayoutManager>(
       std::make_unique<TestLayoutManager>(animation_axis()),
@@ -235,14 +243,18 @@
   // Advance time such that the initial add animation has time to complete.
   task_environment()->FastForwardBy(base::Seconds(1));
 
-  const int expected_size = 40;
+  const int expected_size =
+      animation_axis() == TabCollectionAnimatingLayoutManager::AnimationAxis::
+                              kHorizontalWrappingVertically
+          ? 100
+          : 40;
 
   // Swap tabs.
   host_view()->ReorderChildView(child2, 0);
   host_view()->InvalidateLayout();
   widget()->LayoutRootViewIfNecessary();
 
-  // Ensures preferred size remains stable at 40dips during the swap animation.
+  // Ensures preferred size remains stable during the swap animation.
   // The animation duration is typically 200ms, so we poll 20 times in 10ms
   // steps.
   for (int i = 0; i < 20; ++i) {
@@ -252,7 +264,10 @@
     ASSERT_TRUE(layout_manager()->is_animating());
 
     // Verify stability at every frame along the animation axis.
-    if (is_vertical) {
+    if (animation_axis() ==
+            TabCollectionAnimatingLayoutManager::AnimationAxis::kVertical ||
+        animation_axis() == TabCollectionAnimatingLayoutManager::AnimationAxis::
+                                kHorizontalWrappingVertically) {
       EXPECT_EQ(layout_manager()->GetPreferredSize(host_view()).height(),
                 expected_size);
     } else {
@@ -266,11 +281,19 @@
 
   // Verify final state.
   EXPECT_FALSE(layout_manager()->is_animating());
-  if (is_vertical) {
+  if (animation_axis() ==
+      TabCollectionAnimatingLayoutManager::AnimationAxis::kVertical) {
     EXPECT_EQ(layout_manager()->GetPreferredSize(host_view()).height(),
               expected_size);
     EXPECT_EQ(child2->bounds(), gfx::Rect(0, 0, 100, 20));
     EXPECT_EQ(child1->bounds(), gfx::Rect(0, 20, 100, 20));
+  } else if (animation_axis() ==
+             TabCollectionAnimatingLayoutManager::AnimationAxis::
+                 kHorizontalWrappingVertically) {
+    EXPECT_EQ(layout_manager()->GetPreferredSize(host_view()).height(),
+              expected_size);
+    EXPECT_EQ(child2->bounds(), gfx::Rect(0, 0, 20, 100));
+    EXPECT_EQ(child1->bounds(), gfx::Rect(20, 0, 20, 100));
   } else {
     EXPECT_EQ(layout_manager()->GetPreferredSize(host_view()).width(),
               expected_size);
@@ -286,17 +309,30 @@
         testing::Bool(),
         testing::Values(
             TabCollectionAnimatingLayoutManager::AnimationAxis::kVertical,
-            TabCollectionAnimatingLayoutManager::AnimationAxis::kHorizontal)),
+            TabCollectionAnimatingLayoutManager::AnimationAxis::kHorizontal,
+            TabCollectionAnimatingLayoutManager::AnimationAxis::
+                kHorizontalWrappingVertically)),
     [](const testing::TestParamInfo<
         TabCollectionAnimatingLayoutManagerTest::ParamType>& info) {
       const bool duration_enabled = std::get<0>(info.param);
       const auto axis = std::get<1>(info.param);
+      std::string axis_str;
+      switch (axis) {
+        case TabCollectionAnimatingLayoutManager::AnimationAxis::kVertical:
+          axis_str = "Vertical";
+          break;
+        case TabCollectionAnimatingLayoutManager::AnimationAxis::kHorizontal:
+          axis_str = "Horizontal";
+          break;
+        case TabCollectionAnimatingLayoutManager::AnimationAxis::
+            kHorizontalWrappingVertically:
+          axis_str = "HorizontalWrappingVertically";
+          break;
+      }
       return base::StrCat({
           duration_enabled ? "AnimationDurationEnabled"
                            : "AnimationDurationDisabled",
           "_",
-          axis == TabCollectionAnimatingLayoutManager::AnimationAxis::kVertical
-              ? "Vertical"
-              : "Horizontal",
+          axis_str,
       });
     });
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc
index 0560e6d5..f2c7a6f 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -1134,30 +1134,32 @@
   }
 }
 
-void AppMenu::RunMenu(views::MenuButtonController* host) {
+void AppMenu::RunMenu(views::MenuButtonController* host,
+                      ui::mojom::MenuSourceType source_type) {
   base::RecordAction(UserMetricsAction("ShowAppMenu"));
   UMA_HISTOGRAM_ENUMERATION("WrenchMenu.MenuAction", MENU_ACTION_MENU_OPENED,
                             LIMIT_MENU_ACTION);
 
-  menu_runner_->RunMenuAt(
-      host->button()->GetWidget(), host,
-      host->button()->GetAnchorBoundsInScreen(),
-      views::MenuAnchorPosition::kTopRight, ui::mojom::MenuSourceType::kNone,
-      /*native_view_for_gestures=*/gfx::NativeView(), /*corners=*/std::nullopt,
-      "Chrome.AppMenu.MenuHostInitToNextFramePresented");
+  menu_runner_->RunMenuAt(host->button()->GetWidget(), host,
+                          host->button()->GetAnchorBoundsInScreen(),
+                          views::MenuAnchorPosition::kTopRight, source_type,
+                          /*native_view_for_gestures=*/gfx::NativeView(),
+                          /*corners=*/std::nullopt,
+                          "Chrome.AppMenu.MenuHostInitToNextFramePresented");
 }
 
 void AppMenu::RunMenu(views::Widget* parent,
-                      const gfx::Rect& anchor_screen_bounds) {
+                      const gfx::Rect& anchor_screen_bounds,
+                      ui::mojom::MenuSourceType source_type) {
   base::RecordAction(UserMetricsAction("ShowAppMenu"));
   UMA_HISTOGRAM_ENUMERATION("WrenchMenu.MenuAction", MENU_ACTION_MENU_OPENED,
                             LIMIT_MENU_ACTION);
 
-  menu_runner_->RunMenuAt(
-      parent, nullptr, anchor_screen_bounds,
-      views::MenuAnchorPosition::kTopRight, ui::mojom::MenuSourceType::kNone,
-      /*native_view_for_gestures=*/gfx::NativeView(), /*corners=*/std::nullopt,
-      "Chrome.AppMenu.MenuHostInitToNextFramePresented");
+  menu_runner_->RunMenuAt(parent, nullptr, anchor_screen_bounds,
+                          views::MenuAnchorPosition::kTopRight, source_type,
+                          /*native_view_for_gestures=*/gfx::NativeView(),
+                          /*corners=*/std::nullopt,
+                          "Chrome.AppMenu.MenuHostInitToNextFramePresented");
 }
 
 void AppMenu::CloseMenu() {
diff --git a/chrome/browser/ui/views/toolbar/app_menu.h b/chrome/browser/ui/views/toolbar/app_menu.h
index 63aa030..ede47bd 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.h
+++ b/chrome/browser/ui/views/toolbar/app_menu.h
@@ -20,7 +20,7 @@
 #include "chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_everything_menu.h"
 #include "components/saved_tab_groups/public/saved_tab_group.h"
 #include "ui/base/models/menu_model.h"
-#include "ui/base/mojom/menu_source_type.mojom-forward.h"
+#include "ui/base/mojom/menu_source_type.mojom.h"
 #include "ui/views/controls/menu/menu_delegate.h"
 
 class BookmarkMenuDelegate;
@@ -48,10 +48,17 @@
   ~AppMenu() override;
 
   // Shows the menu relative to the specified controller's button.
-  void RunMenu(views::MenuButtonController* host);
+  // TODO(crbug.com/543867698): Specify a reasonable source type instead of
+  // defaulting to kNone.
+  void RunMenu(
+      views::MenuButtonController* host,
+      ui::mojom::MenuSourceType source_type = ui::mojom::MenuSourceType::kNone);
 
   // Shows the menu with an anchor in the parent widget given its bounds.
-  void RunMenu(views::Widget* parent, const gfx::Rect& anchor_screen_bounds);
+  void RunMenu(
+      views::Widget* parent,
+      const gfx::Rect& anchor_screen_bounds,
+      ui::mojom::MenuSourceType source_type = ui::mojom::MenuSourceType::kNone);
 
   // Closes the menu if it is open, otherwise does nothing.
   void CloseMenu();
diff --git a/chrome/browser/ui/views/toolbar/webui_app_menu_control.cc b/chrome/browser/ui/views/toolbar/webui_app_menu_control.cc
index 12bc812b..8a7383d 100644
--- a/chrome/browser/ui/views/toolbar/webui_app_menu_control.cc
+++ b/chrome/browser/ui/views/toolbar/webui_app_menu_control.cc
@@ -6,19 +6,17 @@
 
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/toolbar/app_menu_model.h"
 #include "chrome/browser/ui/views/frame/app_menu_button_observer.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/toolbar/app_menu.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/browser/ui/views/toolbar/webui_toolbar_web_view.h"
-#include "chrome/grit/branded_strings.h"
-#include "chrome/grit/generated_resources.h"
 #include "ui/base/interaction/element_tracker.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/views/accessible_pane_view.h"
 #include "ui/views/controls/menu/menu_runner.h"
 #include "ui/views/interaction/element_tracker_views.h"
-#include "ui/views/view_utils.h"
 
 namespace {
 
@@ -101,7 +99,7 @@
 }
 
 bool WebUIAppMenuControl::IsMenuShowing() const {
-  return menu_runner_ && menu_runner_->IsRunning();
+  return menu_ && menu_->IsShowing();
 }
 
 views::DialogDelegate* WebUIAppMenuControl::GetDialogDelegate() {
@@ -110,8 +108,8 @@
 }
 
 void WebUIAppMenuControl::CloseMenu() {
-  if (menu_runner_) {
-    menu_runner_->Cancel();
+  if (menu_) {
+    menu_->CloseMenu();
   }
 }
 
@@ -149,11 +147,19 @@
     return;
   }
 
-  ToolbarView* toolbar_view =
-      BrowserView::GetBrowserViewForBrowser(delegate_->GetBrowser())->toolbar();
+  BrowserWindowInterface* browser_window = delegate_->GetBrowser();
+  BrowserView* browser_view =
+      BrowserView::GetBrowserViewForBrowser(browser_window);
+  CHECK(browser_view);
+  ToolbarView* toolbar_view = browser_view->toolbar();
   CHECK(toolbar_view);
-  Browser* browser = toolbar_view->browser();
+  Browser* browser = browser_window->GetBrowserForMigrationOnly();
 
+  // Explicitly destroy the old menu UI before recreating the model. `AppMenu`
+  // holds a `raw_ptr` to `AppMenuModel`. If we don't reset `menu_` first,
+  // recreating `menu_model_` would temporarily leave `menu_` with a dangling
+  // pointer to the destroyed model.
+  menu_.reset();
   menu_model_ = std::make_unique<AppMenuModel>(
       toolbar_view, browser, toolbar_view->app_menu_icon_controller(),
       AppMenuModel::GetAlertItemForRunningTutorial(browser));
@@ -165,14 +171,12 @@
                  views::MenuRunner::INVOKED_FROM_KEYBOARD;
   }
 
-  menu_runner_ = std::make_unique<views::MenuRunner>(
-      menu_model_.get(), run_flags,
+  menu_ = std::make_unique<AppMenu>(
+      browser, menu_model_.get(), run_flags,
       base::BindRepeating(&WebUIAppMenuControl::UpdateOpenState,
-                          base::Unretained(this)));
+                          weak_ptr_factory_.GetWeakPtr()));
 
-  menu_runner_->RunMenuAt(delegate_->GetView()->GetWidget(), nullptr,
-                          anchor_bounds, views::MenuAnchorPosition::kTopRight,
-                          source);
+  menu_->RunMenu(delegate_->GetView()->GetWidget(), anchor_bounds, source);
   UpdateOpenState();
 }
 
diff --git a/chrome/browser/ui/views/toolbar/webui_app_menu_control.h b/chrome/browser/ui/views/toolbar/webui_app_menu_control.h
index 02cecd5..fb90fa7 100644
--- a/chrome/browser/ui/views/toolbar/webui_app_menu_control.h
+++ b/chrome/browser/ui/views/toolbar/webui_app_menu_control.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/memory/raw_ref.h"
+#include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "chrome/browser/ui/toolbar/app_menu_icon_controller.h"
 #include "chrome/browser/ui/views/toolbar/app_menu_control.h"
@@ -17,9 +18,9 @@
 
 namespace views {
 class AccessiblePaneView;
-class MenuRunner;
 }  // namespace views
 
+class AppMenu;
 class AppMenuButtonObserver;
 class AppMenuModel;
 class WebUIToolbarControlDelegate;
@@ -69,10 +70,11 @@
   int trailing_margin_ = 0;
   // Caches the focus state of the button within the WebUI.
   bool focused_ = false;
-  std::unique_ptr<AppMenuModel> menu_model_;
-  std::unique_ptr<views::MenuRunner> menu_runner_;
-
   base::ObserverList<AppMenuButtonObserver>::Unchecked observer_list_;
+  std::unique_ptr<AppMenuModel> menu_model_;
+  std::unique_ptr<AppMenu> menu_;
+
+  base::WeakPtrFactory<WebUIAppMenuControl> weak_ptr_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_TOOLBAR_WEBUI_APP_MENU_CONTROL_H_
diff --git a/chrome/browser/ui/views/toolbar/webui_avatar_toolbar_button.cc b/chrome/browser/ui/views/toolbar/webui_avatar_toolbar_button.cc
index e248c09f..de333e5b 100644
--- a/chrome/browser/ui/views/toolbar/webui_avatar_toolbar_button.cc
+++ b/chrome/browser/ui/views/toolbar/webui_avatar_toolbar_button.cc
@@ -8,7 +8,6 @@
 #include "base/functional/bind.h"
 #include "base/task/single_thread_task_runner.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
@@ -74,12 +73,11 @@
 }  // namespace
 
 WebUIAvatarToolbarButton::WebUIAvatarToolbarButton(
-    WebUIToolbarControlDelegate* delegate,
-    Browser* browser)
+    WebUIToolbarControlDelegate* delegate)
     : delegate_(delegate) {
-  if (browser) {
-    state_manager_ =
-        std::make_unique<AvatarToolbarButtonStateManager>(*this, browser);
+  if (delegate_->GetBrowser()->GetBrowserForMigrationOnly()) {
+    state_manager_ = std::make_unique<AvatarToolbarButtonStateManager>(
+        *this, delegate_->GetBrowser()->GetBrowserForMigrationOnly());
     state_manager_->InitializeStates();
   }
 }
diff --git a/chrome/browser/ui/views/toolbar/webui_avatar_toolbar_button.h b/chrome/browser/ui/views/toolbar/webui_avatar_toolbar_button.h
index 420f07a..3d93d297 100644
--- a/chrome/browser/ui/views/toolbar/webui_avatar_toolbar_button.h
+++ b/chrome/browser/ui/views/toolbar/webui_avatar_toolbar_button.h
@@ -14,7 +14,6 @@
 #include "components/browser_apis/ui_controllers/toolbar/icon_handle.h"
 #include "ui/views/controls/button/button.h"
 
-class Browser;
 class AvatarToolbarButtonStateManager;
 class WebUIToolbarControlDelegate;
 class AvatarToolbarButtonTestAccessor;
@@ -23,8 +22,7 @@
 // WebUI-based implementation of the avatar button in the toolbar.
 class WebUIAvatarToolbarButton : public AvatarToolbarButtonInterface {
  public:
-  WebUIAvatarToolbarButton(WebUIToolbarControlDelegate* delegate,
-                           Browser* browser);
+  explicit WebUIAvatarToolbarButton(WebUIToolbarControlDelegate* delegate);
   WebUIAvatarToolbarButton(const WebUIAvatarToolbarButton&) = delete;
   WebUIAvatarToolbarButton& operator=(const WebUIAvatarToolbarButton&) = delete;
   ~WebUIAvatarToolbarButton() override;
diff --git a/chrome/browser/ui/views/toolbar/webui_toolbar_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/webui_toolbar_interactive_uitest.cc
index ceb0aa2..cd01b6c 100644
--- a/chrome/browser/ui/views/toolbar/webui_toolbar_interactive_uitest.cc
+++ b/chrome/browser/ui/views/toolbar/webui_toolbar_interactive_uitest.cc
@@ -258,6 +258,16 @@
     ASSERT_NO_FATAL_FAILURE(SetUpWebUI(kWebUIToolbarElementIdentifier, &element,
                                        &webui_toolbar_view, &web_view,
                                        browser));
+
+    // Force the physical mouse cursor off the toolbar and into the center of
+    // the main web page. We do this at the very beginning of the test to ensure
+    // any CSS hover fade-out transitions have time to finish while the WebUI
+    // finishes loading during `WaitForLoadStop`.
+    BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
+    gfx::Point safe_page_center =
+        browser_view->contents_web_view()->GetBoundsInScreen().CenterPoint();
+    ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(safe_page_center));
+
     // Wait for the WebContents to finish loading before checking `IsLoading()`,
     // in case a load is triggered, which can happen whenever
     // `WebUIToolbarWebView::AddedToWidget()` is called if WebUI is not
diff --git a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.cc b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.cc
index 690bdbd..f59d700 100644
--- a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.cc
+++ b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/lifetime/browser_shutdown.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_command_controller.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
@@ -277,7 +276,7 @@
       performance_intervention_control_(this),
       app_menu_control_(*this),
       battery_saver_control_(this),
-      avatar_control_(this, browser->GetBrowserForMigrationOnly()),
+      avatar_control_(this),
       location_bar_(std::move(location_bar)),
       extensions_container_(this),
       back_control_(this, BackForwardButton::Direction::kBack),
diff --git a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc
index ee4d4ba..e16fbaac 100644
--- a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc
+++ b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc
@@ -738,6 +738,7 @@
   content::WaitForAccessibilityTreeToContainNodeWithName(
       web_view->GetWebContents(), cookies_blocked_name);
   find_criteria.name = cookies_blocked_name;
+  find_criteria.role = ax::mojom::Role::kButton;
   ui::AXPlatformNodeDelegate* cookies_node =
       content::FindAccessibilityNode(web_view->GetWebContents(), find_criteria);
   ASSERT_TRUE(cookies_node);
diff --git a/chrome/browser/ui/views/translate/translate_bubble_controller.cc b/chrome/browser/ui/views/translate/translate_bubble_controller.cc
index 581fea38..8391531 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_controller.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_controller.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
 #include "chrome/browser/ui/views/translate/partial_translate_bubble_view.h"
+#include "components/feature_engagement/public/feature_constants.h"
 #include "components/tabs/public/tab_interface.h"
 #include "components/touch_to_search/core/browser/contextual_search_delegate_impl.h"
 #include "components/translate/content/browser/contextual_translate_delegate.h"
@@ -32,6 +33,7 @@
 #include "components/translate/core/browser/translate_ui_languages_manager.h"
 #include "components/translate/core/common/translate_constants.h"
 #include "components/translate/core/common/translate_errors.h"
+#include "components/translate/core/common/translate_features.h"
 #include "components/translate/core/common/translate_util.h"
 #include "content/public/browser/web_contents.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -140,6 +142,22 @@
 
   action_item_->SetIsShowingBubble(true);
 
+  // Trigger PDF translate IPH
+  if (web_contents &&
+      base::FeatureList::IsEnabled(translate::kEnableTranslatePdf)) {
+    if (auto* translate_manager =
+            ChromeTranslateClient::GetManagerFromWebContents(web_contents)) {
+      if (translate_manager->GetLanguageState()->pdf_translatability_status() ==
+          translate::LanguageState::PdfTranslatabilityStatus::kTranslatable) {
+        if (auto* user_education =
+                BrowserUserEducationInterface::From(browser_window_)) {
+          user_education->MaybeShowFeaturePromo(
+              feature_engagement::kIPHPdfTranslateBubbleFeature);
+        }
+      }
+    }
+  }
+
   return bubble_widget;
 }
 
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service.cc b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
index 85f7a42b..1e851ff2 100644
--- a/chrome/browser/ui/views/user_education/browser_user_education_service.cc
+++ b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
@@ -14,6 +14,7 @@
 #include "base/time/time.h"
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
+#include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/background/glic/glic_launcher_configuration.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/contextual_tasks/contextual_tasks_ui.h"
@@ -63,6 +64,7 @@
 #include "chrome/browser/ui/views/page_action/page_action_view.h"
 #include "chrome/browser/ui/views/tabs/tab/tab_icon.h"
 #include "chrome/browser/ui/views/toolbar/pinned_action_toolbar_button.h"
+#include "chrome/browser/ui/views/translate/translate_bubble_view.h"
 #include "chrome/browser/ui/views/user_education/autofill_help_bubble_factory.h"
 #include "chrome/browser/ui/views/user_education/browser_help_bubble.h"
 #include "chrome/browser/ui/views/user_education/browser_ntp_promos.h"
@@ -1655,6 +1657,20 @@
           .SetMetadata(108, "agale@chromium.org",
                        "Triggered when device is low on memory.")));
 
+  // kIPHPdfTranslateBubbleFeature
+  registry.RegisterFeature(std::move(
+      FeaturePromoSpecification::CreateForToastPromo(
+          feature_engagement::kIPHPdfTranslateBubbleFeature,
+          TranslateBubbleView::kIdentifier, IDS_PDF_TRANSLATE_PROMO_TEXT,
+          IDS_PDF_TRANSLATE_PROMO_ACCESSIBLE_TEXT,
+          FeaturePromoSpecification::AcceleratorInfo())
+          .SetBubbleArrow(HelpBubbleArrow::kTopCenter)
+          .SetBubbleIcon(&kMenuBookIcon)
+          .SetBubbleTitleText(IDS_PDF_TRANSLATE_PROMO_TITLE)
+          .SetMetadata(153, "danft@chromium.org",
+                       "Shows an IPH when a user translates a PDF for the "
+                       "first time.")));
+
   // kIPHDiscardRingFeature:
   registry.RegisterFeature(std::move(
       FeaturePromoSpecification::CreateForCustomAction(
@@ -2495,6 +2511,12 @@
           153, "mtatarski@google.com",
           "Show the new badge on Send to Your Devices context menu items.")));
 
+  registry.RegisterFeature(user_education::NewBadgeSpecification(
+      features::kReadAnythingLineFocus,
+      user_education::Metadata(
+          153, "kristislee@google.com",
+          "Shown on the Line Focus menu item in Reading Mode settings menu.")));
+
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
   registry.RegisterFeature(user_education::NewBadgeSpecification(
       switches::kCrossDeviceSigninFromDesktop,
diff --git a/chrome/browser/ui/webui/BUILD.gn b/chrome/browser/ui/webui/BUILD.gn
index 5dee1d4..db0e12e7 100644
--- a/chrome/browser/ui/webui/BUILD.gn
+++ b/chrome/browser/ui/webui/BUILD.gn
@@ -1013,6 +1013,7 @@
       "//chrome/browser/resource_coordinator",
       "//chrome/browser/ui",
       "//chrome/browser/ui/webui/top_chrome:test_support",
+      "//chrome/browser/ui/webui/tracked_element:browser_tests",
       "//chrome/common",
       "//chrome/test:test_support",
       "//components/local_state",
diff --git a/chrome/browser/ui/webui/ai_overlay_dialog/BUILD.gn b/chrome/browser/ui/webui/ai_overlay_dialog/BUILD.gn
index 7e421aa..85b9c88f 100644
--- a/chrome/browser/ui/webui/ai_overlay_dialog/BUILD.gn
+++ b/chrome/browser/ui/webui/ai_overlay_dialog/BUILD.gn
@@ -13,6 +13,7 @@
   ]
   public_deps = [
     "//mojo/public/mojom/base",
+    "//third_party/blink/public/mojom:dom_node_id",
     "//ui/gfx/geometry/mojom",
     "//url/mojom:url_mojom_gurl",
   ]
@@ -63,8 +64,8 @@
     "//chrome/browser/translate",
     "//chrome/browser/ttc/resources",
     "//chrome/browser/ttc/resources:generate_tool_definitions",
+    "//chrome/browser/ui",
     "//chrome/browser/ui:browser_actions",
-    "//chrome/browser/ui:ui",
     "//chrome/browser/ui:ui_features",
     "//chrome/browser/ui/tabs:tab_strip",
     "//chrome/browser/ui/webui:webui_util",
diff --git a/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.cc b/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.cc
index 6f60fb6..10d79b4 100644
--- a/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.cc
+++ b/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.cc
@@ -783,7 +783,7 @@
       &task_tracker_);
 }
 
-void AiOverlayTools::SetText(int32_t dom_node_id,
+void AiOverlayTools::SetText(const blink::DOMNodeIdType& dom_node_id,
                              const std::string& text,
                              SetTextCallback callback) {
   RecordToolCallInvoked("SetText");
@@ -814,7 +814,8 @@
 
   auto invocation = actor::mojom::ToolInvocation::New();
   invocation->task_id = actor::TaskId();
-  invocation->target = actor::mojom::ToolTarget::NewDomNodeId(dom_node_id);
+  invocation->target =
+      actor::mojom::ToolTarget::NewDomNodeId(dom_node_id.value());
   invocation->action =
       actor::mojom::ToolAction::NewType(std::move(type_action));
 
@@ -834,7 +835,7 @@
           std::move(chrome_render_frame), std::move(callback)));
 }
 
-void AiOverlayTools::ClickElement(int32_t dom_node_id,
+void AiOverlayTools::ClickElement(const blink::DOMNodeIdType& dom_node_id,
                                   ClickElementCallback callback) {
   RecordToolCallInvoked("ClickElement");
   content::WebContents* contents =
@@ -859,7 +860,8 @@
 
   auto invocation = actor::mojom::ToolInvocation::New();
   invocation->task_id = actor::TaskId();
-  invocation->target = actor::mojom::ToolTarget::NewDomNodeId(dom_node_id);
+  invocation->target =
+      actor::mojom::ToolTarget::NewDomNodeId(dom_node_id.value());
   invocation->action =
       actor::mojom::ToolAction::NewClick(std::move(click_action));
 
@@ -895,7 +897,7 @@
   std::move(callback).Run(base::ok(std::monostate()));
 }
 
-void AiOverlayTools::SelectOption(int32_t dom_node_id,
+void AiOverlayTools::SelectOption(const blink::DOMNodeIdType& dom_node_id,
                                   const std::string& value,
                                   SelectOptionCallback callback) {
   RecordToolCallInvoked("SelectOption");
@@ -920,7 +922,8 @@
 
   auto invocation = actor::mojom::ToolInvocation::New();
   invocation->task_id = actor::TaskId();
-  invocation->target = actor::mojom::ToolTarget::NewDomNodeId(dom_node_id);
+  invocation->target =
+      actor::mojom::ToolTarget::NewDomNodeId(dom_node_id.value());
   invocation->action =
       actor::mojom::ToolAction::NewSelect(std::move(select_action));
 
diff --git a/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.h b/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.h
index 6a619fa..d05c3833 100644
--- a/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.h
+++ b/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.h
@@ -13,6 +13,7 @@
 #include "content/public/browser/weak_document_ptr.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/public/common/dom/dom_node_id.h"
 #include "third_party/blink/public/mojom/annotation/annotation.mojom.h"
 #include "third_party/blink/public/mojom/dom/dom_node_id.mojom.h"
 
@@ -65,14 +66,14 @@
   void RemoveBookmark(RemoveBookmarkCallback callback) override;
   void OpenPage(const std::string& query,
                 OpenPageCallback callback) override;
-  void SetText(int32_t dom_node_id,
+  void SetText(const blink::DOMNodeIdType& dom_node_id,
                const std::string& text,
                SetTextCallback callback) override;
-  void ClickElement(int32_t dom_node_id,
+  void ClickElement(const blink::DOMNodeIdType& dom_node_id,
                     ClickElementCallback callback) override;
   void SetFullscreen(bool fullscreen,
                      SetFullscreenCallback callback) override;
-  void SelectOption(int32_t dom_node_id,
+  void SelectOption(const blink::DOMNodeIdType& dom_node_id,
                     const std::string& value,
                     SelectOptionCallback callback) override;
   void InvokeGlic(const std::string& prompt,
diff --git a/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.mojom b/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.mojom
index 3daba30..a1c1ce0a 100644
--- a/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.mojom
+++ b/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.mojom
@@ -5,6 +5,7 @@
 module ai_overlay_dialog.mojom;
 
 import "mojo/public/mojom/base/empty.mojom";
+import "third_party/blink/public/mojom/dom/dom_node_id.mojom";
 import "url/mojom/url.mojom";
 
 struct SwitchTabResult {
@@ -133,7 +134,7 @@
   // dom_node_id: The numeric DOM node ID of the target field from the Page
   // Content Summary (e.g. 101).
   // text: The text to set into the element.
-  SetText(int32 dom_node_id, string text)
+  SetText(blink.mojom.DOMNodeId dom_node_id, string text)
       => result<mojo_base.mojom.Empty, string>;
 
   // Click or toggle an interactive element (such as a button, radio button,
@@ -141,8 +142,7 @@
   //
   // dom_node_id: The numeric DOM node ID of the target element from the Page
   // Content Summary (e.g. 101).
-  // TODO(crbug.com/541165698): Migrate dom_node_id to blink.mojom.DOMNodeId.
-  ClickElement(int32 dom_node_id)
+  ClickElement(blink.mojom.DOMNodeId dom_node_id)
       => result<mojo_base.mojom.Empty, string>;
 
   // Set or exit full screen mode for the active browser window.
@@ -156,8 +156,9 @@
   // dom_node_id: The numeric DOM node ID of the target dropdown element from
   // the Page Content Summary (e.g. 101).
   // value: The value of the <option> element to select.
-  SelectOption(int32 dom_node_id, string value)
+  SelectOption(blink.mojom.DOMNodeId dom_node_id, string value)
       => result<mojo_base.mojom.Empty, string>;
+
   // @NON_BLOCKING
   // Use for tasks taking >1s to process, email, or to generate images, audio,
   // video, or long text. Prefer direct voice replies for quick (<1s) tasks.
diff --git a/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools_browsertest.cc b/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools_browsertest.cc
index 6f94591..a849a044 100644
--- a/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools_browsertest.cc
+++ b/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools_browsertest.cc
@@ -31,6 +31,7 @@
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/dom/dom_node_id.h"
 #include "ui/base/base_window.h"
 
 namespace ttc {
@@ -797,7 +798,8 @@
           .value();
 
   base::test::TestFuture<base::expected<std::monostate, std::string>> future;
-  tools()->SetText(dom_node_id, "new_value", future.GetCallback());
+  tools()->SetText(blink::DOMNodeIdType(dom_node_id), "new_value",
+                   future.GetCallback());
 
   EXPECT_TRUE(future.Get().has_value());
   EXPECT_EQ("new_value",
@@ -811,7 +813,8 @@
            "value='old' /></body></html>")));
 
   base::test::TestFuture<base::expected<std::monostate, std::string>> future;
-  tools()->SetText(999, "new_value", future.GetCallback());
+  tools()->SetText(blink::DOMNodeIdType(999), "new_value",
+                   future.GetCallback());
 
   EXPECT_FALSE(future.Get().has_value());
 }
@@ -829,7 +832,8 @@
           .value();
 
   base::test::TestFuture<base::expected<std::monostate, std::string>> future;
-  tools()->ClickElement(dom_node_id, future.GetCallback());
+  tools()->ClickElement(blink::DOMNodeIdType(dom_node_id),
+                        future.GetCallback());
 
   EXPECT_TRUE(future.Get().has_value());
   EXPECT_EQ(true,
@@ -849,7 +853,8 @@
           .value();
 
   base::test::TestFuture<base::expected<std::monostate, std::string>> future;
-  tools()->ClickElement(dom_node_id, future.GetCallback());
+  tools()->ClickElement(blink::DOMNodeIdType(dom_node_id),
+                        future.GetCallback());
 
   EXPECT_TRUE(future.Get().has_value());
   EXPECT_EQ(true,
@@ -881,7 +886,8 @@
           .value();
 
   base::test::TestFuture<base::expected<std::monostate, std::string>> future;
-  tools()->SelectOption(dom_node_id, "opt2", future.GetCallback());
+  tools()->SelectOption(blink::DOMNodeIdType(dom_node_id), "opt2",
+                        future.GetCallback());
 
   EXPECT_TRUE(future.Get().has_value());
   EXPECT_EQ("opt2",
diff --git a/chrome/browser/ui/webui/context_hub/context_hub_page_handler_unittest.cc b/chrome/browser/ui/webui/context_hub/context_hub_page_handler_unittest.cc
index 6767013..b95bc34 100644
--- a/chrome/browser/ui/webui/context_hub/context_hub_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/context_hub/context_hub_page_handler_unittest.cc
@@ -1008,7 +1008,7 @@
 
   base::test::TestFuture<bool> future;
   handler_->GenerateTabBasedTodos(future.GetCallback());
-  EXPECT_FALSE(future.Get());
+  EXPECT_TRUE(future.Get());
 }
 #endif
 
diff --git a/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.cc b/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.cc
index 0e7bf74..0e9bf10 100644
--- a/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.cc
+++ b/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.cc
@@ -57,6 +57,7 @@
 #include "components/omnibox/browser/contextual_search_provider.h"
 #include "components/omnibox/browser/omnibox_client.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
+#include "components/omnibox/browser/omnibox_metrics_constants.h"
 #include "components/omnibox/browser/omnibox_popup_selection.h"
 #include "components/omnibox/browser/omnibox_prefs.h"
 #include "components/omnibox/browser/searchbox_utils.h"
@@ -1701,7 +1702,9 @@
     base::TimeTicks match_selection_timestamp,
     OmniboxClient::ExtensionControlledDialogResult dialog_result) {
   if (dialog_result ==
-      OmniboxClient::ExtensionControlledDialogResult::kAccept) {
+          OmniboxClient::ExtensionControlledDialogResult::kAccept ||
+      dialog_result ==
+          OmniboxClient::ExtensionControlledDialogResult::kNoDialogShown) {
     OpenMatch(selection, match, disposition, match_selection_timestamp);
   } else if (dialog_result ==
              OmniboxClient::ExtensionControlledDialogResult::kReject) {
diff --git a/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.h b/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.h
index 9d032f9..07f43b7 100644
--- a/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.h
+++ b/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.h
@@ -220,6 +220,11 @@
                            QueryAutocomplete_SetsLensInputs);
   FRIEND_TEST_ALL_PREFIXES(ContextualSearchboxHandlerTest,
                            QueryAutocomplete_SetsLensInputs_InToolModes);
+  FRIEND_TEST_ALL_PREFIXES(WebuiOmniboxHandlerTest,
+                           OpenMatchResumesNavigationWhenNoDialogShown);
+  FRIEND_TEST_ALL_PREFIXES(WebuiOmniboxHandlerTest,
+                           OpenMatchDropsNavigationWhenDialogCancelled);
+
   SearchboxHandler(
       mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler,
       mojo::PendingRemote<searchbox::mojom::Page> pending_page,
diff --git a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_generator.cc b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_generator.cc
index e1b7f90..56809d45 100644
--- a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_generator.cc
+++ b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_generator.cc
@@ -76,6 +76,11 @@
           : kMaxActionChips);
 }
 
+bool IsScaledChipsEnabled() {
+  return base::FeatureList::IsEnabled(ntp_features::kNtpScaledActionChips) ||
+         base::FeatureList::IsEnabled(ntp_features::kNtpScaledActionChipsSmall);
+}
+
 template <typename T>
 void AssignMojoField(const T& source, T& dest) {
   dest = source;
@@ -343,123 +348,106 @@
 }
 
 ActionChipPtr CreateBrainstormChip() {
-  constexpr auto kBrainstormSuggestions = std::to_array<int>({
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_1,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_2,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_3,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_4,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_5,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_6,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_7,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_8,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_9,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_10,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_11,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_12,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_13,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_14,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_15,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_16,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_17,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_18,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_19,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_20,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_21,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_22,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_23,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_24,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_25,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_26,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_27,
-      IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_28,
-  });
-  size_t index = base::RandGenerator(std::size(kBrainstormSuggestions));
-
   ActionChipPtr chip = ActionChip::New();
-  chip->suggestion = l10n_util::GetStringUTF8(kBrainstormSuggestions[index]);
+  chip->suggestion = std::string();
   chip->suggest_template_info = SuggestTemplateInfo::New();
   chip->suggest_template_info->type_icon = IconType::kDraftSpark;
   chip->suggest_template_info->primary_text =
       action_chips::mojom::FormattedString::New();
   chip->suggest_template_info->primary_text->text =
       l10n_util::GetStringUTF8(IDS_NTP_ACTION_CHIP_BRAINSTORM_HEADING);
+  chip->suggest_template_info->fusebox_action =
+      fusebox_action::mojom::FuseboxAction::New();
+  chip->suggest_template_info->fusebox_action->preferred_inventory =
+      omnibox::SUGGEST_INVENTORY_BRAINSTORM;
   return chip;
 }
 
 std::optional<ActionChipPtr> CreateBrainstormChipIfEligible(
     std::string_view suggestion,
     const AimEligibilityService* aim_eligibility_service) {
-  if (!base::FeatureList::IsEnabled(ntp_features::kNtpScaledActionChipsSmall)) {
+  if (!IsScaledChipsEnabled()) {
     return std::nullopt;
   }
   return CreateBrainstormChip();
 }
 
 ActionChipPtr CreateLearnChip() {
-  constexpr auto kLearnSuggestions = std::to_array<int>({
-      IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_1,
-      IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_2,
-      IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_3,
-      IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_4,
-      IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_5,
-      IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_6,
-      IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_7,
-  });
-  size_t index = base::RandGenerator(std::size(kLearnSuggestions));
-
   ActionChipPtr chip = ActionChip::New();
-  chip->suggestion = l10n_util::GetStringUTF8(kLearnSuggestions[index]);
+  chip->suggestion = std::string();
   chip->suggest_template_info = SuggestTemplateInfo::New();
   chip->suggest_template_info->type_icon = IconType::kDraftSpark;
   chip->suggest_template_info->primary_text =
       action_chips::mojom::FormattedString::New();
   chip->suggest_template_info->primary_text->text =
       l10n_util::GetStringUTF8(IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_HEADING);
+  chip->suggest_template_info->fusebox_action =
+      fusebox_action::mojom::FuseboxAction::New();
+  chip->suggest_template_info->fusebox_action->preferred_inventory =
+      omnibox::SUGGEST_INVENTORY_HELP_ME_LEARN;
   return chip;
 }
 
 std::optional<ActionChipPtr> CreateLearnChipIfEligible(
     std::string_view suggestion,
     const AimEligibilityService* aim_eligibility_service) {
-  if (!base::FeatureList::IsEnabled(ntp_features::kNtpScaledActionChipsSmall)) {
+  if (!IsScaledChipsEnabled()) {
     return std::nullopt;
   }
   return CreateLearnChip();
 }
 
 ActionChipPtr CreateWriteChip() {
-  constexpr auto kWriteSuggestions = std::to_array<int>({
-      IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_1,
-      IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_2,
-      IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_3,
-      IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_4,
-      IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_5,
-      IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_6,
-      IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_7,
-      IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_8,
-  });
-  size_t index = base::RandGenerator(std::size(kWriteSuggestions));
-
   ActionChipPtr chip = ActionChip::New();
-  chip->suggestion = l10n_util::GetStringUTF8(kWriteSuggestions[index]);
+  chip->suggestion = std::string();
   chip->suggest_template_info = SuggestTemplateInfo::New();
   chip->suggest_template_info->type_icon = IconType::kDraftSpark;
   chip->suggest_template_info->primary_text =
       action_chips::mojom::FormattedString::New();
   chip->suggest_template_info->primary_text->text =
       l10n_util::GetStringUTF8(IDS_NTP_ACTION_CHIP_WRITE_EDIT_HEADING);
+  chip->suggest_template_info->fusebox_action =
+      fusebox_action::mojom::FuseboxAction::New();
+  chip->suggest_template_info->fusebox_action->preferred_inventory =
+      omnibox::SUGGEST_INVENTORY_WRITE_OR_EDIT;
   return chip;
 }
 
 std::optional<ActionChipPtr> CreateWriteChipIfEligible(
     std::string_view suggestion,
     const AimEligibilityService* aim_eligibility_service) {
-  if (!base::FeatureList::IsEnabled(ntp_features::kNtpScaledActionChipsSmall)) {
+  if (!IsScaledChipsEnabled()) {
     return std::nullopt;
   }
   return CreateWriteChip();
 }
 
+ActionChipPtr CreateAddImageChip() {
+  ActionChipPtr chip = ActionChip::New();
+  chip->suggestion = "";
+  chip->suggest_template_info = SuggestTemplateInfo::New();
+  chip->suggest_template_info->type_icon = IconType::kDraftSpark;
+  chip->suggest_template_info->primary_text =
+      action_chips::mojom::FormattedString::New();
+  chip->suggest_template_info->primary_text->text = "Add Image";
+  chip->suggest_template_info->fusebox_action =
+      fusebox_action::mojom::FuseboxAction::New();
+  chip->suggest_template_info->fusebox_action->preselected_input_source =
+      fusebox_action::mojom::InputSource::kInputSourceGallery;
+  return chip;
+}
+
+std::optional<ActionChipPtr> CreateAddImageChipIfEligible(
+    std::string_view suggestion,
+    const AimEligibilityService* aim_eligibility_service) {
+  if (!(base::FeatureList::IsEnabled(
+            ntp_features::kNtpScaledActionChipsSmall) &&
+        ntp_features::kNtpScaledActionChipsSmallInTestMode.Get())) {
+    return std::nullopt;
+  }
+  return CreateAddImageChip();
+}
+
 std::vector<omnibox::ToolMode> GetAllowedTools(
     const AimEligibilityService* aim_eligibility_service) {
   std::vector<omnibox::ToolMode> tools;
@@ -503,20 +491,26 @@
   using GeneratorFn = const base::FunctionRef<std::optional<ActionChipPtr>(
       std::string_view, const AimEligibilityService*)>;
   static const GeneratorFn kNewGenerators[] = {
+      &CreateBrainstormChipIfEligible,
+      &CreateLearnChipIfEligible,
+      &CreateWriteChipIfEligible,
       &CreateStarterChipIfEligible,
+      // TODO(crbug.com/537040757): Remove from here, only adding for test
+      // purposes until the server sends new chip types.
+      &CreateAddImageChipIfEligible,
       &CreateImageCreationChipIfEligible,
       &CreateCanvasChipIfEligible,
       &CreateDeepSearchChipIfEligible,
   };
   static const GeneratorFn kOldGenerators[] = {
-      &CreateDeepSearchChipIfEligible, &CreateImageCreationChipIfEligible,
-      &CreateBrainstormChipIfEligible, &CreateLearnChipIfEligible,
-      &CreateWriteChipIfEligible,
+      &CreateDeepSearchChipIfEligible,
+      &CreateImageCreationChipIfEligible,
   };
 
   const base::span<GeneratorFn> generators =
       base::FeatureList::IsEnabled(ntp_features::kNtpNextCanvasChip) ||
-              base::FeatureList::IsEnabled(ntp_features::kNtpStarterChip)
+              base::FeatureList::IsEnabled(ntp_features::kNtpStarterChip) ||
+              IsScaledChipsEnabled()
           ? base::span<GeneratorFn>(kNewGenerators)
           : base::span<GeneratorFn>(kOldGenerators);
   const size_t max_num_chips = GetMaxNumChips();
@@ -616,7 +610,8 @@
   // loader.
   loader_.reset();
 
-  if (ntp_features::kNtpNextShowStaticTextParam.Get()) {
+  if (ntp_features::kNtpNextShowStaticTextParam.Get() ||
+      ntp_features::kNtpScaledActionChipsShowFallback.Get()) {
     std::move(callback).Run(CreateChipsForSteadyState(
         CreateTabInfo(*tab_id_generator_, tab), aim_eligibility_service_));
     return;
diff --git a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_generator_unittest.cc b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_generator_unittest.cc
index b40d25da..91315909 100644
--- a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_generator_unittest.cc
+++ b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_generator_unittest.cc
@@ -266,18 +266,42 @@
         omnibox::SUGGEST_INVENTORY_AIM_CONVERSATION_STARTERS;
     return CreateActionChip(
         /*suggestion=*/"",
-        SuggestTemplateInfo::New(
-            IconType::kSearchLoopWithSparkle,
-            CreateFormattedString(
-                l10n_util::GetStringUTF8(IDS_NTP_ACTION_CHIP_STARTER_HEADING)),
-            CreateFormattedString(
-                l10n_util::GetStringUTF8(IDS_NTP_ACTION_CHIP_STARTER_BODY)),
-            std::move(action)),
+        SuggestTemplateInfo::New(IconType::kSearchLoopWithSparkle,
+                                 CreateFormattedString(l10n_util::GetStringUTF8(
+                                     IDS_NTP_ACTION_CHIP_STARTER_HEADING)),
+                                 CreateFormattedString(l10n_util::GetStringUTF8(
+                                     IDS_NTP_ACTION_CHIP_STARTER_BODY)),
+                                 std::move(action)),
         /*tab=*/nullptr);
   }());
   return *kInstance;
 }
 
+MATCHER(BrainstormChip, "") {
+  return arg && arg->suggest_template_info &&
+         arg->suggest_template_info->type_icon == IconType::kDraftSpark &&
+         arg->suggest_template_info->primary_text &&
+         arg->suggest_template_info->primary_text->text ==
+             l10n_util::GetStringUTF8(IDS_NTP_ACTION_CHIP_BRAINSTORM_HEADING);
+}
+
+MATCHER(LearnChip, "") {
+  return arg && arg->suggest_template_info &&
+         arg->suggest_template_info->type_icon == IconType::kDraftSpark &&
+         arg->suggest_template_info->primary_text &&
+         arg->suggest_template_info->primary_text->text ==
+             l10n_util::GetStringUTF8(
+                 IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_HEADING);
+}
+
+MATCHER(WriteChip, "") {
+  return arg && arg->suggest_template_info &&
+         arg->suggest_template_info->type_icon == IconType::kDraftSpark &&
+         arg->suggest_template_info->primary_text &&
+         arg->suggest_template_info->primary_text->text ==
+             l10n_util::GetStringUTF8(IDS_NTP_ACTION_CHIP_WRITE_EDIT_HEADING);
+}
+
 // A container to store WebContents and its dependency.
 // The main usage is to populate TabInterface.
 class TabFixture {
@@ -428,6 +452,30 @@
                           Eq(std::cref(GetStaticImageGenerationChip()))));
 }
 
+TEST(ActionChipGeneratorTest, GenerateFallbackChips) {
+  EnvironmentFixture env;
+  GeneratorFixture generator_fixture;
+
+  EXPECT_CALL(generator_fixture.mock_aim_eligibility_service(),
+              IsCreateImagesEligible())
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(generator_fixture.mock_aim_eligibility_service(),
+              IsDeepSearchEligible())
+      .WillRepeatedly(Return(false));
+
+  base::test::ScopedFeatureList list;
+  list.InitWithFeaturesAndParameters(
+      {{ntp_features::kNtpScaledActionChips,
+        {{ntp_features::kNtpScaledActionChipsShowFallback.name, "true"}}}},
+      {});
+
+  base::RunLoop run_loop;
+  std::vector<ActionChipPtr> actual;
+  generator_fixture.GenerateActionChips(std::nullopt, run_loop, actual);
+  run_loop.Run();
+  EXPECT_THAT(actual, ElementsAre(BrainstormChip(), LearnChip(), WriteChip()));
+}
+
 TEST(ActionChipGeneratorTest,
      GenerateStaticChipsWithoutCanvasChipWhenCanvasFlagDisabled) {
   EnvironmentFixture env;
@@ -679,10 +727,9 @@
   TabInfoPtr tab_info = CreateTabInfo(&tab_fixture.mock_tab());
   ActionChipPtr chip0 = CreateActionChip(
       base::UTF16ToUTF8(recent_tab_suggestion),
-      SuggestTemplateInfo::New(IconType::kFavicon,
-                               CreateFormattedString(recent_tab_title),
-                               CreateFormattedString(recent_tab_subtitle),
-                               nullptr),
+      SuggestTemplateInfo::New(
+          IconType::kFavicon, CreateFormattedString(recent_tab_title),
+          CreateFormattedString(recent_tab_subtitle), nullptr),
       tab_info->Clone());
 
   auto ds_fusebox_action = fusebox_action::mojom::FuseboxAction::New();
@@ -1429,4 +1476,47 @@
       fusebox_action::mojom::QueryActionOverride::kPaste);
 }
 
+TEST(ActionChipsGeneratorTest, SteadyStateFallbackChipsHavePreferredInventory) {
+  EnvironmentFixture env;
+  GeneratorFixture generator_fixture;
+
+  EXPECT_CALL(generator_fixture.mock_aim_eligibility_service(),
+              IsCreateImagesEligible())
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(generator_fixture.mock_aim_eligibility_service(),
+              IsDeepSearchEligible())
+      .WillRepeatedly(Return(false));
+
+  base::test::ScopedFeatureList list;
+  list.InitWithFeaturesAndParameters(
+      {{ntp_features::kNtpScaledActionChips,
+        {{ntp_features::kNtpScaledActionChipsShowFallback.name, "true"}}}},
+      {ntp_features::kNtpNextCanvasChip, ntp_features::kNtpStarterChip});
+
+  base::RunLoop run_loop;
+  std::vector<ActionChipPtr> actual;
+  generator_fixture.GenerateActionChips(std::nullopt, run_loop, actual);
+  run_loop.Run();
+
+  // Brainstorm chip.
+  EXPECT_TRUE(actual[0]->suggestion.empty());
+  ASSERT_TRUE(actual[0]->suggest_template_info->fusebox_action);
+  EXPECT_EQ(
+      actual[0]->suggest_template_info->fusebox_action->preferred_inventory,
+      omnibox::SuggestInventory::SUGGEST_INVENTORY_BRAINSTORM);
+
+  // Help me learn chip.
+  EXPECT_TRUE(actual[1]->suggestion.empty());
+  ASSERT_TRUE(actual[1]->suggest_template_info->fusebox_action);
+  EXPECT_EQ(
+      actual[1]->suggest_template_info->fusebox_action->preferred_inventory,
+      omnibox::SuggestInventory::SUGGEST_INVENTORY_HELP_ME_LEARN);
+
+  // Write or edit chip.
+  EXPECT_TRUE(actual[2]->suggestion.empty());
+  ASSERT_TRUE(actual[2]->suggest_template_info->fusebox_action);
+  EXPECT_EQ(
+      actual[2]->suggest_template_info->fusebox_action->preferred_inventory,
+      omnibox::SuggestInventory::SUGGEST_INVENTORY_WRITE_OR_EDIT);
+}
 }  // namespace
diff --git a/chrome/browser/ui/webui/omnibox_popup/mojom/omnibox_popup.mojom b/chrome/browser/ui/webui/omnibox_popup/mojom/omnibox_popup.mojom
index 1d1a181c..ad525c9 100644
--- a/chrome/browser/ui/webui/omnibox_popup/mojom/omnibox_popup.mojom
+++ b/chrome/browser/ui/webui/omnibox_popup/mojom/omnibox_popup.mojom
@@ -96,4 +96,7 @@
 
   // Called by the browser when focus changes within the active tab.
   SetFocus(bool is_focused);
+
+  // Clears autocomplete results and resets active query ID in WebUI.
+  ClearAutocompleteMatches();
 };
diff --git a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_handler.cc b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_handler.cc
index f79f781..108c48e 100644
--- a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_handler.cc
+++ b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_handler.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
 #include "chrome/browser/ui/omnibox/omnibox_popup_view.h"
 #include "chrome/browser/ui/omnibox/omnibox_view.h"
+#include "chrome/browser/ui/views/omnibox/omnibox_view_views.h"
 #include "components/omnibox/browser/omnibox_popup_selection.h"
 #include "ui/base/models/menu_model.h"
 
@@ -56,6 +57,16 @@
   }
   latest_selection_ = selection;
   show_full_url_ = show_full_url;
+
+  // Update the selection range of the native view so that when keyboard focus
+  // is transferred to the native view upon click on top container, that typed
+  // text is entered at the correct place.
+  if (controller_) {
+    if (auto* omnibox_view_views =
+            static_cast<OmniboxViewViews*>(controller_->edit_model()->view())) {
+      omnibox_view_views->SetSelectedRange(selection);
+    }
+  }
 }
 
 void OmniboxPopupHandler::Revert(uint32_t sequence_number) {
@@ -63,7 +74,13 @@
     return;
   }
   if (controller_) {
+    if (auto* popup_view = controller_->edit_model()->popup_view()) {
+      popup_view->SetIsReverting(true);
+    }
     controller_->edit_model()->Revert();
+    if (auto* popup_view = controller_->edit_model()->popup_view()) {
+      popup_view->SetIsReverting(false);
+    }
   }
   latest_selection_ = gfx::Range(0, 0);
 }
@@ -180,6 +197,10 @@
   page_->SetFocus(is_focused);
 }
 
+void OmniboxPopupHandler::ClearAutocompleteMatches() {
+  page_->ClearAutocompleteMatches();
+}
+
 void OmniboxPopupHandler::LogEscapeAction(
     omnibox_popup::mojom::OmniboxEscapeAction action) {
   base::UmaHistogramEnumeration("Omnibox.Escape", action);
diff --git a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_handler.h b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_handler.h
index df6dbd0..cc5d7dca 100644
--- a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_handler.h
+++ b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_handler.h
@@ -67,6 +67,7 @@
                      bool show_full_url,
                      bool query_zps);
   void SetFocus(bool is_focused);
+  void ClearAutocompleteMatches();
 
   const gfx::Range& latest_selection() const { return latest_selection_; }
   bool show_full_url() const { return show_full_url_; }
diff --git a/chrome/browser/ui/webui/organizer_panel/BUILD.gn b/chrome/browser/ui/webui/organizer_panel/BUILD.gn
index 6aeeaba..04b4347 100644
--- a/chrome/browser/ui/webui/organizer_panel/BUILD.gn
+++ b/chrome/browser/ui/webui/organizer_panel/BUILD.gn
@@ -11,8 +11,11 @@
   public_deps = [ "//content/public/browser" ]
 
   deps = [
+    "//chrome/app:generated_resources",
     "//chrome/browser/resources/organizer_panel:resources",
     "//chrome/common",
+    "//components/strings:components_strings",
+    "//ui/base",
     "//ui/webui",
   ]
 }
diff --git a/chrome/browser/ui/webui/organizer_panel/organizer_panel_ui.cc b/chrome/browser/ui/webui/organizer_panel/organizer_panel_ui.cc
index fa9095b..6397fbd6 100644
--- a/chrome/browser/ui/webui/organizer_panel/organizer_panel_ui.cc
+++ b/chrome/browser/ui/webui/organizer_panel/organizer_panel_ui.cc
@@ -5,12 +5,15 @@
 #include "chrome/browser/ui/webui/organizer_panel/organizer_panel_ui.h"
 
 #include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/generated_resources.h"
 #include "chrome/grit/organizer_panel_resources.h"
 #include "chrome/grit/organizer_panel_resources_map.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
+#include "ui/base/accelerators/accelerator.h"
 #include "ui/webui/webui_util.h"
 
 OrganizerPanelUIConfig::OrganizerPanelUIConfig()
@@ -23,6 +26,16 @@
       web_ui->GetWebContents()->GetBrowserContext(),
       chrome::kChromeUIOrganizerPanelHost);
 
+  static constexpr webui::LocalizedString kStrings[] = {
+      {"clearSearch", IDS_CLEAR_SEARCH},
+      {"searchTabs", IDS_TAB_SEARCH_SEARCH_TABS},
+  };
+  source->AddLocalizedStrings(kStrings);
+
+  ui::Accelerator accelerator(ui::VKEY_A,
+                              ui::EF_SHIFT_DOWN | ui::EF_PLATFORM_ACCELERATOR);
+  source->AddString("shortcutText", accelerator.GetShortcutText());
+
   webui::SetupWebUIDataSource(source, kOrganizerPanelResources,
                               IDR_ORGANIZER_PANEL_ORGANIZER_PANEL_HTML);
 }
diff --git a/chrome/browser/ui/webui/searchbox/full_webui_omnibox_interactive_uitest.cc b/chrome/browser/ui/webui/searchbox/full_webui_omnibox_interactive_uitest.cc
index 70aa3f8fe..18e03b3f 100644
--- a/chrome/browser/ui/webui/searchbox/full_webui_omnibox_interactive_uitest.cc
+++ b/chrome/browser/ui/webui/searchbox/full_webui_omnibox_interactive_uitest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/base64.h"
+#include "base/scoped_observation.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -13,12 +14,16 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/omnibox/omnibox_controller.h"
 #include "chrome/browser/ui/omnibox/omnibox_next_features.h"
+#include "chrome/browser/ui/omnibox/omnibox_popup_state_manager.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/contents_web_view.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_popup_presenter.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_popup_view_webui.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_popup_webui_base_content.h"
+#include "chrome/browser/ui/views/omnibox/omnibox_view_views.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/browser/ui/webui/searchbox/searchbox_interactive_test_mixin.h"
 #include "chrome/browser/ui/webui/test_support/webui_interactive_test_mixin.h"
@@ -35,7 +40,10 @@
 #include "ui/events/event_constants.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/interaction/element_tracker_views.h"
 #include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
 
 namespace {
 DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kPopupWebView);
@@ -63,6 +71,37 @@
   ~FullWebUIOmniboxInteractiveTestBase() override = default;
 
  protected:
+  auto WaitForBrowserActive() {
+    return Do([this]() {
+      views::Widget* widget =
+          BrowserView::GetBrowserViewForBrowser(browser())->GetWidget();
+      if (!widget->IsActive()) {
+        base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
+        class WidgetActivationWaiter : public views::WidgetObserver {
+         public:
+          WidgetActivationWaiter(views::Widget* widget,
+                                 base::OnceClosure quit_closure)
+              : quit_closure_(std::move(quit_closure)) {
+            observation_.Observe(widget);
+          }
+          void OnWidgetActivationChanged(views::Widget* widget,
+                                         bool active) override {
+            if (active) {
+              std::move(quit_closure_).Run();
+            }
+          }
+
+         private:
+          base::OnceClosure quit_closure_;
+          base::ScopedObservation<views::Widget, views::WidgetObserver>
+              observation_{this};
+        };
+        WidgetActivationWaiter waiter(widget, run_loop.QuitClosure());
+        run_loop.Run();
+      }
+    });
+  }
+
   auto GetActivePopupWebView() {
     return base::BindLambdaForTesting([&]() -> views::View* {
       auto* popup_view = static_cast<OmniboxPopupViewWebUI*>(
@@ -100,7 +139,9 @@
                const fullText = '%s';
                for (let i = 0; i < fullText.length; i++) {
                  el.value = fullText.substring(0, i + 1);
+                 el.setSelectionRange(i + 1, i + 1);
                  el.dispatchEvent(new Event('input'));
+                 document.dispatchEvent(new Event('selectionchange'));
                }
              })",
                                                              text.c_str()))),
@@ -228,7 +269,7 @@
 
   auto OpenInitialTabAndFocusOmnibox(ui::ElementIdentifier tab_id,
                                      const GURL& url) {
-    return Steps(AddInstrumentedTab(tab_id, url),
+    return Steps(WaitForBrowserActive(), AddInstrumentedTab(tab_id, url),
                  WaitForWebContentsReady(tab_id),
                  WaitForPopupTransitionLockout(), Do([this]() {
                    if (auto* popup_view = BrowserWindow::FromBrowser(browser())
@@ -301,23 +342,6 @@
       CheckWebUIInputFocus(true));
 }
 
-// Verifies that clicking outside on the webpage body closes the WebUI
-// suggestions dropdown.
-IN_PROC_BROWSER_TEST_F(FullWebUIOmniboxInteractiveTest,
-                       ClickOutsideClosesDropdown) {
-  RunTestSequence(
-      // Open Tab 1 and focus Omnibox to open WebUI popup with suggestions.
-      OpenInitialTabAndFocusOmnibox(kTab1, GURL("chrome://version/")),
-      InputWebUIText("a"),
-      WaitForMatch(kPopupWebView, kFirstSuggestionMatchContents,
-                   "suggestion-1"),
-      // Click on the webpage body to blur the Omnibox.
-      ClickWebPageBody(kTab1),
-      // Verify dropdown matches list closed.
-      WaitForJsConditionAt(kPopupWebView, kPopupSearchbox,
-                           "(el) => el && !el.dropdownIsVisible"));
-}
-
 // Verifies that clicking outside on the webpage body while an active user draft
 // exists keeps the popup open while shifting focus to the webpage.
 IN_PROC_BROWSER_TEST_F(FullWebUIOmniboxInteractiveTest, ActiveUnfocusedDraft) {
@@ -327,6 +351,8 @@
       InputWebUIText("ffffff"),
       // Click on the webpage body of Tab 1 to blur the Omnibox.
       ClickWebPageBody(kTab1),
+      WaitForJsConditionAt(kPopupWebView, kPopupSearchbox,
+                           "(el) => el && !el.dropdownIsVisible"),
       // Verify popup remains open, but focus shifts to the webpage.
       InAnyContext(WaitForShow(OmniboxPopupPresenter::kRoundedResultsFrame)),
       CheckWebUIInputFocus(false),
@@ -622,11 +648,33 @@
   base::HistogramTester histogram_tester;
   RunTestSequence(
       OpenInitialTabAndFocusOmnibox(kTab1, GURL("chrome://version/")),
-      PasteWebUIText("example.com"), WaitForWebUIInputValue("example.com"),
-      CheckWebUIInputFocus(true),
+      ClearWebUIText(), PasteWebUIText("example.com"),
+      WaitForWebUIInputValue("example.com"), CheckWebUIInputFocus(true),
       Do([&]() { histogram_tester.ExpectBucketCount("Omnibox.Paste", 1, 1); }));
 }
 
+// Verifies that clicking on the webpage content area closes the popup.
+IN_PROC_BROWSER_TEST_F(FullWebUIOmniboxInteractiveTest,
+                       ClickWebpageClosesPopup) {
+  RunTestSequence(
+      OpenInitialTabAndFocusOmnibox(kTab1, GURL("chrome://version/")),
+      InputWebUIText("a"),
+      InAnyContext(WaitForShow(OmniboxPopupPresenter::kRoundedResultsFrame)),
+      ClearWebUIText(), ClickWebPageBody(kTab1),
+      InAnyContext(WaitForHide(OmniboxPopupPresenter::kRoundedResultsFrame)));
+}
+
+// Verifies that clicking the location bar keeps the popup open.
+IN_PROC_BROWSER_TEST_F(FullWebUIOmniboxInteractiveTest,
+                       ClickLocationBarKeepsPopupOpen) {
+  RunTestSequence(
+      OpenInitialTabAndFocusOmnibox(kTab1, GURL("chrome://version/")),
+      InputWebUIText("a"),
+      InAnyContext(WaitForShow(OmniboxPopupPresenter::kRoundedResultsFrame)),
+      MoveMouseTo(kOmniboxElementId), ClickMouse(),
+      InAnyContext(WaitForShow(OmniboxPopupPresenter::kRoundedResultsFrame)));
+}
+
 #if !BUILDFLAG(IS_MAC)
 // Verifies that pressing Shift+Arrow keys after Select All adjusts the
 // selection character-by-character on Windows and Linux rather than collapsing
@@ -800,7 +848,6 @@
   style_applied.where = kContextButton;
   style_applied.test_function =
       "(el) => el && window.getComputedStyle(el).borderRadius === \"100px\"";
-
   RunTestSequence(
       SetAimEligibleResponse(),
       OpenInitialTabAndFocusOmnibox(kTab1, GURL("chrome://version/")),
diff --git a/chrome/browser/ui/webui/searchbox/searchbox_handler_unittest.cc b/chrome/browser/ui/webui/searchbox/searchbox_handler_unittest.cc
index 174bcc84..f67504e 100644
--- a/chrome/browser/ui/webui/searchbox/searchbox_handler_unittest.cc
+++ b/chrome/browser/ui/webui/searchbox/searchbox_handler_unittest.cc
@@ -62,6 +62,7 @@
 #include "content/public/test/test_web_ui.h"
 #include "content/public/test/test_web_ui_data_source.h"
 #include "content/public/test/web_contents_tester.h"
+#include "extensions/common/extension_features.h"
 #include "lens_searchbox_handler.h"
 #include "realbox_handler.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -1098,6 +1099,67 @@
   handler_->OpenLensSearch();
 }
 
+TEST_F(WebuiOmniboxHandlerTest, OpenMatchResumesNavigationWhenNoDialogShown) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      extensions_features::kSearchEngineExplicitChoiceDialog);
+  auto* client = static_cast<TestOmniboxClient*>(omnibox_controller_->client());
+  AutocompleteMatch match(nullptr, 500, false,
+                          AutocompleteMatchType::SEARCH_SUGGEST);
+  match.destination_url = GURL("https://www.example.com/?q=foo");
+  match.keyword = u"example";
+  EXPECT_CALL(*client, ShowConfirmationDialogIfDefaultSearchExtensionControlled(
+                           match.destination_url, testing::_))
+      .WillOnce([](const GURL&,
+                   base::OnceCallback<void(
+                       OmniboxClient::ExtensionControlledDialogResult)> cb) {
+        // No dialog is shown; report that rather than a user decision.
+        std::move(cb).Run(
+            OmniboxClient::ExtensionControlledDialogResult::kNoDialogShown);
+        return true;
+      })
+      .WillRepeatedly(testing::Return(false));
+
+  // The withheld navigation must still happen.
+  EXPECT_CALL(*client, OnAutocompleteAccept(match.destination_url, testing::_,
+                                            testing::_, testing::_, testing::_,
+                                            testing::_, testing::_, testing::_,
+                                            testing::_, testing::_, testing::_))
+      .Times(1);
+  handler_->OpenMatch(OmniboxPopupSelection(0), match,
+                      WindowOpenDisposition::CURRENT_TAB,
+                      base::TimeTicks::Now());
+}
+
+TEST_F(WebuiOmniboxHandlerTest, OpenMatchDropsNavigationWhenDialogCancelled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      extensions_features::kSearchEngineExplicitChoiceDialog);
+  auto* client = static_cast<TestOmniboxClient*>(omnibox_controller_->client());
+  AutocompleteMatch match(nullptr, 500, false,
+                          AutocompleteMatchType::SEARCH_SUGGEST);
+  match.destination_url = GURL("https://www.example.com/?q=foo");
+  match.keyword = u"example";
+  EXPECT_CALL(*client, ShowConfirmationDialogIfDefaultSearchExtensionControlled(
+                           match.destination_url, testing::_))
+      .WillOnce([](const GURL&,
+                   base::OnceCallback<void(
+                       OmniboxClient::ExtensionControlledDialogResult)> cb) {
+        std::move(cb).Run(
+            OmniboxClient::ExtensionControlledDialogResult::kCancel);
+        return true;
+      });
+  EXPECT_CALL(*client, OnAutocompleteAccept(testing::_, testing::_, testing::_,
+                                            testing::_, testing::_, testing::_,
+                                            testing::_, testing::_, testing::_,
+                                            testing::_, testing::_))
+      .Times(0);
+
+  handler_->OpenMatch(OmniboxPopupSelection(0), match,
+                      WindowOpenDisposition::CURRENT_TAB,
+                      base::TimeTicks::Now());
+}
+
 #endif
 
 namespace {
diff --git a/chrome/browser/ui/webui/searchbox/searchbox_test_utils.h b/chrome/browser/ui/webui/searchbox/searchbox_test_utils.h
index 32228ee..36037a8 100644
--- a/chrome/browser/ui/webui/searchbox/searchbox_test_utils.h
+++ b/chrome/browser/ui/webui/searchbox/searchbox_test_utils.h
@@ -139,6 +139,7 @@
               (omnibox_popup::mojom::OmniboxInputStatePtr state),
               (override));
   MOCK_METHOD(void, SetFocus, (bool is_focused), (override));
+  MOCK_METHOD(void, ClearAutocompleteMatches, (), (override));
 };
 #endif
 
diff --git a/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.cc b/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.cc
index ef62edd..4c63b13 100644
--- a/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.cc
+++ b/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.cc
@@ -33,6 +33,7 @@
 #include "chrome/browser/ui/tab_ui_helper.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/views/location_bar/selected_keyword_view.h"
+#include "chrome/browser/ui/views/omnibox/omnibox_view_views.h"
 #include "chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h"
 #include "chrome/browser/ui/webui/cr_components/searchbox/searchbox_omnibox_client.h"
 #include "chrome/browser/ui/webui/metrics_reporter/metrics_reporter.h"
@@ -77,6 +78,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/webui/resource_path.h"
 #include "ui/base/window_open_disposition_utils.h"
+#include "ui/views/widget/widget.h"
 
 namespace {
 
@@ -205,6 +207,27 @@
   }
 }
 
+void WebuiOmniboxHandler::QueryAutocomplete(
+    int32_t query_id,
+    const std::u16string& input,
+    bool prevent_inline_autocomplete,
+    uint32_t cursor_position,
+    omnibox::SuggestInventory suggest_inventory,
+    bool is_on_focus,
+    const std::string& keyword,
+    searchbox::mojom::InputMethod input_method) {
+  SearchboxHandler::QueryAutocomplete(
+      query_id, input, prevent_inline_autocomplete, cursor_position,
+      suggest_inventory, is_on_focus, keyword, input_method);
+
+  if (auto* omnibox_view_views =
+          static_cast<OmniboxViewViews*>(edit_model()->view())) {
+    omnibox_view_views->SetWindowTextAndCaretPos(input, cursor_position,
+                                                 /*update_popup=*/false,
+                                                 /*notify_text_changed=*/false);
+  }
+}
+
 void WebuiOmniboxHandler::OpenLensSearch() {
   edit_model()->OpenLensSearch();
 }
diff --git a/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.h b/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.h
index 0f226fe..cbb993f5 100644
--- a/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.h
+++ b/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.h
@@ -59,6 +59,14 @@
                      bool delay_upload,
                      searchbox::mojom::TabAttachmentSource source,
                      AddTabContextCallback) override;
+  void QueryAutocomplete(int32_t query_id,
+                         const std::u16string& input,
+                         bool prevent_inline_autocomplete,
+                         uint32_t cursor_position,
+                         omnibox::SuggestInventory suggest_inventory,
+                         bool is_on_focus,
+                         const std::string& keyword,
+                         searchbox::mojom::InputMethod input_method) override;
 
   void StepSelection(OmniboxPopupSelection::Direction direction,
                      OmniboxPopupSelection::Step step);
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index cb864ab..9b78f496 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -894,6 +894,9 @@
        IDS_SETTINGS_GLIC_KEEP_SIDEPANEL_OPEN_ON_NEW_TABS},
       {"glicKeepSidepanelOpenOnNewTabsToggleSublabel",
        IDS_SETTINGS_GLIC_KEEP_SIDEPANEL_OPEN_ON_NEW_TABS_SUBLABEL},
+      {"glicShakeTriggerToggle", IDS_SETTINGS_GLIC_SHAKE_TRIGGER_TOGGLE},
+      {"glicShakeTriggerToggleSublabel",
+       IDS_SETTINGS_GLIC_SHAKE_TRIGGER_TOGGLE_SUBLABEL},
       {"glicLocationToggle", IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE},
       {"glicLocationToggleSublabel",
        IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE_SUBLABEL},
@@ -1162,6 +1165,9 @@
   html_source->AddBoolean(
       "showGlicExperimentalTriggering",
       GlicHandler::ShouldShowExperimentalTriggeringToggle(profile));
+  html_source->AddBoolean(
+      "showGlicShakeTrigger",
+      base::FeatureList::IsEnabled(features::kGlicShakeTrigger));
 }
 
 void AddResetStrings(content::WebUIDataSource* html_source, Profile* profile) {
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/BUILD.gn b/chrome/browser/ui/webui/side_panel/read_anything/BUILD.gn
index 9fc8f809..8a9c694 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/BUILD.gn
+++ b/chrome/browser/ui/webui/side_panel/read_anything/BUILD.gn
@@ -46,6 +46,7 @@
     "//chrome/browser/ui/tabs:tab_strip",
     "//chrome/browser/ui/toolbar",
     "//chrome/browser/ui/webui:theme_source",
+    "//chrome/browser/user_education",
     "//chrome/common",
     "//components/dom_distiller/content/browser",
     "//components/language/core/common",
@@ -54,6 +55,7 @@
     "//components/user_education/webui",
     "//extensions/browser/mime_handler",
     "//pdf:features",
+    "//ui/strings",
     "//ui/webui",
     "//ui/webui/tracked_element",
   ]
@@ -88,6 +90,7 @@
     "//chrome/browser/ui/side_panel",
     "//chrome/browser/ui/side_panel:side_panel_views_dependent",
     "//chrome/browser/ui/views/translate",
+    "//chrome/browser/user_education",
     "//chrome/test:test_support",
     "//chrome/test:test_support_ui",
     "//components/language_detection/core",
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
index f3a02b1..21a0cf2 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
@@ -34,6 +34,7 @@
 #include "chrome/browser/ui/read_anything/read_anything_side_panel_controller.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/browser/ui/toolbar/pinned_toolbar/pinned_toolbar_actions_model.h"
+#include "chrome/browser/user_education/user_education_service.h"
 #include "chrome/common/chrome_isolated_world_ids.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/read_anything/read_anything.mojom-shared.h"
@@ -1028,6 +1029,21 @@
   }
 }
 
+void ReadAnythingUntrustedPageHandler::ShouldShowLineFocusNewBadge(
+    ShouldShowLineFocusNewBadgeCallback callback) {
+  bool show = features::IsReadAnythingLineFocusEnabled() &&
+              UserEducationService::MaybeShowNewBadge(
+                  profile_, features::kReadAnythingLineFocus);
+  std::move(callback).Run(show);
+}
+
+void ReadAnythingUntrustedPageHandler::OnLineFocusFeatureUsed() {
+  if (features::IsReadAnythingLineFocusEnabled()) {
+    UserEducationService::MaybeNotifyNewBadgeFeatureUsed(
+        profile_, features::kReadAnythingLineFocus);
+  }
+}
+
 void ReadAnythingUntrustedPageHandler::OnReadAloudAudioStateChange(
     bool playing) {
   // Show the tab audio icon when read aloud is playing, and hide it when it
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
index 58eec9c4..77dc7cb 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h
@@ -218,6 +218,9 @@
   void OnLineFocusChanged(
       read_anything::mojom::LineFocus current_line_focus,
       read_anything::mojom::LineFocus last_non_disabled_line_focus) override;
+  void OnLineFocusFeatureUsed() override;
+  void ShouldShowLineFocusNewBadge(
+      ShouldShowLineFocusNewBadgeCallback callback) override;
   void GetVoicePackInfo(const std::string& language) override;
   void InstallVoicePack(const std::string& language) override;
   void UninstallVoice(const std::string& language) override;
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_browsertest.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_browsertest.cc
index ff4d10d1..f89e573 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_browsertest.cc
@@ -12,6 +12,7 @@
 
 #include "base/command_line.h"
 #include "base/run_loop.h"
+#include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/run_until.h"
 #include "base/test/values_test_util.h"
@@ -32,6 +33,8 @@
 #include "chrome/browser/ui/toolbar/pinned_toolbar/pinned_toolbar_actions_model.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/translate/translate_bubble_controller.h"
+#include "chrome/browser/user_education/user_education_service.h"
+#include "chrome/browser/user_education/user_education_service_factory.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/read_anything/read_anything.mojom-shared.h"
 #include "chrome/common/read_anything/read_anything.mojom.h"
@@ -42,6 +45,8 @@
 #include "components/prefs/pref_value_map.h"
 #include "components/tabs/public/tab_interface.h"
 #include "components/translate/core/browser/translate_manager.h"
+#include "components/user_education/common/new_badge/new_badge_specification.h"
+#include "components/user_education/common/user_education_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -1020,6 +1025,17 @@
 }
 
 IN_PROC_BROWSER_TEST_P(ReadAnythingUntrustedPageHandlerTest,
+                       ShouldShowLineFocusNewBadge_RunsCallback) {
+  handler_ = CreateHandler();
+
+  base::RunLoop run_loop;
+  handler_->ShouldShowLineFocusNewBadge(
+      base::BindLambdaForTesting([&](bool show) { run_loop.Quit(); }));
+
+  run_loop.Run();
+}
+
+IN_PROC_BROWSER_TEST_P(ReadAnythingUntrustedPageHandlerTest,
                        OnVoiceChange_StoresInPrefs) {
   const char kLang1[] = "hi";
   const char kLang2[] = "ja";
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc
index c6819859..c9da4085 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc
@@ -33,6 +33,7 @@
 #include "ui/accessibility/accessibility_features.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/webui/web_ui_util.h"
+#include "ui/strings/grit/ui_strings.h"
 #include "ui/views/style/platform_style.h"
 #include "ui/webui/resources/grit/webui_resources.h"
 #include "ui/webui/webui_util.h"
@@ -196,6 +197,7 @@
       {"readingModeVoiceDownloadedMessage",
        IDS_READING_MODE_VOICE_DOWNLOADED_MESSAGE},
       {"menu", IDS_MENU},
+      {"newBadgeLabel", IDS_NEW_BADGE},
       {"selected", IDS_READING_MODE_ITEM_SELECTED},
       {"allocationError", IDS_READING_MODE_LANGUAGE_MENU_NO_SPACE},
       {"allocationErrorHighQuality",
diff --git a/chrome/browser/ui/webui/skills/BUILD.gn b/chrome/browser/ui/webui/skills/BUILD.gn
index 03c987d..38dc9825 100644
--- a/chrome/browser/ui/webui/skills/BUILD.gn
+++ b/chrome/browser/ui/webui/skills/BUILD.gn
@@ -101,6 +101,12 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
+  if (is_chromeos) {
+    deps += [
+      "//chrome/browser/chromeos/policy/dlp",
+      "//chrome/browser/chromeos/policy/dlp/test:test_support",
+    ]
+  }
 }
 
 if (!is_android) {
diff --git a/chrome/browser/ui/webui/skills/skills_page_handler.cc b/chrome/browser/ui/webui/skills/skills_page_handler.cc
index b28ba9c5..e459ed5 100644
--- a/chrome/browser/ui/webui/skills/skills_page_handler.cc
+++ b/chrome/browser/ui/webui/skills/skills_page_handler.cc
@@ -230,6 +230,8 @@
   }
 }
 
+// TODO(b/543404064): Rename to `GetInitialDiscoverySkills()` to reflect that
+// this returns discovery and enterprise provided skills, not just 1P skills.
 void SkillsPageHandler::GetInitial1PSkills(
     GetInitial1PSkillsCallback callback) {
   auto scoped_callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
@@ -240,10 +242,20 @@
     std::move(scoped_callback).Run(mojom::BrowseSkillsInitialState::New());
     return;
   }
-  auto state = mojom::BrowseSkillsInitialState::New();
-  state->topics_info_list = service->Get1PTopicsInfo();
-  state->skill_map = Translate1PSkills(service->Get1PSkills());
-  std::move(scoped_callback).Run(std::move(state));
+  std::move(scoped_callback)
+      .Run(GetDiscoverySkills(service->Get1PTopicsInfo(),
+                              service->Get1PSkills()));
+}
+
+void SkillsPageHandler::OnProvidedSkillsChanged(
+    skills::SkillsProvider* provider) {
+  auto* service =
+      SkillsServiceFactory::GetForProfile(base::to_address(profile_));
+  if (!service) {
+    return;
+  }
+  page_->Update1PSkills(
+      GetDiscoverySkills(service->Get1PTopicsInfo(), service->Get1PSkills()));
 }
 
 void SkillsPageHandler::OnDiscoverySkillsUpdated(
@@ -268,10 +280,9 @@
 
   // If the data exists that means we have an updated list of skills.
   if (first_party_skill_data) {
-    auto state = mojom::BrowseSkillsInitialState::New();
-    state->topics_info_list = first_party_skill_data->topics_info_list;
-    state->skill_map = Translate1PSkills(first_party_skill_data->skills_list);
-    page_->Update1PSkills(std::move(state));
+    page_->Update1PSkills(
+        GetDiscoverySkills(first_party_skill_data->topics_info_list,
+                           first_party_skill_data->skills_list));
   }
 }
 
@@ -299,6 +310,39 @@
   skills::RecordSkillsManagementAction(page, action);
 }
 
+mojom::BrowseSkillsInitialStatePtr SkillsPageHandler::GetDiscoverySkills(
+    const std::vector<skills::proto::TopicInfo>& topics_info_list,
+    const skills::SkillProtoList& skills_list) {
+  auto state = mojom::BrowseSkillsInitialState::New();
+  state->topics_info_list = topics_info_list;
+  state->skill_map = Translate1PSkills(skills_list);
+  AppendProvidedSkills(state.get());
+  return state;
+}
+
+void SkillsPageHandler::AppendProvidedSkills(
+    mojom::BrowseSkillsInitialState* state) {
+  auto* service =
+      SkillsServiceFactory::GetForProfile(base::to_address(profile_));
+  if (!service) {
+    return;
+  }
+
+  std::vector<skills::Skill> enterprise_skills;
+  for (const auto& [id, skill] : service->GetProvidedSkills()) {
+    enterprise_skills.push_back(*skill);
+  }
+
+  if (!enterprise_skills.empty()) {
+    std::sort(enterprise_skills.begin(), enterprise_skills.end(),
+              [](const skills::Skill& a, const skills::Skill& b) {
+                return a.name < b.name;
+              });
+    // TODO(b/540008460): Create a shared constant.
+    state->skill_map["From your organization"] = std::move(enterprise_skills);
+  }
+}
+
 void SkillsPageHandler::OnSkillsEnabledPrefChanged() {
   page_->SetSkillsEnabled(SkillsServiceFactory::IsSkillsEnabledForProfile(
       base::to_address(profile_)));
diff --git a/chrome/browser/ui/webui/skills/skills_page_handler.h b/chrome/browser/ui/webui/skills/skills_page_handler.h
index 4fb61ae..1ba3cdb 100644
--- a/chrome/browser/ui/webui/skills/skills_page_handler.h
+++ b/chrome/browser/ui/webui/skills/skills_page_handler.h
@@ -63,6 +63,7 @@
                       bool is_position_changed) override;
   void OnDiscoverySkillsUpdated(
       const FirstPartySkillData* first_party_skill_data) override;
+  void OnProvidedSkillsChanged(skills::SkillsProvider* provider) override;
   void OnSkillsServiceShuttingDown() override;
   void OnTemporarySkillDisplay(
       std::string_view skill_id,
@@ -82,6 +83,10 @@
   // complete within kMax1PDownloadTimeout seconds.
   void On1PDownloadTimeout();
   void OnSkillsEnabledPrefChanged();
+  mojom::BrowseSkillsInitialStatePtr GetDiscoverySkills(
+      const std::vector<skills::proto::TopicInfo>& topics_info_list,
+      const skills::SkillProtoList& skills_list);
+  void AppendProvidedSkills(mojom::BrowseSkillsInitialState* state);
   mojo::Receiver<skills::mojom::PageHandler> receiver_;
   mojo::Remote<skills::mojom::SkillsPage> page_;
   PrefChangeRegistrar pref_registrar_;
diff --git a/chrome/browser/ui/webui/skills/skills_page_handler_unittest.cc b/chrome/browser/ui/webui/skills/skills_page_handler_unittest.cc
index 2f10940..b8d111c 100644
--- a/chrome/browser/ui/webui/skills/skills_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/skills/skills_page_handler_unittest.cc
@@ -41,6 +41,11 @@
 using ::testing::ReturnRef;
 using ::testing::StrictMock;
 
+inline constexpr char kEnterpriseSkillId[] = "enterprise_id";
+inline constexpr char kEnterpriseSkillName[] = "Enterprise Skill";
+inline constexpr char kEnterpriseSkillIcon[] = "enterprise_icon";
+inline constexpr char kEnterpriseSkillPrompt[] = "Enterprise Prompt";
+
 class MockSkillsPage : public skills::mojom::SkillsPage {
  public:
   mojo::PendingRemote<skills::mojom::SkillsPage> BindAndGetRemote() {
@@ -301,5 +306,79 @@
   run_loop.Run();
 }
 
+// Verifies that when provided enterprise skills change, SkillsPageHandler
+// updates the Browse Skills page (Update1PSkills) with the provided skills.
+TEST_F(SkillsPageHandlerTest, OnProvidedSkillsChanged_IncludesProvidedSkills) {
+  auto* mock_service = static_cast<MockSkillsService*>(
+      SkillsServiceFactory::GetForProfile(&profile_));
+  EXPECT_CALL(*mock_service, GetServiceStatus())
+      .WillRepeatedly(testing::Return(SkillsService::ServiceStatus::kReady));
+
+  std::unordered_map<std::string, std::unique_ptr<skills::Skill>>
+      mock_provided_skills;
+  auto enterprise_skill = std::make_unique<skills::Skill>(
+      kEnterpriseSkillId, kEnterpriseSkillName, kEnterpriseSkillIcon,
+      kEnterpriseSkillPrompt);
+  enterprise_skill->source = sync_pb::SkillSource::SKILL_SOURCE_ENTERPRISE;
+  mock_provided_skills[enterprise_skill->id] = std::move(enterprise_skill);
+
+  std::vector<skills::proto::TopicInfo> empty_topics;
+  skills::SkillProtoList empty_1p_skills;
+  EXPECT_CALL(*mock_service, GetProvidedSkills())
+      .WillRepeatedly(testing::ReturnRef(mock_provided_skills));
+  EXPECT_CALL(*mock_service, Get1PTopicsInfo())
+      .WillRepeatedly(testing::ReturnRef(empty_topics));
+  EXPECT_CALL(*mock_service, Get1PSkills())
+      .WillRepeatedly(testing::ReturnRef(empty_1p_skills));
+
+  base::RunLoop run_loop;
+  EXPECT_CALL(mock_page_, Update1PSkills(_))
+      .WillOnce([&run_loop](mojom::BrowseSkillsInitialStatePtr state) {
+        EXPECT_TRUE(state->skill_map.contains("From your organization"));
+        const auto& skills = state->skill_map.at("From your organization");
+        ASSERT_EQ(1u, skills.size());
+        EXPECT_EQ(kEnterpriseSkillId, skills[0].id);
+        EXPECT_EQ(kEnterpriseSkillName, skills[0].name);
+        run_loop.Quit();
+      });
+
+  handler_->OnProvidedSkillsChanged(nullptr);
+  run_loop.Run();
+}
+
+TEST_F(SkillsPageHandlerTest, OnDiscoverySkillsUpdated_IncludesProvidedSkills) {
+  auto* mock_service = static_cast<MockSkillsService*>(
+      SkillsServiceFactory::GetForProfile(&profile_));
+  EXPECT_CALL(*mock_service, GetServiceStatus())
+      .WillRepeatedly(testing::Return(SkillsService::ServiceStatus::kReady));
+
+  std::unordered_map<std::string, std::unique_ptr<skills::Skill>>
+      mock_provided_skills;
+  auto enterprise_skill = std::make_unique<skills::Skill>(
+      kEnterpriseSkillId, kEnterpriseSkillName, kEnterpriseSkillIcon,
+      kEnterpriseSkillPrompt);
+  enterprise_skill->source = sync_pb::SkillSource::SKILL_SOURCE_ENTERPRISE;
+  mock_provided_skills[enterprise_skill->id] = std::move(enterprise_skill);
+
+  EXPECT_CALL(*mock_service, GetProvidedSkills())
+      .WillRepeatedly(ReturnRef(mock_provided_skills));
+
+  auto first_party_skill_data = std::make_unique<FirstPartySkillData>();
+
+  base::RunLoop run_loop;
+  EXPECT_CALL(mock_page_, Update1PSkills(_))
+      .WillOnce([&run_loop](mojom::BrowseSkillsInitialStatePtr state) {
+        EXPECT_TRUE(state->skill_map.contains("From your organization"));
+        const auto& skills = state->skill_map.at("From your organization");
+        ASSERT_EQ(1u, skills.size());
+        EXPECT_EQ(kEnterpriseSkillId, skills[0].id);
+        EXPECT_EQ(kEnterpriseSkillName, skills[0].name);
+        run_loop.Quit();
+      });
+
+  handler_->OnDiscoverySkillsUpdated(first_party_skill_data.get());
+  run_loop.Run();
+}
+
 }  // namespace
 }  // namespace skills
diff --git a/chrome/browser/ui/webui/skills/skills_ui.cc b/chrome/browser/ui/webui/skills/skills_ui.cc
index b40e1e3..7324ba6d 100644
--- a/chrome/browser/ui/webui/skills/skills_ui.cc
+++ b/chrome/browser/ui/webui/skills/skills_ui.cc
@@ -33,6 +33,7 @@
 #include "components/skills/public/skill.h"
 #include "components/skills/public/skills_metrics.h"
 #include "components/strings/grit/components_strings.h"
+#include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_data_source.h"
@@ -237,7 +238,25 @@
 void SkillsUI::BindInterface(
     mojo::PendingReceiver<::skills::mojom::SkillsPageHandler> receiver) {
   CHECK(base::FeatureList::IsEnabled(features::kSkillsWebViewV2Enabled));
+
   Profile* profile = Profile::FromWebUI(web_ui());
+  bool is_internal_user = false;
+  if (auto* identity_manager = IdentityManagerFactory::GetForProfile(profile)) {
+    is_internal_user = gaia::IsGoogleInternalAccountEmail(
+        identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)
+            .email);
+  }
+
+  // If delegate is null, that means a user is trying to navigate to /dialog
+  // themselves. Allow internal users to navigate directly.
+  GURL url = web_ui()->GetWebContents()->GetVisibleURL();
+  if (url.path() == "/dialog" && !delegate_ && !is_internal_user) {
+    web_ui()->GetWebContents()->GetController().LoadURL(
+        GURL(chrome::kChromeUISkillsURL), content::Referrer(),
+        ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
+    return;
+  }
+
   page_handler_v2_ = std::make_unique<skills::SkillsPageHandlerV2>(
       std::move(receiver), profile,
       IdentityManagerFactory::GetForProfile(profile),
diff --git a/chrome/browser/ui/webui/tracked_element/BUILD.gn b/chrome/browser/ui/webui/tracked_element/BUILD.gn
new file mode 100644
index 0000000..c15aa94
--- /dev/null
+++ b/chrome/browser/ui/webui/tracked_element/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2026 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+assert(!is_android)
+
+source_set("browser_tests") {
+  testonly = true
+
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+
+  sources = [ "tracked_element_manager_browsertest.cc" ]
+  deps = [
+    "//chrome/browser/ui",
+    "//chrome/browser/ui/webui/user_education_internals",
+    "//chrome/test:test_support",
+    "//chrome/test/interaction",
+    "//content/public/browser",
+    "//content/test:test_support",
+    "//testing/gtest",
+  ]
+}
diff --git a/chrome/browser/ui/webui/tracked_element/OWNERS b/chrome/browser/ui/webui/tracked_element/OWNERS
new file mode 100644
index 0000000..e37176b4
--- /dev/null
+++ b/chrome/browser/ui/webui/tracked_element/OWNERS
@@ -0,0 +1 @@
+file://ui/webui/resources/js/tracked_element/OWNERS
diff --git a/chrome/browser/ui/webui/tracked_element/tracked_element_manager_browsertest.cc b/chrome/browser/ui/webui/tracked_element/tracked_element_manager_browsertest.cc
new file mode 100644
index 0000000..2514b3a
--- /dev/null
+++ b/chrome/browser/ui/webui/tracked_element/tracked_element_manager_browsertest.cc
@@ -0,0 +1,94 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <set>
+#include <string>
+#include <string_view>
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/interaction/browser_elements.h"
+#include "chrome/browser/ui/webui/user_education_internals/user_education_internals_ui.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/interaction/interactive_browser_test.h"
+#include "components/prefs/pref_service.h"
+#include "components/webui/chrome_urls/pref_names.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/webui/tracked_element/tracked_element_web_ui.h"
+#include "url/gurl.h"
+
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTabElementId);
+constexpr std::string_view kElementName = "MenuItemElement";
+
+class TrackedElementManagerBrowsertest : public InteractiveBrowserTest {
+ public:
+  TrackedElementManagerBrowsertest() = default;
+  ~TrackedElementManagerBrowsertest() override = default;
+
+  void SetUpOnMainThread() override {
+    InteractiveBrowserTest::SetUpOnMainThread();
+    g_browser_process->local_state()->SetBoolean(
+        chrome_urls::kInternalOnlyUisEnabled, true);
+
+    // Get the window and resize it to the maximum width of the current display.
+    // This prevents the target page from going into its "collapsed" state where
+    // the elements we want are hidden.
+    auto* const window = browser()->GetWindow();
+    const auto display = display::Screen::Get()->GetDisplayNearestWindow(
+        window->GetNativeWindow());
+    gfx::Rect bounds = window->GetBounds();
+    bounds.set_x(display.work_area().x());
+    bounds.set_width(display.work_area().width());
+    window->SetBounds(bounds);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(TrackedElementManagerBrowsertest, CheckElementsExist) {
+  gfx::Rect bounds;
+  std::string secondary_id;
+  ui::TrackedElement* target_menu_item = nullptr;
+  RunTestSequence(
+      InstrumentTab(kTabElementId),
+      NavigateWebContents(kTabElementId,
+                          GURL(chrome::kChromeUIUserEducationInternalsURL)),
+      InAnyContext(AfterShow(UserEducationInternalsUI::kMenuElementId,
+                             [&bounds, &secondary_id](ui::TrackedElement* el) {
+                               auto* const dom_el =
+                                   el->AsA<ui::TrackedElementWebUI>();
+                               ASSERT_NE(nullptr, dom_el);
+                               bounds = dom_el->GetScreenBounds();
+                               secondary_id = dom_el->secondary_identifier();
+                             }),
+                   WaitForElementCount(
+                       UserEducationInternalsUI::kMenuItemElementId, 8U)),
+      Do([&bounds, &target_menu_item] {
+        const auto menu_items =
+            ui::ElementTracker::GetElementTracker()
+                ->GetAllMatchingElementsInAnyContext(
+                    UserEducationInternalsUI::kMenuItemElementId);
+        EXPECT_EQ(8U, menu_items.size());
+        std::set<std::string> secondary_ids;
+        for (auto* menu_item : menu_items) {
+          auto* const dom_el = menu_item->AsA<ui::TrackedElementWebUI>();
+          ASSERT_NE(nullptr, dom_el);
+          const auto secondary_id = dom_el->secondary_identifier();
+          const auto item_bounds = dom_el->GetScreenBounds();
+          ASSERT_FALSE(secondary_id.empty());
+          ASSERT_TRUE(secondary_ids.insert(secondary_id).second)
+              << "Duplicate secondary ID: " << secondary_id;
+          EXPECT_TRUE(bounds.Contains(item_bounds));
+          if (secondary_id == "index:2") {
+            target_menu_item = dom_el;
+          }
+        }
+      }),
+      NameElement(kElementName, std::ref(target_menu_item)),
+      PressButton(kElementName),
+      CheckJsResultAt(kTabElementId, {"user-education-internals"},
+                      "el => el.selectedTabIndex", 2));
+}
diff --git a/chrome/browser/ui/webui/user_education_internals/user_education_internals_ui.cc b/chrome/browser/ui/webui/user_education_internals/user_education_internals_ui.cc
index ee4f7c9..5a1d517d 100644
--- a/chrome/browser/ui/webui/user_education_internals/user_education_internals_ui.cc
+++ b/chrome/browser/ui/webui/user_education_internals/user_education_internals_ui.cc
@@ -27,6 +27,10 @@
 #include "ui/webui/tracked_element/tracked_element_handler_document_singleton.h"
 #include "ui/webui/webui_util.h"
 
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(UserEducationInternalsUI, kMenuElementId);
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(UserEducationInternalsUI,
+                                      kMenuItemElementId);
+
 UserEducationInternalsUIConfig::UserEducationInternalsUIConfig()
     : DefaultInternalWebUIConfig(chrome::kChromeUIUserEducationInternalsHost) {}
 
@@ -52,7 +56,11 @@
                               IDR_USER_EDUCATION_INTERNALS_INDEX_HTML);
 
   ui::TrackedElementHandlerDocumentSingleton::Register(
-      this, std::vector<ui::ElementIdentifier>{kWebUIIPHDemoElementIdentifier});
+      this, std::vector<ui::ElementIdentifier>{
+                kWebUIIPHDemoElementIdentifier,
+                kMenuElementId,
+                kMenuItemElementId,
+            });
 }
 
 UserEducationInternalsUI::~UserEducationInternalsUI() = default;
diff --git a/chrome/browser/ui/webui/user_education_internals/user_education_internals_ui.h b/chrome/browser/ui/webui/user_education_internals/user_education_internals_ui.h
index 6b3a9be..2c6dd1b 100644
--- a/chrome/browser/ui/webui/user_education_internals/user_education_internals_ui.h
+++ b/chrome/browser/ui/webui/user_education_internals/user_education_internals_ui.h
@@ -34,6 +34,9 @@
   explicit UserEducationInternalsUI(content::WebUI* web_ui);
   ~UserEducationInternalsUI() override;
 
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kMenuElementId);
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kMenuItemElementId);
+
   void BindInterface(
       mojo::PendingReceiver<
           mojom::user_education_internals::UserEducationInternalsPageHandler>
diff --git a/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_layout_css_helper.cc b/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_layout_css_helper.cc
index ab84d3c0..7e0fefa 100644
--- a/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_layout_css_helper.cc
+++ b/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_layout_css_helper.cc
@@ -216,6 +216,9 @@
   }
 
   // Add insets.
+  AddInsets("--location-bar-page-action-icon-padding",
+            GetLayoutInsets(LOCATION_BAR_PAGE_ACTION_ICON_PADDING), css_string);
+
   AddInsets("--location-bar-page-info-icon-padding",
             GetLayoutInsets(LOCATION_BAR_PAGE_INFO_ICON_PADDING), css_string);
 
diff --git a/chrome/browser/uid/android/junit/src/org/chromium/chrome/browser/uid/SettingsSecureBasedIdentificationGeneratorTest.java b/chrome/browser/uid/android/junit/src/org/chromium/chrome/browser/uid/SettingsSecureBasedIdentificationGeneratorTest.java
index 57e4d626..16d1b611 100644
--- a/chrome/browser/uid/android/junit/src/org/chromium/chrome/browser/uid/SettingsSecureBasedIdentificationGeneratorTest.java
+++ b/chrome/browser/uid/android/junit/src/org/chromium/chrome/browser/uid/SettingsSecureBasedIdentificationGeneratorTest.java
@@ -16,13 +16,11 @@
 import org.mockito.Spy;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.util.HashUtil;
 
 /** Unit tests for {@link SettingsSecureBasedIdentificationGenerator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(UniqueIdentificationGeneratorFactoryTest.IDENTITY_GENERATOR_BATCH_NAME)
 public class SettingsSecureBasedIdentificationGeneratorTest {
     // Tell R8 this class is spied on and shouldn't be made final.
     @Spy SettingsSecureBasedIdentificationGenerator mGenerator;
diff --git a/chrome/browser/uid/android/junit/src/org/chromium/chrome/browser/uid/UniqueIdentificationGeneratorFactoryTest.java b/chrome/browser/uid/android/junit/src/org/chromium/chrome/browser/uid/UniqueIdentificationGeneratorFactoryTest.java
index aaa644f6..c4d7b65 100644
--- a/chrome/browser/uid/android/junit/src/org/chromium/chrome/browser/uid/UniqueIdentificationGeneratorFactoryTest.java
+++ b/chrome/browser/uid/android/junit/src/org/chromium/chrome/browser/uid/UniqueIdentificationGeneratorFactoryTest.java
@@ -12,12 +12,10 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 
 /** Unit tests for {@link UniqueIdentificationGeneratorFactory}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(UniqueIdentificationGeneratorFactoryTest.IDENTITY_GENERATOR_BATCH_NAME)
 public class UniqueIdentificationGeneratorFactoryTest {
     public static final String IDENTITY_GENERATOR_BATCH_NAME = "identity_generator";
 
diff --git a/chrome/browser/uid/android/junit/src/org/chromium/chrome/browser/uid/UuidBasedUniqueIdentificationGeneratorTest.java b/chrome/browser/uid/android/junit/src/org/chromium/chrome/browser/uid/UuidBasedUniqueIdentificationGeneratorTest.java
index 824425e..f981e46 100644
--- a/chrome/browser/uid/android/junit/src/org/chromium/chrome/browser/uid/UuidBasedUniqueIdentificationGeneratorTest.java
+++ b/chrome/browser/uid/android/junit/src/org/chromium/chrome/browser/uid/UuidBasedUniqueIdentificationGeneratorTest.java
@@ -19,13 +19,11 @@
 import org.mockito.Spy;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
 
 /** Unit tests for {@link UuidBasedUniqueIdentificationGenerator}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(UniqueIdentificationGeneratorFactoryTest.IDENTITY_GENERATOR_BATCH_NAME)
 public class UuidBasedUniqueIdentificationGeneratorTest {
     // Tell R8 this class is spied on and shouldn't be made final.
     @Spy UuidBasedUniqueIdentificationGenerator mGenerator;
diff --git a/chrome/browser/webauthn/passkey_unlock_manager.cc b/chrome/browser/webauthn/passkey_unlock_manager.cc
index 03bb5bf..317dc6e2 100644
--- a/chrome/browser/webauthn/passkey_unlock_manager.cc
+++ b/chrome/browser/webauthn/passkey_unlock_manager.cc
@@ -381,8 +381,6 @@
           syncer::UserSelectableType::kPasswords)) {
     return;
   }
-  // Ensure that we will not try to record the metric again.
-  password_readiness_recorded_on_startup_ = true;
   // The metric should only be recorded for users with the trusted vault
   // passphrase. For users with a different passphrase type on startup we don't
   // record this metric.
@@ -392,6 +390,18 @@
       syncer::PassphraseType::kTrustedVaultPassphrase) {
     return;
   }
+  if (!user_settings->IsTrustedVaultKeyRequiredForPreferredDataTypes() &&
+      !sync_service()->GetActiveDataTypes().Has(syncer::PASSWORDS)) {
+    // When Sync starts up, it asynchronously checks whether trusted vault keys
+    // are available locally. While this check is in flight,
+    // `IsTrustedVaultKeyRequiredForPreferredDataTypes()` temporarily returns
+    // false and `syncer::PASSWORDS` is not active yet. In this case we should
+    // not publish the password readiness metric yet.
+    return;
+  }
+  // Ensure that we will not try to record the metric again.
+  password_readiness_recorded_on_startup_ = true;
+
   bool password_readiness =
       !user_settings->IsTrustedVaultKeyRequiredForPreferredDataTypes();
   base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
diff --git a/chrome/browser/webauthn/passkey_unlock_manager_unittest.cc b/chrome/browser/webauthn/passkey_unlock_manager_unittest.cc
index 474c179..2813b2a5 100644
--- a/chrome/browser/webauthn/passkey_unlock_manager_unittest.cc
+++ b/chrome/browser/webauthn/passkey_unlock_manager_unittest.cc
@@ -67,7 +67,8 @@
       GpmPinStatus gpm_pin_status,
       syncer::PassphraseType passphrase_type =
           syncer::PassphraseType::kKeystorePassphrase,
-      bool trusted_vault_key_required = false) {
+      bool trusted_vault_key_required = false,
+      syncer::DataTypeSet inactive_sync_data_types = {}) {
     test_enclave_manager_ = CreateMockEnclaveManager(
         /*is_enclave_manager_loaded=*/true,
         /*is_enclave_manager_ready=*/enclave_manager_status == kEnclaveReady,
@@ -78,6 +79,7 @@
     test_sync_service()->GetUserSettings()->SetPassphraseType(passphrase_type);
     test_sync_service()->GetUserSettings()->SetTrustedVaultKeyRequired(
         trusted_vault_key_required);
+    test_sync_service()->SetFailedDataTypes(inactive_sync_data_types);
     passkey_unlock_manager_ = std::make_unique<PasskeyUnlockManager>(
         test_enclave_manager_.get(), test_passkey_model_.get(),
         test_sync_service_.get());
@@ -518,6 +520,33 @@
       "PasswordManager.TrustedVaultPasswordReadiness", 0);
 }
 
+TEST_F(PasskeyUnlockManagerTest,
+       DoesNotLogPasswordReadinessHistogramUntilPasswordsAreActive) {
+  base::test::ScopedFeatureList scoped_feature_list{
+      password_manager::features::kRecordPasswordReadiness};
+
+  base::HistogramTester histogram_tester;
+  ConfigureProfileAndSyncService(
+      kEnclaveNotReady, kPasskeyModelReady, GpmPinStatus::kGpmPinUnset,
+      syncer::PassphraseType::kTrustedVaultPassphrase,
+      /*trusted_vault_key_required=*/false,
+      /*inactive_sync_data_types=*/{syncer::PASSWORDS});
+
+  AdvanceClock(base::Seconds(31));
+  histogram_tester.ExpectTotalCount(
+      "PasswordManager.TrustedVaultPasswordReadiness", 0);
+
+  // This makes `PASSWORDS` an active sync data type.
+  test_sync_service()->SetFailedDataTypes({});
+  test_sync_service()->FireStateChanged();
+
+  AdvanceClock(base::Seconds(31));
+  histogram_tester.ExpectUniqueSample(
+      "PasswordManager.TrustedVaultPasswordReadiness",
+      /*sample=*/true,
+      /*expected_bucket_count=*/1);
+}
+
 }  // namespace
 
 }  // namespace webauthn
diff --git a/chrome/build/android-arm.orderfile.txt b/chrome/build/android-arm.orderfile.txt
index f2aeca5..a460587 100644
--- a/chrome/build/android-arm.orderfile.txt
+++ b/chrome/build/android-arm.orderfile.txt
@@ -1 +1 @@
-_LdrdHvski_AZrqEW3ffRQ42K2m-9l46sk7ZXy69IUgC
+l9l3O1DtD0_TkEL8smKpSlnmgVj6H4Xj1LC3lilYQokC
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index cf96008..6c1cacb 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1786103907-f91c4152076cdcc3b3236fd2cbe80f381d0ac20d-8b6ca0062a68dc42fe3b3c2d2b45e041a50b9d87.profdata
+chrome-android32-main-1786125592-e0cabfef195409c5914aca365070c03822ac381a-69baff311c8306dfcb75efa91113a542e09077b7.profdata
diff --git a/chrome/build/android-arm64.orderfile.txt b/chrome/build/android-arm64.orderfile.txt
index 3d94914..2e9b20e2 100644
--- a/chrome/build/android-arm64.orderfile.txt
+++ b/chrome/build/android-arm64.orderfile.txt
@@ -1 +1 @@
-_Pe5DcvfU3x3N_WqhmtSXbueBFkWKVcBPybbXLrB3acC
+3f4T33QVECHRaa_V9_zhzCmXwGHtMUGefAmXtf9huTQC
diff --git a/chrome/build/android-desktop-x64.pgo.txt b/chrome/build/android-desktop-x64.pgo.txt
index 83476f3..9814156 100644
--- a/chrome/build/android-desktop-x64.pgo.txt
+++ b/chrome/build/android-desktop-x64.pgo.txt
@@ -1 +1 @@
-chrome-android-desktop-x64-main-1786103907-21a426c9b89665f9a20c64a901764155f52e5ec7-8b6ca0062a68dc42fe3b3c2d2b45e041a50b9d87.profdata
+chrome-android-desktop-x64-main-1786125592-9cfad408ab128ba03ee6bf1ae11657f97abbb607-69baff311c8306dfcb75efa91113a542e09077b7.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 41da063..2b8a128f 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1786103907-e15c0a9f7518157a1e85072b005d0ca28b7bd069-8b6ca0062a68dc42fe3b3c2d2b45e041a50b9d87.profdata
+chrome-linux-main-1786125592-1e60dd3280bd92bc6ca0efaf7d836fd74beee79d-69baff311c8306dfcb75efa91113a542e09077b7.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 0133f684..64a1f89 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1786118379-07c9589e32c3080e65bb76e12c4f4db26134d5b3-42c4b08da6a0adf6862f31684917b86c8ea11bed.profdata
+chrome-mac-arm-main-1786132764-0d65d391554a5026d40ea300bf7ea02f19b136cf-69a1d4459aadc90e128219fa1f52e2154d61a1fe.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 6e0547d..d3c708d 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1786103907-b690c7f21f054261bd4070d1a05638b94ad3eb9a-8b6ca0062a68dc42fe3b3c2d2b45e041a50b9d87.profdata
+chrome-mac-main-1786125592-cf7ff6ec8c8a9a96284f275cffabef8f599a0747-69baff311c8306dfcb75efa91113a542e09077b7.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 0152b28..d8d4d0d 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1786103907-bed2e96655ca6fb9a0329b4221f381cde963a622-8b6ca0062a68dc42fe3b3c2d2b45e041a50b9d87.profdata
+chrome-win-arm64-main-1786125592-a5829b3a27628f12c40bcb5c25d6f5f38c9302f4-69baff311c8306dfcb75efa91113a542e09077b7.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 0d93eb4..dacf488 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1786103907-3089540b6149a24e8f003fc07757cb342dcd4777-8b6ca0062a68dc42fe3b3c2d2b45e041a50b9d87.profdata
+chrome-win64-main-1786125592-f097de6a8f4a19573d198bcd090182715ff347a1-69baff311c8306dfcb75efa91113a542e09077b7.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index baa3900..64d5418 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -779,9 +779,6 @@
                    &kGlicLearnMoreURLConfig,
                    "glic-experimental-triggering-toggle-learn-more-url",
                    "https://support.google.com/chrome?p=gemini_spark");
-// WARNING: If this URL is changed, update the substring match check in the
-// accessibility script injected in
-// chrome/browser/resources/glic/experimental_opt_in/experimental_opt_in.ts
 BASE_FEATURE_PARAM(std::string,
                    kGlicExperimentalTriggeringSafetyURL,
                    &kGlicLearnMoreURLConfig,
@@ -792,18 +789,12 @@
 // ("use-policy" and "unexpected_results") to apply accessibility labels.
 // Finch configurations overriding these URLs should retain these substrings
 // or update the WebUI logic accordingly.
-// WARNING: If this URL is changed, update the substring match check in the
-// accessibility script injected in
-// chrome/browser/resources/glic/experimental_opt_in/experimental_opt_in.ts
 BASE_FEATURE_PARAM(
     std::string,
     kGlicWebActuationToggleConsiderSafelyURL,
     &kGlicLearnMoreURLConfig,
     "glic-actuation-on-web-toggle-things-to-consider-safely-url",
     "https://policies.google.com/terms/generative-ai/use-policy");
-// WARNING: If this URL is changed, update the substring match check in the
-// accessibility script injected in
-// chrome/browser/resources/glic/experimental_opt_in/experimental_opt_in.ts
 BASE_FEATURE_PARAM(
     std::string,
     kGlicWebActuationToggleConsiderUnexpectedResultsURL,
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index ea9bbfa..39a7f40 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -138,6 +138,9 @@
 // This is the profile creation time.
 inline constexpr char kProfileCreationTime[] = "profile.creation_time";
 
+// Preference which stores randomly generated profile ID for LOM.
+inline constexpr char kLomProfileId[] = "private_metrics.lom.profile_id";
+
 #if BUILDFLAG(IS_WIN)
 // This is a timestamp of the last time this profile was reset by a third party
 // tool. On Windows, a third party tool may set a registry value that will be
diff --git a/chrome/common/read_anything/read_anything.mojom b/chrome/common/read_anything/read_anything.mojom
index a30a150..c2deed6 100644
--- a/chrome/common/read_anything/read_anything.mojom
+++ b/chrome/common/read_anything/read_anything.mojom
@@ -205,6 +205,12 @@
   // loaded.
   GetDependencyParserModel() => (mojo_base.mojom.File? model_file);
 
+  // Called by the renderer to check if a "new" badge should be shown for line
+  // focus.
+  // TODO(crbug.com/543113387): Remove this when the WebUI new badge supports
+  // auto-disappearing logic itself.
+  ShouldShowLineFocusNewBadge() => (bool show);
+
   // Called by the renderer to get the current ReadingMode state from
   // ReadAnythingController. The response for this call comes via
   // UntrustedPage::OnGetPresentationState.
@@ -288,6 +294,11 @@
   OnLineFocusChanged(read_anything.mojom.LineFocus current_line_focus,
                      read_anything.mojom.LineFocus last_non_disabled_line_focus);
 
+  // Informs the browser controller that the user used the line focus feature.
+  // TODO(crbug.com/543113387): Remove this when the WebUI new badge supports
+  // auto-disappearing logic itself.
+  OnLineFocusFeatureUsed();
+
   // Informs the browser controller whether audio for read aloud is currently
   // playing.
   OnReadAloudAudioStateChange(bool playing);
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc
index c2a3b8a..bb11765 100644
--- a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc
+++ b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc
@@ -1539,7 +1539,6 @@
                    &ReadAnythingAppController::GetDistillationMethod)
       .SetProperty("isLineFocusEnabled",
                    &ReadAnythingAppController::IsLineFocusEnabled)
-      .SetProperty("isChromeOsAsh", &ReadAnythingAppController::IsChromeOsAsh)
       .SetProperty("baseLanguageForSpeech",
                    &ReadAnythingAppController::GetLanguageCodeForSpeech)
       .SetProperty("requiresDistillation",
@@ -1587,6 +1586,10 @@
                  &ReadAnythingAppController::OnLinksEnabledToggled)
       .SetMethod("onTranslationRequested",
                  &ReadAnythingAppController::OnTranslationRequested)
+      .SetMethod("requestShouldShowLineFocusNewBadge",
+                 &ReadAnythingAppController::RequestShouldShowLineFocusNewBadge)
+      .SetMethod("onLineFocusFeatureUsed",
+                 &ReadAnythingAppController::OnLineFocusFeatureUsed)
       .SetMethod("onImagesEnabledToggled",
                  &ReadAnythingAppController::OnImagesEnabledToggled)
       .SetMethod("onScroll", &ReadAnythingAppController::OnScroll)
@@ -2291,12 +2294,21 @@
   return features::IsReadAnythingLineFocusEnabled();
 }
 
-bool ReadAnythingAppController::IsChromeOsAsh() const {
-#if BUILDFLAG(IS_CHROMEOS)
-  return true;
-#else
-  return false;
-#endif
+void ReadAnythingAppController::RequestShouldShowLineFocusNewBadge() {
+  page_handler_->ShouldShowLineFocusNewBadge(base::BindOnce(
+      &ReadAnythingAppController::OnShouldShowLineFocusNewBadgeResponse,
+      weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ReadAnythingAppController::OnShouldShowLineFocusNewBadgeResponse(
+    bool show) {
+  ExecuteJavaScript(
+      "chrome.readingMode.onShouldShowLineFocusNewBadgeResponse(" +
+      base::ToString(show) + ")");
+}
+
+void ReadAnythingAppController::OnLineFocusFeatureUsed() {
+  page_handler_->OnLineFocusFeatureUsed();
 }
 
 bool ReadAnythingAppController::IsGoogleDocs() const {
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.h b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.h
index 103b4e3..dad6031 100644
--- a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.h
+++ b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.h
@@ -238,6 +238,13 @@
   int HighlightGranularity() const;
   int LastNonDisabledLineFocus() const;
   bool IsLineFocusOn() const;
+  // The following 3 functions are for handling the auto-disappearing logic
+  // for the line focus new badge.
+  // TODO(crbug.com/543113387): Remove these when the WebUI new badge supports
+  // auto-disappearing logic itself.
+  void RequestShouldShowLineFocusNewBadge();
+  void OnShouldShowLineFocusNewBadgeResponse(bool show);
+  void OnLineFocusFeatureUsed();
   bool IsHighlightOn();
   int StandardLineSpacing() const;
   int LooseLineSpacing() const;
@@ -283,7 +290,6 @@
   std::string GetStoredVoice() const;
   std::vector<std::string> GetLanguagesEnabledInPref() const;
   std::vector<ui::AXNodeID> GetChildren(ui::AXNodeID ax_node_id) const;
-  std::string GetDataFontCss(ui::AXNodeID ax_node_id) const;
   std::string GetHtmlTag(ui::AXNodeID ax_node_id) const;
   std::string GetLanguage(ui::AXNodeID ax_node_id) const;
   std::u16string GetTextContent(ui::AXNodeID ax_node_id) const;
@@ -338,7 +344,6 @@
   bool IsReadabilitySelectTextEnabled() const;
   bool IsLineFocusEnabled() const;
   bool IsReadabilityWithLinksEnabled() const;
-  bool IsChromeOsAsh() const;
   bool IsPhraseHighlightingEnabled() const;
   void OnLetterSpacingChange(int value);
   void OnLineSpacingChange(int value);
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_controller_browsertest.cc b/chrome/renderer/accessibility/read_anything/read_anything_app_controller_browsertest.cc
index 43388f5..5547e4b 100644
--- a/chrome/renderer/accessibility/read_anything/read_anything_app_controller_browsertest.cc
+++ b/chrome/renderer/accessibility/read_anything/read_anything_app_controller_browsertest.cc
@@ -74,6 +74,10 @@
               (GetDependencyParserModelCallback mojo_callback),
               (override));
   MOCK_METHOD(void,
+              ShouldShowLineFocusNewBadge,
+              (ShouldShowLineFocusNewBadgeCallback mojo_callback),
+              (override));
+  MOCK_METHOD(void,
               GetVoicePackInfo,
               (const std::string& language),
               (override));
@@ -135,6 +139,7 @@
               (read_anything::mojom::LineFocus current_line_focus,
                read_anything::mojom::LineFocus last_non_disabled_line_focus),
               (override));
+  MOCK_METHOD(void, OnLineFocusFeatureUsed, (), (override));
   MOCK_METHOD(void,
               OnImageDataRequested,
               (const ::ui::AXTreeID& target_tree_id, int32_t target_node_id),
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java
index e828c4b..a25e8c20 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java
@@ -12,6 +12,7 @@
 import android.view.View;
 
 import org.chromium.base.test.transit.Element;
+import org.chromium.base.test.transit.OptionalViewElement;
 import org.chromium.base.test.transit.SimpleConditions;
 import org.chromium.base.test.transit.TripBuilder;
 import org.chromium.base.test.transit.ViewElement;
@@ -21,6 +22,7 @@
 import org.chromium.chrome.browser.suggestions.SiteSuggestion;
 import org.chromium.chrome.test.transit.page.CtaPageStation;
 import org.chromium.chrome.test.transit.page.NativePageCondition;
+import org.chromium.chrome.test.transit.page.WebPageStation;
 
 import java.util.Collections;
 import java.util.List;
@@ -32,6 +34,7 @@
  */
 public class RegularNewTabPageStation extends CtaPageStation {
     public ViewElement<View> searchBoxElement;
+    public OptionalViewElement<View> ntpMicButtonElement;
     public ViewElement<UrlBar> urlBarElement;
     public ViewElement<View> logoElement;
     public Element<NewTabPage> nativePageElement;
@@ -51,6 +54,7 @@
 
         logoElement = declareView(withId(R.id.search_provider_logo));
         searchBoxElement = declareView(withId(R.id.search_box));
+        ntpMicButtonElement = declareOptionalView(withId(R.id.voice_search_button));
 
         nativePageElement =
                 declareEnterConditionAsElement(
@@ -90,6 +94,16 @@
         return focusOnMvts(siteSuggestions, Collections.emptySet());
     }
 
+    /**
+     * Clicks the NTP mic button on the search box, expecting to navigate to a search results page.
+     *
+     * @param query the query to expect in the search URL
+     * @return the {@link WebPageStation} representing the search results page
+     */
+    public WebPageStation clickNtpMicToSearchPage(String query) {
+        return ntpMicButtonElement.clickTo().arriveAt(createSearchPageStation(query));
+    }
+
     @Override
     protected TripBuilder clickUrlBarOrSearchBarTo() {
         return searchBoxElement.clickTo();
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java
index b67faea..ad87079f 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java
@@ -20,6 +20,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.UrlBar;
 import org.chromium.chrome.test.transit.page.CtaPageStation;
+import org.chromium.chrome.test.transit.page.WebPageStation;
 import org.chromium.components.browser_ui.widget.scrim.ScrimView;
 
 /** Represents the Omnibox focused state showing the URL bar and accepting keyboard input. */
@@ -70,4 +71,14 @@
         return runOnUiThreadTo(() -> urlBarElement.value().setText(textToSetAndExpect))
                 .enterFacility(new OmniboxEnteredTextFacility(this, textToSetAndExpect));
     }
+
+    /**
+     * Clicks the omnibox mic button, expecting to navigate to a search results page.
+     *
+     * @param query the query to expect in the search URL
+     * @return the {@link WebPageStation} representing the search results page
+     */
+    public WebPageStation clickOmniboxMicToSearchPage(String query) {
+        return micButtonElement.clickTo().arriveAt(mHostStation.createSearchPageStation(query));
+    }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/CtaPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/CtaPageStation.java
index 8149905..811e1ad 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/CtaPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/CtaPageStation.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.test.transit.page;
 
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 
 import static org.hamcrest.CoreMatchers.allOf;
@@ -50,7 +51,14 @@
  * the expected title, etc.
  */
 public class CtaPageStation extends BasePageStation<ChromeTabbedActivity> {
+    private static final String SEARCH_PATH = "/search";
+    private static final String SEARCH_QUERY_PARAM = "q";
     public static final ViewSpec<UrlBar> URL_BAR = viewSpec(UrlBar.class, withId(R.id.url_bar));
+    public static final ViewSpec<View> TOOLBAR_MIC_BUTTON =
+            viewSpec(
+                    withId(R.id.optional_toolbar_button),
+                    withContentDescription(R.string.accessibility_toolbar_btn_mic));
+    public final OptionalViewElement<View> optionalToolbarMicButtonElement;
     public final OptionalViewElement<View> homeButtonElement;
     // TODO(crbug.com/477035792): Temporarily nullable while the toolbar is being migrated.
     public final @Nullable ViewElement<ToolbarControlContainer> toolbarElement;
@@ -97,6 +105,28 @@
                 declareOptionalView(
                         allOf(withId(R.id.home_button), isDisplayed()),
                         ViewElement.unscopedOption());
+
+        // The optional toolbar mic button specifically.
+        optionalToolbarMicButtonElement = declareOptionalView(TOOLBAR_MIC_BUTTON);
+    }
+
+    /**
+     * Clicks the mic button in the toolbar, expecting to navigate to a search results page.
+     *
+     * @param query the query to expect in the search URL
+     * @return the {@link WebPageStation} representing the search results page
+     */
+    public WebPageStation clickToolbarMicToSearchPage(String query) {
+        return optionalToolbarMicButtonElement.clickTo().arriveAt(createSearchPageStation(query));
+    }
+
+    /** Returns a {@link WebPageStation} expecting a search results page for the given query. */
+    public WebPageStation createSearchPageStation(String query) {
+        return WebPageStation.newBuilder()
+                .initFrom(this)
+                // TODO(b/543914644): Create a method for verifying url args directly.
+                .withExpectedUrlSubstring(SEARCH_PATH + "?" + SEARCH_QUERY_PARAM + "=" + query)
+                .build();
     }
 
     /** Long presses the tab switcher button to open the action menu. */
diff --git a/chrome/test/data/webui/contextual_tasks/overflow_menu_test.ts b/chrome/test/data/webui/contextual_tasks/overflow_menu_test.ts
index 7489154..0aca974b 100644
--- a/chrome/test/data/webui/contextual_tasks/overflow_menu_test.ts
+++ b/chrome/test/data/webui/contextual_tasks/overflow_menu_test.ts
@@ -180,10 +180,45 @@
         webuiRoundedIconsEnabled: false,
       });
       overflowMenu = document.createElement('contextual-tasks-overflow-menu');
+      overflowMenu.isHandshakeComplete = true;
+      overflowMenu.isCobrowseEligible = true;
       document.body.appendChild(overflowMenu);
       await microtasksFinished();
     });
 
+    test('hides pin button when handshake is not complete', async () => {
+      overflowMenu.isHandshakeComplete = false;
+      await microtasksFinished();
+
+      let pinButton =
+          overflowMenu.shadowRoot.querySelector<HTMLElement>('#pinButton');
+      assertFalse(!!pinButton);
+
+      overflowMenu.isHandshakeComplete = true;
+      await microtasksFinished();
+      pinButton =
+          overflowMenu.shadowRoot.querySelector<HTMLElement>('#pinButton');
+      assertTrue(!!pinButton);
+    });
+
+    test('hides pin button when not cobrowse eligible', async () => {
+      overflowMenu.isCobrowseEligible = false;
+      await microtasksFinished();
+
+      const pinButton =
+          overflowMenu.shadowRoot.querySelector<HTMLElement>('#pinButton');
+      assertFalse(!!pinButton);
+    });
+
+    test('hides pin button when not on AI page', async () => {
+      overflowMenu.isAiPage = false;
+      await microtasksFinished();
+
+      const pinButton =
+          overflowMenu.shadowRoot.querySelector<HTMLElement>('#pinButton');
+      assertFalse(!!pinButton);
+    });
+
     test('records metrics on pin click', async () => {
       const pinButton =
           overflowMenu.shadowRoot.querySelector<HTMLElement>('#pinButton');
diff --git a/chrome/test/data/webui/contextual_tasks/top_toolbar_test.ts b/chrome/test/data/webui/contextual_tasks/top_toolbar_test.ts
index 94037d1..7d26467 100644
--- a/chrome/test/data/webui/contextual_tasks/top_toolbar_test.ts
+++ b/chrome/test/data/webui/contextual_tasks/top_toolbar_test.ts
@@ -402,14 +402,67 @@
       loadTimeData.overrideValues({
         enablePinButton: true,
         isAiPage: true,
+        isCobrowseEligible: true,
         pinTooltip: 'Pin side panel',
         unpinTooltip: 'Unpin side panel',
       });
       topToolbar = document.createElement('top-toolbar');
+      topToolbar.isHandshakeComplete = true;
       document.body.appendChild(topToolbar);
       await microtasksFinished();
     });
 
+    test('hides pin button when handshake is not complete', async () => {
+      topToolbar.isHandshakeComplete = false;
+      await microtasksFinished();
+
+      const moreButton =
+          topToolbar.shadowRoot.querySelector<CrIconButtonElement>(
+              '#overflowMenuButton');
+      assertTrue(!!moreButton);
+      moreButton.click();
+      await microtasksFinished();
+
+      const menu = topToolbar.$.overflowMenu.get();
+      const pinButton =
+          menu.shadowRoot.querySelector<HTMLElement>('#pinButton');
+      assertFalse(!!pinButton);
+    });
+
+    test('hides pin button when not cobrowse eligible', async () => {
+      topToolbar.isCobrowseEligible = false;
+      await microtasksFinished();
+
+      const moreButton =
+          topToolbar.shadowRoot.querySelector<CrIconButtonElement>(
+              '#overflowMenuButton');
+      assertTrue(!!moreButton);
+      moreButton.click();
+      await microtasksFinished();
+
+      const menu = topToolbar.$.overflowMenu.get();
+      const pinButton =
+          menu.shadowRoot.querySelector<HTMLElement>('#pinButton');
+      assertFalse(!!pinButton);
+    });
+
+    test('hides pin button when not on AI page', async () => {
+      topToolbar.isAiPage = false;
+      await microtasksFinished();
+
+      const moreButton =
+          topToolbar.shadowRoot.querySelector<CrIconButtonElement>(
+              '#overflowMenuButton');
+      assertTrue(!!moreButton);
+      moreButton.click();
+      await microtasksFinished();
+
+      const menu = topToolbar.$.overflowMenu.get();
+      const pinButton =
+          menu.shadowRoot.querySelector<HTMLElement>('#pinButton');
+      assertFalse(!!pinButton);
+    });
+
     test('handles pin button click', async () => {
       const moreButton =
           topToolbar.shadowRoot.querySelector<CrIconButtonElement>(
@@ -455,6 +508,26 @@
       pinButton.click();
       await proxy.handler.whenCalled('unpinSidePanel');
     });
+
+    test('resets handshake complete when leaving AI page', async () => {
+      assertTrue(topToolbar.isHandshakeComplete);
+
+      topToolbar.isAiPage = false;
+      await microtasksFinished();
+
+      assertFalse(topToolbar.isHandshakeComplete);
+      assertFalse(topToolbar.isAiPage);
+    });
+
+    test('sets handshake complete on callback', async () => {
+      topToolbar.isHandshakeComplete = false;
+      await microtasksFinished();
+
+      proxy.callbackRouterRemote.onHandshakeComplete();
+      await microtasksFinished();
+
+      assertTrue(topToolbar.isHandshakeComplete);
+    });
   });
 
   suite('Expand button and menu for lens flows disabled', () => {
diff --git a/chrome/test/data/webui/cr_components/searchbox/searchbox_mixin_test.ts b/chrome/test/data/webui/cr_components/searchbox/searchbox_mixin_test.ts
index de7a337..1f89a17 100644
--- a/chrome/test/data/webui/cr_components/searchbox/searchbox_mixin_test.ts
+++ b/chrome/test/data/webui/cr_components/searchbox/searchbox_mixin_test.ts
@@ -644,6 +644,59 @@
     assertEquals(matches[0]!.destinationUrl, args.url);
   });
 
+  test(
+      'clicking remove button after interaction freeze unfreezes and accepts new results',
+      async () => {
+        const mockInput = element.getInputElement();
+        await simulateUserTextInput(mockInput, 'hello');
+        const queryId = element.activeQueryId;
+
+        const matches = [
+          createUrlMatch(
+              {supportsDeletion: true, destinationUrl: 'https://first.com'}),
+          createUrlMatch(
+              {supportsDeletion: true, destinationUrl: 'https://second.com'}),
+        ];
+        element.onAutocompleteResultChanged(createAutocompleteResultForTesting({
+          queryId: queryId,
+          input: 'hello',
+          matches: matches,
+        }));
+        await microtasksFinished();
+        assertEquals(2, element.result!.matches.length);
+
+        // Freeze `activeQueryId` by simulating user interaction. New
+        // autocomplete results for 'hello' will be ignored to avoid clobbering
+        // user actions.
+        const arrowDownEvent = createKeyboardEvent('ArrowDown');
+        mockInput.inputElement.dispatchEvent(arrowDownEvent);
+        await microtasksFinished();
+        assertEquals(-1, element.activeQueryId);
+
+        // Click remove button on the first match.
+        const matchEl = element.getDropdownElement().shadowRoot.querySelector(
+            'cr-searchbox-match')!;
+        matchEl.$.remove.click();
+
+        const args =
+            await testProxy.handler.whenCalled('deleteAutocompleteMatch');
+        assertEquals(0, args.line);
+        assertEquals(matches[0]!.destinationUrl, args.url);
+
+        // Backend sends updated results without the deleted match.
+        element.onAutocompleteResultChanged(createAutocompleteResultForTesting({
+          queryId: queryId,
+          input: 'hello',
+          matches: [matches[1]!],
+        }));
+        await microtasksFinished();
+
+        // The new results should be accepted despite the prior freeze.
+        assertEquals(1, element.result!.matches.length);
+        assertEquals(
+            'https://second.com', element.result!.matches[0]!.destinationUrl);
+      });
+
   // TODO(crbug.com/453570027): Test is flaky.
   test.skip('arrow up/down moves selection / focus', async () => {
     const mockInput = element.getInputElement();
diff --git a/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_test.ts b/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_test.ts
index 8631264..3da3236 100644
--- a/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_test.ts
@@ -22,6 +22,15 @@
     field.onSearchTermSearch();
   }
 
+  async function addSlottedChild() {
+    const child = document.createElement('div');
+    child.slot = 'suffixElement';
+    child.textContent = 'Ctrl+Shift+A';
+    field.appendChild(child);
+    await field.updateComplete;
+    return child;
+  }
+
   setup(function() {
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
     field = document.createElement('cr-toolbar-search-field');
@@ -274,4 +283,34 @@
     const searchTermBeforeInputEvent = await searchTermBeforeInputEventPromise;
     assertEquals(beforeInputEvent, searchTermBeforeInputEvent.detail.e);
   });
+
+  test('renders slotted content', async () => {
+    const child = await addSlottedChild();
+
+    const slot = field.shadowRoot.querySelector<HTMLSlotElement>(
+        'slot[name=suffixElement]');
+    assertTrue(!!slot);
+    const assignedNodes = slot.assignedNodes();
+    assertEquals(1, assignedNodes.length);
+    assertEquals(child, assignedNodes[0]);
+  });
+
+  test('hides slotted content when searching for text', async () => {
+    await addSlottedChild();
+
+    // Slot is present when there is no search text.
+    assertTrue(!!field.shadowRoot.querySelector('slot[name=suffixElement]'));
+
+    // Searching for text removes slot and shows clear search button.
+    simulateSearch('query');
+    await field.updateComplete;
+    assertFalse(!!field.shadowRoot.querySelector('slot[name=suffixElement]'));
+    assertTrue(!!field.shadowRoot.querySelector('#clearSearch'));
+
+    // Clearing search text restores slot.
+    field.setValue('');
+    await field.updateComplete;
+    assertTrue(!!field.shadowRoot.querySelector('slot[name=suffixElement]'));
+    assertFalse(!!field.shadowRoot.querySelector('#clearSearch'));
+  });
 });
diff --git a/chrome/test/data/webui/glic/browser_tests/glic_api_browsertest.ts b/chrome/test/data/webui/glic/browser_tests/glic_api_browsertest.ts
index e7ec5bb..df9c928 100644
--- a/chrome/test/data/webui/glic/browser_tests/glic_api_browsertest.ts
+++ b/chrome/test/data/webui/glic/browser_tests/glic_api_browsertest.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 import {HostCapability, InvocationSource, MetricUserInputReactionType, PanelStateKind, Platform, ResponseStopCause, WebClientMode} from '/glic/glic_api/glic_api.js';
-import type {CancelActionsResult, FocusedTabData, InvokeOptions, OpenPanelInfo, PanelOpeningData, TabData, UserProfileInfo} from '/glic/glic_api/glic_api.js';
+import type {CancelActionsResult, FocusedTabData, OpenPanelInfo, PanelOpeningData, TabData, UserProfileInfo} from '/glic/glic_api/glic_api.js';
 
 import {ApiTestError, ApiTestFixtureBase, assertDefined, assertEquals, assertFalse, assertNotEquals, assertRejects, assertTrue, assertUndefined, checkDefined, mapObservable, observeSequence, readStream, runUntil, sleep, testMain, waitFor, WebClient} from './browser_test_base.js';
 import type {SequencedSubscriber} from './browser_test_base.js';
@@ -81,52 +81,6 @@
         panelOpenData.invocationSource, InvocationSource.TOP_CHROME_BUTTON);
   }
 
-  async testOpenGlicSettingsPage() {
-    assertDefined(this.host.openGlicSettingsPage);
-    this.host.openGlicSettingsPage();
-    // There is a problem with InProcessBrowserTest::QuitBrowsers(). Opening a
-    // browser at the same time as exiting a test results in QuitBrowsers()
-    // never exiting. This sleep avoids this problem.
-    await sleep(500);
-  }
-
-  async testOpenPasswordManagerSettingsPage() {
-    assertDefined(this.host.openPasswordManagerSettingsPage);
-    this.host.openPasswordManagerSettingsPage();
-  }
-
-
-
-  async testCanAttachPanelToFallbackEmbedder() {
-    assertDefined(this.host.getFocusedTabStateV2);
-    assertDefined(this.host.getPinnedTabs);
-    assertDefined(this.host.getPanelState);
-    assertDefined(this.host.detachPanel);
-    assertDefined(this.host.canAttachPanel);
-    const link = document.createElement('a');
-    link.setAttribute('href', 'https://www.chromium.org');
-    link.setAttribute('target', '_blank');
-    document.body.appendChild(link);
-    link.click();
-    // The opened tab should be pinned.
-    await observeSequence(this.host.getPinnedTabs())
-        .waitFor(tabs => tabs.length === 2);
-
-    // Detach panel
-    const panelStates = observeSequence(this.host.getPanelState());
-    await panelStates.waitFor(state => state.kind === PanelStateKind.ATTACHED);
-
-    this.host.detachPanel();
-    await panelStates.waitFor(state => state.kind === PanelStateKind.DETACHED);
-
-    // Wait for C++ to close the tab.
-    await this.advanceToNextStep();
-
-    // The panel should still be attachable.
-    await observeSequence(this.host.canAttachPanel()).waitForValue(true);
-  }
-
-
   async testErrorShownOnMojoPipeError() {}
 
   async testPanelActiveWithMicrophone() {
@@ -1770,25 +1724,6 @@
 }
 
 
-class WebClientWithInvoke extends WebClient {
-  invokePromise = Promise.withResolvers<InvokeOptions>();
-  override async invoke(options: InvokeOptions): Promise<void> {
-    this.invokePromise.resolve(options);
-  }
-}
-
-class ApiTestWithInvoke extends ApiTestFixtureBase {
-  override createWebClient(): WebClient {
-    return new WebClientWithInvoke();
-  }
-
-  async testInvoke() {
-    const options =
-        await (this.client as WebClientWithInvoke).invokePromise.promise;
-    assertEquals(options.invocationSource, InvocationSource.TOP_CHROME_BUTTON);
-  }
-}
-
 // All test fixtures. We look up tests by name, and the fixture name is ignored.
 // Therefore all tests must have unique names.
 const TEST_FIXTURES = [
@@ -1796,7 +1731,6 @@
   DaisyChainApiTests,
   NotifyPanelWillOpenTest,
   ApiTestWithoutOpen,
-  ApiTestWithInvoke,
 ];
 
 testMain(TEST_FIXTURES);
diff --git a/chrome/test/data/webui/glic/browser_tests/new_glic_api_browsertest.ts b/chrome/test/data/webui/glic/browser_tests/new_glic_api_browsertest.ts
index e939f82..767b50b 100644
--- a/chrome/test/data/webui/glic/browser_tests/new_glic_api_browsertest.ts
+++ b/chrome/test/data/webui/glic/browser_tests/new_glic_api_browsertest.ts
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {CancelActionsResult, ClientCapabilities, ExperimentalTriggeringUpdateType, FileUploadPolicyState, FormFactor, HostCapability, PanelStateKind, Platform, SbThreatType, ScreenshotEncryptionScheme, ScrollToErrorReason, SkillSource, WebClientMode} from '/glic/glic_api/glic_api.js';
+import {CancelActionsResult, ClientCapabilities, ExperimentalTriggeringUpdateType, FileUploadPolicyState, FormFactor, HostCapability, InvocationSource, PanelStateKind, Platform, SbThreatType, ScreenshotEncryptionScheme, ScrollToErrorReason, SkillSource, WebClientMode} from '/glic/glic_api/glic_api.js';
 import type {AdditionalContext, CounterAbuseVerdict, ExperimentalTriggeringUpdate, FocusedTabData, GetPinCandidatesOptions, GlicBrowserHost, GlicWebClient, InvokeOptions, Observable, Observable2, OpenPanelInfo, PageMetadata, PanelOpeningData, PanelState, ScrollToError, TabData, UserConfirmationDialogRequest, UserProfileInfo, ZeroStateSuggestionsV2} from '/glic/glic_api/glic_api.js';
 import {Subject} from '/glic/observable.js';
 
 import {ApiTestError, ApiTestFixtureBase, assertDefined, assertEquals, assertFalse, assertRejects, assertTrue, assertUndefined, checkDefined, mapObservable, observeSequence, runUntil, sleep, testMain, waitFor, WebClient} from './browser_test_base.js';
 
-
 class ApiTests extends ApiTestFixtureBase {
   override async setUpTest() {
     await this.client.waitForFirstOpen();
@@ -95,6 +94,52 @@
     link.click();
   }
 
+  async testOpenGlicSettingsPage() {
+    assertDefined(this.host.openGlicSettingsPage);
+    this.host.openGlicSettingsPage();
+  }
+
+  async testOpenPasswordManagerSettingsPage() {
+    assertDefined(this.host.openPasswordManagerSettingsPage);
+    this.host.openPasswordManagerSettingsPage();
+  }
+
+  async testCanAttachPanelToFallbackEmbedder() {
+    assertDefined(this.host.getFocusedTabStateV2);
+    assertDefined(this.host.getPinnedTabs);
+    assertDefined(this.host.getPanelState);
+    assertDefined(this.host.detachPanel);
+    assertDefined(this.host.canAttachPanel);
+    const link = document.createElement('a');
+    link.setAttribute('href', 'https://www.chromium.org');
+    link.setAttribute('target', '_blank');
+    document.body.appendChild(link);
+    link.click();
+    // The opened tab should be pinned.
+    await observeSequence(this.host.getPinnedTabs())
+        .waitFor(tabs => tabs.length === 2);
+
+    // Detach panel
+    this.host.detachPanel!();
+
+    // Verify state.
+    await observeSequence(this.host.getPanelState!())
+        .waitFor(
+            state =>
+                state !== undefined && state.kind === PanelStateKind.DETACHED);
+
+    // The user will close the tab in C++.
+    await this.advanceToNextStep();
+
+    // The panel should still be detached.
+    const panelStateAfterClose = this.host.getPanelState!().getCurrentValue();
+    assertDefined(panelStateAfterClose);
+    assertEquals(panelStateAfterClose.kind, PanelStateKind.DETACHED);
+
+    // Verify it is possible to attach
+    await observeSequence(this.host.canAttachPanel!()).waitForValue(true);
+  }
+
   async testUnresponsive() {
     // Don't respond to responsiveness checks.
     this.client.checkResponsive = () => {
@@ -1514,14 +1559,16 @@
 
 class InvokeClient extends WebClient {
   calls: string[] = [];
+  lastInvokeOptions: InvokeOptions|null = null;
   override async notifyPanelWillOpen(
       panelOpeningData: PanelOpeningData&PanelState): Promise<OpenPanelInfo> {
     this.calls.push('notifyPanelWillOpen');
     return super.notifyPanelWillOpen!(panelOpeningData);
   }
 
-  override async invoke(_options: InvokeOptions): Promise<void> {
+  override async invoke(options: InvokeOptions): Promise<void> {
     this.calls.push('invoke');
+    this.lastInvokeOptions = options;
   }
 }
 
@@ -1530,14 +1577,29 @@
     return new InvokeClient();
   }
 
+  getInvokeClient(): InvokeClient {
+    return this.client as InvokeClient;
+  }
+
   async testInvokeWaitsForNotifyPanelWillOpen() {
-    const client: InvokeClient = this.client as InvokeClient;
+    const client = this.getInvokeClient();
     await runUntil(() => {
       return client.calls.length === 2;
     });
 
     assertEquals('notifyPanelWillOpen,invoke', client.calls.join(','));
   }
+
+  async testInvoke() {
+    const client = this.getInvokeClient();
+    await runUntil(() => {
+      return client.lastInvokeOptions !== null;
+    });
+    assertDefined(client.lastInvokeOptions);
+    assertEquals(
+        client.lastInvokeOptions!.invocationSource,
+        InvocationSource.TOP_CHROME_BUTTON);
+  }
 }
 
 class TriggeringUpdatesClient extends WebClient {
diff --git a/chrome/test/data/webui/glic/test_data/page.html b/chrome/test/data/webui/glic/test_data/page.html
index 1c9a1602..4f1898d 100644
--- a/chrome/test/data/webui/glic/test_data/page.html
+++ b/chrome/test/data/webui/glic/test_data/page.html
@@ -1,28 +1,9 @@
 <!DOCTYPE html>
 <head>
   <link rel="icon" href="./blue32x32.ico">
-  <script>
-    setTimeout(() => {
-      const link1 = document.createElement('a');
-      link1.href = "https://policies.google.com/terms/generative-ai/use-policy";
-      link1.textContent = "safely";
-      document.body.appendChild(link1);
-
-      const link2 = document.createElement('a');
-      link2.href = "https://support.google.com/gen-ai-unexpected_results";
-      link2.textContent = "unexpected results";
-      document.body.appendChild(link2);
-
-      const link3 = document.createElement('a');
-      link3.href = "https://support.google.com/chrome?p=gemini_spark_safety";
-      link3.textContent = "Review the risks";
-      document.body.appendChild(link3);
-    }, 100);
-  </script>
 </head>
 <title>Why did the web developer leave the restaurant?</title>
 <body>
 <h1>Because of the table layout.</h1>
 <p>Ba dum tss 🥁</p>
 </body>
-</html>
diff --git a/chrome/test/data/webui/new_tab_page/action_chips/action_chips_test.ts b/chrome/test/data/webui/new_tab_page/action_chips/action_chips_test.ts
index 95189d9..10bcf31 100644
--- a/chrome/test/data/webui/new_tab_page/action_chips/action_chips_test.ts
+++ b/chrome/test/data/webui/new_tab_page/action_chips/action_chips_test.ts
@@ -123,6 +123,7 @@
               preferredInventory: null,
               preselectedModel: ModelMode.kUnspecified,
               queryActionOverride: null,
+              preselectedInputSource: null,
               ...chip.suggestTemplateInfo.fuseboxAction,
             } :
                                                                     null,
diff --git a/chrome/test/data/webui/new_tab_page/app_test.ts b/chrome/test/data/webui/new_tab_page/app_test.ts
index e40b9df7..1231e5e 100644
--- a/chrome/test/data/webui/new_tab_page/app_test.ts
+++ b/chrome/test/data/webui/new_tab_page/app_test.ts
@@ -4,10 +4,9 @@
 
 import {ActionChipsApiProxyImpl, VoiceSearchAction} from 'chrome://new-tab-page/lazy_load.js';
 import type {Module} from 'chrome://new-tab-page/lazy_load.js';
-import {ActionChipsRetrievalState, ComposeboxProxyImpl, counterfactualLoad, ModuleDescriptor, ModuleRegistry} from 'chrome://new-tab-page/lazy_load.js';
-import type {NtpComposeboxElement} from 'chrome://new-tab-page/lazy_load.js';
-import {ActionChipsHandlerRemote, ActionChipsPageCallbackRouter, IconType} from 'chrome://new-tab-page/new_tab_page.js';
-import type {ActionChipsPageRemote, CustomizeButtonsDocumentRemote, TabInfo} from 'chrome://new-tab-page/new_tab_page.js';
+import {ActionChipsRetrievalState, ComposeboxProxyImpl, counterfactualLoad, ModuleDescriptor, ModuleRegistry, NtpComposeboxElement} from 'chrome://new-tab-page/lazy_load.js';
+import {ActionChipsHandlerRemote, ActionChipsPageCallbackRouter, IconType, InputSource} from 'chrome://new-tab-page/new_tab_page.js';
+import type {ActionChipsPageRemote, CustomizeButtonsDocumentRemote, FuseboxAction, TabInfo} from 'chrome://new-tab-page/new_tab_page.js';
 import {$$, BackgroundManager, BrowserCommandProxy, CONTEXTUAL_ENTRYPOINT_ELEMENT_ID, CUSTOMIZE_CHROME_BUTTON_ELEMENT_ID, CustomizeButtonsDocumentCallbackRouter, CustomizeButtonsHandlerRemote, CustomizeButtonsProxy, CustomizeChromeSection, CustomizeDialogPage, GlifAnimationState, NewTabPageProxy, NtpCustomizeChromeEntryPoint, NtpElement, SearchboxBrowserProxy, SidePanelOpenTrigger, VoiceAction, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import type {AppElement, CustomizeButtonsElement, NtpSearchboxElement, PageRemote} from 'chrome://new-tab-page/new_tab_page.js';
 import {NtpBackgroundImageSource, PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.js';
@@ -1349,6 +1348,46 @@
       assertEquals('latest', state.text);
     });
 
+    test(
+        'action chip click opens composebox and passes fuseboxAction',
+        async () => {
+          let handleFuseboxActionArg: FuseboxAction|null = null;
+          const originalHandleFuseboxAction =
+              NtpComposeboxElement.prototype.handleFuseboxAction;
+          NtpComposeboxElement.prototype.handleFuseboxAction = function(
+              action: FuseboxAction) {
+            handleFuseboxActionArg = action;
+            originalHandleFuseboxAction.call(this, action);
+          };
+
+          const action: FuseboxAction = {
+            preselectedTool: null,
+            preferredInventory: null,
+            preselectedModel: null,
+            queryActionOverride: null,
+            preselectedInputSource: InputSource.kInputSourceGallery,
+          };
+
+          // Act.
+          app['onActionChipClick_'](new CustomEvent('action-chip-click', {
+            detail: {
+              suggestion: '',
+              files: [],
+              fuseboxAction: action,
+            },
+          }));
+          await microtasksFinished();
+
+          // Assert.
+          const composebox =
+              app.shadowRoot.querySelector<NtpComposeboxElement>('#composebox');
+          assertTrue(!!composebox);
+          assertDeepEquals(action, handleFuseboxActionArg);
+
+          NtpComposeboxElement.prototype.handleFuseboxAction =
+              originalHandleFuseboxAction;
+        });
+
     test('composebox state toggles inert attribute on siblings', async () => {
       // Arrange: Verify blocked elements do NOT have inert initially.
       callbackRouterRemote.setTheme(createTheme());
@@ -2838,6 +2877,7 @@
                 preferredInventory: null,
                 preselectedModel: ModelMode.kUnspecified,
                 queryActionOverride: null,
+                preselectedInputSource: null,
               },
             },
             tab: fakeTab,
@@ -2853,6 +2893,7 @@
                 preferredInventory: null,
                 preselectedModel: ModelMode.kUnspecified,
                 queryActionOverride: null,
+                preselectedInputSource: null,
               },
             },
             tab: null,
@@ -2868,6 +2909,7 @@
                 preferredInventory: null,
                 preselectedModel: ModelMode.kUnspecified,
                 queryActionOverride: null,
+                preselectedInputSource: null,
               },
             },
             tab: null,
@@ -3035,6 +3077,7 @@
                 preferredInventory: null,
                 preselectedModel: ModelMode.kUnspecified,
                 queryActionOverride: null,
+                preselectedInputSource: null,
               },
             },
             tab: {
@@ -3089,6 +3132,7 @@
                 preferredInventory: null,
                 preselectedModel: ModelMode.kGeminiPro,
                 queryActionOverride: null,
+                preselectedInputSource: null,
               },
             },
             tab: null,
diff --git a/chrome/test/data/webui/new_tab_page/composebox/composebox_test.ts b/chrome/test/data/webui/new_tab_page/composebox/composebox_test.ts
index 22102df..67208de2 100644
--- a/chrome/test/data/webui/new_tab_page/composebox/composebox_test.ts
+++ b/chrome/test/data/webui/new_tab_page/composebox/composebox_test.ts
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ComposeboxElement, SubmitButtonIconType} from 'chrome://new-tab-page/lazy_load.js';
-import {$$} from 'chrome://new-tab-page/new_tab_page.js';
+import {ComposeboxElement, NtpComposeboxElement, SubmitButtonIconType} from 'chrome://new-tab-page/lazy_load.js';
+import {$$, InputSource} from 'chrome://new-tab-page/new_tab_page.js';
 import {InputType, ToolMode} from 'chrome://resources/cr_components/composebox/composebox_query.mojom-webui.js';
 import type {ContextualEntrypointAndMenuElement} from 'chrome://resources/cr_components/composebox/contextual_entrypoint_and_menu.js';
 import {WindowProxy as CrWindowProxy} from 'chrome://resources/cr_components/composebox/window_proxy.js';
@@ -872,6 +872,60 @@
               window.getComputedStyle(voiceSearchContainer).position);
         });
   });
+
+  test(
+      'handleFuseboxAction triggers imageInput click for kInputSourceGallery',
+      async () => {
+        const composebox = new NtpComposeboxElement();
+        document.body.appendChild(composebox);
+        await microtasksFinished();
+
+        let imageInputClicked = false;
+        const imageInput =
+            composebox.$.fileInputs.shadowRoot.querySelector<HTMLInputElement>(
+                '#imageInput')!;
+        imageInput.addEventListener('click', (e: Event) => {
+          e.preventDefault();
+          imageInputClicked = true;
+        });
+
+        composebox.handleFuseboxAction({
+          preselectedTool: null,
+          preferredInventory: null,
+          preselectedModel: null,
+          queryActionOverride: null,
+          preselectedInputSource: InputSource.kInputSourceGallery,
+        });
+
+        assertTrue(imageInputClicked);
+      });
+
+  test(
+      'handleFuseboxAction triggers fileInput click for kInputSourceFilePicker',
+      async () => {
+        const composebox = new NtpComposeboxElement();
+        document.body.appendChild(composebox);
+        await microtasksFinished();
+
+        let fileInputClicked = false;
+        const fileInput =
+            composebox.$.fileInputs.shadowRoot.querySelector<HTMLInputElement>(
+                '#fileInput')!;
+        fileInput.addEventListener('click', (e: Event) => {
+          e.preventDefault();
+          fileInputClicked = true;
+        });
+
+        composebox.handleFuseboxAction({
+          preselectedTool: null,
+          preferredInventory: null,
+          preselectedModel: null,
+          queryActionOverride: null,
+          preselectedInputSource: InputSource.kInputSourceFilePicker,
+        });
+
+        assertTrue(fileInputClicked);
+      });
 });
 
 // ==========================================================
diff --git a/chrome/test/data/webui/organizer_panel/app_test.ts b/chrome/test/data/webui/organizer_panel/app_test.ts
index 523e53d9..10854f8 100644
--- a/chrome/test/data/webui/organizer_panel/app_test.ts
+++ b/chrome/test/data/webui/organizer_panel/app_test.ts
@@ -6,7 +6,7 @@
 
 import type {OrganizerPanelAppElement} from 'chrome://organizer-panel.top-chrome/app.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 suite('OrganizerPanelAppTest', () => {
@@ -14,13 +14,24 @@
 
   setup(async () => {
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
-    loadTimeData.resetForTesting({});
+    loadTimeData.resetForTesting({
+      clearSearch: 'Clear search',
+      searchTabs: 'Search Tabs',
+      shortcutText: 'Ctrl+Shift+A',
+    });
     app = document.createElement('organizer-panel-app');
     document.body.appendChild(app);
     await microtasksFinished();
   });
 
-  test('renders app', () => {
-    assertTrue(!!app);
+  test('renders search field with correct label and shortcut', () => {
+    const searchField = app.shadowRoot.querySelector('cr-toolbar-search-field');
+    assertTrue(!!searchField);
+    assertEquals('Search Tabs', searchField.getAttribute('label'));
+    assertEquals('Clear search', searchField.getAttribute('clear-label'));
+
+    const shortcut = searchField.querySelector('#shortcut');
+    assertTrue(!!shortcut);
+    assertEquals('Ctrl+Shift+A', shortcut.textContent);
   });
 });
diff --git a/chrome/test/data/webui/settings/a11y_page_test.ts b/chrome/test/data/webui/settings/a11y_page_test.ts
index 69495f9..d402358 100644
--- a/chrome/test/data/webui/settings/a11y_page_test.ts
+++ b/chrome/test/data/webui/settings/a11y_page_test.ts
@@ -19,7 +19,6 @@
 import {CrSettingsPrefs, loadTimeData, PrefsBrowserProxy, PrefService} from 'chrome://settings/settings.js';
 import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
 import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
-import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 import {getFakeLanguagePrefs} from './fake_language_settings_private.js';
@@ -145,8 +144,6 @@
 
     // Set up languages helper.
     const settingsLanguages = document.createElement('settings-languages');
-    settingsLanguages.prefs = settingsPrefs.prefs!;
-    fakeDataBind(settingsPrefs, settingsLanguages, 'prefs');
     document.body.appendChild(settingsLanguages);
     await settingsLanguages.whenReady();
 
diff --git a/chrome/test/data/webui/settings/glic_subpage_test.ts b/chrome/test/data/webui/settings/glic_subpage_test.ts
index 29b63a53..8d21a57 100644
--- a/chrome/test/data/webui/settings/glic_subpage_test.ts
+++ b/chrome/test/data/webui/settings/glic_subpage_test.ts
@@ -108,6 +108,7 @@
       glicDisallowedByAdmin: false,
       glicSelectionFeatureEnabled: true,
       headlessCaptionsEnabled: false,
+      showGlicShakeTrigger: false,
     });
     resetRouterForTesting();
     return CrSettingsPrefs.initialized;
@@ -678,6 +679,72 @@
     });
   });
 
+  suite('ShakeTriggerToggleEnabled', () => {
+    setup(async () => {
+      document.body.innerHTML = window.trustedTypes!.emptyHTML;
+      loadTimeData.overrideValues({
+        showGlicShakeTrigger: true,
+      });
+      await createGlicPage('⌃A', true);
+    });
+
+    test('ShakeTriggerFeatureEnabled', () => {
+      const shakeTriggerToggle =
+          $<SettingsToggleButtonElement>('shakeTriggerToggle')!;
+      assertTrue(isVisible(shakeTriggerToggle));
+    });
+
+    test('ShakeTriggerToggleEnabled', () => {
+      page.setPrefValue(PrefName.SHAKE_TRIGGER_ENABLED, true);
+
+      assertTrue(
+          $<SettingsToggleButtonElement>(
+              'shakeTriggerToggle')!.checked);
+    });
+
+    test('ShakeTriggerToggleDisabled', () => {
+      page.setPrefValue(
+          PrefName.SHAKE_TRIGGER_ENABLED, false);
+
+      assertFalse(
+          $<SettingsToggleButtonElement>(
+              'shakeTriggerToggle')!.checked);
+    });
+
+    test('ShakeTriggerToggleChanged', async () => {
+      page.setPrefValue(
+          PrefName.SHAKE_TRIGGER_ENABLED, false);
+
+      const shakeTriggerToggle =
+          $<SettingsToggleButtonElement>('shakeTriggerToggle')!;
+      assertTrue(!!shakeTriggerToggle);
+
+      shakeTriggerToggle.click();
+      assertTrue(page.getPref<boolean>(
+                         PrefName.SHAKE_TRIGGER_ENABLED)
+                     .value);
+      assertTrue(shakeTriggerToggle.checked);
+      await verifyUserAction(
+          'Glic.Settings.ShakeTrigger.Enabled');
+
+      shakeTriggerToggle.click();
+      assertFalse(page.getPref<boolean>(
+                          PrefName.SHAKE_TRIGGER_ENABLED)
+                      .value);
+      assertFalse(shakeTriggerToggle.checked);
+      await verifyUserAction(
+          'Glic.Settings.ShakeTrigger.Disabled');
+    });
+  });
+
+  suite('ShakeTriggerToggleHidden', () => {
+    test('assert toggle is hidden', () => {
+      const shakeTriggerToggle =
+          $<SettingsToggleButtonElement>('shakeTriggerToggle')!;
+      assertFalse(isVisible(shakeTriggerToggle));
+    });
+  });
+
   suite('DefaultTabContextSettingFeatureEnabled', () => {
     test('DefaultTabContextSettingFeatureEnabled', () => {
       const defaultTabAccessToggle =
diff --git a/chrome/test/data/webui/settings/languages_page_metrics_test_browser.ts b/chrome/test/data/webui/settings/languages_page_metrics_test_browser.ts
index 0e44b41..3db73359 100644
--- a/chrome/test/data/webui/settings/languages_page_metrics_test_browser.ts
+++ b/chrome/test/data/webui/settings/languages_page_metrics_test_browser.ts
@@ -5,19 +5,20 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {LanguageHelper, SettingsLanguagesPageElement} from 'chrome://settings/lazy_load.js';
 import {LanguagesBrowserProxyImpl, LanguageSettingsMetricsProxyImpl, LanguageSettingsPageImpressionType} from 'chrome://settings/lazy_load.js';
-import {CrSettingsPrefs, loadTimeData} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData, PrefsBrowserProxy, PrefService} from 'chrome://settings/settings.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {FakeSettingsPrivate} from 'chrome://webui-test/fake_settings_private.js';
 import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
-
 // <if expr="is_win">
 import {LanguageSettingsActionType} from 'chrome://settings/lazy_load.js';
+import {microtasksFinished} from 'chrome://webui-test/test_util.js';
 // </if>
 
 import type {FakeLanguageSettingsPrivate} from './fake_language_settings_private.js';
 import {getFakeLanguagePrefs} from './fake_language_settings_private.js';
 import {TestLanguagesBrowserProxy} from './test_languages_browser_proxy.js';
 import {TestLanguageSettingsMetricsProxy} from './test_languages_settings_metrics_proxy.js';
+import {TestPrefsBrowserProxy} from './test_prefs_browser_proxy.js';
 
 suite('LanguagesPageMetricsBrowser', function() {
   let languageHelper: LanguageHelper;
@@ -37,6 +38,12 @@
     document.body.appendChild(settingsPrefs);
 
     await CrSettingsPrefs.initialized;
+
+    const prefsBrowserProxy = new TestPrefsBrowserProxy(getFakeLanguagePrefs());
+    PrefsBrowserProxy.setInstance(prefsBrowserProxy);
+    PrefService.resetInstanceForTesting();
+    await PrefService.getInstance().whenInitialized();
+
     // Sets up test browser proxy.
     browserProxy = new TestLanguagesBrowserProxy();
     LanguagesBrowserProxyImpl.setInstance(browserProxy);
@@ -51,17 +58,11 @@
         .setSettingsPrefs(settingsPrefs);
 
     const settingsLanguages = document.createElement('settings-languages');
-    settingsLanguages.prefs = settingsPrefs.prefs!;
-    fakeDataBind(settingsPrefs, settingsLanguages, 'prefs');
     document.body.appendChild(settingsLanguages);
     languageHelper = settingsLanguages;
 
     languagesPage = document.createElement('settings-languages-page');
 
-    // Prefs would normally be data-bound to settings-languages-page.
-    languagesPage.prefs = settingsLanguages.prefs;
-    fakeDataBind(settingsLanguages, languagesPage, 'prefs');
-
     languagesPage.languages = settingsLanguages.languages;
     fakeDataBind(settingsLanguages, languagesPage, 'languages');
 
@@ -99,6 +100,8 @@
     // Adding language with supportsUI = true in
     // fake_language_settings_private.ts
     languageHelper.enableLanguage('sw');
+    await microtasksFinished();
+    flush();
     // Testing the 'Change Chrome Language' button with 'sw'
     const languagesSection =
         languagesPage.shadowRoot!.querySelector('#languagesSection');
diff --git a/chrome/test/data/webui/settings/languages_page_test.ts b/chrome/test/data/webui/settings/languages_page_test.ts
index c4d1a72..e1170d2 100644
--- a/chrome/test/data/webui/settings/languages_page_test.ts
+++ b/chrome/test/data/webui/settings/languages_page_test.ts
@@ -9,15 +9,15 @@
 import type {CrCheckboxElement, LanguageHelper, SettingsAddLanguagesDialogElement, SettingsLanguagesPageElement} from 'chrome://settings/lazy_load.js';
 import {LanguagesBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import type {CrActionMenuElement, CrButtonElement} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs, loadTimeData, convertLanguageCodeForTranslate} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, loadTimeData, convertLanguageCodeForTranslate, PrefsBrowserProxy, PrefService} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertGE, assertGT, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {FakeSettingsPrivate} from 'chrome://webui-test/fake_settings_private.js';
 import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
 import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
 
 import type {FakeLanguageSettingsPrivate} from './fake_language_settings_private.js';
 import {getFakeLanguagePrefs} from './fake_language_settings_private.js';
 import {TestLanguagesBrowserProxy} from './test_languages_browser_proxy.js';
+import {TestPrefsBrowserProxy} from './test_prefs_browser_proxy.js';
 
 // clang-format on
 
@@ -50,11 +50,14 @@
 
   setup(async function() {
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
-    const settingsPrefs = document.createElement('settings-prefs');
-    const settingsPrivate = new FakeSettingsPrivate(getFakeLanguagePrefs());
-    settingsPrefs.initialize(settingsPrivate);
-    document.body.appendChild(settingsPrefs);
+    const prefsBrowserProxy = new TestPrefsBrowserProxy(getFakeLanguagePrefs());
+    PrefsBrowserProxy.setInstance(prefsBrowserProxy);
+    PrefService.resetInstanceForTesting();
+    await PrefService.getInstance().whenInitialized();
 
+    const settingsPrefs = document.createElement('settings-prefs');
+    settingsPrefs.initialize(prefsBrowserProxy.fakeApi);
+    document.body.appendChild(settingsPrefs);
     await CrSettingsPrefs.initialized;
     // Set up test browser proxy.
     browserProxy = new TestLanguagesBrowserProxy();
@@ -66,8 +69,6 @@
     languageSettingsPrivate.setSettingsPrefs(settingsPrefs);
 
     const settingsLanguages = document.createElement('settings-languages');
-    settingsLanguages.prefs = settingsPrefs.prefs!;
-    fakeDataBind(settingsPrefs, settingsLanguages, 'prefs');
     document.body.appendChild(settingsLanguages);
     languageHelper = settingsLanguages;
 
diff --git a/chrome/test/data/webui/settings/languages_test.ts b/chrome/test/data/webui/settings/languages_test.ts
index 3a3d467..7f9020cb 100644
--- a/chrome/test/data/webui/settings/languages_test.ts
+++ b/chrome/test/data/webui/settings/languages_test.ts
@@ -5,14 +5,13 @@
 // clang-format off
 import type {LanguageHelper} from 'chrome://settings/lazy_load.js';
 import {LanguagesBrowserProxyImpl, getLanguageHelperInstance} from 'chrome://settings/lazy_load.js';
-import {CrSettingsPrefs, isTranslateBaseLanguage, getBaseLanguage, convertLanguageCodeForTranslate, convertLanguageCodeForChrome} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, isTranslateBaseLanguage, getBaseLanguage, convertLanguageCodeForTranslate, convertLanguageCodeForChrome, PrefsBrowserProxy, PrefService} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {FakeSettingsPrivate} from 'chrome://webui-test/fake_settings_private.js';
-import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
 
 import type {FakeLanguageSettingsPrivate} from './fake_language_settings_private.js';
 import {getFakeLanguagePrefs} from './fake_language_settings_private.js';
 import {TestLanguagesBrowserProxy} from './test_languages_browser_proxy.js';
+import {TestPrefsBrowserProxy} from './test_prefs_browser_proxy.js';
 
 // clang-format on
 
@@ -35,9 +34,13 @@
 
   setup(async function() {
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
+    const prefsBrowserProxy = new TestPrefsBrowserProxy(getFakeLanguagePrefs());
+    PrefsBrowserProxy.setInstance(prefsBrowserProxy);
+    PrefService.resetInstanceForTesting();
+    await PrefService.getInstance().whenInitialized();
+
     const settingsPrefs = document.createElement('settings-prefs');
-    const settingsPrivate = new FakeSettingsPrivate(getFakeLanguagePrefs());
-    settingsPrefs.initialize(settingsPrivate);
+    settingsPrefs.initialize(prefsBrowserProxy.fakeApi);
     document.body.appendChild(settingsPrefs);
     await CrSettingsPrefs.initialized;
 
@@ -51,8 +54,6 @@
     languageSettingsPrivate.setSettingsPrefs(settingsPrefs);
 
     const settingsLanguages = document.createElement('settings-languages');
-    settingsLanguages.prefs = settingsPrefs.prefs!;
-    fakeDataBind(settingsPrefs, settingsLanguages, 'prefs');
     document.body.appendChild(settingsLanguages);
 
     languageHelper = getLanguageHelperInstance();
diff --git a/chrome/test/data/webui/settings/live_caption_test.ts b/chrome/test/data/webui/settings/live_caption_test.ts
index deb92e2d..2d1fda0 100644
--- a/chrome/test/data/webui/settings/live_caption_test.ts
+++ b/chrome/test/data/webui/settings/live_caption_test.ts
@@ -9,7 +9,6 @@
 import type {SettingsPrefsElement} from 'chrome://settings/settings.js';
 import {CrSettingsPrefs, loadTimeData, PrefsBrowserProxy, PrefService} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertStringContains, assertStringExcludes, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise, isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import {getFakeLanguagePrefs} from './fake_language_settings_private.js';
@@ -77,8 +76,6 @@
     await CrSettingsPrefs.initialized;
 
     const settingsLanguages = document.createElement('settings-languages');
-    settingsLanguages.prefs = settingsPrefs.prefs!;
-    fakeDataBind(settingsPrefs, settingsLanguages, 'prefs');
     document.body.appendChild(settingsLanguages);
 
     // Set up test browser proxy.
diff --git a/chrome/test/data/webui/settings/live_translate_test.ts b/chrome/test/data/webui/settings/live_translate_test.ts
index c06112a..5a349ad 100644
--- a/chrome/test/data/webui/settings/live_translate_test.ts
+++ b/chrome/test/data/webui/settings/live_translate_test.ts
@@ -9,7 +9,6 @@
 import {CaptionsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 import {CrSettingsPrefs, loadTimeData, PrefsBrowserProxy, PrefService} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
 
 import {getFakeLanguagePrefs} from './fake_language_settings_private.js';
 import {TestCaptionsBrowserProxy} from './test_captions_browser_proxy.js';
@@ -59,8 +58,6 @@
     await CrSettingsPrefs.initialized;
 
     const settingsLanguages = document.createElement('settings-languages');
-    settingsLanguages.prefs = settingsPrefs.prefs!;
-    fakeDataBind(settingsPrefs, settingsLanguages, 'prefs');
     document.body.appendChild(settingsLanguages);
 
     // Set up test browser proxy.
diff --git a/chrome/test/data/webui/settings/pref_service_test.ts b/chrome/test/data/webui/settings/pref_service_test.ts
index 043ffa1f..1f72d4a9 100644
--- a/chrome/test/data/webui/settings/pref_service_test.ts
+++ b/chrome/test/data/webui/settings/pref_service_test.ts
@@ -31,6 +31,11 @@
         key2: 2,
       },
     },
+    {
+      key: 'browser.list_pref',
+      type: chrome.settingsPrivate.PrefType.LIST,
+      value: ['item1', 'item2'],
+    },
   ];
 
   setup(function() {
@@ -224,6 +229,41 @@
     assertDeepEquals({key2: 2}, callArgs.value);
   });
 
+  test('AppendPrefListItem', async function() {
+    const key = 'browser.list_pref';
+
+    // Verify initial values.
+    let listPref = service.getPref<string[]>(key);
+    assertDeepEquals(['item1', 'item2'], listPref.value);
+
+    // Append a new item.
+    let promise = service.appendPrefListItem<string>(key, 'item3');
+
+    // Verify synchronous update in cache.
+    listPref = service.getPref<string[]>(key);
+    assertDeepEquals(['item1', 'item2', 'item3'], listPref.value);
+
+    let success = await promise;
+    assertTrue(success);
+
+    // Verify backend is called with correct params.
+    const callArgs = await proxy.fakeApi.whenCalled('setPref');
+    assertEquals(key, callArgs.key);
+    assertDeepEquals(['item1', 'item2', 'item3'], callArgs.value);
+
+    // Append an existing item (no-op).
+    proxy.fakeApi.resetResolver('setPref');
+    promise = service.appendPrefListItem<string>(key, 'item2');
+
+    // Verify cache remains unchanged.
+    listPref = service.getPref<string[]>(key);
+    assertDeepEquals(['item1', 'item2', 'item3'], listPref.value);
+
+    success = await promise;
+    assertTrue(success);
+    assertEquals(0, proxy.fakeApi.getCallCount('setPref'));
+  });
+
   test('addObserverSingleExternalChange', async function() {
     const key1 = 'browser.show_home_button';
     const key2 = 'browser.homepage';
diff --git a/chrome/test/data/webui/settings/settings_browsertest.cc b/chrome/test/data/webui/settings/settings_browsertest.cc
index aaa80c3..eb5deed 100644
--- a/chrome/test/data/webui/settings/settings_browsertest.cc
+++ b/chrome/test/data/webui/settings/settings_browsertest.cc
@@ -473,6 +473,27 @@
           "runMochaSuite('GlicSubpage WebActuationSettingFeatureEnabled')");
 }
 
+class SettingsGlicShakeTriggerEnabledTest : public SettingsBrowserTest {
+ public:
+  SettingsGlicShakeTriggerEnabledTest() {
+    scoped_feature_list_.InitAndEnableFeature(features::kGlicShakeTrigger);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(SettingsGlicShakeTriggerEnabledTest,
+                       GlicSubpageShakeTriggerEnabled) {
+  RunTest("settings/glic_subpage_test.js",
+          "runMochaSuite('GlicSubpage ShakeTriggerToggleEnabled')");
+}
+
+IN_PROC_BROWSER_TEST_F(SettingsTest, GlicSubpageShakeTriggerHidden) {
+  RunTest("settings/glic_subpage_test.js",
+          "runMochaSuite('GlicSubpage ShakeTriggerToggleHidden')");
+}
+
 class SettingsGlicHotkeyLocalScopeEnabledTest : public SettingsBrowserTest {
  public:
   SettingsGlicHotkeyLocalScopeEnabledTest() {
diff --git a/chrome/test/data/webui/settings/spell_check_page_metrics_test_browser.ts b/chrome/test/data/webui/settings/spell_check_page_metrics_test_browser.ts
index 51234f7..0f29d7f9 100644
--- a/chrome/test/data/webui/settings/spell_check_page_metrics_test_browser.ts
+++ b/chrome/test/data/webui/settings/spell_check_page_metrics_test_browser.ts
@@ -5,15 +5,15 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {LanguageSettingsMetricsProxy, LanguageSettingsPageImpressionType, SettingsSpellCheckPageElement} from 'chrome://settings/lazy_load.js';
 import {LanguagesBrowserProxyImpl, LanguageSettingsActionType, LanguageSettingsMetricsProxyImpl} from 'chrome://settings/lazy_load.js';
-import {CrSettingsPrefs} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, PrefsBrowserProxy, PrefService} from 'chrome://settings/settings.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {FakeSettingsPrivate} from 'chrome://webui-test/fake_settings_private.js';
 import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 import type {FakeLanguageSettingsPrivate} from './fake_language_settings_private.js';
 import {getFakeLanguagePrefs} from './fake_language_settings_private.js';
 import {TestLanguagesBrowserProxy} from './test_languages_browser_proxy.js';
+import {TestPrefsBrowserProxy} from './test_prefs_browser_proxy.js';
 
 /**
  * A test version of LanguageSettingsMetricsProxy.
@@ -44,9 +44,13 @@
 
   setup(async function() {
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
+    const prefsBrowserProxy = new TestPrefsBrowserProxy(getFakeLanguagePrefs());
+    PrefsBrowserProxy.setInstance(prefsBrowserProxy);
+    PrefService.resetInstanceForTesting();
+    await PrefService.getInstance().whenInitialized();
+
     const settingsPrefs = document.createElement('settings-prefs');
-    const settingsPrivate = new FakeSettingsPrivate(getFakeLanguagePrefs());
-    settingsPrefs.initialize(settingsPrivate);
+    settingsPrefs.initialize(prefsBrowserProxy.fakeApi);
     document.body.appendChild(settingsPrefs);
 
     await CrSettingsPrefs.initialized;
@@ -64,15 +68,13 @@
         .setSettingsPrefs(settingsPrefs);
 
     const settingsLanguages = document.createElement('settings-languages');
-    settingsLanguages.prefs = settingsPrefs.prefs!;
-    fakeDataBind(settingsPrefs, settingsLanguages, 'prefs');
     document.body.appendChild(settingsLanguages);
 
     spellCheckPage = document.createElement('settings-spell-check-page');
 
     // Prefs would normally be data-bound to settings-languages-page.
-    spellCheckPage.prefs = settingsLanguages.prefs;
-    fakeDataBind(settingsLanguages, spellCheckPage, 'prefs');
+    spellCheckPage.prefs = settingsPrefs.prefs!;
+    fakeDataBind(settingsPrefs, spellCheckPage, 'prefs');
 
     spellCheckPage.languages = settingsLanguages.languages;
     fakeDataBind(settingsLanguages, spellCheckPage, 'languages');
diff --git a/chrome/test/data/webui/settings/spell_check_page_test.ts b/chrome/test/data/webui/settings/spell_check_page_test.ts
index 5a618cb..5357aad1 100644
--- a/chrome/test/data/webui/settings/spell_check_page_test.ts
+++ b/chrome/test/data/webui/settings/spell_check_page_test.ts
@@ -6,7 +6,7 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {LanguageHelper, SettingsSpellCheckPageElement} from 'chrome://settings/lazy_load.js';
 import {LanguagesBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
-import {CrSettingsPrefs} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, PrefsBrowserProxy, PrefService} from 'chrome://settings/settings.js';
 // <if expr="not is_macosx">
 import type {SettingsToggleButtonElement} from 'chrome://settings/settings.js';
 import {loadTimeData} from 'chrome://settings/settings.js';
@@ -23,12 +23,12 @@
 import type {FakeChromeEvent} from 'chrome://webui-test/fake_chrome_event.js';
 // </if>
 
-import {FakeSettingsPrivate} from 'chrome://webui-test/fake_settings_private.js';
 import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
 
 import type {FakeLanguageSettingsPrivate} from './fake_language_settings_private.js';
 import {getFakeLanguagePrefs} from './fake_language_settings_private.js';
 import {TestLanguagesBrowserProxy} from './test_languages_browser_proxy.js';
+import {TestPrefsBrowserProxy} from './test_prefs_browser_proxy.js';
 
 // clang-format on
 
@@ -43,9 +43,13 @@
   });
 
   setup(async function() {
+    const prefsBrowserProxy = new TestPrefsBrowserProxy(getFakeLanguagePrefs());
+    PrefsBrowserProxy.setInstance(prefsBrowserProxy);
+    PrefService.resetInstanceForTesting();
+    await PrefService.getInstance().whenInitialized();
+
     const settingsPrefs = document.createElement('settings-prefs');
-    const settingsPrivate = new FakeSettingsPrivate(getFakeLanguagePrefs());
-    settingsPrefs.initialize(settingsPrivate);
+    settingsPrefs.initialize(prefsBrowserProxy.fakeApi);
     document.body.appendChild(settingsPrefs);
     await CrSettingsPrefs.initialized;
 
@@ -59,8 +63,6 @@
     languageSettingsPrivate.setSettingsPrefs(settingsPrefs);
 
     const settingsLanguages = document.createElement('settings-languages');
-    settingsLanguages.prefs = settingsPrefs.prefs!;
-    fakeDataBind(settingsPrefs, settingsLanguages, 'prefs');
     document.body.appendChild(settingsLanguages);
     languageHelper = settingsLanguages;
 
diff --git a/chrome/test/data/webui/settings/translate_page_metrics_test_browser.ts b/chrome/test/data/webui/settings/translate_page_metrics_test_browser.ts
index c8c44ac..b98e99c 100644
--- a/chrome/test/data/webui/settings/translate_page_metrics_test_browser.ts
+++ b/chrome/test/data/webui/settings/translate_page_metrics_test_browser.ts
@@ -5,9 +5,8 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {LanguageHelper, SettingsAddLanguagesDialogElement, SettingsTranslatePageElement} from 'chrome://settings/lazy_load.js';
 import {LanguagesBrowserProxyImpl, LanguageSettingsActionType, LanguageSettingsMetricsProxyImpl} from 'chrome://settings/lazy_load.js';
-import {CrSettingsPrefs} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, PrefsBrowserProxy, PrefService} from 'chrome://settings/settings.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {FakeSettingsPrivate} from 'chrome://webui-test/fake_settings_private.js';
 import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
@@ -15,6 +14,7 @@
 import {getFakeLanguagePrefs} from './fake_language_settings_private.js';
 import {TestLanguagesBrowserProxy} from './test_languages_browser_proxy.js';
 import {TestLanguageSettingsMetricsProxy} from './test_languages_settings_metrics_proxy.js';
+import {TestPrefsBrowserProxy} from './test_prefs_browser_proxy.js';
 
 suite('TranslatePageMetricsBrowser', function() {
   let languageHelper: LanguageHelper;
@@ -43,9 +43,13 @@
 
   setup(async function() {
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
+    const prefsBrowserProxy = new TestPrefsBrowserProxy(getFakeLanguagePrefs());
+    PrefsBrowserProxy.setInstance(prefsBrowserProxy);
+    PrefService.resetInstanceForTesting();
+    await PrefService.getInstance().whenInitialized();
+
     const settingsPrefs = document.createElement('settings-prefs');
-    const settingsPrivate = new FakeSettingsPrivate(getFakeLanguagePrefs());
-    settingsPrefs.initialize(settingsPrivate);
+    settingsPrefs.initialize(prefsBrowserProxy.fakeApi);
     document.body.appendChild(settingsPrefs);
 
     await CrSettingsPrefs.initialized;
@@ -63,16 +67,14 @@
         .setSettingsPrefs(settingsPrefs);
 
     const settingsLanguages = document.createElement('settings-languages');
-    settingsLanguages.prefs = settingsPrefs.prefs!;
-    fakeDataBind(settingsPrefs, settingsLanguages, 'prefs');
     document.body.appendChild(settingsLanguages);
     languageHelper = settingsLanguages;
 
     translatePage = document.createElement('settings-translate-page');
 
     // Prefs would normally be data-bound to settings-languages-page.
-    translatePage.prefs = settingsLanguages.prefs;
-    fakeDataBind(settingsLanguages, translatePage, 'prefs');
+    translatePage.prefs = settingsPrefs.prefs!;
+    fakeDataBind(settingsPrefs, translatePage, 'prefs');
 
     translatePage.languages = settingsLanguages.languages;
     fakeDataBind(settingsLanguages, translatePage, 'languages');
diff --git a/chrome/test/data/webui/settings/translate_page_test.ts b/chrome/test/data/webui/settings/translate_page_test.ts
index ba92f86..d169ebb 100644
--- a/chrome/test/data/webui/settings/translate_page_test.ts
+++ b/chrome/test/data/webui/settings/translate_page_test.ts
@@ -7,15 +7,15 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import type {CrIconButtonElement, LanguageHelper, SettingsAddLanguagesDialogElement, SettingsTranslatePageElement} from 'chrome://settings/lazy_load.js';
 import {LanguagesBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
-import {CrSettingsPrefs} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, PrefsBrowserProxy, PrefService} from 'chrome://settings/settings.js';
 import {assertDeepEquals, assertEquals, assertTrue, assertFalse} from 'chrome://webui-test/chai_assert.js';
-import {FakeSettingsPrivate} from 'chrome://webui-test/fake_settings_private.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
 
 import type {FakeLanguageSettingsPrivate} from './fake_language_settings_private.js';
 import {getFakeLanguagePrefs} from './fake_language_settings_private.js';
 import {TestLanguagesBrowserProxy} from './test_languages_browser_proxy.js';
+import {TestPrefsBrowserProxy} from './test_prefs_browser_proxy.js';
 // clang-format on
 
 suite('TranslatePage', function() {
@@ -34,9 +34,13 @@
   });
 
   setup(async function() {
+    const prefsBrowserProxy = new TestPrefsBrowserProxy(getFakeLanguagePrefs());
+    PrefsBrowserProxy.setInstance(prefsBrowserProxy);
+    PrefService.resetInstanceForTesting();
+    await PrefService.getInstance().whenInitialized();
+
     const settingsPrefs = document.createElement('settings-prefs');
-    const settingsPrivate = new FakeSettingsPrivate(getFakeLanguagePrefs());
-    settingsPrefs.initialize(settingsPrivate);
+    settingsPrefs.initialize(prefsBrowserProxy.fakeApi);
     document.body.appendChild(settingsPrefs);
 
     await CrSettingsPrefs.initialized;
@@ -50,8 +54,6 @@
     languageSettingsPrivate.setSettingsPrefs(settingsPrefs);
 
     const settingsLanguages = document.createElement('settings-languages');
-    settingsLanguages.prefs = settingsPrefs.prefs!;
-    fakeDataBind(settingsPrefs, settingsLanguages, 'prefs');
     document.body.appendChild(settingsLanguages);
     languageHelper = settingsLanguages;
 
diff --git a/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts b/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts
index 3082777..dc72098 100644
--- a/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/fake_reading_mode.ts
@@ -172,12 +172,6 @@
     return (nodeId > this.maxNodeId) ? [] : [nodeId + 1];
   }
 
-  // Returns content of "data-font-css" html attribute. This is needed for
-  // rendering content from annotated canvas in Google Docs.
-  getDataFontCss(_nodeId: number): string {
-    return '400 14.6667px "Courier New"';
-  }
-
   // Returns the HTML tag of the AXNode for the provided AXNodeID. For testing,
   // odd numbered nodes are divs and even numbered nodes are text.
   getHtmlTag(nodeId: number): string {
@@ -468,12 +462,6 @@
   // the processing of the AX Tree Anchors.
   setAnchorsForTesting(_snapshotLite: Object, _contentNodeIds: number[]) {}
 
-  // Set the theme. Used by tests only.
-  setThemeForTesting(
-      _fontName: string, _fontSize: number, _linksEnabled: boolean,
-      _foregroundColor: number, _backgroundColor: number, _lineSpacing: number,
-      _letterSpacing: number) {}
-
   // Sets the language. Used by tests only.
   setLanguageForTesting(_code: string) {}
 
@@ -631,4 +619,8 @@
       Array<{axNodeId: number, start: number, end: number}> {
     return [];
   }
+
+  requestShouldShowLineFocusNewBadge() {}
+  onShouldShowLineFocusNewBadgeResponse() {}
+  onLineFocusFeatureUsed() {}
 }
diff --git a/chrome/test/data/webui/side_panel/read_anything/line_focus_menu_test.ts b/chrome/test/data/webui/side_panel/read_anything/line_focus_menu_test.ts
index 215484bc..d6a80c1 100644
--- a/chrome/test/data/webui/side_panel/read_anything/line_focus_menu_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/line_focus_menu_test.ts
@@ -43,6 +43,21 @@
         lineFocusMenu.$.menu.menuGroups[0]!.header.shortcut);
   });
 
+  test('notifies of feature use if enabled on close', async () => {
+    let featureUsed = false;
+    chrome.readingMode.onLineFocusFeatureUsed = () => {
+      featureUsed = true;
+    };
+
+    lineFocusMenu.close();
+    assertFalse(featureUsed);
+
+    lineFocusMenu.lineFocusEnabled = true;
+    await microtasksFinished();
+    lineFocusMenu.close();
+    assertTrue(featureUsed);
+  });
+
   test('line focus style prop update changes selected items', async () => {
     const window = LineFocusStyle.MEDIUM_WINDOW;
     lineFocusMenu.lineFocusStyle = window;
diff --git a/chrome/test/data/webui/side_panel/read_anything/line_focus_move_mode_test.ts b/chrome/test/data/webui/side_panel/read_anything/line_focus_move_mode_test.ts
index 94c07272..b8e77063 100644
--- a/chrome/test/data/webui/side_panel/read_anything/line_focus_move_mode_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/line_focus_move_mode_test.ts
@@ -138,7 +138,7 @@
 
       mode.onActivated(container, defaultHeight);
 
-      const expectedFocalPoint = model.getMaxY() / 2;
+      const expectedFocalPoint = styleMode.getCenterY();
       assertEquals(expectedFocalPoint, model.getFocalPoint());
       assertTrue(notifiedContentPositionChange);
     });
@@ -320,7 +320,7 @@
       mode.onTextLocationsChange(container, defaultHeight);
 
       assertTrue(notifiedVisualPositionChange);
-      const center = model.getMaxY() / 2;
+      const center = styleMode.getCenterY();
       assertEquals(center, model.getFocalPoint());
     });
 
diff --git a/chrome/test/data/webui/side_panel/read_anything/line_focus_style_mode_test.ts b/chrome/test/data/webui/side_panel/read_anything/line_focus_style_mode_test.ts
index eb497ff..4e0bba2 100644
--- a/chrome/test/data/webui/side_panel/read_anything/line_focus_style_mode_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/line_focus_style_mode_test.ts
@@ -26,6 +26,13 @@
       assertEquals(style, mode.getStyle());
     });
 
+    test('getCenterY does not offset by toolbar', () => {
+      model.setMinY(20);
+      model.setMaxY(100);
+
+      assertEquals(50, mode.getCenterY());
+    });
+
     test('updateFocusBounds sets top with no height', () => {
       const y = 100;
       model.setFocalPoint(y);
@@ -124,6 +131,13 @@
       model.setAdaptMultiLineWindow(true);
     });
 
+    test('getCenterY offsets by toolbar', () => {
+      model.setMinY(20);
+      model.setMaxY(100);
+
+      assertEquals(60, largeMode.getCenterY());
+    });
+
     test('updateFocusBounds with empty bounds does nothing', () => {
       model.setTextBounds([]);
 
diff --git a/chrome/test/data/webui/side_panel/read_anything/settings_menu_test.ts b/chrome/test/data/webui/side_panel/read_anything/settings_menu_test.ts
index fb624f1..00c107a4 100644
--- a/chrome/test/data/webui/side_panel/read_anything/settings_menu_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/settings_menu_test.ts
@@ -578,4 +578,47 @@
     const event = await whenFired;
     assertEquals(SettingsOption.FONT, event.detail.previousId);
   });
+
+  test(
+      'line focus item shows new badge when showLineFocusNewBadge is true',
+      async () => {
+        settingsMenu.showLineFocusNewBadge = true;
+        await microtasksFinished();
+
+        const actionMenu = settingsMenu.$.lazyMenu.get();
+        const lineFocusItem = actionMenu.querySelector<HTMLButtonElement>(
+            `#${SettingsOption.LINE_FOCUS}`);
+        assertTrue(!!lineFocusItem);
+
+        const badge = lineFocusItem.querySelector('new-badge');
+        assertTrue(!!badge);
+      });
+
+  test(
+      'line focus item hides new badge when showLineFocusNewBadge is false',
+      async () => {
+        settingsMenu.showLineFocusNewBadge = false;
+        await microtasksFinished();
+
+        const actionMenu = settingsMenu.$.lazyMenu.get();
+        const lineFocusItem = actionMenu.querySelector<HTMLButtonElement>(
+            `#${SettingsOption.LINE_FOCUS}`);
+        assertTrue(!!lineFocusItem);
+
+        const badge = lineFocusItem.querySelector('new-badge');
+        assertFalse(!!badge);
+      });
+
+  test('requests line focus new badge on open', () => {
+    let requested = false;
+    chrome.readingMode.requestShouldShowLineFocusNewBadge = () => {
+      requested = true;
+    };
+    const anchor = document.createElement('div');
+    document.body.appendChild(anchor);
+
+    settingsMenu.open(anchor);
+
+    assertTrue(requested);
+  });
 });
diff --git a/chrome/test/data/webui/side_panel/read_anything/tts_voice_filtering_test.ts b/chrome/test/data/webui/side_panel/read_anything/tts_voice_filtering_test.ts
index 4c375f90..3f4e6a1 100644
--- a/chrome/test/data/webui/side_panel/read_anything/tts_voice_filtering_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/tts_voice_filtering_test.ts
@@ -54,11 +54,12 @@
     // Android voices should be filtered out only on ChromeOS Ash
     const possibleVoices: SpeechSynthesisVoice[] = [voice1, voice2];
 
-    if (chrome.readingMode.isChromeOsAsh) {
-      assertDeepEquals([voice1], getFilteredVoiceList(possibleVoices));
-    } else {
-      assertDeepEquals([voice2], getFilteredVoiceList(possibleVoices));
-    }
+    // <if expr="is_chromeos">
+    assertDeepEquals([voice1], getFilteredVoiceList(possibleVoices));
+    // </if>
+    // <if expr="not is_chromeos">
+    assertDeepEquals([voice2], getFilteredVoiceList(possibleVoices));
+    // </if>
   });
 
   test('getFilteredVoiceList filters eSpeak voices', () => {
@@ -75,18 +76,19 @@
 
     const possibleVoices: SpeechSynthesisVoice[] = [voice1, voice2, voice3];
 
-    if (chrome.readingMode.isChromeOsAsh) {
-      // Welsh isn't a Google TTS locale, so the Welsh eSpeak voice should be
-      // kept, but the English eSpeak voices should be filtered out because
-      // Google TTS voices in English (even if in a different locale) exist.
-      assertDeepEquals([voice2], getFilteredVoiceList(possibleVoices));
-    } else {
-      // en-us is kept because it is an exact Google TTS locale. cy is kept
-      // because there is no Google TTS locale that supports it. en-cb is not
-      // kept because there are other locales supported by Google TTS for the
-      // same language.
-      assertDeepEquals([voice1, voice2], getFilteredVoiceList(possibleVoices));
-    }
+    // <if expr="is_chromeos">
+    // Welsh isn't a Google TTS locale, so the Welsh eSpeak voice should be
+    // kept, but the English eSpeak voices should be filtered out because
+    // Google TTS voices in English (even if in a different locale) exist.
+    assertDeepEquals([voice2], getFilteredVoiceList(possibleVoices));
+    // </if>
+    // <if expr="not is_chromeos">
+    // en-us is kept because it is an exact Google TTS locale. cy is kept
+    // because there is no Google TTS locale that supports it. en-cb is not
+    // kept because there are other locales supported by Google TTS for the
+    // same language.
+    assertDeepEquals([voice1, voice2], getFilteredVoiceList(possibleVoices));
+    // </if>
   });
 
   test(
@@ -145,15 +147,15 @@
         const possibleVoices: SpeechSynthesisVoice[] =
             [voice1, voice2, voice3, voice4, voice5, voice6, voice7, voice8];
 
-        if (chrome.readingMode.isChromeOsAsh) {
-          // Don't filter out any system voices on ChromeOS.
-          assertDeepEquals(
-              possibleVoices, getFilteredVoiceList(possibleVoices));
-        } else {
-          // Keep only the default system voice per language.
-          assertDeepEquals(
-              [voice1, voice2, voice4, voice5, voice6, voice8],
-              getFilteredVoiceList(possibleVoices));
-        }
+        // <if expr="is_chromeos">
+        // Don't filter out any system voices on ChromeOS.
+        assertDeepEquals(possibleVoices, getFilteredVoiceList(possibleVoices));
+        // </if>
+        // <if expr="not is_chromeos">
+        // Keep only the default system voice per language.
+        assertDeepEquals(
+            [voice1, voice2, voice4, voice5, voice6, voice8],
+            getFilteredVoiceList(possibleVoices));
+        // </if>
       });
 });
diff --git a/chrome/test/data/webui/webui_toolbar/icon_from_table_test.ts b/chrome/test/data/webui/webui_toolbar/icon_from_table_test.ts
index dd6c41d2..e63aa56 100644
--- a/chrome/test/data/webui/webui_toolbar/icon_from_table_test.ts
+++ b/chrome/test/data/webui/webui_toolbar/icon_from_table_test.ts
@@ -132,7 +132,7 @@
       const style = renderer.computedStyleMap();
       assertEquals(
           'rgba(0, 0, 255, 1.0)',
-          style.get('--cr-icon-button-fill-color')?.toString());
+          style.get('--iron-icon-fill-color')?.toString());
     }
 
     {
diff --git a/chrome/test/data/webui/webui_toolbar/page_action_icon_test.ts b/chrome/test/data/webui/webui_toolbar/page_action_icon_test.ts
index 3eb2141f..225528f 100644
--- a/chrome/test/data/webui/webui_toolbar/page_action_icon_test.ts
+++ b/chrome/test/data/webui/webui_toolbar/page_action_icon_test.ts
@@ -126,6 +126,10 @@
       accessibleName: 'Translate',
       tooltipText: 'Translate this page',
       icon: {handleId: 0n},
+      text: '',
+      shouldShowChip: false,
+      shouldAnimateChipIn: false,
+      shouldAnimateChipOut: false,
     };
   }
 
@@ -142,10 +146,9 @@
   });
 
   test('Render', function() {
-    const button = icon.shadowRoot.querySelector('cr-icon-button');
-    assertTrue(!!button);
-    assertEquals('Translate this page', button.getAttribute('title'));
-    assertEquals('Translate', button.getAttribute('aria-label'));
+    const button = icon.$.button;
+    assertEquals('Translate this page', button.tooltip);
+    assertEquals('Translate', button.ariaLabel);
   });
 
   test('Render without accessible name', async function() {
@@ -154,15 +157,13 @@
       accessibleName: '',
     };
     await microtasksFinished();
-    const button = icon.shadowRoot.querySelector('cr-icon-button');
-    assertTrue(!!button);
+    const button = icon.$.button;
     // Should NOT fallback to tooltipText
-    assertEquals('', button.getAttribute('aria-label') || '');
+    assertEquals('', button.ariaLabel || '');
   });
 
   test('Mouse click triggers Action with kMouse', async function() {
-    const button = icon.shadowRoot.querySelector('cr-icon-button');
-    assertTrue(!!button);
+    const button = icon.$.button;
     button.dispatchEvent(new MouseEvent('click', {
       bubbles: true,
       composed: true,
@@ -176,8 +177,7 @@
   });
 
   test('Keyboard click triggers Action with kKeyboard', async function() {
-    const button = icon.shadowRoot.querySelector('cr-icon-button');
-    assertTrue(!!button);
+    const button = icon.$.button;
     // button.click() triggers click with detail: 0, simulating keyboard.
     button.click();
 
@@ -188,8 +188,7 @@
   });
 
   test('Touch tap triggers Action with kGesture', async function() {
-    const button = icon.shadowRoot.querySelector('cr-icon-button');
-    assertTrue(!!button);
+    const button = icon.$.button;
     button.dispatchEvent(new PointerEvent('click', {
       bubbles: true,
       composed: true,
@@ -203,8 +202,7 @@
   });
 
   test('Pen click triggers Action with kGesture', async function() {
-    const button = icon.shadowRoot.querySelector('cr-icon-button');
-    assertTrue(!!button);
+    const button = icon.$.button;
     button.dispatchEvent(new PointerEvent('click', {
       bubbles: true,
       composed: true,
@@ -218,8 +216,7 @@
   });
 
   test('Pointer events fired', function() {
-    const button = icon.shadowRoot.querySelector('cr-icon-button');
-    assertTrue(!!button);
+    const button = icon.$.button;
 
     let enterFired = false;
     icon.addEventListener('chip-pointerenter', () => {
diff --git a/chrome/test/data/webui/webui_toolbar/toolbar_chip_button_test.ts b/chrome/test/data/webui/webui_toolbar/toolbar_chip_button_test.ts
index 238144d..2fd283ad 100644
--- a/chrome/test/data/webui/webui_toolbar/toolbar_chip_button_test.ts
+++ b/chrome/test/data/webui/webui_toolbar/toolbar_chip_button_test.ts
@@ -28,11 +28,6 @@
     // Default is empty
     assertEquals('', button.getAttribute('aria-label'));
 
-    // Set attribute
-    element.setAttribute('aria-label', 'test-aria-label');
-    await microtasksFinished();
-    assertEquals('test-aria-label', button.getAttribute('aria-label'));
-
     // Set property
     element.ariaLabel = 'new-aria-label';
     await microtasksFinished();
@@ -45,11 +40,6 @@
     // Default is null (not present)
     assertFalse(button.hasAttribute('aria-haspopup'));
 
-    // Set attribute
-    element.setAttribute('aria-haspopup', 'menu');
-    await microtasksFinished();
-    assertEquals('menu', button.getAttribute('aria-haspopup'));
-
     // Set property
     element.ariaHasPopup = 'dialog';
     await microtasksFinished();
@@ -67,11 +57,6 @@
     // Default is null (not present)
     assertFalse(button.hasAttribute('aria-expanded'));
 
-    // Set attribute
-    element.setAttribute('aria-expanded', 'true');
-    await microtasksFinished();
-    assertEquals('true', button.getAttribute('aria-expanded'));
-
     // Set property
     element.ariaExpanded = 'false';
     await microtasksFinished();
@@ -90,12 +75,6 @@
     // Default is empty
     assertEquals('', button.getAttribute('title'));
 
-    // Set attribute 'title'
-    element.setAttribute('title', 'test-title');
-    await microtasksFinished();
-    assertEquals('test-title', button.getAttribute('title'));
-    assertEquals('test-title', element.tooltip);
-
     // Set property 'tooltip'
     element.tooltip = 'new-title';
     await microtasksFinished();
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index ca7e916..d513c6b 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-16778.0.0-1079370
\ No newline at end of file
+16778.0.0-1079378
\ No newline at end of file
diff --git a/clank b/clank
index 9924b3a..60af3ed 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 9924b3ae796f1ead39f335234c232c4226e25c0f
+Subproject commit 60af3ed4b83a012447f9a27fad47768a55e80fb3
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 9f41c11..42c4cb1 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -886,6 +886,7 @@
       "//components/safe_browsing/core/browser/utils:unit_tests",
       "//components/safe_browsing/core/common:unit_tests",
       "//components/safe_browsing/core/common/hashprefix_realtime:unit_tests",
+      "//components/safe_browsing/core/common/phishing_classifier:unit_tests",
     ]
   }
 
@@ -913,7 +914,6 @@
       "//components/safe_browsing/core/browser/password_protection:unit_tests",
       "//components/safe_browsing/core/browser/sync:unittests",
       "//components/safe_browsing/core/common:unit_tests",
-      "//components/safe_browsing/core/common/phishing_classifier:unit_tests",
     ]
   } else if (safe_browsing_mode == 2) {
     deps += [
diff --git a/components/affiliations/core/browser/lookup_affiliation_response_parser.cc b/components/affiliations/core/browser/lookup_affiliation_response_parser.cc
index 8874500..96fd9da 100644
--- a/components/affiliations/core/browser/lookup_affiliation_response_parser.cc
+++ b/components/affiliations/core/browser/lookup_affiliation_response_parser.cc
@@ -112,23 +112,27 @@
     // affiliations must form an equivalence relation. Also check, if the class
     // was requested.
     bool is_class_requested = false;
+    // equivalence_class.facet()[0] establishes the class index that every
+    // other facet in this equivalence class must match.
+    std::optional<size_t> first_facet_class_index;
     for (const auto& facet : equivalence_class.facet()) {
       if (requested_facets.count(facet.id()))
         is_class_requested = true;
 
-      if (!facet_uri_to_class_index.count(facet.id()))
-        facet_uri_to_class_index[facet.id()] = result.size();
+      auto it =
+          facet_uri_to_class_index.try_emplace(facet.id(), result.size()).first;
 
-      if (facet_uri_to_class_index[facet.id()] !=
-          facet_uri_to_class_index[equivalence_class.facet()[0].id()]) {
+      if (!first_facet_class_index) {
+        first_facet_class_index = it->second;
+      }
+
+      if (it->second != first_facet_class_index) {
         return false;
       }
     }
 
     // Filter out duplicate or unrequested equivalence classes in the response.
-    if (is_class_requested &&
-        facet_uri_to_class_index[equivalence_class.facet()[0].id()] ==
-            result.size()) {
+    if (is_class_requested && first_facet_class_index == result.size()) {
       result.push_back(ParseEqClass(equivalence_class));
     }
   }
diff --git a/components/affiliations/core/browser/lookup_affiliation_response_parser_unittest.cc b/components/affiliations/core/browser/lookup_affiliation_response_parser_unittest.cc
index 78c0b796..76ba4847 100644
--- a/components/affiliations/core/browser/lookup_affiliation_response_parser_unittest.cc
+++ b/components/affiliations/core/browser/lookup_affiliation_response_parser_unittest.cc
@@ -200,4 +200,90 @@
             result.groupings[0].branding_info.icon_url.spec());
 }
 
+TEST(LookupAffiliationResponseParserTest,
+     ParseMultipleEquivalenceClassesWithMultipleFacets) {
+  std::vector<FacetURI> requested_facet_uris;
+  requested_facet_uris.push_back(
+      FacetURI::FromCanonicalSpec("https://example.com"));
+  requested_facet_uris.push_back(
+      FacetURI::FromCanonicalSpec("https://other.example.com"));
+
+  affiliation_pb::LookupAffiliationByHashPrefixResponse response;
+  auto* affiliation1 = response.add_affiliations();
+  affiliation1->add_facet()->set_id("https://example.com");
+  affiliation1->add_facet()->set_id("https://example2.com");
+  affiliation1->add_facet()->set_id("https://example3.com");
+
+  auto* affiliation2 = response.add_affiliations();
+  affiliation2->add_facet()->set_id("https://other.example.com");
+  affiliation2->add_facet()->set_id("https://other2.example.com");
+
+  AffiliationFetcherInterface::ParsedFetchResponse result;
+  bool success =
+      ParseLookupAffiliationResponse(requested_facet_uris, response, &result);
+
+  EXPECT_TRUE(success);
+  ASSERT_EQ(2u, result.affiliations.size());
+  EXPECT_EQ(3u, result.affiliations[0].size());
+  EXPECT_EQ(2u, result.affiliations[1].size());
+}
+
+TEST(LookupAffiliationResponseParserTest,
+     RejectPartiallyOverlappingEquivalenceClasses) {
+  std::vector<FacetURI> requested_facet_uris;
+  requested_facet_uris.push_back(
+      FacetURI::FromCanonicalSpec("https://example.com"));
+
+  affiliation_pb::LookupAffiliationByHashPrefixResponse response;
+  auto* affiliation1 = response.add_affiliations();
+  affiliation1->add_facet()->set_id("https://example.com");
+  affiliation1->add_facet()->set_id("https://shared.example.com");
+
+  auto* affiliation2 = response.add_affiliations();
+  affiliation2->add_facet()->set_id("https://shared.example.com");
+  affiliation2->add_facet()->set_id("https://other.example.com");
+
+  AffiliationFetcherInterface::ParsedFetchResponse result;
+  bool success =
+      ParseLookupAffiliationResponse(requested_facet_uris, response, &result);
+
+  EXPECT_FALSE(success);
+}
+
+TEST(LookupAffiliationResponseParserTest, IgnoreDuplicateEquivalenceClasses) {
+  std::vector<FacetURI> requested_facet_uris = {
+      FacetURI::FromCanonicalSpec("https://example.com")};
+
+  affiliation_pb::LookupAffiliationByHashPrefixResponse response;
+  auto* affiliation1 = response.add_affiliations();
+  affiliation1->add_facet()->set_id("https://example.com");
+  affiliation1->add_facet()->set_id("https://example2.com");
+
+  // Duplicate equivalence class in the same response.
+  auto* affiliation2 = response.add_affiliations();
+  affiliation2->add_facet()->set_id("https://example.com");
+  affiliation2->add_facet()->set_id("https://example2.com");
+
+  AffiliationFetcherInterface::ParsedFetchResponse result;
+  EXPECT_TRUE(
+      ParseLookupAffiliationResponse(requested_facet_uris, response, &result));
+  EXPECT_EQ(1u, result.affiliations.size());
+}
+
+TEST(LookupAffiliationResponseParserTest, IgnoreUnrequestedEquivalenceClasses) {
+  // Nothing is requested, so the equivalence class below should be excluded
+  // and nothing should be synthesized either.
+  std::vector<FacetURI> requested_facet_uris;
+
+  affiliation_pb::LookupAffiliationByHashPrefixResponse response;
+  auto* affiliation = response.add_affiliations();
+  affiliation->add_facet()->set_id("https://unrequested.example.com");
+  affiliation->add_facet()->set_id("https://unrequested2.example.com");
+
+  AffiliationFetcherInterface::ParsedFetchResponse result;
+  EXPECT_TRUE(
+      ParseLookupAffiliationResponse(requested_facet_uris, response, &result));
+  EXPECT_TRUE(result.affiliations.empty());
+}
+
 }  // namespace affiliations
diff --git a/components/android_autofill/browser/junit/src/org/chromium/components/autofill/AutofillProviderUMATest.java b/components/android_autofill/browser/junit/src/org/chromium/components/autofill/AutofillProviderUMATest.java
index 768d3af1..6261724 100644
--- a/components/android_autofill/browser/junit/src/org/chromium/components/autofill/AutofillProviderUMATest.java
+++ b/components/android_autofill/browser/junit/src/org/chromium/components/autofill/AutofillProviderUMATest.java
@@ -23,7 +23,6 @@
 import org.chromium.autofill.mojom.SubmissionSource;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.components.autofill.AutofillProviderUMA.AutofillManagerCreationContext;
 import org.chromium.components.autofill.AutofillProviderUMA.AutofillManagerMethod;
@@ -32,7 +31,6 @@
 /** Tests for {@link AutofillProviderUMA} general metrics. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 public class AutofillProviderUMATest {
 
     private AutofillProviderUMA mAutofillUMA;
diff --git a/components/browser_actuator/internal/transport_channel_impl.cc b/components/browser_actuator/internal/transport_channel_impl.cc
index d7603919..c06aa81 100644
--- a/components/browser_actuator/internal/transport_channel_impl.cc
+++ b/components/browser_actuator/internal/transport_channel_impl.cc
@@ -9,6 +9,7 @@
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/uuid.h"
+#include "components/browser_actuator/internal/control_transport_handler.h"
 #include "components/browser_actuator/internal/proto/transport_messages.pb.h"
 #include "components/browser_actuator/internal/transport/resume_body_connection_delegate.h"
 #include "components/browser_actuator/internal/transport/stream_connection_delegate.h"
@@ -25,6 +26,13 @@
       weak_ptr_factory_.GetWeakPtr());
   session_registry_->AddObserver(this);
 
+  control_handler_factory_ = std::make_unique<ControlTransportHandlerFactory>(
+      base::BindRepeating(&TransportChannelImpl::Disconnect,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindRepeating(&TransportSessionRegistryImpl::DestroySession,
+                          session_registry_->GetWeakPtr()));
+  handler_registry_->RegisterFactory(control_handler_factory_.get());
+
   // The resume delegate carries no state: on every connection attempt it asks
   // the channel to rebuild the WatchSessionsRequest body from the current
   // sessions. Unretained is safe because the channel owns the client — and
@@ -49,11 +57,21 @@
   if (session_registry_) {
     session_registry_->RemoveObserver(this);
   }
+  if (handler_registry_ && control_handler_factory_) {
+    handler_registry_->UnregisterFactory(control_handler_factory_.get());
+  }
   if (stream_client_) {
     stream_client_->RemoveObserver(this);
   }
 }
 
+void TransportChannelImpl::Disconnect() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (stream_client_) {
+    stream_client_->Disconnect();
+  }
+}
+
 TransportHandlerFactoryRegistry*
 TransportChannelImpl::GetHandlerFactoryRegistry() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/components/browser_actuator/internal/transport_channel_impl.h b/components/browser_actuator/internal/transport_channel_impl.h
index 4dabd2f..49fe8857 100644
--- a/components/browser_actuator/internal/transport_channel_impl.h
+++ b/components/browser_actuator/internal/transport_channel_impl.h
@@ -19,6 +19,7 @@
 
 namespace browser_actuator {
 
+class ControlTransportHandlerFactory;
 class StreamConnectionDelegate;
 class TransportHandlerFactoryRegistryImpl;
 class TransportSessionRegistryImpl;
@@ -81,6 +82,9 @@
   std::string BuildWatchSessionsRequestBodyForTesting();
 
  private:
+  // Closes the underlying stream connection.
+  void Disconnect();
+
   // Serializes a WatchSessionsRequest describing every session with a known
   // resume position, plus a fresh idempotency id. Handed to the resume
   // delegate as its body provider, so it runs once per connection attempt.
@@ -91,6 +95,9 @@
   // The registry holding factories that create the dynamic feature handlers.
   std::unique_ptr<TransportHandlerFactoryRegistryImpl> handler_registry_;
 
+  // Factory for control command handlers (CloseChannel, CloseSession).
+  std::unique_ptr<ControlTransportHandlerFactory> control_handler_factory_;
+
   // The registry to access and control the life cycles for sessions.
   // Declared before `stream_client_` and after `handler_registry_` to
   // coordinate the correct lifetimes and pointer management.
@@ -99,7 +106,7 @@
   // The underlying network message stream client.
   std::unique_ptr<MessageStreamClient> stream_client_;
 
-  base::WeakPtrFactory<TransportChannel> weak_ptr_factory_{this};
+  base::WeakPtrFactory<TransportChannelImpl> weak_ptr_factory_{this};
 };
 
 }  // namespace browser_actuator
diff --git a/components/browser_actuator/internal/transport_channel_impl_unittest.cc b/components/browser_actuator/internal/transport_channel_impl_unittest.cc
index 71a923a..ba1b7549 100644
--- a/components/browser_actuator/internal/transport_channel_impl_unittest.cc
+++ b/components/browser_actuator/internal/transport_channel_impl_unittest.cc
@@ -305,21 +305,19 @@
   EXPECT_EQ(registry->GetSession("s1"), nullptr);
 }
 
-TEST_F(TransportChannelImplTest, RoutesControlCommandToCloseSession) {
+TEST_F(TransportChannelImplTest, ControlHandlerFactoryRegisteredByDefault) {
+  std::vector<TransportHandlerFactory*> factories =
+      channel_->GetHandlerFactoryRegistry()->GetFactories(
+          PayloadType::kControl);
+  ASSERT_EQ(factories.size(), 1u);
+  EXPECT_EQ(factories[0]->GetFactoryId(), FactoryId::kControl);
+}
+
+TEST_F(TransportChannelImplTest, ControlCommandCloseSessionDestroysSession) {
   TransportSessionRegistryImpl* registry =
       static_cast<TransportSessionRegistryImpl*>(
           channel_->GetSessionRegistry());
-  TransportSession* session = registry->GetOrCreateSession("s1");
-  ASSERT_NE(session, nullptr);
-
-  ControlTransportHandlerFactory factory(
-      base::DoNothing(),
-      base::BindRepeating(
-          [](TransportSessionRegistryImpl* r, std::string_view session_id) {
-            r->DestroySession(session_id);
-          },
-          registry));
-  channel_->GetHandlerFactoryRegistry()->RegisterFactory(&factory);
+  ASSERT_NE(registry->GetOrCreateSession("s1"), nullptr);
 
   ControlCommand command;
   command.mutable_close_session();
@@ -329,16 +327,13 @@
       {{ACTUATOR_DOWNSTREAM_PAYLOAD_TYPE_CONTROL_COMMAND,
         command.SerializeAsString()}}));
 
+  // The session was created, processed the CloseSession command, and was
+  // destroyed.
   EXPECT_EQ(registry->GetSession("s1"), nullptr);
 }
 
-TEST_F(TransportChannelImplTest, RoutesControlCommandToCloseChannel) {
-  ControlTransportHandlerFactory factory(
-      base::BindRepeating(
-          [](FakeMessageStreamClient* client) { client->Disconnect(); },
-          fake_client_.get()),
-      base::DoNothing());
-  channel_->GetHandlerFactoryRegistry()->RegisterFactory(&factory);
+TEST_F(TransportChannelImplTest, ControlCommandCloseChannelDisconnectsStream) {
+  EXPECT_FALSE(fake_client_->disconnected_called());
 
   ControlCommand command;
   command.mutable_close_channel();
@@ -348,6 +343,7 @@
       {{ACTUATOR_DOWNSTREAM_PAYLOAD_TYPE_CONTROL_COMMAND,
         command.SerializeAsString()}}));
 
+  // The CloseChannel command triggers Disconnect on the stream client.
   EXPECT_TRUE(fake_client_->disconnected_called());
 }
 
@@ -358,7 +354,7 @@
 
   fake_client_->Dispatch(SerializedDownstreamMessage(
       "s1", 2,
-      {{ACTUATOR_DOWNSTREAM_PAYLOAD_TYPE_CONTROL_COMMAND, "control_data"}}));
+      {{ACTUATOR_DOWNSTREAM_PAYLOAD_TYPE_UNSPECIFIED, "payload_data"}}));
 
   TransportSessionImpl* session = registry->GetSessionImpl("s1");
   ASSERT_NE(session, nullptr);
diff --git a/components/browser_actuator/internal/transport_session_impl_unittest.cc b/components/browser_actuator/internal/transport_session_impl_unittest.cc
index 6c03279..de1d528 100644
--- a/components/browser_actuator/internal/transport_session_impl_unittest.cc
+++ b/components/browser_actuator/internal/transport_session_impl_unittest.cc
@@ -278,5 +278,37 @@
       TransportSessionImpl::ProcessPayloadError::kHandlerInstantiationFailed);
 }
 
+TEST(TransportSessionImplTest, ProcessDownstreamMessageRoutesControlCommand) {
+  MockTransportChannel channel;
+  TransportHandlerFactoryRegistryImpl registry;
+  EXPECT_CALL(channel, GetHandlerFactoryRegistry())
+      .WillRepeatedly(testing::Return(&registry));
+
+  TransportSessionImpl session("test_session", channel.GetWeakPtr());
+
+  MockTransportHandlerFactory factory({PayloadType::kControl});
+  registry.RegisterFactory(&factory);
+
+  ControlCommand command;
+  command.mutable_close_session();
+  std::string serialized_command = command.SerializeAsString();
+
+  auto handler = std::make_unique<MockTransportHandler>();
+  MockTransportHandler* handler_ptr = handler.get();
+
+  EXPECT_CALL(factory, OnNewSession(&session))
+      .WillOnce(testing::Return(std::move(handler)));
+  EXPECT_CALL(*handler_ptr, OnMessage(serialized_command)).Times(1);
+
+  ActuatorDownstreamMessage message;
+  message.set_session_id("test_session");
+  message.set_sequence_number(1);
+  auto* typed = message.add_typed_payloads();
+  typed->set_payload_type(ACTUATOR_DOWNSTREAM_PAYLOAD_TYPE_CONTROL_COMMAND);
+  typed->mutable_proto_payload()->set_value(serialized_command);
+
+  session.ProcessDownstreamMessage(message);
+}
+
 }  // namespace
 }  // namespace browser_actuator
diff --git a/components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom b/components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom
index c19536c..1c3612fa 100644
--- a/components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom
+++ b/components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom
@@ -426,6 +426,21 @@
   // The icon to display.
   IconHandle icon;
 
+  // The text to display on the page action chip.
+  mojo_base.mojom.String16 text;
+
+  // Represents the target visibility state of the suggestion chip. If true, the
+  // frontend should apply the expanded CSS layout.
+  bool should_show_chip;
+
+  // Represents whether the chip should animate when expanding, as opposed to
+  // snapping open instantly. Has no effect if `should_show_chip` is false.
+  bool should_animate_chip_in;
+
+  // Represents whether the chip should animate when collapsing, as opposed to
+  // snapping shut instantly. Has no effect if `should_show_chip` is true.
+  bool should_animate_chip_out;
+
   // TODO: crbug.com/501449027 - Add remaining fields.
 };
 
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java
index f0f1f1f5d..9940de57 100644
--- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java
+++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java
@@ -123,6 +123,30 @@
     /** The minimum distance between half and full states to allow the half state. */
     private final float mMinHalfFullDistance;
 
+    /** The bottom margin for desktop windowing mode. */
+    private final @Px int mDesktopBottomMargin;
+
+    /** The sheet width for large form factor devices. */
+    private final @Px int mLargeFormFactorWidth;
+
+    /** The gap between the sheet and the edge of the window for large form factor devices. */
+    private final @Px int mLargeFormFactorEdgeGap;
+
+    /** The container width threshold below which narrow sheet layout is used. */
+    private final @Px int mNarrowWidthThreshold;
+
+    /** The sheet width when using narrow layout. */
+    private final @Px int mNarrowWidth;
+
+    /** The default peek height of the sheet. */
+    private final @Px int mDefaultPeekHeight;
+
+    /** The shadow length surrounding the sheet. */
+    private final @Px int mShadowLength;
+
+    /** The shadow length surrounding the sheet for large form factor devices. */
+    private final @Px int mShadowLengthLarge;
+
     /** The view that contains the sheet. */
     private ViewGroup mSheetContainer;
 
@@ -310,8 +334,21 @@
     public BottomSheet(Context context, AttributeSet atts) {
         super(context, atts);
 
+        Resources res = getResources();
         mMinHalfFullDistance =
-                getResources().getDimensionPixelSize(R.dimen.bottom_sheet_min_full_half_distance);
+                res.getDimensionPixelSize(R.dimen.bottom_sheet_min_full_half_distance);
+        mDesktopBottomMargin =
+                res.getDimensionPixelSize(R.dimen.bottom_sheet_desktop_bottom_margin);
+        mLargeFormFactorWidth =
+                res.getDimensionPixelSize(R.dimen.bottom_sheet_large_form_factor_width);
+        mLargeFormFactorEdgeGap =
+                res.getDimensionPixelSize(R.dimen.bottom_sheet_large_form_factor_edge_gap);
+        mNarrowWidthThreshold =
+                res.getDimensionPixelSize(R.dimen.bottom_sheet_narrow_width_threshold);
+        mNarrowWidth = res.getDimensionPixelSize(R.dimen.bottom_sheet_narrow_width);
+        mDefaultPeekHeight = res.getDimensionPixelSize(R.dimen.bottom_sheet_peek_height);
+        mShadowLength = res.getDimensionPixelSize(R.dimen.bottom_sheet_shadow_length);
+        mShadowLengthLarge = res.getDimensionPixelSize(R.dimen.bottom_sheet_shadow_length_large);
         mSheetBgColor = getNonModalBottomSheetBgColor(context);
         mGestureDetector = new BottomSheetSwipeDetector(context, this);
         mIsTouchEnabled = true;
@@ -1068,7 +1105,7 @@
 
         int toolbarHeight;
         if (toolbarView == null) {
-            toolbarHeight = getResources().getDimensionPixelSize(R.dimen.bottom_sheet_peek_height);
+            toolbarHeight = mDefaultPeekHeight;
         } else {
             toolbarHeight = toolbarView.getHeight();
             if (toolbarHeight == 0) {
@@ -1371,9 +1408,7 @@
         if (isLargeFormFactorUiEnabled()) {
             // Clamp the height to leave an empty gap at the top of the window equal to the
             // desktop bottom margin (24dp).
-            int topGap =
-                    getResources()
-                            .getDimensionPixelSize(R.dimen.bottom_sheet_desktop_bottom_margin);
+            int topGap = mDesktopBottomMargin;
             return Math.max(0, mContainerHeight - getContainerBottomMargin() - topGap);
         }
         return mContainerHeight;
@@ -1392,23 +1427,15 @@
     public int getMaxSheetWidth() {
         if (!mAlwaysFullWidth) {
             if (isLargeFormFactorUiEnabled()) {
-                int width =
-                        getResources()
-                                .getDimensionPixelSize(
-                                        R.dimen.bottom_sheet_large_form_factor_width);
+                int width = mLargeFormFactorWidth;
                 // Clamp the sheet's width to ensure a dedicated 16dp horizontal gap from the edge
                 // of the window when it becomes constrained.
-                int edgeGap =
-                        getResources()
-                                .getDimensionPixelSize(
-                                        R.dimen.bottom_sheet_large_form_factor_edge_gap);
+                int edgeGap = mLargeFormFactorEdgeGap;
                 return Math.max(0, Math.min(width, mContainerWidth - 2 * edgeGap));
             }
-            int narrowWidthThreshold =
-                    getResources()
-                            .getDimensionPixelSize(R.dimen.bottom_sheet_narrow_width_threshold);
+            int narrowWidthThreshold = mNarrowWidthThreshold;
             if (mContainerWidth > narrowWidthThreshold) {
-                return getResources().getDimensionPixelSize(R.dimen.bottom_sheet_narrow_width);
+                return mNarrowWidth;
             }
         }
         return mContainerWidth;
@@ -1829,9 +1856,7 @@
         // Enforce the baseline visual requirements for large form factor devices: ensure the sheet
         // physically floats above the logical bottom by attaching a rigid bottom margin offset.
         if (isLargeFormFactorUiEnabled()) {
-            bottomMargin +=
-                    getResources()
-                            .getDimensionPixelSize(R.dimen.bottom_sheet_desktop_bottom_margin);
+            bottomMargin += mDesktopBottomMargin;
         }
 
         // TODO(crbug.com/521433079): Should early return if this doesn't change. Leaving for now to
@@ -1916,10 +1941,7 @@
 
         if (isLargeFormFactorUiEnabled()) {
             mShadowLayer.setBackgroundResource(R.drawable.popup_bg_shadow_16dp);
-            int size =
-                    getContext()
-                            .getResources()
-                            .getDimensionPixelSize(R.dimen.bottom_sheet_shadow_length);
+            int size = mShadowLength;
             MarginLayoutParams lp = (MarginLayoutParams) mShadowLayer.getLayoutParams();
             if (lp != null) {
                 // The shadow drawable actually holds visual pixels that extend outwards
@@ -1947,15 +1969,9 @@
             shadowLayer.setBackgroundResource(R.drawable.top_round_shadow);
             int size;
             if (spec.size == GlowSpec.ShadowSize.LONG) {
-                size =
-                        getContext()
-                                .getResources()
-                                .getDimensionPixelSize(R.dimen.bottom_sheet_shadow_length_large);
+                size = mShadowLengthLarge;
             } else {
-                size =
-                        getContext()
-                                .getResources()
-                                .getDimensionPixelSize(R.dimen.bottom_sheet_shadow_length);
+                size = mShadowLength;
             }
             if (shadowLayer instanceof ShadowLayerView) {
                 ((ShadowLayerView) shadowLayer).setShadowLength(size);
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetRecyclerScrollListenerTest.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetRecyclerScrollListenerTest.java
index 4e5ed67..b9b0a74 100644
--- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetRecyclerScrollListenerTest.java
+++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetRecyclerScrollListenerTest.java
@@ -26,13 +26,11 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
 
 /** Robolectric unit tests for {@link BottomSheetRecyclerScrollListener}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class BottomSheetRecyclerScrollListenerTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/BrowserUiListMenuUnitTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/BrowserUiListMenuUnitTest.java
index baac4e0..a34b553 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/BrowserUiListMenuUnitTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/BrowserUiListMenuUnitTest.java
@@ -11,7 +11,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.when;
 
-import static org.chromium.base.test.util.Batch.UNIT_TESTS;
 import static org.chromium.components.browser_ui.widget.BrowserUiListMenuUtils.getBasicListMenu;
 import static org.chromium.ui.listmenu.ListItemType.MENU_ITEM_WITH_SUBMENU;
 import static org.chromium.ui.listmenu.ListMenuItemProperties.CLICK_LISTENER;
@@ -42,7 +41,6 @@
 
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.ui.listmenu.BasicListMenu;
 import org.chromium.ui.listmenu.ListMenuSubmenuItemProperties;
 import org.chromium.ui.listmenu.ListMenuUtils;
@@ -60,7 +58,6 @@
 @Config(
         manifest = Config.NONE,
         shadows = {ShadowAppCompatResources.class})
-@Batch(UNIT_TESTS)
 public class BrowserUiListMenuUnitTest {
 
     @Rule
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/CoordinatorLayoutForPointerUnitTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/CoordinatorLayoutForPointerUnitTest.java
index ddefaeb8..b1932e77 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/CoordinatorLayoutForPointerUnitTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/CoordinatorLayoutForPointerUnitTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+
 import android.app.Activity;
 import android.content.Context;
 import android.view.MotionEvent;
@@ -20,12 +21,10 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 
 /** Unit tests for {@link CoordinatorLayoutForPointer}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 public class CoordinatorLayoutForPointerUnitTest {
 
     private Activity mActivity;
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/tile/TileViewBinderTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/tile/TileViewBinderTest.java
index eddef9e4..a1cd69c 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/tile/TileViewBinderTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/tile/TileViewBinderTest.java
@@ -26,7 +26,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.browser_ui.test.BrowserUiTestFragmentActivity;
 import org.chromium.components.browser_ui.widget.R;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -35,7 +34,6 @@
 /** Tests for {@link TileViewBinder}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.PER_CLASS)
 public class TileViewBinderTest {
     private Activity mActivity;
     private TileView mTileView;
diff --git a/components/component_updater/installer_policies/prediction_model_component_installer.cc b/components/component_updater/installer_policies/prediction_model_component_installer.cc
index 558e91c..51f9fd0a 100644
--- a/components/component_updater/installer_policies/prediction_model_component_installer.cc
+++ b/components/component_updater/installer_policies/prediction_model_component_installer.cc
@@ -106,9 +106,12 @@
 
   std::string GetName() const override { return config_.component_name(); }
 
+  // LINT.IfChange(TfliteEngineVersion)
   update_client::InstallerAttributes GetInstallerAttributes() const override {
-    return update_client::InstallerAttributes();
+    return update_client::InstallerAttributes{
+        {"tflite_engine_version", "2.22.0"}};
   }
+  // LINT.ThenChange(//components/optimization_guide/core/delivery/prediction_manager.cc:TfliteEngineVersion)
 
   const optimization_guide::proto::OptimizationTarget optimization_target_;
   const optimization_guide::PredictionModelComponentConfig config_;
diff --git a/components/crash/android/junit/src/org/chromium/components/crash/PureJavaExceptionReporterTest.java b/components/crash/android/junit/src/org/chromium/components/crash/PureJavaExceptionReporterTest.java
index 22a1ef54..8a308c23 100644
--- a/components/crash/android/junit/src/org/chromium/components/crash/PureJavaExceptionReporterTest.java
+++ b/components/crash/android/junit/src/org/chromium/components/crash/PureJavaExceptionReporterTest.java
@@ -13,7 +13,6 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.minidump_uploader.CrashTestRule;
 
 import java.io.BufferedReader;
@@ -23,7 +22,6 @@
 
 /** Unittests for {@link PureJavaExceptionReporter}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 public class PureJavaExceptionReporterTest {
     @Rule public CrashTestRule mTestRule = new CrashTestRule();
 
diff --git a/components/crash/core/app/BUILD.gn b/components/crash/core/app/BUILD.gn
index 00f91a2..180d551 100644
--- a/components/crash/core/app/BUILD.gn
+++ b/components/crash/core/app/BUILD.gn
@@ -61,6 +61,8 @@
     "crash_switches.h",
     "crashpad.cc",
     "crashpad.h",
+    "shared_memory_user_stream_reader.cc",
+    "shared_memory_user_stream_reader.h",
     "shared_memory_user_stream_writer.cc",
     "shared_memory_user_stream_writer.h",
   ]
@@ -306,7 +308,10 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "shared_memory_user_stream_writer_unittest.cc" ]
+  sources = [
+    "shared_memory_user_stream_reader_unittest.cc",
+    "shared_memory_user_stream_writer_unittest.cc",
+  ]
   deps = [
     ":app",
     ":lib",
diff --git a/components/crash/core/app/shared_memory_user_stream_reader.cc b/components/crash/core/app/shared_memory_user_stream_reader.cc
new file mode 100644
index 0000000..dd8788e71
--- /dev/null
+++ b/components/crash/core/app/shared_memory_user_stream_reader.cc
@@ -0,0 +1,185 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/core/app/shared_memory_user_stream_reader.h"
+
+#include <stdint.h>
+
+#include <atomic>
+#include <optional>
+#include <vector>
+
+#include "base/containers/span.h"
+#include "base/containers/to_vector.h"
+#include "base/memory/aligned_memory.h"
+#include "base/numerics/checked_math.h"
+#include "base/threading/platform_thread.h"
+#include "base/types/expected.h"
+#include "components/crash/core/common/shared_memory_user_stream.h"
+
+namespace crash_reporter {
+
+namespace {
+
+// Reads a 32-bit field atomically from read-only shared memory.
+uint32_t AtomicLoad(const uint32_t& val) {
+  return std::atomic_ref<uint32_t>(const_cast<uint32_t&>(val))
+      .load(std::memory_order_acquire);
+}
+
+// Validates the envelope header (magic, version, and alignment).
+const internal::SharedMemoryUserStreamHeader* ValidateAndGetHeader(
+    base::span<const uint8_t> user_stream_buffer) {
+  if (user_stream_buffer.size() <
+          sizeof(internal::SharedMemoryUserStreamHeader) ||
+      !base::IsAligned(user_stream_buffer.data(),
+                       alignof(internal::SharedMemoryUserStreamHeader))) {
+    return nullptr;
+  }
+
+  const auto* header =
+      reinterpret_cast<const internal::SharedMemoryUserStreamHeader*>(
+          user_stream_buffer.data());
+  if (header->magic != internal::kUserStreamMagic ||
+      header->version != internal::kUserStreamVersion) {
+    return nullptr;
+  }
+
+  return header;
+}
+
+// Validates that `active_slot_header_offset` points to a valid, aligned slot
+// header within `user_stream_buffer` and is not the
+// `kNoActiveSlotHeaderOffset` sentinel.
+const internal::SharedMemoryUserStreamSlotHeader* ValidateAndGetSlotHeader(
+    base::span<const uint8_t> user_stream_buffer,
+    uint32_t active_slot_header_offset) {
+  // `kNoActiveSlotHeaderOffset` indicates no stream data has been published
+  // yet.
+  if (active_slot_header_offset == internal::kNoActiveSlotHeaderOffset ||
+      active_slot_header_offset <
+          sizeof(internal::SharedMemoryUserStreamHeader) ||
+      !base::IsAligned(active_slot_header_offset,
+                       alignof(internal::SharedMemoryUserStreamSlotHeader))) {
+    return nullptr;
+  }
+
+  size_t slot_header_end = 0;
+  if (!base::CheckAdd(active_slot_header_offset,
+                      sizeof(internal::SharedMemoryUserStreamSlotHeader))
+           .AssignIfValid(&slot_header_end) ||
+      slot_header_end > user_stream_buffer.size()) {
+    return nullptr;
+  }
+
+  return reinterpret_cast<const internal::SharedMemoryUserStreamSlotHeader*>(
+      user_stream_buffer.subspan(active_slot_header_offset).data());
+}
+
+// Reason why a single attempt to read a slot's payload failed.
+enum class ReadSlotError {
+  // A concurrent writer was actively modifying the slot (dirty generation or
+  // generation counter changed during the read).
+  kTransientConflict,
+
+  // The slot contents or header fields are malformed or exceed buffer bounds.
+  kCorruptData,
+};
+
+// Attempts a single lock-free seqlock read of the slot's payload.
+//
+// Returns a vector containing the copied payload on success.
+// Returns `ReadSlotError::kTransientConflict` if the read encountered a
+// concurrent modification (odd generation or generation changed during read).
+// Returns `ReadSlotError::kCorruptData` if payload size bounds or arithmetic
+// overflow occur while the slot is in a stable state.
+base::expected<std::vector<uint8_t>, ReadSlotError> TryReadSlotPayload(
+    base::span<const uint8_t> user_stream_buffer,
+    const internal::SharedMemoryUserStreamSlotHeader* slot_header,
+    uint32_t active_slot_header_offset) {
+  // Readers must check `generation` before and after copying a slot to detect
+  // concurrent modifications and retry.
+  const uint32_t generation_before = AtomicLoad(slot_header->generation);
+  if (generation_before & 1u) {
+    // Odd value indicates an in-progress update ("dirty bit").
+    return base::unexpected(ReadSlotError::kTransientConflict);
+  }
+
+  const uint32_t payload_size = slot_header->payload_size;
+
+  size_t payload_start = 0;
+  size_t payload_end = 0;
+  if (!base::CheckAdd(active_slot_header_offset,
+                      sizeof(internal::SharedMemoryUserStreamSlotHeader))
+           .AssignIfValid(&payload_start) ||
+      !base::CheckAdd(payload_start, payload_size)
+           .AssignIfValid(&payload_end) ||
+      payload_end > user_stream_buffer.size()) {
+    // If the slot was modified concurrently during the read, treat invalid
+    // bounds as a transient conflict rather than permanent corruption.
+    const uint32_t current_generation = AtomicLoad(slot_header->generation);
+    if (current_generation != generation_before) {
+      return base::unexpected(ReadSlotError::kTransientConflict);
+    }
+    return base::unexpected(ReadSlotError::kCorruptData);
+  }
+
+  const base::span<const uint8_t> payload =
+      user_stream_buffer.subspan(payload_start, payload_size);
+  std::vector<uint8_t> payload_copy = base::ToVector(payload);
+
+  const uint32_t generation_after = AtomicLoad(slot_header->generation);
+  if (generation_after != generation_before) {
+    // Slot was modified concurrently during the read.
+    return base::unexpected(ReadSlotError::kTransientConflict);
+  }
+
+  return payload_copy;
+}
+
+}  // namespace
+
+std::optional<UserStreamData> ExtractSharedMemoryUserStreamData(
+    base::span<const uint8_t> user_stream_buffer) {
+  const internal::SharedMemoryUserStreamHeader* header =
+      ValidateAndGetHeader(user_stream_buffer);
+  if (!header) {
+    return std::nullopt;
+  }
+
+  // Retry loop to capture a consistent slot snapshot across concurrent updates.
+  constexpr int kMaxAttempts = 10;
+  for (int attempt = 0; attempt < kMaxAttempts; ++attempt) {
+    const uint32_t active_slot_header_offset =
+        AtomicLoad(header->active_slot_header_offset);
+
+    const internal::SharedMemoryUserStreamSlotHeader* slot_header =
+        ValidateAndGetSlotHeader(user_stream_buffer, active_slot_header_offset);
+    if (!slot_header) {
+      return std::nullopt;
+    }
+
+    base::expected<std::vector<uint8_t>, ReadSlotError> payload =
+        TryReadSlotPayload(user_stream_buffer, slot_header,
+                           active_slot_header_offset);
+    if (payload.has_value()) {
+      return UserStreamData{
+          .user_stream_type = header->user_stream_type,
+          .payload = std::move(payload).value(),
+      };
+    }
+
+    if (payload.error() == ReadSlotError::kCorruptData) {
+      return std::nullopt;
+    }
+
+    if (attempt + 1 < kMaxAttempts) {
+      base::PlatformThread::YieldCurrentThread();
+    }
+  }
+
+  return std::nullopt;
+}
+
+}  // namespace crash_reporter
diff --git a/components/crash/core/app/shared_memory_user_stream_reader.h b/components/crash/core/app/shared_memory_user_stream_reader.h
new file mode 100644
index 0000000..0933606
--- /dev/null
+++ b/components/crash/core/app/shared_memory_user_stream_reader.h
@@ -0,0 +1,37 @@
+// Copyright 2026 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_CRASH_CORE_APP_SHARED_MEMORY_USER_STREAM_READER_H_
+#define COMPONENTS_CRASH_CORE_APP_SHARED_MEMORY_USER_STREAM_READER_H_
+
+#include <stdint.h>
+
+#include <optional>
+#include <vector>
+
+#include "base/containers/span.h"
+
+namespace crash_reporter {
+
+// Holds minidump user stream metadata and payload extracted from a shared
+// memory region.
+struct UserStreamData {
+  uint32_t user_stream_type;
+  std::vector<uint8_t> payload;
+};
+
+// Extracts the payload and stream type from `user_stream_buffer`, returning
+// std::nullopt if the buffer or active slot is invalid, corrupted, unaligned,
+// or modified concurrently during all read attempts.
+//
+// Even though `user_stream_buffer` is a generic span, it is assumed to point
+// to memory formatted according to the SharedMemoryUserStream protocol (defined
+// in shared_memory_user_stream.h). It uses retry loops to safely read from
+// memory that may be concurrently modified.
+std::optional<UserStreamData> ExtractSharedMemoryUserStreamData(
+    base::span<const uint8_t> user_stream_buffer);
+
+}  // namespace crash_reporter
+
+#endif  // COMPONENTS_CRASH_CORE_APP_SHARED_MEMORY_USER_STREAM_READER_H_
diff --git a/components/crash/core/app/shared_memory_user_stream_reader_unittest.cc b/components/crash/core/app/shared_memory_user_stream_reader_unittest.cc
new file mode 100644
index 0000000..1d069d5
--- /dev/null
+++ b/components/crash/core/app/shared_memory_user_stream_reader_unittest.cc
@@ -0,0 +1,185 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/crash/core/app/shared_memory_user_stream_reader.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <optional>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/containers/span.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "components/crash/core/app/shared_memory_user_stream_writer.h"
+#include "components/crash/core/common/shared_memory_user_stream.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace crash_reporter {
+namespace {
+
+constexpr uint32_t kDefaultUserStreamType = 123;
+constexpr uint32_t kDefaultSlotCapacityBytes = 128;
+
+class SharedMemoryUserStreamReaderTest : public testing::Test {
+ protected:
+  // Creates a mapped shared memory buffer populated with a valid header and
+  // payload.
+  std::vector<uint8_t> CreateValidBuffer(
+      base::span<const uint8_t> payload = {},
+      uint32_t user_stream_type = kDefaultUserStreamType,
+      uint32_t slot_capacity_bytes = kDefaultSlotCapacityBytes) {
+    SharedMemoryUserStreamWriter writer(user_stream_type, slot_capacity_bytes);
+
+    EXPECT_TRUE(writer.WriteData(
+        [&](base::span<uint8_t> data) -> std::optional<size_t> {
+          std::ranges::copy(payload, data.begin());
+          return payload.size();
+        }));
+
+    const base::ReadOnlySharedMemoryRegion region = writer.DuplicateRegion();
+    const base::ReadOnlySharedMemoryMapping mapping = region.Map();
+    EXPECT_TRUE(mapping.IsValid());
+    if (!mapping.IsValid()) {
+      return {};
+    }
+
+    const auto span = mapping.GetMemoryAsSpan<const uint8_t>();
+    return std::vector<uint8_t>(span.begin(), span.end());
+  }
+
+  static internal::SharedMemoryUserStreamHeader* GetHeader(
+      std::vector<uint8_t>& user_stream_buffer) {
+    if (user_stream_buffer.size() <
+        sizeof(internal::SharedMemoryUserStreamHeader)) {
+      return nullptr;
+    }
+    return UNSAFE_BUFFERS(
+        reinterpret_cast<internal::SharedMemoryUserStreamHeader*>(
+            user_stream_buffer.data()));
+  }
+
+  static internal::SharedMemoryUserStreamSlotHeader* GetSlotHeader(
+      std::vector<uint8_t>& user_stream_buffer,
+      uint32_t slot_header_offset) {
+    if (slot_header_offset +
+            sizeof(internal::SharedMemoryUserStreamSlotHeader) >
+        user_stream_buffer.size()) {
+      return nullptr;
+    }
+    return UNSAFE_BUFFERS(
+        reinterpret_cast<internal::SharedMemoryUserStreamSlotHeader*>(
+            base::span(user_stream_buffer).subspan(slot_header_offset).data()));
+  }
+};
+
+TEST_F(SharedMemoryUserStreamReaderTest, ExtractValidData) {
+  const std::vector<uint8_t> expected_data = {0x01, 0x02, 0x03, 0x04, 0x05};
+  std::vector<uint8_t> buffer = CreateValidBuffer(expected_data);
+
+  std::optional<UserStreamData> stream_data =
+      ExtractSharedMemoryUserStreamData(buffer);
+  ASSERT_TRUE(stream_data.has_value());
+  EXPECT_EQ(stream_data->user_stream_type, kDefaultUserStreamType);
+  EXPECT_EQ(stream_data->payload, expected_data);
+}
+
+TEST_F(SharedMemoryUserStreamReaderTest, ExtractHandlesZeroBytePayload) {
+  std::vector<uint8_t> buffer = CreateValidBuffer();
+
+  std::optional<UserStreamData> stream_data =
+      ExtractSharedMemoryUserStreamData(buffer);
+  ASSERT_TRUE(stream_data.has_value());
+  EXPECT_EQ(stream_data->user_stream_type, kDefaultUserStreamType);
+  EXPECT_TRUE(stream_data->payload.empty());
+}
+
+TEST_F(SharedMemoryUserStreamReaderTest, ExtractRejectsInvalidBuffer) {
+  // Empty buffer
+  EXPECT_FALSE(ExtractSharedMemoryUserStreamData(base::span<const uint8_t>())
+                   .has_value());
+
+  // Buffer smaller than header
+  std::vector<uint8_t> small_buffer(
+      sizeof(internal::SharedMemoryUserStreamHeader) - 1, 0);
+  EXPECT_FALSE(ExtractSharedMemoryUserStreamData(small_buffer).has_value());
+}
+
+TEST_F(SharedMemoryUserStreamReaderTest,
+       ExtractReturnsNulloptWhenNoActiveSlot) {
+  const SharedMemoryUserStreamWriter writer(kDefaultUserStreamType,
+                                            kDefaultSlotCapacityBytes);
+
+  const base::ReadOnlySharedMemoryRegion region = writer.DuplicateRegion();
+  const base::ReadOnlySharedMemoryMapping mapping = region.Map();
+  ASSERT_TRUE(mapping.IsValid());
+
+  // No WriteData() called yet -> active_slot_header_offset is
+  // kNoActiveSlotHeaderOffset.
+  EXPECT_FALSE(ExtractSharedMemoryUserStreamData(
+                   mapping.GetMemoryAsSpan<const uint8_t>())
+                   .has_value());
+}
+
+TEST_F(SharedMemoryUserStreamReaderTest, ExtractRejectsInvalidMagicOrVersion) {
+  std::vector<uint8_t> buffer = CreateValidBuffer({1, 2, 3});
+  auto* const header = GetHeader(buffer);
+  ASSERT_NE(header, nullptr);
+
+  // Invalid magic
+  header->magic = 0xDEADBEEF;
+  EXPECT_FALSE(ExtractSharedMemoryUserStreamData(buffer).has_value());
+
+  // Invalid version
+  header->magic = internal::kUserStreamMagic;
+  header->version = 999;
+  EXPECT_FALSE(ExtractSharedMemoryUserStreamData(buffer).has_value());
+}
+
+TEST_F(SharedMemoryUserStreamReaderTest, ExtractRejectsCorruptPayloadSize) {
+  std::vector<uint8_t> buffer = CreateValidBuffer({1, 2, 3});
+  auto* const header = GetHeader(buffer);
+  ASSERT_NE(header, nullptr);
+  auto* const slot_header =
+      GetSlotHeader(buffer, header->active_slot_header_offset);
+  ASSERT_NE(slot_header, nullptr);
+
+  // Set an impossible payload size that exceeds the buffer.
+  slot_header->payload_size = 0xFFFFFFFF;
+  EXPECT_FALSE(ExtractSharedMemoryUserStreamData(buffer).has_value());
+}
+
+TEST_F(SharedMemoryUserStreamReaderTest,
+       ExtractRejectsCorruptActiveSlotHeaderOffset) {
+  std::vector<uint8_t> buffer = CreateValidBuffer({1, 2, 3});
+  auto* const header = GetHeader(buffer);
+  ASSERT_NE(header, nullptr);
+
+  // Offset that causes arithmetic overflow when computing slot header end.
+  header->active_slot_header_offset = 0xFFFFFFF0;
+  EXPECT_FALSE(ExtractSharedMemoryUserStreamData(buffer).has_value());
+
+  // Offset out of bounds.
+  header->active_slot_header_offset =
+      static_cast<uint32_t>(buffer.size() + 1024);
+  EXPECT_FALSE(ExtractSharedMemoryUserStreamData(buffer).has_value());
+}
+
+TEST_F(SharedMemoryUserStreamReaderTest, ExtractRejectsDirtySlotGeneration) {
+  std::vector<uint8_t> buffer = CreateValidBuffer({1, 2, 3});
+  auto* const header = GetHeader(buffer);
+  ASSERT_NE(header, nullptr);
+  auto* const slot_header =
+      GetSlotHeader(buffer, header->active_slot_header_offset);
+  ASSERT_NE(slot_header, nullptr);
+
+  // Set odd generation (in-progress/dirty bit).
+  slot_header->generation = 1;
+  EXPECT_FALSE(ExtractSharedMemoryUserStreamData(buffer).has_value());
+}
+
+}  // namespace
+}  // namespace crash_reporter
diff --git a/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridgeTest.java b/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridgeTest.java
index 8dcb14a..66043da 100644
--- a/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridgeTest.java
+++ b/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridgeTest.java
@@ -38,7 +38,6 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.WindowAndroid;
@@ -50,7 +49,6 @@
 
 /** Tests for the ThirdPartyCredentialManagerBridge. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.PER_CLASS)
 public class ThirdPartyCredentialManagerBridgeTest {
 
     private static final String USERNAME = "username";
diff --git a/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerMetricsRecorderTest.java b/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerMetricsRecorderTest.java
index 14e356a..3fb1190 100644
--- a/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerMetricsRecorderTest.java
+++ b/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerMetricsRecorderTest.java
@@ -21,12 +21,10 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.HistogramWatcher;
 
 /** Tests for the ThirdPartyCredentialManagerMetricsRecorder. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.PER_CLASS)
 public class ThirdPartyCredentialManagerMetricsRecorderTest {
 
     private static class FakeCreateCredentialException extends CreateCredentialException {
diff --git a/components/enterprise/connectors/core/realtime_reporting_client_base.cc b/components/enterprise/connectors/core/realtime_reporting_client_base.cc
index 071fd69..38d16594 100644
--- a/components/enterprise/connectors/core/realtime_reporting_client_base.cc
+++ b/components/enterprise/connectors/core/realtime_reporting_client_base.cc
@@ -185,8 +185,6 @@
 void RealtimeReportingClientBase::ReportEvent(
     ::chrome::cros::reporting::proto::Event event,
     const ReportingSettings& settings) {
-  DCHECK(base::FeatureList::IsEnabled(
-      policy::kUploadRealtimeReportingEventsUsingProto));
   policy::CloudPolicyClient* client =
       GetReportingClient(settings.dm_token, settings.per_profile);
   if (!client) {
@@ -280,57 +278,6 @@
   std::move(callback).Run(std::move(upload_result));
 }
 
-void RealtimeReportingClientBase::ReportEventWithTimestampDeprecated(
-    const std::string& name,
-    const ReportingSettings& settings,
-    base::DictValue event,
-    const base::Time& time,
-    bool include_profile_user_name) {
-  // TODO(Bug:394403600) - Replace with a DCHECK once all callers are migrated.
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
-    DLOG(WARNING) << "ReportEventWithTimestampDeprecated called when proto "
-                     "format enabled for event "
-                  << name;
-    return;
-  }
-
-  if (rejected_dm_token_timers_.contains(settings.dm_token)) {
-    return;
-  }
-
-#ifndef NDEBUG
-  // Make sure the event is included in the kAllReportingEnabledEvents or the
-  // kAllReportingOptInEvents array.
-  bool found = std::ranges::contains(kAllReportingEnabledEvents, name) ||
-               std::ranges::contains(kAllReportingOptInEvents, name);
-  DCHECK(found);
-#endif
-
-  // Make sure real-time reporting is initialized.
-  InitRealtimeReportingClient(settings.per_profile, settings.dm_token);
-  if ((settings.per_profile && !profile_client_) ||
-      (!settings.per_profile && !browser_client_)) {
-    return;
-  }
-
-  policy::CloudPolicyClient* client =
-      settings.per_profile ? profile_client_.get() : browser_client_.get();
-  event.Set(kKeyProfileIdentifier, GetProfileIdentifier());
-  if (include_profile_user_name) {
-    event.Set(kKeyProfileUserName, GetProfileUserName());
-  }
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
-  MaybeCollectDeviceSignalsAndReportEventDeprecated(std::move(event), client,
-                                                    name, settings, time);
-#else
-  // Regardless of collecting device signals or not, upload the security event
-  // report.
-  UploadSecurityEventReportDeprecated(std::move(event), client, name, settings,
-                                      time);
-#endif
-}
-
 void RealtimeReportingClientBase::UploadSecurityEvent(
     ::chrome::cros::reporting::proto::Event event,
     policy::CloudPolicyClient* client,
@@ -371,60 +318,6 @@
                               std::move(request), std::move(upload_callback));
 }
 
-void RealtimeReportingClientBase::UploadSecurityEventReportDeprecated(
-    base::DictValue event,
-    policy::CloudPolicyClient* client,
-    std::string name,
-    const ReportingSettings& settings,
-    base::Time time) {
-  base::DictValue event_wrapper =
-      base::DictValue()
-          .Set("time", base::TimeFormatAsIso8601(time))
-          .Set(name, std::move(event));
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
-      base::BindOnce(&GetLocalIpAddresses),
-      base::BindOnce(
-          &RealtimeReportingClientBase::OnIpAddressesFetchedDeprecated,
-          AsWeakPtr(), std::move(event_wrapper), client, name, settings, time));
-  return;
-}
-
-void RealtimeReportingClientBase::OnIpAddressesFetchedDeprecated(
-    base::DictValue event_wrapper,
-    policy::CloudPolicyClient* client,
-    std::string name,
-    const ReportingSettings& settings,
-    base::Time time,
-    std::vector<std::string> ip_addresses) {
-  event_wrapper.Set("localIps", base::ToValueList(ip_addresses));
-  FinishUploadSecurityEventReportDeprecated(std::move(event_wrapper), client,
-                                            name, settings);
-}
-
-void RealtimeReportingClientBase::FinishUploadSecurityEventReportDeprecated(
-    base::DictValue event_wrapper,
-    policy::CloudPolicyClient* client,
-    std::string name,
-    const ReportingSettings& settings) {
-  DVLOG(1) << "enterprise.connectors: security event: "
-           << event_wrapper.DebugString();
-
-  base::DictValue report =
-      policy::RealtimeReportingJobConfiguration::BuildReport(
-          base::ListValue().Append(std::move(event_wrapper)), GetContext());
-
-  auto upload_callback =
-      base::BindOnce(&RealtimeReportingClientBase::UploadCallbackDeprecated,
-                     AsWeakPtr(), report.Clone(), settings.per_profile, client,
-                     enterprise_connectors::GetUmaEnumFromEventName(name),
-                     base::TimeTicks::Now());
-
-  client->UploadSecurityEventReport(
-      ShouldIncludeDeviceInfo(settings.per_profile), std::move(report),
-      std::move(upload_callback));
-}
-
 const std::string
 RealtimeReportingClientBase::GetProfilePolicyClientDescription() {
   return kProfilePolicyClientDescription;
diff --git a/components/enterprise/connectors/core/realtime_reporting_client_base.h b/components/enterprise/connectors/core/realtime_reporting_client_base.h
index 14ecf667..3a68e6a5 100644
--- a/components/enterprise/connectors/core/realtime_reporting_client_base.h
+++ b/components/enterprise/connectors/core/realtime_reporting_client_base.h
@@ -60,14 +60,6 @@
   virtual void ReportEvent(::chrome::cros::reporting::proto::Event event,
                            const ReportingSettings& settings);
 
-  // Function that uploads security events, parameterized with the time. We
-  // should stop using this once the migration for the reporting events from
-  // dictionary to proto is done.
-  void ReportEventWithTimestampDeprecated(const std::string& name,
-                                          const ReportingSettings& settings,
-                                          base::DictValue event,
-                                          const base::Time& time,
-                                          bool include_profile_user_name);
 
   // Report a SaaS usage event to the reporting server. The `per_profile`
   // parameter determines if the browser or profile client will be used. The
@@ -123,31 +115,18 @@
 
   // Sub-method called by ReportEventWithTimestamp() to collect device signals
   // on Windows/Mac/Linux platforms. Regardless of collecting device signals or
-  // not, this method is expected to call `UploadSecurityEventReport()` in the
+  // not, this method is expected to call `UploadSecurityEvent()` in the
   // end.
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
-  virtual void MaybeCollectDeviceSignalsAndReportEventDeprecated(
-      base::DictValue event,
-      policy::CloudPolicyClient* client,
-      std::string name,
-      const ReportingSettings& settings,
-      base::Time time) = 0;
+
   virtual void MaybeCollectDeviceSignalsAndReportEvent(
       ::chrome::cros::reporting::proto::Event event,
       policy::CloudPolicyClient* client,
       const ReportingSettings& settings) = 0;
 #endif
 
-  // Callback used with UploadSecurityEventReport() to upload events to the
-  // reporting server.
-  virtual void UploadCallbackDeprecated(
-      base::DictValue event_wrapper,
-      bool per_profile,
-      policy::CloudPolicyClient* client,
-      EnterpriseReportingEventType event_type,
-      base::TimeTicks upload_started_at,
-      policy::CloudPolicyClient::Result upload_result) = 0;
-
+  // Callback used in FinishUploadSecurityEvent() to log UMA metrics and add
+  // events with response to chrome://safe-browsing#tab-reporting page.
   virtual void UploadCallback(
       bool per_profile,
       policy::CloudPolicyClient* client,
@@ -155,10 +134,6 @@
       base::TimeTicks upload_started_at,
       policy::CloudPolicyClient::Result upload_result) = 0;
 
-  // Returns a dictionary of information added to reporting events,
-  // corresponding to the Device, Browser and Profile protos defined in
-  // google3/google/internal/chrome/reporting/v1/chromereporting.proto.
-  virtual base::DictValue GetContext() = 0;
 
   // Creates and returns an UploadEventsRequest proto with the Device, Browser
   // and Profile protos set.
@@ -186,27 +161,6 @@
                                  policy::CloudPolicyClient* client,
                                  const ReportingSettings& settings);
 
-  void OnIpAddressesFetchedDeprecated(base::DictValue event,
-                                      policy::CloudPolicyClient* client,
-                                      std::string name,
-                                      const ReportingSettings& settings,
-                                      base::Time time,
-                                      std::vector<std::string> ip_addresses);
-
-  // Prepares information required by
-  // CloudPolicyClient::UploadSecurityEventReportDeprecated() and calls it.
-  // DEPRECATED: Use UploadSecurityEvent() instead.
-  void UploadSecurityEventReportDeprecated(base::DictValue event,
-                                           policy::CloudPolicyClient* client,
-                                           std::string name,
-                                           const ReportingSettings& settings,
-                                           base::Time time);
-
-  void FinishUploadSecurityEventReportDeprecated(
-      base::DictValue event,
-      policy::CloudPolicyClient* client,
-      std::string name,
-      const ReportingSettings& settings);
 
   const std::string GetProfilePolicyClientDescription();
 
diff --git a/components/enterprise/connectors/core/reporting_event_router.cc b/components/enterprise/connectors/core/reporting_event_router.cc
index f57725a..cd7b397 100644
--- a/components/enterprise/connectors/core/reporting_event_router.cc
+++ b/components/enterprise/connectors/core/reporting_event_router.cc
@@ -41,23 +41,6 @@
   return false;
 }
 
-void AddAnalysisConnectorVerdictToEvent(
-    const ContentAnalysisResponse::Result& result,
-    base::DictValue& event) {
-  base::ListValue triggered_rule_info;
-  for (const TriggeredRule& trigger : result.triggered_rules()) {
-    base::DictValue triggered_rule;
-    triggered_rule.Set(kKeyTriggeredRuleName, trigger.rule_name());
-    int rule_id_int = 0;
-    if (base::StringToInt(trigger.rule_id(), &rule_id_int)) {
-      triggered_rule.Set(kKeyTriggeredRuleId, rule_id_int);
-    }
-    triggered_rule.Set(kKeyUrlCategory, trigger.url_category());
-
-    triggered_rule_info.Append(std::move(triggered_rule));
-  }
-  event.Set(kKeyTriggeredRuleInfo, std::move(triggered_rule_info));
-}
 
 std::string MalwareRuleToThreatType(const std::string& rule_name) {
   if (rule_name == "uws") {
@@ -166,8 +149,6 @@
     return;
   }
 
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
     chrome::cros::reporting::proto::Event event;
     *event.mutable_login_event() =
         GetLoginEvent(url, is_federated, federated_origin, username,
@@ -176,19 +157,6 @@
     *event.mutable_time() = ToProtoTimestamp(base::Time::Now());
 
     reporting_client_->ReportEvent(std::move(event), settings.value());
-  } else {
-    base::DictValue event;
-    event.Set(kKeyUrl, url.spec());
-    event.Set(kKeyIsFederated, is_federated);
-    if (is_federated) {
-      event.Set(kKeyFederatedOrigin, federated_origin.Serialize());
-    }
-    event.Set(kKeyLoginUserName, MaskUsername(username));
-
-    reporting_client_->ReportEventWithTimestampDeprecated(
-        kKeyLoginEvent, std::move(settings.value()), std::move(event),
-        base::Time::Now(), /*include_profile_user_name=*/true);
-  }
 }
 
 void ReportingEventRouter::OnPasswordBreach(
@@ -206,8 +174,6 @@
     return;
   }
 
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
     chrome::cros::reporting::proto::Event event;
     std::optional<chrome::cros::reporting::proto::PasswordBreachEvent>
         password_breach_event =
@@ -222,34 +188,6 @@
     *event.mutable_time() = ToProtoTimestamp(base::Time::Now());
 
     reporting_client_->ReportEvent(std::move(event), settings.value());
-  } else {
-    base::ListValue identities_list;
-    for (const std::pair<GURL, std::u16string>& i : identities) {
-      if (!IsUrlMatched(matcher.get(), i.first)) {
-        continue;
-      }
-
-      base::DictValue identity;
-      identity.Set(kKeyPasswordBreachIdentitiesUrl, i.first.spec());
-      identity.Set(kKeyPasswordBreachIdentitiesUsername,
-                   MaskUsername(i.second));
-      identities_list.Append(std::move(identity));
-    }
-
-    if (identities_list.empty()) {
-      // Don't send an empty event if none of the breached identities matched a
-      // pattern in the URL filters.
-      return;
-    }
-
-    base::DictValue event;
-    event.Set(kKeyTrigger, trigger);
-    event.Set(kKeyPasswordBreachIdentities, std::move(identities_list));
-
-    reporting_client_->ReportEventWithTimestampDeprecated(
-        kKeyPasswordBreachEvent, std::move(settings.value()), std::move(event),
-        base::Time::Now(), /*include_profile_user_name=*/true);
-  }
 }
 
 void ReportingEventRouter::OnPasswordReuse(
@@ -264,8 +202,6 @@
 
   std::optional<ReportingSettings> settings =
       reporting_client_->GetReportingSettings();
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
     chrome::cros::reporting::proto::Event event;
     *event.mutable_password_reuse_event() = GetPasswordReuseEvent(
         url, user_name, is_phishing_url, warning_shown,
@@ -274,24 +210,6 @@
     *event.mutable_time() = ToProtoTimestamp(base::Time::Now());
 
     reporting_client_->ReportEvent(std::move(event), settings.value());
-  } else {
-    base::DictValue event;
-    event.Set(kKeyUrl, url.spec());
-    event.Set(kKeyUserName, user_name);
-    event.Set(kKeyIsPhishingUrl, is_phishing_url);
-    event.Set(kKeyEventResult,
-              EventResultToString(warning_shown ? EventResult::WARNED
-                                                : EventResult::ALLOWED));
-
-    if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedFieldsForSecOps)) {
-      AddReferrerChainToEvent(referrer_chain, event);
-    }
-
-    reporting_client_->ReportEventWithTimestampDeprecated(
-        kKeyPasswordReuseEvent, std::move(settings.value()), std::move(event),
-        base::Time::Now(),
-        /*include_profile_user_name=*/true);
-  }
 }
 
 void ReportingEventRouter::OnPasswordChanged(const std::string& user_name) {
@@ -301,8 +219,6 @@
 
   std::optional<ReportingSettings> settings =
       reporting_client_->GetReportingSettings();
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
     chrome::cros::reporting::proto::Event event;
     *event.mutable_password_changed_event() = GetPasswordChangedEvent(
         user_name, reporting_client_->GetProfileIdentifier(),
@@ -310,15 +226,6 @@
     *event.mutable_time() = ToProtoTimestamp(base::Time::Now());
 
     reporting_client_->ReportEvent(std::move(event), settings.value());
-  } else {
-    base::DictValue event;
-    event.Set(kKeyUserName, user_name);
-
-    reporting_client_->ReportEventWithTimestampDeprecated(
-        kKeyPasswordChangedEvent, std::move(settings.value()), std::move(event),
-        base::Time::Now(),
-        /*include_profile_user_name=*/true);
-  }
 }
 
 void ReportingEventRouter::OnUrlFilteringInterstitial(
@@ -334,8 +241,6 @@
       reporting_client_->GetReportingSettings();
   std::string active_user = reporting_client_->GetContentAreaAccountEmail(url);
 
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
     chrome::cros::reporting::proto::Event event;
     *event.mutable_url_filtering_interstitial_event() =
         GetUrlFilteringInterstitialEvent(
@@ -347,30 +252,6 @@
     *event.mutable_time() = ToProtoTimestamp(base::Time::Now());
 
     reporting_client_->ReportEvent(std::move(event), settings.value());
-  } else {
-    base::DictValue event;
-    event.Set(kKeyUrl, url.spec());
-    EventResult event_result = GetEventResultFromThreatType(threat_type);
-    event.Set(kKeyClickedThrough, event_result == EventResult::BYPASSED);
-    if (!threat_type.empty()) {
-      event.Set(kKeyThreatType, threat_type);
-    }
-
-    if (!active_user.empty()) {
-      event.Set(kKeyWebAppSignedInAccount, active_user);
-    }
-    AddTriggeredRuleInfoToUrlFilteringInterstitialEvent(response, event);
-    event.Set(kKeyEventResult, EventResultToString(event_result));
-
-    if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedFieldsForSecOps)) {
-      AddReferrerChainToEvent(referrer_chain, event);
-    }
-
-    reporting_client_->ReportEventWithTimestampDeprecated(
-        kKeyUrlFilteringInterstitialEvent, std::move(settings.value()),
-        std::move(event), base::Time::Now(),
-        /*include_profile_user_name=*/true);
-  }
 }
 
 void ReportingEventRouter::OnSecurityInterstitialProceeded(
@@ -384,8 +265,6 @@
 
   std::optional<ReportingSettings> settings =
       reporting_client_->GetReportingSettings();
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
     chrome::cros::reporting::proto::Event event;
     *event.mutable_interstitial_event() = GetInterstitialEvent(
         url, reason, net_error_code,
@@ -395,23 +274,6 @@
     *event.mutable_time() = ToProtoTimestamp(base::Time::Now());
 
     reporting_client_->ReportEvent(std::move(event), settings.value());
-  } else {
-    base::DictValue event;
-    event.Set(kKeyUrl, url.spec());
-    event.Set(kKeyReason, reason);
-    event.Set(kKeyNetErrorCode, net_error_code);
-    event.Set(kKeyClickedThrough, true);
-    event.Set(kKeyEventResult, EventResultToString(EventResult::BYPASSED));
-
-    if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedFieldsForSecOps)) {
-      AddReferrerChainToEvent(referrer_chain, event);
-    }
-
-    reporting_client_->ReportEventWithTimestampDeprecated(
-        kKeyInterstitialEvent, std::move(settings.value()), std::move(event),
-        base::Time::Now(),
-        /*include_profile_user_name=*/true);
-  }
 }
 
 void ReportingEventRouter::OnSecurityInterstitialShown(
@@ -429,8 +291,6 @@
   EventResult event_result =
       proceed_anyway_disabled ? EventResult::BLOCKED : EventResult::WARNED;
 
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
     chrome::cros::reporting::proto::Event event;
     *event.mutable_interstitial_event() = GetInterstitialEvent(
         url, reason, net_error_code,
@@ -440,23 +300,6 @@
     *event.mutable_time() = ToProtoTimestamp(base::Time::Now());
 
     reporting_client_->ReportEvent(std::move(event), settings.value());
-  } else {
-    base::DictValue event;
-    event.Set(kKeyUrl, url.spec());
-    event.Set(kKeyReason, reason);
-    event.Set(kKeyNetErrorCode, net_error_code);
-    event.Set(kKeyClickedThrough, false);
-    event.Set(kKeyEventResult, EventResultToString(event_result));
-
-    if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedFieldsForSecOps)) {
-      AddReferrerChainToEvent(referrer_chain, event);
-    }
-
-    reporting_client_->ReportEventWithTimestampDeprecated(
-        kKeyInterstitialEvent, std::move(settings.value()), std::move(event),
-        base::Time::Now(),
-        /*include_profile_user_name=*/true);
-  }
 }
 
 void ReportingEventRouter::SendEventOnGotHash(
@@ -480,17 +323,6 @@
                                  std::move(reporting_settings));
 }
 
-void ReportingEventRouter::SendEventOnGotHashDeprecated(
-    const std::string& name,
-    ReportingSettings reporting_settings,
-    base::DictValue event,
-    std::string hash) {
-  DCHECK(std::ranges::all_of(hash, base::IsHexDigit<char>));
-  event.Set(kKeyDownloadDigestSha256, hash);
-  reporting_client_->ReportEventWithTimestampDeprecated(
-      name, std::move(reporting_settings), std::move(event), base::Time::Now(),
-      /*include_profile_user_name=*/true);
-}
 
 void ReportingEventRouter::OnUnscannedFileEvent(
     const GURL& url,
@@ -522,8 +354,6 @@
       file_name,
       reporting_client_->ShouldIncludeDeviceInfo(settings->per_profile));
 
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
     chrome::cros::reporting::proto::Event event;
     *event.mutable_unscanned_file_event() = GetUnscannedFileEvent(
         url, tab_url, source, destination, final_file_name,
@@ -542,43 +372,6 @@
     } else {
       std::move(send_event_cb).Run(download_digest_sha256);
     }
-  } else {
-    base::DictValue event;
-    event.Set(kKeyUrl, url.spec());
-    event.Set(kKeyTabUrl, tab_url.spec());
-    event.Set(kKeySource, source);
-    event.Set(kKeyDestination, destination);
-    event.Set(kKeyFileName, final_file_name);
-    event.Set(kKeyDownloadDigestSha256, download_digest_sha256);
-    event.Set(kKeyContentType, mime_type);
-    event.Set(kKeyScanId, scan_id);
-    event.Set(kKeyUnscannedReason, reason);
-    // |content_size| can be set to -1 to indicate an unknown size, in
-    // which case the field is not set.
-    if (content_size >= 0) {
-      event.Set(kKeyContentSize, base::Int64ToValue(content_size));
-    }
-    event.Set(kKeyTrigger, trigger);
-    if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedFieldsForSecOps)) {
-      AddReferrerChainToEvent(referrer_chain, event);
-    }
-    event.Set(kKeyEventResult, EventResultToString(event_result));
-    event.Set(kKeyClickedThrough, event_result == EventResult::BYPASSED);
-    if (!content_transfer_method.empty()) {
-      event.Set(kKeyContentTransferMethod, content_transfer_method);
-    }
-
-    auto send_event_cb =
-        base::BindOnce(&ReportingEventRouter::SendEventOnGotHashDeprecated,
-                       weak_ptr_factory_.GetWeakPtr(), kKeyUnscannedFileEvent,
-                       std::move(settings.value()), std::move(event));
-    if (std::holds_alternative<RegisterOnGotHashCallback>(sha256_or_cb)) {
-      std::get<RegisterOnGotHashCallback>(sha256_or_cb)
-          .Run(std::move(send_event_cb));
-    } else {
-      std::move(send_event_cb).Run(download_digest_sha256);
-    }
-  }
 }
 
 void ReportingEventRouter::OnSensitiveDataEvent(
@@ -640,8 +433,6 @@
       event.file_name,
       reporting_client_->ShouldIncludeDeviceInfo(settings->per_profile));
 
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
     chrome::cros::reporting::proto::Event proto_event;
     *proto_event.mutable_sensitive_data_event() = GetDlpSensitiveDataEvent(
         event.url, event.tab_url, event.source, event.destination,
@@ -663,63 +454,6 @@
     } else {
       std::move(send_event_cb).Run(download_digest_sha256);
     }
-  } else {
-    base::DictValue dict_event;
-    dict_event.Set(kKeyUrl, event.url.spec());
-    dict_event.Set(kKeyTabUrl, event.tab_url.spec());
-    dict_event.Set(kKeySource, event.source);
-    dict_event.Set(kKeyDestination, event.destination);
-    dict_event.Set(
-        kKeyFileName,
-        GetFileName(event.file_name, reporting_client_->ShouldIncludeDeviceInfo(
-                                         settings->per_profile)));
-    dict_event.Set(kKeyDownloadDigestSha256, download_digest_sha256);
-    dict_event.Set(kKeyContentType, event.mime_type);
-    // |content_size| can be set to -1 to indicate an unknown size, in
-    // which case the field is not set.
-    if (event.content_size >= 0) {
-      dict_event.Set(kKeyContentSize, base::Int64ToValue(event.content_size));
-    }
-    dict_event.Set(kKeyTrigger, event.trigger);
-
-    if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedFieldsForSecOps)) {
-      AddReferrerChainToEvent(event.referrer_chain, dict_event);
-    }
-
-    dict_event.Set(kKeyEventResult, EventResultToString(event.event_result));
-    dict_event.Set(kKeyClickedThrough,
-                   event.event_result == EventResult::BYPASSED);
-    dict_event.Set(kKeyScanId, event.scan_id);
-
-    if (!event.content_transfer_method.empty()) {
-      dict_event.Set(kKeyContentTransferMethod, event.content_transfer_method);
-    }
-    if (!event.content_area_account_email.empty()) {
-      dict_event.Set(kKeyWebAppSignedInAccount,
-                     event.content_area_account_email);
-    }
-    if (!event.source_email.empty()) {
-      dict_event.Set(kKeySourceWebAppSignedInAccount, event.source_email);
-    }
-    if (event.user_justification.has_value()) {
-      dict_event.Set(kKeyUserJustification, event.user_justification.value());
-    }
-
-    AddFrameUrlChainToEvent(event.frame_url_chain, dict_event);
-
-    AddAnalysisConnectorVerdictToEvent(event.result, dict_event);
-
-    auto send_event_cb =
-        base::BindOnce(&ReportingEventRouter::SendEventOnGotHashDeprecated,
-                       weak_ptr_factory_.GetWeakPtr(), kKeySensitiveDataEvent,
-                       std::move(settings.value()), std::move(dict_event));
-    if (std::holds_alternative<RegisterOnGotHashCallback>(event.sha256_or_cb)) {
-      std::get<RegisterOnGotHashCallback>(event.sha256_or_cb)
-          .Run(std::move(send_event_cb));
-    } else {
-      std::move(send_event_cb).Run(download_digest_sha256);
-    }
-  }
 }
 
 void ReportingEventRouter::OnDangerousDownloadEvent(
@@ -773,8 +507,6 @@
       file_name,
       reporting_client_->ShouldIncludeDeviceInfo(settings->per_profile));
 
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
     chrome::cros::reporting::proto::Event event;
     *event.mutable_dangerous_download_event() = GetDangerousDownloadEvent(
         url, tab_url, source, destination, final_file_name,
@@ -793,49 +525,6 @@
     } else {
       std::move(send_event_cb).Run(download_digest_sha256);
     }
-  } else {
-    base::DictValue event;
-    event.Set(kKeyUrl, url.spec());
-    event.Set(kKeyTabUrl, tab_url.spec());
-    event.Set(kKeySource, source);
-    event.Set(kKeyDestination, destination);
-    event.Set(kKeyFileName, final_file_name);
-    event.Set(kKeyDownloadDigestSha256, download_digest_sha256);
-    event.Set(kKeyThreatType, threat_type);
-    event.Set(kKeyContentType, mime_type);
-    // |content_size| can be set to -1 to indicate an unknown size, in
-    // which case the field is not set.
-    if (content_size >= 0) {
-      event.Set(kKeyContentSize, base::Int64ToValue(content_size));
-    }
-    event.Set(kKeyTrigger, trigger);
-    if (base::FeatureList::IsEnabled(safe_browsing::kEnhancedFieldsForSecOps)) {
-      AddReferrerChainToEvent(referrer_chain, event);
-    }
-    event.Set(kKeyEventResult, EventResultToString(event_result));
-    event.Set(kKeyClickedThrough, event_result == EventResult::BYPASSED);
-    // The scan ID can be empty when the reported dangerous download is from a
-    // Safe Browsing verdict.
-    if (!scan_id.empty()) {
-      event.Set(kKeyScanId, scan_id);
-    }
-    if (!content_transfer_method.empty()) {
-      event.Set(kKeyContentTransferMethod, content_transfer_method);
-    }
-
-    AddFrameUrlChainToEvent(frame_url_chain, event);
-
-    auto send_event_cb = base::BindOnce(
-        &ReportingEventRouter::SendEventOnGotHashDeprecated,
-        weak_ptr_factory_.GetWeakPtr(), kKeyDangerousDownloadEvent,
-        std::move(settings.value()), std::move(event));
-    if (std::holds_alternative<RegisterOnGotHashCallback>(sha256_or_cb)) {
-      std::get<RegisterOnGotHashCallback>(sha256_or_cb)
-          .Run(std::move(send_event_cb));
-    } else {
-      std::move(send_event_cb).Run(download_digest_sha256);
-    }
-  }
 }
 
 void ReportingEventRouter::OnAnalysisConnectorResult(
@@ -1038,8 +727,6 @@
   std::optional<ReportingSettings> settings =
       reporting_client_->GetReportingSettings();
 
-  if (base::FeatureList::IsEnabled(
-          policy::kUploadRealtimeReportingEventsUsingProto)) {
     chrome::cros::reporting::proto::Event event;
     *event.mutable_sensitive_data_event() = GetDataControlsSensitiveDataEvent(
         url, tab_url, source, destination, mime_type, trigger,
@@ -1050,46 +737,6 @@
     *event.mutable_time() = ToProtoTimestamp(base::Time::Now());
 
     reporting_client_->ReportEvent(std::move(event), settings.value());
-  } else {
-    base::DictValue event;
-    event.Set(kKeyUrl, url.spec());
-    event.Set(kKeyTabUrl, tab_url.spec());
-    event.Set(kKeySource, source);
-    event.Set(kKeyDestination, destination);
-    event.Set(kKeyContentType, mime_type);
-    // |content_size| can be set to -1 to indicate an unknown size, in
-    // which case the field is not set.
-    if (content_size >= 0) {
-      event.Set(kKeyContentSize, base::Int64ToValue(content_size));
-    }
-    event.Set(kKeyTrigger, trigger);
-    if (!content_area_account_email.empty()) {
-      event.Set(kKeyWebAppSignedInAccount, content_area_account_email);
-    }
-    if (!source_active_user_email.empty()) {
-      event.Set(kKeySourceWebAppSignedInAccount, source_active_user_email);
-    }
-    event.Set(kKeyEventResult, EventResultToString(event_result));
-
-    base::ListValue triggered_rule_info;
-    triggered_rule_info.reserve(triggered_rules.size());
-    for (const auto& [index, rule] : triggered_rules) {
-      base::DictValue triggered_rule;
-      int rule_id_int = 0;
-      if (base::StringToInt(rule.rule_id, &rule_id_int)) {
-        triggered_rule.Set(kKeyTriggeredRuleId, rule_id_int);
-      }
-      triggered_rule.Set(kKeyTriggeredRuleName, rule.rule_name);
-
-      triggered_rule_info.Append(std::move(triggered_rule));
-    }
-    event.Set(kKeyTriggeredRuleInfo, std::move(triggered_rule_info));
-
-    reporting_client_->ReportEventWithTimestampDeprecated(
-        kKeySensitiveDataEvent, std::move(settings.value()), std::move(event),
-        base::Time::Now(),
-        /*include_profile_user_name=*/true);
-  }
 }
 #endif  // BUILDFLAG(ENTERPRISE_DATA_CONTROLS)
 
diff --git a/components/enterprise/connectors/core/reporting_test_utils.cc b/components/enterprise/connectors/core/reporting_test_utils.cc
index 79412d54..a11203a 100644
--- a/components/enterprise/connectors/core/reporting_test_utils.cc
+++ b/components/enterprise/connectors/core/reporting_test_utils.cc
@@ -99,7 +99,8 @@
 
 ::chrome::cros::reporting::proto::TriggeredRuleInfo MakeTriggeredRuleInfo(
     ::chrome::cros::reporting::proto::TriggeredRuleInfo::Action action,
-    bool has_watermark) {
+    bool has_watermark,
+    bool has_screenshot_protection) {
   ::chrome::cros::reporting::proto::TriggeredRuleInfo info;
   info.set_action(action);
   info.set_rule_id(123);
@@ -108,6 +109,9 @@
   if (has_watermark) {
     info.set_has_watermarking(true);
   }
+  if (has_screenshot_protection) {
+    info.set_has_screenshot_protection(true);
+  }
   return info;
 }
 
diff --git a/components/enterprise/connectors/core/reporting_test_utils.h b/components/enterprise/connectors/core/reporting_test_utils.h
index cfca217..f27d6b4 100644
--- a/components/enterprise/connectors/core/reporting_test_utils.h
+++ b/components/enterprise/connectors/core/reporting_test_utils.h
@@ -83,7 +83,8 @@
 // Helper function to create a TriggeredRuleInfo for tests.
 ::chrome::cros::reporting::proto::TriggeredRuleInfo MakeTriggeredRuleInfo(
     ::chrome::cros::reporting::proto::TriggeredRuleInfo::Action action,
-    bool has_watermark);
+    bool has_watermark,
+    bool has_screenshot_protection = false);
 
 // Helper function to create a ReferrerChainEntry referrer for tests.
 safe_browsing::ReferrerChainEntry MakeReferrerChainEntry();
diff --git a/components/enterprise/connectors/core/reporting_utils.cc b/components/enterprise/connectors/core/reporting_utils.cc
index 8325add..5a1c22c 100644
--- a/components/enterprise/connectors/core/reporting_utils.cc
+++ b/components/enterprise/connectors/core/reporting_utils.cc
@@ -87,20 +87,6 @@
   }
 }
 
-std::string ActionFromVerdictType(
-    safe_browsing::RTLookupResponse::ThreatInfo::VerdictType verdict_type) {
-  switch (verdict_type) {
-    case safe_browsing::RTLookupResponse::ThreatInfo::DANGEROUS:
-      return "BLOCK";
-    case safe_browsing::RTLookupResponse::ThreatInfo::WARN:
-      return "WARN";
-    case safe_browsing::RTLookupResponse::ThreatInfo::SAFE:
-      return "REPORT_ONLY";
-    case safe_browsing::RTLookupResponse::ThreatInfo::SUSPICIOUS:
-    case safe_browsing::RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED:
-      return "ACTION_UNKNOWN";
-  }
-}
 
 proto::TriggeredRuleInfo::Action ActionProtoFromVerdictType(
     safe_browsing::RTLookupResponse::ThreatInfo::VerdictType verdict_type) {
@@ -445,39 +431,11 @@
   triggered_rule_info.set_action(ActionProtoFromVerdictType(verdict_type));
   triggered_rule_info.set_has_watermarking(
       navigation_rule.has_watermark_message());
+  triggered_rule_info.set_has_screenshot_protection(
+      navigation_rule.block_screenshot());
   return triggered_rule_info;
 }
 
-void AddTriggeredRuleInfoToUrlFilteringInterstitialEvent(
-    const safe_browsing::RTLookupResponse& response,
-    base::DictValue& event) {
-  base::ListValue triggered_rule_info;
-
-  for (const safe_browsing::RTLookupResponse::ThreatInfo& threat_info :
-       response.threat_info()) {
-    base::DictValue triggered_rule;
-    triggered_rule.Set(kKeyTriggeredRuleName,
-                       threat_info.matched_url_navigation_rule().rule_name());
-    int rule_id = 0;
-    if (base::StringToInt(threat_info.matched_url_navigation_rule().rule_id(),
-                          &rule_id)) {
-      triggered_rule.Set(kKeyTriggeredRuleId, rule_id);
-    }
-    triggered_rule.Set(
-        kKeyUrlCategory,
-        threat_info.matched_url_navigation_rule().matched_url_category());
-    triggered_rule.Set(kKeyAction,
-                       ActionFromVerdictType(threat_info.verdict_type()));
-
-    if (threat_info.matched_url_navigation_rule().has_watermark_message()) {
-      triggered_rule.Set(kKeyHasWatermarking, true);
-    }
-
-    triggered_rule_info.Append(std::move(triggered_rule));
-  }
-  event.Set(kKeyTriggeredRuleInfo, std::move(triggered_rule_info));
-}
-
 std::optional<proto::PasswordBreachEvent> GetPasswordBreachEvent(
     const std::string& trigger,
     const std::vector<std::pair<GURL, std::u16string>>& identities,
@@ -880,34 +838,6 @@
   return ip_addresses;
 }
 
-void AddReferrerChainToEvent(
-    const google::protobuf::RepeatedPtrField<safe_browsing::ReferrerChainEntry>&
-        referrer_chain,
-    base::DictValue& event) {
-  base::ListValue referrers;
-  for (const auto& referrer : referrer_chain) {
-    if (!referrer.url().empty() || !referrer.ip_addresses().empty()) {
-      base::DictValue referrer_dict;
-      referrer_dict.Set("url", referrer.url());
-      if (referrer.ip_addresses().size() > 0) {
-        referrer_dict.Set("ip", referrer.ip_addresses()[0]);
-      }
-      referrers.Append(std::move(referrer_dict));
-    }
-  }
-  event.Set(kKeyReferrers, std::move(referrers));
-}
-
-void AddFrameUrlChainToEvent(
-    const google::protobuf::RepeatedPtrField<std::string>& frame_url_chain,
-    base::DictValue& event) {
-  base::ListValue iframe_urls;
-  for (const auto& frame_url : frame_url_chain) {
-    iframe_urls.Append(frame_url);
-  }
-  event.Set(kKeyIframeUrls, std::move(iframe_urls));
-}
-
 void MaybeTruncateLongUrls(proto::Event& event_variant) {
   switch (event_variant.event_case()) {
     case proto::Event::kPasswordReuseEvent: {
diff --git a/components/enterprise/connectors/core/reporting_utils.h b/components/enterprise/connectors/core/reporting_utils.h
index 51ef3c67..55ec7523 100644
--- a/components/enterprise/connectors/core/reporting_utils.h
+++ b/components/enterprise/connectors/core/reporting_utils.h
@@ -48,12 +48,6 @@
 // Map `threat_type` to `EventResult`.
 EventResult GetEventResultFromThreatType(std::string threat_type);
 
-// Extract triggered rules from `response` and add them to the url filtering
-// events.
-void AddTriggeredRuleInfoToUrlFilteringInterstitialEvent(
-    const safe_browsing::RTLookupResponse& response,
-    base::DictValue& event);
-
 // Create a URLMatcher representing the filters in
 // `settings.enabled_opt_in_events` for `event_type`. This field of the
 // reporting settings connector contains a map where keys are event types and
@@ -203,13 +197,6 @@
 // Returns a list of the local IPv4 and IPv6 addresses of the device.
 std::vector<std::string> GetLocalIpAddresses();
 
-void AddReferrerChainToEvent(const ReferrerChain& referrer_chain,
-                             base::DictValue& event);
-
-void AddFrameUrlChainToEvent(
-    const google::protobuf::RepeatedPtrField<std::string>& frame_url_chain,
-    base::DictValue& event);
-
 void MaybeTruncateLongUrls(
     ::chrome::cros::reporting::proto::Event& event_variant);
 
diff --git a/components/enterprise/connectors/core/reporting_utils_unittest.cc b/components/enterprise/connectors/core/reporting_utils_unittest.cc
index 7a4b6e8e..39034c76 100644
--- a/components/enterprise/connectors/core/reporting_utils_unittest.cc
+++ b/components/enterprise/connectors/core/reporting_utils_unittest.cc
@@ -195,6 +195,7 @@
   matched_url_navigation_rule->set_rule_id("123");
   matched_url_navigation_rule->set_rule_name("test rule name");
   matched_url_navigation_rule->set_matched_url_category("test rule category");
+  matched_url_navigation_rule->set_block_screenshot(true);
 
   auto event = GetUrlFilteringInterstitialEvent(
       /*url=*/GURL("https://filteredurl.com"),
@@ -221,6 +222,7 @@
   ASSERT_EQ(triggered_rule_info.action(),
             chrome::cros::reporting::proto::TriggeredRuleInfo::BLOCK);
   ASSERT_FALSE(triggered_rule_info.has_watermarking());
+  ASSERT_TRUE(triggered_rule_info.has_screenshot_protection());
   ASSERT_EQ(event.profile_identifier(), "identifier");
   ASSERT_EQ(event.profile_user_name(), "profile_username");
   ASSERT_EQ(event.web_app_signed_in_account(), "active_user@example.com");
@@ -546,39 +548,4 @@
   EXPECT_FALSE(IsUrlMatched(url_matcher.get(), GURL("gmail.com")));
 }
 
-TEST(ReportingUtilsTest, TestAddReferrerChainToEvent) {
-  google::protobuf::RepeatedPtrField<safe_browsing::ReferrerChainEntry>
-      referrer_chain;
-  referrer_chain.Add(test::MakeReferrerChainEntry());
-  base::DictValue event;
-  AddReferrerChainToEvent(referrer_chain, event);
-  EXPECT_EQ(event.size(), 1u);
-  EXPECT_EQ(event.FindList(kKeyReferrers)->size(), 1u);
-}
-
-TEST(ReportingUtilsTest, TestEmptyReferrerChainAdded) {
-  google::protobuf::RepeatedPtrField<safe_browsing::ReferrerChainEntry>
-      referrer_chain;
-  base::DictValue event;
-  AddReferrerChainToEvent(referrer_chain, event);
-  EXPECT_EQ(event.size(), 1u);
-  EXPECT_TRUE(event.contains(kKeyReferrers));
-  EXPECT_TRUE(event.FindList(kKeyReferrers)->empty());
-}
-
-TEST(ReportingUtilsTest, TestAddFrameUrlChainToEvent) {
-  google::protobuf::RepeatedPtrField<std::string> frame_url_chain;
-  *frame_url_chain.Add() = "https://frame1.com/";
-  *frame_url_chain.Add() = "https://frame2.com/";
-
-  base::DictValue event;
-  AddFrameUrlChainToEvent(frame_url_chain, event);
-
-  const base::ListValue* iframe_urls = event.FindList(kKeyIframeUrls);
-  ASSERT_TRUE(iframe_urls);
-  ASSERT_EQ(iframe_urls->size(), 2u);
-  EXPECT_EQ((*iframe_urls)[0].GetString(), "https://frame1.com/");
-  EXPECT_EQ((*iframe_urls)[1].GetString(), "https://frame2.com/");
-}
-
 }  // namespace enterprise_connectors
diff --git a/components/enterprise/device_trust/core/BUILD.gn b/components/enterprise/device_trust/core/BUILD.gn
index 4523a16..1b191b7f 100644
--- a/components/enterprise/device_trust/core/BUILD.gn
+++ b/components/enterprise/device_trust/core/BUILD.gn
@@ -46,6 +46,27 @@
   ]
 }
 
+source_set("test_support") {
+  testonly = true
+  configs += [ "//components/enterprise:extra_warnings" ]
+
+  public = [ "fake_device_trust_connector_service.h" ]
+  sources = [ "fake_device_trust_connector_service.cc" ]
+
+  deps = [
+    "//components/enterprise/device_trust",
+    "//components/pref_registry",
+    "//components/prefs:test_support",
+    "//components/sync_preferences:test_support",
+  ]
+
+  public_deps = [
+    ":core",
+    ":device_trust_connector_service",
+    "//base",
+  ]
+}
+
 source_set("unit_tests") {
   testonly = true
 
diff --git a/components/enterprise/device_trust/core/DEPS b/components/enterprise/device_trust/core/DEPS
index 42911ee..8c9a202 100644
--- a/components/enterprise/device_trust/core/DEPS
+++ b/components/enterprise/device_trust/core/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/sync_preferences",
   "+components/url_matcher",
   "-content",
 ]
diff --git a/chrome/browser/enterprise/connectors/device_trust/fake_device_trust_connector_service.cc b/components/enterprise/device_trust/core/fake_device_trust_connector_service.cc
similarity index 92%
rename from chrome/browser/enterprise/connectors/device_trust/fake_device_trust_connector_service.cc
rename to components/enterprise/device_trust/core/fake_device_trust_connector_service.cc
index 480cb11..bf974a36 100644
--- a/chrome/browser/enterprise/connectors/device_trust/fake_device_trust_connector_service.cc
+++ b/components/enterprise/device_trust/core/fake_device_trust_connector_service.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/enterprise/connectors/device_trust/fake_device_trust_connector_service.h"
+#include "components/enterprise/device_trust/core/fake_device_trust_connector_service.h"
 
 #include <string>
 #include <utility>
diff --git a/chrome/browser/enterprise/connectors/device_trust/fake_device_trust_connector_service.h b/components/enterprise/device_trust/core/fake_device_trust_connector_service.h
similarity index 76%
rename from chrome/browser/enterprise/connectors/device_trust/fake_device_trust_connector_service.h
rename to components/enterprise/device_trust/core/fake_device_trust_connector_service.h
index 5141b367..3f81662 100644
--- a/chrome/browser/enterprise/connectors/device_trust/fake_device_trust_connector_service.h
+++ b/components/enterprise/device_trust/core/fake_device_trust_connector_service.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_FAKE_DEVICE_TRUST_CONNECTOR_SERVICE_H_
-#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_FAKE_DEVICE_TRUST_CONNECTOR_SERVICE_H_
+#ifndef COMPONENTS_ENTERPRISE_DEVICE_TRUST_CORE_FAKE_DEVICE_TRUST_CONNECTOR_SERVICE_H_
+#define COMPONENTS_ENTERPRISE_DEVICE_TRUST_CORE_FAKE_DEVICE_TRUST_CONNECTOR_SERVICE_H_
 
 #include "base/memory/raw_ptr.h"
 #include "base/values.h"
@@ -31,4 +31,4 @@
 
 }  // namespace enterprise_connectors
 
-#endif  // CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_FAKE_DEVICE_TRUST_CONNECTOR_SERVICE_H_
+#endif  // COMPONENTS_ENTERPRISE_DEVICE_TRUST_CORE_FAKE_DEVICE_TRUST_CONNECTOR_SERVICE_H_
diff --git a/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/DeviceDelegateTest.java b/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/DeviceDelegateTest.java
index 4cad706..c99a49f 100644
--- a/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/DeviceDelegateTest.java
+++ b/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/DeviceDelegateTest.java
@@ -38,7 +38,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.ui.base.WindowAndroid;
@@ -53,7 +52,6 @@
 
 /** Tests for {@link DeviceDelegate}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @SmallTest
 public class DeviceDelegateTest {
     private static final int A2A_TRANSACTION_OUTCOME_SUCCEED = 1;
diff --git a/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientBridgeUnitTest.java b/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientBridgeUnitTest.java
index 803dbd2..472f93c6 100644
--- a/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientBridgeUnitTest.java
+++ b/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientBridgeUnitTest.java
@@ -21,14 +21,12 @@
 import org.mockito.junit.MockitoRule;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 
 import java.util.Arrays;
 import java.util.List;
 
 /** Tests for the native bridge of the facilitated payment API client. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @SmallTest
 public class FacilitatedPaymentsApiClientBridgeUnitTest {
     private static final long NATIVE_FACILITATED_PAYMENTS_API_CLIENT_ANDROID = 0x12345678;
diff --git a/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientUnitTest.java b/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientUnitTest.java
index 6a43f1b..21396fa 100644
--- a/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientUnitTest.java
+++ b/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientUnitTest.java
@@ -12,7 +12,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.content_public.browser.RenderFrameHost;
 
@@ -20,7 +19,6 @@
 
 /** Tests for the facilitated payment API client. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @SmallTest
 public class FacilitatedPaymentsApiClientUnitTest {
     private static final byte[] TEST_CLIENT_TOKEN = new byte[] {'C', 'l', 'i', 'e', 'n', 't'};
diff --git a/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/SecurePayloadTest.java b/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/SecurePayloadTest.java
index 56fc4b27..5eb3c91d 100644
--- a/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/SecurePayloadTest.java
+++ b/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/SecurePayloadTest.java
@@ -11,7 +11,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 
 import java.util.Arrays;
 import java.util.Collections;
@@ -19,7 +18,6 @@
 
 /** Unit tests for {@link SecurePayload}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @SmallTest
 public class SecurePayloadTest {
     private static final List<SecureData> SECURE_DATA = Arrays.asList(new SecureData(1, "value_1"));
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc
index 7c3defd..265bc26 100644
--- a/components/feature_engagement/public/feature_constants.cc
+++ b/components/feature_engagement/public/feature_constants.cc
@@ -190,6 +190,9 @@
 BASE_FEATURE(kIPHPdfTextAnnotationsFeature,
              "IPH_PdfTextAnnotations",
              base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kIPHPdfTranslateBubbleFeature,
+             "IPH_PdfTranslateBubble",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 BASE_FEATURE(kIPHPerformanceInterventionDialogFeature,
              "IPH_PerformanceInterventionDialogFeature",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h
index 7f9511f..57324fc 100644
--- a/components/feature_engagement/public/feature_constants.h
+++ b/components/feature_engagement/public/feature_constants.h
@@ -116,6 +116,7 @@
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHPdfInkSignaturesFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHPdfSearchifyFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHPdfTextAnnotationsFeature);
+FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHPdfTranslateBubbleFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHPerformanceInterventionDialogFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHPowerBookmarksSidePanelFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHPriceInsightsPageActionIconLabelFeature);
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc
index 5e101092..135cb15 100644
--- a/components/feature_engagement/public/feature_list.cc
+++ b/components/feature_engagement/public/feature_list.cc
@@ -298,6 +298,7 @@
     &kIPHPdfInkSignaturesFeature,
     &kIPHPdfSearchifyFeature,
     &kIPHPdfTextAnnotationsFeature,
+    &kIPHPdfTranslateBubbleFeature,
     &kIPHPerformanceInterventionDialogFeature,
     &kIPHPowerBookmarksSidePanelFeature,
     &kIPHPriceInsightsPageActionIconLabelFeature,
diff --git a/components/heap_profiling/in_process/heap_profiler_controller.cc b/components/heap_profiling/in_process/heap_profiler_controller.cc
index 8194033..b042d98 100644
--- a/components/heap_profiling/in_process/heap_profiler_controller.cc
+++ b/components/heap_profiling/in_process/heap_profiler_controller.cc
@@ -35,7 +35,6 @@
 #include "base/sampling_heap_profiler/lock_free_address_hash_set.h"
 #include "base/sampling_heap_profiler/lock_free_bloom_filter.h"
 #include "base/sampling_heap_profiler/poisson_allocation_sampler.h"
-#include "base/sampling_heap_profiler/sampling_heap_churn_profiler.h"
 #include "base/sampling_heap_profiler/sampling_heap_profiler.h"
 #include "base/sequence_checker.h"
 #include "base/strings/strcat.h"
@@ -287,9 +286,6 @@
 
   if (profiling_session_) {
     base::SamplingHeapProfiler::Get()->Stop(*profiling_session_);
-    if (base::FeatureList::IsEnabled(kHeapProfilerChurnReporting)) {
-      base::SamplingHeapProfiler::Get()->churn_profiler().Stop();
-    }
   }
 
   // BrowserProcessSnapshotController must be deleted on the sequence that its
@@ -329,11 +325,6 @@
   profiling_session_ = base::SamplingHeapProfiler::Get()->Start(
       expected_sampling_rate_,
       base::SamplingHeapProfiler::Priority::kBackground);
-  if (base::FeatureList::IsEnabled(kHeapProfilerChurnReporting)) {
-    auto& churn_profiler = base::SamplingHeapProfiler::Get()->churn_profiler();
-    churn_profiler.SetSubsamplingChance(kHeapChurnSubsamplingChance.Get());
-    churn_profiler.Start();
-  }
 
   if (process_type_ != ProcessType::kBrowser) {
     // ChildProcessSnapshotController will trigger snapshots.
@@ -432,9 +423,6 @@
             if (!stopped->data.IsSet()) {
               // Log metrics about the snapshot, but don't upload it to UMA.
               RetrieveAndLogSnapshot(process_type, expected_sampling_interval);
-              std::ignore = base::SamplingHeapProfiler::Get()
-                                ->churn_profiler()
-                                .TakeSamples();
             }
           },
           process_type_, stopped_, expected_sampling_rate_));
@@ -575,58 +563,14 @@
       base::HashMetricName("process_percent");  // 0xd598e4d0a9e55408
   static const uint64_t kProcessIndexHash =
       base::HashMetricName("process_index");  // 0x28f4372e67b3f8f8
-  static const uint64_t kSamplingIntervalHash =
-      base::HashMetricName("sampling_interval");
 
   profile_builder.AddProfileMetadata(
       base::MetadataRecorder::Item(kProcessPercentHash, std::nullopt,
                                    std::nullopt, process_probability_pct));
   profile_builder.AddProfileMetadata(base::MetadataRecorder::Item(
       kProcessIndexHash, std::nullopt, std::nullopt, process_index));
-  profile_builder.AddProfileMetadata(base::MetadataRecorder::Item(
-      kSamplingIntervalHash, std::nullopt, std::nullopt,
-      expected_sampling_interval.InBytes()));
 
   profile_builder.OnProfileCompleted(base::TimeDelta(), base::TimeDelta());
-
-  std::vector<Sample> churn_samples =
-      base::SamplingHeapProfiler::Get()->churn_profiler().TakeSamples();
-  if (!churn_samples.empty()) {
-    sampling_profiler::CallStackProfileParams churn_params(
-        process_type, sampling_profiler::ProfilerThreadType::kUnknown,
-        sampling_profiler::CallStackProfileParams::Trigger::
-            kPeriodicHeapChurnCollection,
-        time_since_profiler_creation);
-    metrics::CallStackProfileBuilder churn_profile_builder(churn_params);
-
-    SampleMap merged_churn_samples = MergeSamples(churn_samples);
-    for (auto& pair : merged_churn_samples) {
-      const Sample& sample = pair.first;
-      const SampleValue& value = pair.second;
-
-      const size_t stack_size = sample.stack.size();
-      std::vector<base::Frame> frames;
-      frames.reserve(stack_size);
-      for (const void* frame : sample.stack) {
-        const uintptr_t address = reinterpret_cast<const uintptr_t>(frame);
-        const base::ModuleCache::Module* module =
-            module_cache.GetModuleForAddress(address);
-        frames.emplace_back(address, module);
-      }
-      churn_profile_builder.OnSampleCompleted(
-          std::move(frames), base::TimeTicks(), value.total, value.count);
-    }
-    churn_profile_builder.AddProfileMetadata(
-        base::MetadataRecorder::Item(kProcessPercentHash, std::nullopt,
-                                     std::nullopt, process_probability_pct));
-    churn_profile_builder.AddProfileMetadata(base::MetadataRecorder::Item(
-        kProcessIndexHash, std::nullopt, std::nullopt, process_index));
-    churn_profile_builder.AddProfileMetadata(base::MetadataRecorder::Item(
-        kSamplingIntervalHash, std::nullopt, std::nullopt,
-        expected_sampling_interval.InBytes()));
-    churn_profile_builder.OnProfileCompleted(base::TimeDelta(),
-                                             base::TimeDelta());
-  }
 }
 
 }  // namespace heap_profiling
diff --git a/components/heap_profiling/in_process/heap_profiler_controller_unittest.cc b/components/heap_profiling/in_process/heap_profiler_controller_unittest.cc
index 4723763d..b8faf09 100644
--- a/components/heap_profiling/in_process/heap_profiler_controller_unittest.cc
+++ b/components/heap_profiling/in_process/heap_profiler_controller_unittest.cc
@@ -30,7 +30,6 @@
 #include "base/process/launch.h"
 #include "base/process/process.h"
 #include "base/sampling_heap_profiler/poisson_allocation_sampler.h"
-#include "base/sampling_heap_profiler/sampling_heap_churn_profiler.h"
 #include "base/sampling_heap_profiler/sampling_heap_profiler.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/bind_post_task.h"
@@ -202,10 +201,9 @@
     ASSERT_TRUE(profile);
     ASSERT_TRUE(base::OptionalUnwrapTo(
         profile->contents.As<metrics::SampledProfile>(), sampled_profile));
-    EXPECT_TRUE((profile_type == metrics::mojom::ProfileType::kHeap ||
-                 profile_type == metrics::mojom::ProfileType::kHeapChurn) &&
-                sampled_profile.trigger_event() ==
-                    metrics::SampledProfile::PERIODIC_HEAP_COLLECTION);
+    EXPECT_EQ(profile_type == metrics::mojom::ProfileType::kHeap,
+              sampled_profile.trigger_event() ==
+                  metrics::SampledProfile::PERIODIC_HEAP_COLLECTION);
     collector_callback_.Run(start_timestamp, std::move(sampled_profile));
   }
 
@@ -726,9 +724,6 @@
   std::vector<FeatureRefAndParams> enabled_features;
   if (feature_enabled) {
     enabled_features.emplace_back(kHeapProfilerReporting, ToFieldTrialParams());
-    enabled_features.emplace_back(
-        kHeapProfilerChurnReporting,
-        base::FieldTrialParams{{"subsampling-chance", "1.0"}});
   }
   return enabled_features;
 }
@@ -737,7 +732,6 @@
   std::vector<FeatureRef> disabled_features;
   if (!feature_enabled) {
     disabled_features.emplace_back(kHeapProfilerReporting);
-    disabled_features.emplace_back(kHeapProfilerChurnReporting);
   }
   return disabled_features;
 }
@@ -994,7 +988,7 @@
   // The profiler should continue to collect snapshots as long as this memory is
   // allocated. If not the test will time out.
   while (profile_count < kSnapshotsToCollect) {
-    task_env().FastForwardBy(kCollectionInterval.Get());
+    task_env().FastForwardBy(base::Days(1));
   }
 
   // Free all recorded memory so the address list is empty for the next test.
@@ -1029,40 +1023,6 @@
   EXPECT_TRUE(sample_received_);
 }
 
-TEST_P(HeapProfilerControllerTest, HeapChurnProfile) {
-  int heap_profiles_received = 0;
-  int churn_profiles_received = 0;
-  auto check_profile = [&](base::TimeTicks time,
-                           metrics::SampledProfile profile) {
-    if (profile.trigger_event() ==
-        metrics::SampledProfile::PERIODIC_HEAP_COLLECTION) {
-      heap_profiles_received++;
-    } else if (profile.trigger_event() ==
-               metrics::SampledProfile::PERIODIC_HEAP_CHURN_COLLECTION) {
-      churn_profiles_received++;
-    }
-  };
-
-  StartHeapProfiling(version_info::Channel::STABLE,
-                     ProfilerProcessType::kBrowser,
-                     /*expect_enabled=*/true,
-                     /*first_snapshot_callback=*/base::DoNothing(),
-                     base::BindLambdaForTesting(check_profile));
-  base::SamplingHeapProfiler::Get()->churn_profiler().SetSubsamplingChance(1.0);
-
-  auto* sampler = base::PoissonAllocationSampler::Get();
-  void* const kAddress = reinterpret_cast<void*>(0x1234);
-  sampler->OnAllocation(
-      AllocationNotificationData(kAddress, kAllocationSize, nullptr,
-                                 AllocationSubsystem::kManualForTesting));
-  sampler->OnFree(
-      FreeNotificationData(kAddress, AllocationSubsystem::kManualForTesting));
-
-  task_env().FastForwardBy(kCollectionInterval.Get());
-  EXPECT_EQ(heap_profiles_received, 1);
-  EXPECT_EQ(churn_profiles_received, 1);
-}
-
 TEST_P(HeapProfilerControllerTest, SamplingIntervalVariance) {
   ScopedCallbacks callbacks = CreateScopedCallbacks(
       /*expect_take_snapshot=*/true, /*expect_sampled_profile=*/true);
@@ -1565,10 +1525,7 @@
                  GetProfileMetadataFunc("process_index"),
                  // Processes can be sampled in any order, so just check the
                  // range of "process_index".
-                 Optional(AllOf(Ge(0), Lt(sampled_processes)))),
-        ResultOf("sampling_interval metadata",
-                 GetProfileMetadataFunc("sampling_interval"),
-                 Optional(testing::Gt(0))));
+                 Optional(AllOf(Ge(0), Lt(sampled_processes)))));
   };
 
   // GMock matcher that tests that the given SampledProfile is a heap snapshot
diff --git a/components/heap_profiling/in_process/heap_profiler_parameters.cc b/components/heap_profiling/in_process/heap_profiler_parameters.cc
index 3339556..fb16321e 100644
--- a/components/heap_profiling/in_process/heap_profiler_parameters.cc
+++ b/components/heap_profiling/in_process/heap_profiler_parameters.cc
@@ -126,10 +126,6 @@
 }  // namespace
 
 BASE_FEATURE(kHeapProfilerReporting, base::FEATURE_ENABLED_BY_DEFAULT);
-BASE_FEATURE(kHeapProfilerChurnReporting, base::FEATURE_ENABLED_BY_DEFAULT);
-
-const base::FeatureParam<double> kHeapChurnSubsamplingChance{
-    &kHeapProfilerChurnReporting, "subsampling-chance", 0.005};
 
 const base::FeatureParam<double> kStableProbability{
     &kHeapProfilerReporting, "stable-probability", kDefaultStableProbability};
diff --git a/components/heap_profiling/in_process/heap_profiler_parameters.h b/components/heap_profiling/in_process/heap_profiler_parameters.h
index 04867cb..6d7f9d6 100644
--- a/components/heap_profiling/in_process/heap_profiler_parameters.h
+++ b/components/heap_profiling/in_process/heap_profiler_parameters.h
@@ -20,13 +20,6 @@
 // reporting is enabled.
 BASE_DECLARE_FEATURE(kHeapProfilerReporting);
 
-// If this is enabled, the client will collect heap churn profiles (alloc-free
-// churn) in addition to live heap profiles.
-BASE_DECLARE_FEATURE(kHeapProfilerChurnReporting);
-
-// Subsampling probability for heap churn profiling (alloc-free memory).
-extern const base::FeatureParam<double> kHeapChurnSubsamplingChance;
-
 // Chance that this client will report heap samples through a metrics
 // provider if it's on the stable channel.
 extern const base::FeatureParam<double> kStableProbability;
diff --git a/components/metrics/call_stacks/call_stack_profile_builder.cc b/components/metrics/call_stacks/call_stack_profile_builder.cc
index 26ab49f2..09bb466 100644
--- a/components/metrics/call_stacks/call_stack_profile_builder.cc
+++ b/components/metrics/call_stacks/call_stack_profile_builder.cc
@@ -75,17 +75,6 @@
   sampled_profile_.set_thread(ToExecutionContextThread(profile_params.thread));
   sampled_profile_.set_trigger_event(
       ToSampledProfileTriggerEvent(profile_params.trigger));
-  if (profile_params.trigger == sampling_profiler::CallStackProfileParams::
-                                    Trigger::kPeriodicHeapCollection) {
-    profile_type_ = mojom::ProfileType::kHeap;
-  } else if (profile_params.trigger ==
-             sampling_profiler::CallStackProfileParams::Trigger::
-                 kPeriodicHeapChurnCollection) {
-    profile_type_ = mojom::ProfileType::kHeapChurn;
-  } else {
-    profile_type_ = mojom::ProfileType::kCPU;
-  }
-
   if (!profile_params.time_offset.is_zero()) {
     DCHECK(profile_params.time_offset.is_positive());
     CallStackProfile* call_stack_profile =
@@ -323,8 +312,8 @@
     GetBrowserProcessReceiverCallbackInstance().Run(profile_start_time,
                                                     std::move(sampled_profile));
   } else {
-    GetChildCallStackProfileCollector()->Collect(
-        profile_start_time, profile_type_, std::move(sampled_profile));
+    GetChildCallStackProfileCollector()->Collect(profile_start_time,
+                                                 std::move(sampled_profile));
   }
 }
 
diff --git a/components/metrics/call_stacks/call_stack_profile_builder.h b/components/metrics/call_stacks/call_stack_profile_builder.h
index 3c0032d..d2ab145 100644
--- a/components/metrics/call_stacks/call_stack_profile_builder.h
+++ b/components/metrics/call_stacks/call_stack_profile_builder.h
@@ -152,9 +152,6 @@
 
   // Maintains the current metadata to apply to samples.
   CallStackProfileMetadata metadata_;
-
-  // The type of profile being collected.
-  mojom::ProfileType profile_type_ = mojom::ProfileType::kCPU;
 };
 
 }  // namespace metrics
diff --git a/components/metrics/call_stacks/call_stack_profile_collector.cc b/components/metrics/call_stacks/call_stack_profile_collector.cc
index 2c02ab65..60d2d51 100644
--- a/components/metrics/call_stacks/call_stack_profile_collector.cc
+++ b/components/metrics/call_stacks/call_stack_profile_collector.cc
@@ -27,9 +27,7 @@
                                         mojom::ProfileType profile_type,
                                         mojom::SampledProfilePtr profile) {
   CallStackProfileMetricsProvider::ReceiveSerializedProfile(
-      start_timestamp,
-      profile_type == mojom::ProfileType::kHeap ||
-          profile_type == mojom::ProfileType::kHeapChurn,
+      start_timestamp, profile_type == mojom::ProfileType::kHeap,
       std::move(profile));
 }
 
diff --git a/components/metrics/call_stacks/call_stack_profile_encoding.cc b/components/metrics/call_stacks/call_stack_profile_encoding.cc
index a68a961..d1eecac 100644
--- a/components/metrics/call_stacks/call_stack_profile_encoding.cc
+++ b/components/metrics/call_stacks/call_stack_profile_encoding.cc
@@ -75,9 +75,6 @@
     case sampling_profiler::CallStackProfileParams::Trigger::
         kPeriodicHeapCollection:
       return SampledProfile::PERIODIC_HEAP_COLLECTION;
-    case sampling_profiler::CallStackProfileParams::Trigger::
-        kPeriodicHeapChurnCollection:
-      return SampledProfile::PERIODIC_HEAP_CHURN_COLLECTION;
   }
   NOTREACHED();
 }
diff --git a/components/metrics/call_stacks/child_call_stack_profile_collector.cc b/components/metrics/call_stacks/child_call_stack_profile_collector.cc
index b0fca1b..424a9db 100644
--- a/components/metrics/call_stacks/child_call_stack_profile_collector.cc
+++ b/components/metrics/call_stacks/child_call_stack_profile_collector.cc
@@ -65,7 +65,6 @@
 }
 
 void ChildCallStackProfileCollector::Collect(base::TimeTicks start_timestamp,
-                                             mojom::ProfileType profile_type,
                                              SampledProfile profile) {
   base::AutoLock alock(lock_);
   if (task_runner_ &&
@@ -78,7 +77,7 @@
         FROM_HERE, base::BindOnce(&ChildCallStackProfileCollector::Collect,
                                   // This class has lazy instance lifetime.
                                   base::Unretained(this), start_timestamp,
-                                  profile_type, std::move(profile)));
+                                  std::move(profile)));
     return;
   }
 
@@ -90,6 +89,11 @@
   mojom::SampledProfilePtr mojo_profile = mojom::SampledProfile::New();
   mojo_profile->contents = mojo_base::ProtoWrapper(profile);
 
+  const mojom::ProfileType profile_type =
+      profile.trigger_event() == SampledProfile::PERIODIC_HEAP_COLLECTION
+          ? mojom::ProfileType::kHeap
+          : mojom::ProfileType::kCPU;
+
   if (parent_collector_) {
     parent_collector_->Collect(start_timestamp, profile_type,
                                std::move(mojo_profile));
diff --git a/components/metrics/call_stacks/child_call_stack_profile_collector.h b/components/metrics/call_stacks/child_call_stack_profile_collector.h
index dca378e..ee97ea5 100644
--- a/components/metrics/call_stacks/child_call_stack_profile_collector.h
+++ b/components/metrics/call_stacks/child_call_stack_profile_collector.h
@@ -65,9 +65,7 @@
           parent_collector);
 
   // Collects |profile| whose collection start time is |start_timestamp|.
-  void Collect(base::TimeTicks start_timestamp,
-               mojom::ProfileType profile_type,
-               SampledProfile profile);
+  void Collect(base::TimeTicks start_timestamp, SampledProfile profile);
 
  private:
   friend class ChildCallStackProfileCollectorTest;
diff --git a/components/metrics/call_stacks/child_call_stack_profile_collector_unittest.cc b/components/metrics/call_stacks/child_call_stack_profile_collector_unittest.cc
index 2df82711..7746d5d 100644
--- a/components/metrics/call_stacks/child_call_stack_profile_collector_unittest.cc
+++ b/components/metrics/call_stacks/child_call_stack_profile_collector_unittest.cc
@@ -61,13 +61,11 @@
       const ChildCallStackProfileCollectorTest&) = delete;
 
   // Collects a profile and returns its start timestamp.
-  base::TimeTicks CollectProfile(
-      SampledProfile::TriggerEvent trigger_event,
-      mojom::ProfileType profile_type = mojom::ProfileType::kCPU) {
+  base::TimeTicks CollectProfile(SampledProfile::TriggerEvent trigger_event) {
     base::TimeTicks start_timestamp = task_environment_.NowTicks();
     SampledProfile profile;
     profile.set_trigger_event(trigger_event);
-    child_collector_.Collect(start_timestamp, profile_type, std::move(profile));
+    child_collector_.Collect(start_timestamp, std::move(profile));
     return start_timestamp;
   }
 
@@ -158,8 +156,8 @@
   EXPECT_EQ(0u, profiles().size());
 
   // Add a profile before providing the interface.
-  base::TimeTicks start_timestamp = CollectProfile(
-      SampledProfile::PERIODIC_HEAP_COLLECTION, mojom::ProfileType::kHeap);
+  base::TimeTicks start_timestamp =
+      CollectProfile(SampledProfile::PERIODIC_HEAP_COLLECTION);
   ASSERT_EQ(1u, profiles().size());
   ExpectProfile(profiles()[0], start_timestamp, mojom::ProfileType::kHeap);
 
diff --git a/components/metrics/private_metrics/lom_recorder.cc b/components/metrics/private_metrics/lom_recorder.cc
index 2fb38b2..06f4f361 100644
--- a/components/metrics/private_metrics/lom_recorder.cc
+++ b/components/metrics/private_metrics/lom_recorder.cc
@@ -26,7 +26,7 @@
 void LomRecorder::RecordBoolean(PumaType puma_type,
                                 std::string_view name,
                                 bool sample,
-                                std::optional<std::string> profile_name) {
+                                std::optional<uint64_t> profile_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // kRc is currently the only valid PUMA type as all other types are
   // deprecated. Additionally, Private UMA (PUMA) for RC (Regulatory Compliance)
@@ -34,14 +34,14 @@
   if (puma_type != PumaType::kRc) {
     return;
   }
-  RecordSample(name, sample ? 1 : 0, 1, 2, profile_name);
+  RecordSample(name, sample ? 1 : 0, 1, 2, profile_id);
 }
 
 void LomRecorder::RecordExactLinear(PumaType puma_type,
                                     std::string_view name,
                                     int sample,
                                     int exclusive_max,
-                                    std::optional<std::string> profile_name) {
+                                    std::optional<uint64_t> profile_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // kRc is currently the only valid PUMA type as all other types are
   // deprecated.
@@ -50,7 +50,7 @@
   if (puma_type != PumaType::kRc) {
     return;
   }
-  RecordSample(name, sample, 1, exclusive_max, profile_name);
+  RecordSample(name, sample, 1, exclusive_max, profile_id);
 }
 
 std::vector<::private_metrics::ProfileKeyedHistogramEvent>
@@ -61,10 +61,10 @@
       profile_events_map;
 
   for (auto& [_, profile_map] : histograms_) {
-    for (auto& [profile_hash, histogram_event] : profile_map) {
-      auto [it, inserted] = profile_events_map.try_emplace(profile_hash);
+    for (auto& [profile_id, histogram_event] : profile_map) {
+      auto [it, inserted] = profile_events_map.try_emplace(profile_id);
       if (inserted) {
-        it->second.set_profile_id(profile_hash);
+        it->second.set_profile_id(profile_id);
       }
       *it->second.add_histogram_events() = std::move(histogram_event);
     }
@@ -83,15 +83,11 @@
                                int64_t sample,
                                int64_t min,
                                int64_t max,
-                               std::optional<std::string> profile_name) {
+                               std::optional<uint64_t> profile_id) {
   uint64_t histogram_hash = base::HashMetricName(name);
-  uint64_t profile_name_hash = 0;
-  if (profile_name && !profile_name->empty()) {
-    profile_name_hash = base::HashMetricName(*profile_name);
-  }
+  uint64_t profile_id_key = profile_id.value_or(0u);
 
-  auto [it, inserted] =
-      histograms_[histogram_hash].try_emplace(profile_name_hash);
+  auto [it, inserted] = histograms_[histogram_hash].try_emplace(profile_id_key);
   metrics::HistogramEventProto& histogram_event = it->second;
 
   if (inserted) {
diff --git a/components/metrics/private_metrics/lom_recorder.h b/components/metrics/private_metrics/lom_recorder.h
index ba40040a..5186f93 100644
--- a/components/metrics/private_metrics/lom_recorder.h
+++ b/components/metrics/private_metrics/lom_recorder.h
@@ -34,26 +34,25 @@
   static LomRecorder* Get();
 
   // Records a boolean sample.
-  // `profile_name` is the name of the profile to associate the metric with.
-  // There is no difference in behavior between std::nullopt and an empty string
+  // `profile_id` is the ID of the profile to associate the metric with.
+  // There is no difference in behavior between std::nullopt and 0
   // (both associate the metric with the default (global) profile where profile
-  // name hash is 0).
+  // ID is 0).
   void RecordBoolean(PumaType puma_type,
                      std::string_view name,
                      bool sample,
-                     std::optional<std::string> profile_name = std::nullopt);
+                     std::optional<uint64_t> profile_id = std::nullopt);
 
   // Records an exact linear sample.
-  // `profile_name` is the name of the profile to associate the metric with.
-  // There is no difference in behavior between std::nullopt and an empty string
+  // `profile_id` is the ID of the profile to associate the metric with.
+  // There is no difference in behavior between std::nullopt and 0
   // (both associate the metric with the default (global) profile where profile
-  // name hash is 0).
-  void RecordExactLinear(
-      PumaType puma_type,
-      std::string_view name,
-      int sample,
-      int exclusive_max,
-      std::optional<std::string> profile_name = std::nullopt);
+  // ID is 0).
+  void RecordExactLinear(PumaType puma_type,
+                         std::string_view name,
+                         int sample,
+                         int exclusive_max,
+                         std::optional<uint64_t> profile_id = std::nullopt);
 
   // Clears and returns all recorded histogram events grouped by profile.
   std::vector<::private_metrics::ProfileKeyedHistogramEvent>
@@ -64,7 +63,7 @@
                     int64_t sample,
                     int64_t min,
                     int64_t max,
-                    std::optional<std::string> profile_name);
+                    std::optional<uint64_t> profile_id);
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/components/metrics/private_metrics/lom_recorder_unittest.cc b/components/metrics/private_metrics/lom_recorder_unittest.cc
index 1984391..d496cae0 100644
--- a/components/metrics/private_metrics/lom_recorder_unittest.cc
+++ b/components/metrics/private_metrics/lom_recorder_unittest.cc
@@ -27,9 +27,9 @@
 
 const ::private_metrics::ProfileKeyedHistogramEvent* FindProfileEvent(
     const std::vector<::private_metrics::ProfileKeyedHistogramEvent>& events,
-    uint64_t profile_hash) {
+    uint64_t profile_id) {
   for (const auto& event : events) {
-    if (event.profile_id() == profile_hash) {
+    if (event.profile_id() == profile_id) {
       return &event;
     }
   }
@@ -38,9 +38,9 @@
 
 const metrics::HistogramEventProto* FindEvent(
     const std::vector<::private_metrics::ProfileKeyedHistogramEvent>& events,
-    uint64_t profile_hash,
+    uint64_t profile_id,
     uint64_t name_hash) {
-  const auto* profile_event = FindProfileEvent(events, profile_hash);
+  const auto* profile_event = FindProfileEvent(events, profile_id);
   if (!profile_event) {
     return nullptr;
   }
@@ -67,14 +67,13 @@
 
 void ExpectEvent(
     const std::vector<::private_metrics::ProfileKeyedHistogramEvent>& events,
-    uint64_t profile_hash,
+    uint64_t profile_id,
     uint64_t name_hash,
     int64_t expected_sum,
     const std::vector<ExpectedBucket>& expected_buckets) {
-  const auto* event = FindEvent(events, profile_hash, name_hash);
-  ASSERT_NE(event, nullptr)
-      << "Event not found for profile_hash=" << profile_hash
-      << ", name_hash=" << name_hash;
+  const auto* event = FindEvent(events, profile_id, name_hash);
+  ASSERT_NE(event, nullptr) << "Event not found for profile_id=" << profile_id
+                            << ", name_hash=" << name_hash;
   EXPECT_EQ(event->sum(), expected_sum);
   ASSERT_EQ(event->bucket_size(), static_cast<int>(expected_buckets.size()));
   for (const auto& eb : expected_buckets) {
@@ -118,6 +117,8 @@
 };
 
 TEST_F(LomRecorderEnabledTest, ValidateRecorderRecords) {
+  constexpr uint64_t kProfileId1 = 12345u;
+
   GetRecorder()->RecordBoolean(PumaType::kRc, "Test.Boolean", true);
   GetRecorder()->RecordBoolean(PumaType::kRc, "Test.Boolean", false);
   GetRecorder()->RecordExactLinear(PumaType::kRc, "Test.Linear", 2, 10);
@@ -128,7 +129,7 @@
   GetRecorder()->RecordExactLinear(PumaType::kRc, "Test.Linear2", 9, 10000);
   GetRecorder()->RecordExactLinear(PumaType::kRc, "Test.Linear2", 7001, 10000);
   GetRecorder()->RecordBoolean(PumaType::kRc, "Test.ProfileBoolean", true,
-                               "Profile1");
+                               kProfileId1);
 
   // Global helper function should also route to LomRecorder when enabled.
   PumaHistogramBoolean(PumaType::kRc, "Test.GlobalBoolean", true);
@@ -139,17 +140,16 @@
   auto events = GetRecorder()->TakeHistogramEvents();
   ASSERT_EQ(events.size(), 2u);
 
-  ExpectEvent(events, /*profile_hash=*/0u, base::HashMetricName("Test.Boolean"),
+  ExpectEvent(events, /*profile_id=*/0u, base::HashMetricName("Test.Boolean"),
               /*expected_sum=*/1,
               {{std::numeric_limits<int64_t>::min(), 1, 1}, {1, 2, 1}});
-  ExpectEvent(events, /*profile_hash=*/0u, base::HashMetricName("Test.Linear"),
+  ExpectEvent(events, /*profile_id=*/0u, base::HashMetricName("Test.Linear"),
               /*expected_sum=*/12, {{2, 3, 1}, {3, 4, 2}, {4, 5, 1}});
-  ExpectEvent(events, /*profile_hash=*/0u, base::HashMetricName("Test.Linear2"),
+  ExpectEvent(events, /*profile_id=*/0u, base::HashMetricName("Test.Linear2"),
               /*expected_sum=*/7017, {{7, 8, 1}, {9, 10, 1}, {7001, 7002, 1}});
-  ExpectEvent(events, base::HashMetricName("Profile1"),
-              base::HashMetricName("Test.ProfileBoolean"), /*expected_sum=*/1,
-              {{1, 2, 1}});
-  ExpectEvent(events, /*profile_hash=*/0u,
+  ExpectEvent(events, kProfileId1, base::HashMetricName("Test.ProfileBoolean"),
+              /*expected_sum=*/1, {{1, 2, 1}});
+  ExpectEvent(events, /*profile_id=*/0u,
               base::HashMetricName("Test.GlobalBoolean"), /*expected_sum=*/1,
               {{1, 2, 1}});
 
@@ -157,30 +157,33 @@
   EXPECT_TRUE(GetRecorder()->TakeHistogramEvents().empty());
 }
 
-TEST_F(LomRecorderEnabledTest, ValidateProfileHash) {
+TEST_F(LomRecorderEnabledTest, ValidateProfileId) {
+  constexpr uint64_t kProfileIdA = 11111u;
+  constexpr uint64_t kProfileIdB = 22222u;
+
   // Record the same metric name under three different profile configurations.
   GetRecorder()->RecordBoolean(PumaType::kRc, "Test.MultiProfile", true);
   GetRecorder()->RecordBoolean(PumaType::kRc, "Test.MultiProfile", true);
   GetRecorder()->RecordBoolean(PumaType::kRc, "Test.MultiProfile", false,
-                               "ProfileA");
+                               kProfileIdA);
   GetRecorder()->RecordBoolean(PumaType::kRc, "Test.MultiProfile", true,
-                               "ProfileB");
+                               kProfileIdB);
   GetRecorder()->RecordBoolean(PumaType::kRc, "Test.MultiProfile", true,
-                               "ProfileB");
+                               kProfileIdB);
   GetRecorder()->RecordBoolean(PumaType::kRc, "Test.MultiProfile", false,
-                               "ProfileB");
+                               kProfileIdB);
 
   auto events = GetRecorder()->TakeHistogramEvents();
   ASSERT_EQ(events.size(), 3u);
 
   uint64_t metric_hash = base::HashMetricName("Test.MultiProfile");
 
-  ExpectEvent(events, /*profile_hash=*/0u, metric_hash, /*expected_sum=*/2,
+  ExpectEvent(events, /*profile_id=*/0u, metric_hash, /*expected_sum=*/2,
               {{1, 2, 2}});
-  ExpectEvent(events, base::HashMetricName("ProfileA"), metric_hash,
+  ExpectEvent(events, kProfileIdA, metric_hash,
               /*expected_sum=*/0,
               {{std::numeric_limits<int64_t>::min(), 1, 1}});
-  ExpectEvent(events, base::HashMetricName("ProfileB"), metric_hash,
+  ExpectEvent(events, kProfileIdB, metric_hash,
               /*expected_sum=*/2,
               {{std::numeric_limits<int64_t>::min(), 1, 1}, {1, 2, 2}});
 }
diff --git a/components/metrics/private_metrics/puma_histogram_functions.cc b/components/metrics/private_metrics/puma_histogram_functions.cc
index eb45784..f24bbd5 100644
--- a/components/metrics/private_metrics/puma_histogram_functions.cc
+++ b/components/metrics/private_metrics/puma_histogram_functions.cc
@@ -13,10 +13,11 @@
 
 void PumaHistogramBoolean(PumaType puma_type,
                           std::string_view name,
-                          bool sample) {
+                          bool sample,
+                          std::optional<uint64_t> profile_id) {
   if (base::FeatureList::IsEnabled(metrics::private_metrics::kLomFeature)) {
     metrics::private_metrics::LomRecorder::Get()->RecordBoolean(
-        puma_type, name, sample, /*profile_name=*/std::nullopt);
+        puma_type, name, sample, profile_id);
     return;
   }
   base::HistogramBase* histogram = base::BooleanHistogram::FactoryGet(
@@ -27,10 +28,11 @@
 void PumaHistogramExactLinear(PumaType puma_type,
                               std::string_view name,
                               int sample,
-                              int exclusive_max) {
+                              int exclusive_max,
+                              std::optional<uint64_t> profile_id) {
   if (base::FeatureList::IsEnabled(metrics::private_metrics::kLomFeature)) {
     metrics::private_metrics::LomRecorder::Get()->RecordExactLinear(
-        puma_type, name, sample, exclusive_max, /*profile_name=*/std::nullopt);
+        puma_type, name, sample, exclusive_max, profile_id);
     return;
   }
   base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
diff --git a/components/metrics/private_metrics/puma_histogram_functions.h b/components/metrics/private_metrics/puma_histogram_functions.h
index 058f5a0..0fc79e36 100644
--- a/components/metrics/private_metrics/puma_histogram_functions.h
+++ b/components/metrics/private_metrics/puma_histogram_functions.h
@@ -26,6 +26,7 @@
 
 #include <stdint.h>
 
+#include <optional>
 #include <string_view>
 
 #include "base/check_op.h"
@@ -60,24 +61,29 @@
 COMPONENT_EXPORT(PRIVATE_METRICS_RECORDERS)
 void PumaHistogramBoolean(PumaType puma_type,
                           std::string_view name,
-                          bool sample);
+                          bool sample,
+                          std::optional<uint64_t> profile_id = std::nullopt);
 
 // PUMA version of base::UmaHistogramExactLinear().
 // This function should only be called on the main thread. This is checked with
 // sequence checker in `components/metrics/private_metrics/lom_recorder.h`.
 COMPONENT_EXPORT(PRIVATE_METRICS_RECORDERS)
-void PumaHistogramExactLinear(PumaType puma_type,
-                              std::string_view name,
-                              int sample,
-                              int exclusive_max);
+void PumaHistogramExactLinear(
+    PumaType puma_type,
+    std::string_view name,
+    int sample,
+    int exclusive_max,
+    std::optional<uint64_t> profile_id = std::nullopt);
 
 // PUMA version of base::UmaHistogramEnumeration().
 // This function should only be called on the main thread. This is checked with
 // sequence checker in `components/metrics/private_metrics/lom_recorder.h`.
 template <typename T>
-void PumaHistogramEnumeration(PumaType puma_type,
-                              std::string_view name,
-                              T sample) {
+void PumaHistogramEnumeration(
+    PumaType puma_type,
+    std::string_view name,
+    T sample,
+    std::optional<uint64_t> profile_id = std::nullopt) {
   static_assert(std::is_enum_v<T>, "T is not an enum.");
   // This also ensures that an enumeration that doesn't define kMaxValue fails
   // with a semi-useful error ("no member named 'kMaxValue' in ...").
@@ -87,22 +93,25 @@
   DCHECK_LE(static_cast<uintmax_t>(sample),
             static_cast<uintmax_t>(T::kMaxValue));
   return PumaHistogramExactLinear(puma_type, name, static_cast<int>(sample),
-                                  static_cast<int>(T::kMaxValue) + 1);
+                                  static_cast<int>(T::kMaxValue) + 1,
+                                  profile_id);
 }
 
 // PUMA version of base::UmaHistogramEnumeration().
 // This function should only be called on the main thread. This is checked with
 // sequence checker in `components/metrics/private_metrics/lom_recorder.h`.
 template <typename T>
-void PumaHistogramEnumeration(PumaType puma_type,
-                              std::string_view name,
-                              T sample,
-                              T enum_size) {
+void PumaHistogramEnumeration(
+    PumaType puma_type,
+    std::string_view name,
+    T sample,
+    T enum_size,
+    std::optional<uint64_t> profile_id = std::nullopt) {
   static_assert(std::is_enum_v<T>, "T is not an enum.");
   DCHECK_LE(static_cast<uintmax_t>(enum_size), static_cast<uintmax_t>(INT_MAX));
   DCHECK_LT(static_cast<uintmax_t>(sample), static_cast<uintmax_t>(enum_size));
   return PumaHistogramExactLinear(puma_type, name, static_cast<int>(sample),
-                                  static_cast<int>(enum_size));
+                                  static_cast<int>(enum_size), profile_id);
 }
 
 }  // namespace metrics::private_metrics
diff --git a/components/metrics/private_metrics/puma_service_unittest.cc b/components/metrics/private_metrics/puma_service_unittest.cc
index 65f6aff1..dc9a54a 100644
--- a/components/metrics/private_metrics/puma_service_unittest.cc
+++ b/components/metrics/private_metrics/puma_service_unittest.cc
@@ -235,7 +235,7 @@
   EXPECT_TRUE(report->has_rc_profile());
 
   if (IsLomFeatureEnabled()) {
-    EXPECT_EQ(report->profile_keyed_histogram_events_size(), 1);
+    ASSERT_EQ(report->profile_keyed_histogram_events_size(), 1);
     const auto& profile_keyed_event = report->profile_keyed_histogram_events(0);
     EXPECT_EQ(profile_keyed_event.profile_id(), 0u);
     EXPECT_EQ(profile_keyed_event.histogram_events_size(), 1);
@@ -243,7 +243,7 @@
     EXPECT_TRUE(histogram_event.has_name_hash());
     EXPECT_EQ(histogram_event.bucket_size(), 1);
   } else {
-    EXPECT_EQ(report->histogram_events_size(), 1);
+    ASSERT_EQ(report->histogram_events_size(), 1);
     auto histogram_event = report->histogram_events().at(0);
     EXPECT_TRUE(histogram_event.has_name_hash());
     EXPECT_EQ(histogram_event.bucket_size(), 1);
@@ -559,7 +559,7 @@
 
   auto report = puma_service_->BuildPrivateMetricRcReport();
   ASSERT_TRUE(report.has_value());
-  EXPECT_EQ(report->profile_keyed_histogram_events_size(), 1);
+  ASSERT_EQ(report->profile_keyed_histogram_events_size(), 1);
 
   const auto& profile_keyed_event = report->profile_keyed_histogram_events(0);
   EXPECT_EQ(profile_keyed_event.profile_id(), 0u);
@@ -580,28 +580,32 @@
 }
 
 TEST_F(PumaServiceWithLomFeatureTest, BuildReportWithLomFeatureAndProfile) {
+  constexpr uint64_t kProfileId1 = 12345u;
+  constexpr uint64_t kProfileId2 = 67890u;
+
   LomRecorder::Get()->RecordBoolean(PumaType::kRc,
                                     "PUMA.PumaServiceTestHistogram.ProfileBool",
-                                    true, "Profile1");
+                                    true, kProfileId1);
   LomRecorder::Get()->RecordBoolean(PumaType::kRc,
                                     "PUMA.PumaServiceTestHistogram.ProfileBool",
-                                    true, "Profile1");
+                                    true, kProfileId1);
   LomRecorder::Get()->RecordBoolean(PumaType::kRc,
                                     "PUMA.PumaServiceTestHistogram.ProfileBool",
-                                    false, "Profile1");
+                                    false, kProfileId1);
   LomRecorder::Get()->RecordExactLinear(
       PumaType::kRc, "PUMA.PumaServiceTestHistogram.ProfileLinear", 5, 10,
-      "Profile1");
+      kProfileId1);
   LomRecorder::Get()->RecordExactLinear(
       PumaType::kRc, "PUMA.PumaServiceTestHistogram.ProfileLinear", 7, 10,
-      "Profile1");
+      kProfileId1);
 
   auto report = puma_service_->BuildPrivateMetricRcReport();
   ASSERT_TRUE(report.has_value());
-  EXPECT_EQ(report->profile_keyed_histogram_events_size(), 1);
+  ASSERT_EQ(report->profile_keyed_histogram_events_size(), 1);
 
   const auto& profile_keyed_event = report->profile_keyed_histogram_events(0);
-  EXPECT_EQ(profile_keyed_event.profile_id(), base::HashMetricName("Profile1"));
+  const uint64_t profile_id = profile_keyed_event.profile_id();
+  EXPECT_EQ(profile_id, kProfileId1);
   EXPECT_EQ(profile_keyed_event.histogram_events_size(), 2);
 
   ExpectReportEvent(
@@ -616,6 +620,46 @@
 
   // Second build report should return nullopt as events were cleared.
   EXPECT_FALSE(puma_service_->BuildPrivateMetricRcReport().has_value());
+
+  // Record again for Profile1 to verify ID matches.
+  LomRecorder::Get()->RecordBoolean(PumaType::kRc,
+                                    "PUMA.PumaServiceTestHistogram.ProfileBool",
+                                    true, kProfileId1);
+
+  auto second_report = puma_service_->BuildPrivateMetricRcReport();
+  ASSERT_TRUE(second_report.has_value());
+  ASSERT_EQ(second_report->profile_keyed_histogram_events_size(), 1);
+  EXPECT_EQ(second_report->profile_keyed_histogram_events(0).profile_id(),
+            kProfileId1);
+
+  // Third build report should return nullopt as events were cleared.
+  EXPECT_FALSE(puma_service_->BuildPrivateMetricRcReport().has_value());
+
+  // Record for Profile2 to verify profile differentiation.
+  LomRecorder::Get()->RecordBoolean(PumaType::kRc,
+                                    "PUMA.PumaServiceTestHistogram.ProfileBool",
+                                    true, kProfileId2);
+
+  auto third_report = puma_service_->BuildPrivateMetricRcReport();
+  ASSERT_TRUE(third_report.has_value());
+  ASSERT_EQ(third_report->profile_keyed_histogram_events_size(), 1);
+
+  const uint64_t profile2_id =
+      third_report->profile_keyed_histogram_events(0).profile_id();
+  EXPECT_EQ(profile2_id, kProfileId2);
+  EXPECT_NE(profile2_id, profile_id);
+
+  // Fourth build report should return nullopt as events were cleared.
+  EXPECT_FALSE(puma_service_->BuildPrivateMetricRcReport().has_value());
+
+  // Record without a profile ID (global profile).
+  LomRecorder::Get()->RecordBoolean(
+      PumaType::kRc, "PUMA.PumaServiceTestHistogram.GlobalBool", true);
+
+  auto fourth_report = puma_service_->BuildPrivateMetricRcReport();
+  ASSERT_TRUE(fourth_report.has_value());
+  ASSERT_EQ(fourth_report->profile_keyed_histogram_events_size(), 1);
+  EXPECT_EQ(fourth_report->profile_keyed_histogram_events(0).profile_id(), 0u);
 }
 
 }  // namespace metrics::private_metrics
diff --git a/components/metrics/profile_metrics_service.cc b/components/metrics/profile_metrics_service.cc
index eaa8c9eb..e889186 100644
--- a/components/metrics/profile_metrics_service.cc
+++ b/components/metrics/profile_metrics_service.cc
@@ -112,10 +112,10 @@
     private_metrics::PumaType puma_type,
     std::string_view name,
     bool sample,
-    std::optional<std::string> profile_name) const {
+    std::optional<uint64_t> profile_id) const {
   if (base::FeatureList::IsEnabled(metrics::private_metrics::kLomFeature)) {
     metrics::private_metrics::LomRecorder::Get()->RecordBoolean(
-        puma_type, name, sample, /*profile_name=*/profile_name);
+        puma_type, name, sample, profile_id);
   }
 }
 
@@ -124,10 +124,10 @@
     std::string_view name,
     int sample,
     int exclusive_max,
-    std::optional<std::string> profile_name) const {
+    std::optional<uint64_t> profile_id) const {
   if (base::FeatureList::IsEnabled(metrics::private_metrics::kLomFeature)) {
     metrics::private_metrics::LomRecorder::Get()->RecordExactLinear(
-        puma_type, name, sample, exclusive_max, /*profile_name=*/profile_name);
+        puma_type, name, sample, exclusive_max, profile_id);
   }
 }
 
diff --git a/components/metrics/profile_metrics_service.h b/components/metrics/profile_metrics_service.h
index 61278bf..bd78346 100644
--- a/components/metrics/profile_metrics_service.h
+++ b/components/metrics/profile_metrics_service.h
@@ -81,14 +81,14 @@
       private_metrics::PumaType puma_type,
       std::string_view name,
       bool sample,
-      std::optional<std::string> profile_name = std::nullopt) const;
+      std::optional<uint64_t> profile_id = std::nullopt) const;
 
   void PumaHistogramExactLinear(
       private_metrics::PumaType puma_type,
       std::string_view name,
       int sample,
       int exclusive_max,
-      std::optional<std::string> profile_name = std::nullopt) const;
+      std::optional<uint64_t> profile_id = std::nullopt) const;
 
  private:
   const ProfileMetricsContext profile_metrics_context_;
diff --git a/components/metrics/public/mojom/call_stack_profile_collector.mojom b/components/metrics/public/mojom/call_stack_profile_collector.mojom
index 2d81251c..ac9692d 100644
--- a/components/metrics/public/mojom/call_stack_profile_collector.mojom
+++ b/components/metrics/public/mojom/call_stack_profile_collector.mojom
@@ -20,7 +20,6 @@
 enum ProfileType {
   kCPU,
   kHeap,
-  kHeapChurn,
 };
 
 // Passes SampledProfile reports from a sampling profiler running in a child
@@ -33,10 +32,9 @@
   // `profile_type` is the type of sampled data in the report.
   //
   // Note that the `trigger_event` field of the SampledProfile proto should be
-  // PERIODIC_HEAP_COLLECTION iff `profile_type` is kHeap, or
-  // PERIODIC_HEAP_CHURN_COLLECTION iff `profile_type` is kHeapChurn. But the
-  // profile comes from an untrusted process so the implementation must not
-  // depend on this.
+  // PERIODIC_HEAP_COLLECTION iff `profile_type` is kHeap. But the profile
+  // comes from an untrusted process so the implementation must not depend on
+  // this.
   Collect(mojo_base.mojom.TimeTicks start_timestamp,
           ProfileType profile_type,
           SampledProfile profile);
diff --git a/components/omnibox/browser/fusebox_action.mojom b/components/omnibox/browser/fusebox_action.mojom
index 9c32f3e9..508a5e79 100644
--- a/components/omnibox/browser/fusebox_action.mojom
+++ b/components/omnibox/browser/fusebox_action.mojom
@@ -15,10 +15,22 @@
 };
 // LINT.ThenChange(//third_party/omnibox_proto/suggest_template_info.proto:QueryActionOverride)
 
+// LINT.IfChange(InputSource)
+enum InputSource {
+  kInputSourceUnspecified = 0,
+  kInputSourceGallery = 1,
+  kInputSourceCamera = 2,
+  kInputSourceFilePicker = 3,
+  kInputSourceDrive = 4,
+};
+// LINT.ThenChange(//third_party/omnibox_proto/input_source.proto:InputSource)
+
 // TODO(crbug.com/539651790): Add more fields to FuseboxAction.
 struct FuseboxAction {
   composebox_query.mojom.ToolMode? preselected_tool;
   searchbox.mojom.SuggestInventory? preferred_inventory;
   composebox_query.mojom.ModelMode? preselected_model;
   QueryActionOverride? query_action_override;
+  // The source that should be opened, if available.
+  InputSource? preselected_input_source;
 };
diff --git a/components/omnibox/browser/omnibox_client.h b/components/omnibox/browser/omnibox_client.h
index e0b8567..27e073c 100644
--- a/components/omnibox/browser/omnibox_client.h
+++ b/components/omnibox/browser/omnibox_client.h
@@ -68,6 +68,13 @@
 
     // Don't search at all.
     kCancel,
+
+    // No dialog was ever shown. Confirmation turned out to be unnecessary.
+    // The pending navigation must proceed as originally requested. This is
+    // distinct from kCancel: the user made no choice because they were never
+    // asked. Conflating the two silently drops the navigation
+    // (See https://crbug.com/540532980).
+    kNoDialogShown,
   };
 
   OmniboxClient() = default;
diff --git a/components/omnibox/browser/suggest_inventory_fallback_prompts.h b/components/omnibox/browser/suggest_inventory_fallback_prompts.h
index 1705d46..9762e848 100644
--- a/components/omnibox/browser/suggest_inventory_fallback_prompts.h
+++ b/components/omnibox/browser/suggest_inventory_fallback_prompts.h
@@ -11,57 +11,90 @@
 
 namespace omnibox {
 
-inline constexpr auto kBrainstormPrompts = std::to_array<int>({
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_1,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_2,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_3,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_4,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_5,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_6,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_7,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_8,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_9,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_10,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_11,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_12,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_13,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_14,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_15,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_16,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_17,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_18,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_19,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_20,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_21,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_22,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_23,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_24,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_25,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_26,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_27,
-    IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_28,
-});
+struct SuggestFallbackPrompt {
+  int short_prompt_id;
+  int long_prompt_id;
+};
 
-inline constexpr auto kHelpMeLearnPrompts = std::to_array<int>({
-    IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_1,
-    IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_2,
-    IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_3,
-    IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_4,
-    IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_5,
-    IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_6,
-    IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_7,
-});
+inline constexpr auto kBrainstormPrompts =
+    std::to_array<SuggestFallbackPrompt>({
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_1,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_3},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_2,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_5},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_3,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_6},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_4,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_7},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_5,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_8},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_6,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_9},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_7,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_11},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_8,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_12},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_9,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_13},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_10,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_15},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_11,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_17},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_12,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_18},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_13,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_19},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_14,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_23},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_15,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_24},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_16,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_25},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_17,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_26},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_18,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_27},
+        {IDS_NTP_ACTION_CHIP_BRAINSTORM_SHORT_PROMPT_19,
+         IDS_NTP_ACTION_CHIP_BRAINSTORM_PROMPT_28},
+    });
 
-inline constexpr auto kWriteOrEditPrompts = std::to_array<int>({
-    IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_1,
-    IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_2,
-    IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_3,
-    IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_4,
-    IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_5,
-    IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_6,
-    IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_7,
-    IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_8,
-});
+inline constexpr auto kHelpMeLearnPrompts =
+    std::to_array<SuggestFallbackPrompt>({
+        {IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_SHORT_PROMPT_1,
+         IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_1},
+        {IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_SHORT_PROMPT_2,
+         IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_2},
+        {IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_SHORT_PROMPT_3,
+         IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_3},
+        {IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_SHORT_PROMPT_4,
+         IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_4},
+        {IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_SHORT_PROMPT_5,
+         IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_5},
+        {IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_SHORT_PROMPT_6,
+         IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_6},
+        {IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_SHORT_PROMPT_7,
+         IDS_NTP_ACTION_CHIP_HELP_ME_LEARN_PROMPT_7},
+    });
+
+inline constexpr auto kWriteOrEditPrompts =
+    std::to_array<SuggestFallbackPrompt>({
+        {IDS_NTP_ACTION_CHIP_WRITE_EDIT_SHORT_PROMPT_1,
+         IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_1},
+        {IDS_NTP_ACTION_CHIP_WRITE_EDIT_SHORT_PROMPT_2,
+         IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_2},
+        {IDS_NTP_ACTION_CHIP_WRITE_EDIT_SHORT_PROMPT_3,
+         IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_3},
+        {IDS_NTP_ACTION_CHIP_WRITE_EDIT_SHORT_PROMPT_4,
+         IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_4},
+        {IDS_NTP_ACTION_CHIP_WRITE_EDIT_SHORT_PROMPT_5,
+         IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_5},
+        {IDS_NTP_ACTION_CHIP_WRITE_EDIT_SHORT_PROMPT_6,
+         IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_6},
+        {IDS_NTP_ACTION_CHIP_WRITE_EDIT_SHORT_PROMPT_7,
+         IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_7},
+        {IDS_NTP_ACTION_CHIP_WRITE_EDIT_SHORT_PROMPT_8,
+         IDS_NTP_ACTION_CHIP_WRITE_EDIT_PROMPT_8},
+    });
 
 }  // namespace omnibox
 
diff --git a/components/omnibox/browser/suggest_inventory_fallback_utils.cc b/components/omnibox/browser/suggest_inventory_fallback_utils.cc
index 76c80a73..133fb41 100644
--- a/components/omnibox/browser/suggest_inventory_fallback_utils.cc
+++ b/components/omnibox/browser/suggest_inventory_fallback_utils.cc
@@ -24,10 +24,10 @@
 
 namespace omnibox {
 
-std::vector<std::u16string> GetFallbackPromptsForSuggestInventory(
-    SuggestInventory inventory,
-    size_t num_suggestions) {
-  std::vector<int> resource_ids;
+std::vector<std::pair<std::u16string, std::u16string>>
+GetFallbackPromptsForSuggestInventory(SuggestInventory inventory,
+                                      size_t num_suggestions) {
+  std::vector<SuggestFallbackPrompt> resource_ids;
   switch (inventory) {
     case SuggestInventory::SUGGEST_INVENTORY_BRAINSTORM: {
       resource_ids.assign(kBrainstormPrompts.begin(), kBrainstormPrompts.end());
@@ -49,10 +49,12 @@
 
   base::RandomShuffle(resource_ids.begin(), resource_ids.end());
 
-  std::vector<std::u16string> prompts;
+  std::vector<std::pair<std::u16string, std::u16string>> prompts;
   size_t count = std::min(num_suggestions, resource_ids.size());
   for (size_t i = 0; i < count; ++i) {
-    prompts.push_back(l10n_util::GetStringUTF16(resource_ids[i]));
+    prompts.emplace_back(
+        l10n_util::GetStringUTF16(resource_ids[i].short_prompt_id),
+        l10n_util::GetStringUTF16(resource_ids[i].long_prompt_id));
   }
   return prompts;
 }
@@ -62,8 +64,9 @@
     AutocompleteProviderClient* client,
     const AutocompleteInput& input,
     size_t num_suggestions) {
-  std::vector<std::u16string> prompts = GetFallbackPromptsForSuggestInventory(
-      input.suggest_inventory(), num_suggestions);
+  std::vector<std::pair<std::u16string, std::u16string>> prompts =
+      GetFallbackPromptsForSuggestInventory(input.suggest_inventory(),
+                                            num_suggestions);
   if (prompts.empty()) {
     return {};
   }
@@ -85,13 +88,13 @@
                             AutocompleteMatchType::SEARCH_SUGGEST);
     match.keyword = template_url->keyword();
     match.suggestion_group_id = omnibox::GROUP_AI_MODE_ZERO_SUGGEST_CANNED;
-    match.contents = prompt;
+    match.contents = prompt.first;
     match.contents_class = ClassifyTermMatches({}, match.contents.length(),
                                                ACMatchClassification::NONE,
                                                ACMatchClassification::NONE);
-    match.fill_into_edit = prompt;
+    match.fill_into_edit = prompt.second;
     match.search_terms_args =
-        std::make_unique<TemplateURLRef::SearchTermsArgs>(prompt);
+        std::make_unique<TemplateURLRef::SearchTermsArgs>(prompt.second);
     match.search_terms_args->request_source = input.request_source();
     match.search_terms_args->page_classification =
         input.current_page_classification();
diff --git a/components/omnibox/browser/suggest_inventory_fallback_utils.h b/components/omnibox/browser/suggest_inventory_fallback_utils.h
index a4af39d3..bed9a1d 100644
--- a/components/omnibox/browser/suggest_inventory_fallback_utils.h
+++ b/components/omnibox/browser/suggest_inventory_fallback_utils.h
@@ -24,7 +24,8 @@
 // the specified `inventory` and `{} if no fallback prompts exist. Suggest
 // inventory uses the `azi` param and is supplied by the server for currently
 // for NTP Action Chips.
-std::vector<std::u16string> GetFallbackPromptsForSuggestInventory(
+std::vector<std::pair<std::u16string, std::u16string>>
+GetFallbackPromptsForSuggestInventory(
     SuggestInventory inventory,
     size_t num_suggestions = kDefaultFallbackNumSuggestions);
 
diff --git a/components/omnibox/browser/suggest_inventory_fallback_utils_unittest.cc b/components/omnibox/browser/suggest_inventory_fallback_utils_unittest.cc
index 07daff86..974c630 100644
--- a/components/omnibox/browser/suggest_inventory_fallback_utils_unittest.cc
+++ b/components/omnibox/browser/suggest_inventory_fallback_utils_unittest.cc
@@ -44,7 +44,8 @@
       SuggestInventory::SUGGEST_INVENTORY_BRAINSTORM, /*num_suggestions=*/3);
   EXPECT_EQ(prompts.size(), 3u);
   for (const auto& prompt : prompts) {
-    EXPECT_FALSE(prompt.empty());
+    EXPECT_FALSE(prompt.first.empty());
+    EXPECT_FALSE(prompt.second.empty());
   }
 }
 
@@ -55,7 +56,8 @@
       /*num_suggestions=*/3);
   EXPECT_EQ(prompts.size(), 3u);
   for (const auto& prompt : prompts) {
-    EXPECT_FALSE(prompt.empty());
+    EXPECT_FALSE(prompt.first.empty());
+    EXPECT_FALSE(prompt.second.empty());
   }
 }
 
@@ -66,7 +68,8 @@
       /*num_suggestions=*/3);
   EXPECT_EQ(prompts.size(), 3u);
   for (const auto& prompt : prompts) {
-    EXPECT_FALSE(prompt.empty());
+    EXPECT_FALSE(prompt.first.empty());
+    EXPECT_FALSE(prompt.second.empty());
   }
 }
 
@@ -108,9 +111,10 @@
                   ->GetDefaultSearchProvider()
                   ->keyword());
     EXPECT_FALSE(match.contents.empty());
-    EXPECT_EQ(match.fill_into_edit, match.contents);
+    EXPECT_FALSE(match.fill_into_edit.empty());
+    EXPECT_NE(match.fill_into_edit, match.contents);
     ASSERT_TRUE(match.search_terms_args);
-    EXPECT_EQ(match.search_terms_args->search_terms, match.contents);
+    EXPECT_EQ(match.search_terms_args->search_terms, match.fill_into_edit);
     EXPECT_EQ(match.search_terms_args->page_classification,
               OEP::NTP_COMPOSEBOX);
     EXPECT_TRUE(match.destination_url.is_valid());
diff --git a/components/omnibox/browser/test_omnibox_client.h b/components/omnibox/browser/test_omnibox_client.h
index 11ca9f1..642364d 100644
--- a/components/omnibox/browser/test_omnibox_client.h
+++ b/components/omnibox/browser/test_omnibox_client.h
@@ -105,6 +105,11 @@
               (),
               (const, override));
   MOCK_METHOD(GURL, GetContextualTasksInnerFrameURL, (), (const, override));
+  MOCK_METHOD(bool,
+              ShowConfirmationDialogIfDefaultSearchExtensionControlled,
+              (const GURL&,
+               base::OnceCallback<void(ExtensionControlledDialogResult)>),
+              (override));
 
   base::WeakPtr<OmniboxClient> AsWeakPtr() override;
 
diff --git a/components/omnibox_strings.grdp b/components/omnibox_strings.grdp
index dd9435f..15bdadc 100644
--- a/components/omnibox_strings.grdp
+++ b/components/omnibox_strings.grdp
@@ -142,6 +142,12 @@
       <message name="IDS_OMNIBOX_NAVATTACH_FILES" desc="The text displayed in the omnibox attachments popup. When clicked - opens file picker." formatter_data="android_java">
         Files
       </message>
+      <message name="IDS_OMNIBOX_NAVATTACH_ADD_IMAGES" desc="The text displayed in the omnibox attachments popup. When clicked - opens image gallery." formatter_data="android_java">
+        Add images
+      </message>
+      <message name="IDS_OMNIBOX_NAVATTACH_ADD_FILES" desc="The text displayed in the omnibox attachments popup. When clicked - opens file picker." formatter_data="android_java">
+        Add files
+      </message>
       <message name="IDS_OMNIBOX_NAVATTACH_TABS" desc="The text displayed in the omnibox attachments popup. When clicked - opens tabs picker." formatter_data="android_java">
         Tabs
       </message>
diff --git a/components/omnibox_strings_grdp/IDS_OMNIBOX_NAVATTACH_ADD_FILES.png.sha1 b/components/omnibox_strings_grdp/IDS_OMNIBOX_NAVATTACH_ADD_FILES.png.sha1
new file mode 100644
index 0000000..077ff723
--- /dev/null
+++ b/components/omnibox_strings_grdp/IDS_OMNIBOX_NAVATTACH_ADD_FILES.png.sha1
@@ -0,0 +1 @@
+92c4065107589c8dbb14e3a897672a75734f9fda
\ No newline at end of file
diff --git a/components/omnibox_strings_grdp/IDS_OMNIBOX_NAVATTACH_ADD_IMAGES.png.sha1 b/components/omnibox_strings_grdp/IDS_OMNIBOX_NAVATTACH_ADD_IMAGES.png.sha1
new file mode 100644
index 0000000..077ff723
--- /dev/null
+++ b/components/omnibox_strings_grdp/IDS_OMNIBOX_NAVATTACH_ADD_IMAGES.png.sha1
@@ -0,0 +1 @@
+92c4065107589c8dbb14e3a897672a75734f9fda
\ No newline at end of file
diff --git a/components/on_device_translation/service/service_launcher.cc b/components/on_device_translation/service/service_launcher.cc
index fa431e9b..22eb3f0 100644
--- a/components/on_device_translation/service/service_launcher.cc
+++ b/components/on_device_translation/service/service_launcher.cc
@@ -76,12 +76,16 @@
       std::string_view service_display_name_suffix,
       OnDeviceTranslationInstaller* installer) override {
     mojo::PendingRemote<mojom::OnDeviceTranslationService> remote;
-    auto receiver = remote.InitWithNewPipeAndPassReceiver();
 
     CHECK(installer);
     const base::FilePath binary_path = installer->GetLibraryPath();
-    CHECK(!binary_path.empty())
-        << "Got an empty path to TranslateKit binary on the device.";
+    if (binary_path.empty() || !binary_path.IsAbsolute()) {
+      DLOG(ERROR) << "TranslateKit binary path is empty or relative: "
+                  << binary_path;
+      return remote;
+    }
+
+    auto receiver = remote.InitWithNewPipeAndPassReceiver();
 
     std::vector<std::string> extra_switches;
     extra_switches.push_back(
diff --git a/components/on_device_translation/service_controller.cc b/components/on_device_translation/service_controller.cc
index 1f5eeee..ca956de 100644
--- a/components/on_device_translation/service_controller.cc
+++ b/components/on_device_translation/service_controller.cc
@@ -402,8 +402,13 @@
     return true;
   }
 
-  service_remote_.Bind(
-      launcher_->Launch(service_display_name_suffix_, installer_));
+  mojo::PendingRemote<mojom::OnDeviceTranslationService> pending_remote =
+      launcher_->Launch(service_display_name_suffix_, installer_);
+  if (!pending_remote.is_valid()) {
+    return false;
+  }
+
+  service_remote_.Bind(std::move(pending_remote));
   service_remote_.reset_on_disconnect();
   service_remote_.set_idle_handler(
       service_idle_timeout_,
diff --git a/components/optimization_guide/core/delivery/prediction_manager.cc b/components/optimization_guide/core/delivery/prediction_manager.cc
index 0c51528..d05b4ff3 100644
--- a/components/optimization_guide/core/delivery/prediction_manager.cc
+++ b/components/optimization_guide/core/delivery/prediction_manager.cc
@@ -190,9 +190,11 @@
   // initialization flow since the model engine version needs to continuously be
   // updated for the fetch.
   proto::ModelInfo base_model_info;
+  // LINT.IfChange(TfliteEngineVersion)
   // There should only be one supported model engine version at a time.
   base_model_info.add_supported_model_engine_versions(
       proto::MODEL_ENGINE_VERSION_TFLITE_2_22_0);
+  // LINT.ThenChange(//components/component_updater/installer_policies/prediction_model_component_installer.cc:TfliteEngineVersion)
   // This histogram is used for integration tests. Do not remove.
   // Update this to be 10000 if/when we exceed 100 model engine versions.
   LOCAL_HISTOGRAM_COUNTS_100(
diff --git a/components/policy/core/browser/BUILD.gn b/components/policy/core/browser/BUILD.gn
index 454adab..00544e7 100644
--- a/components/policy/core/browser/BUILD.gn
+++ b/components/policy/core/browser/BUILD.gn
@@ -207,6 +207,7 @@
     "url_list/url_blocklist_policy_handler_unittest.cc",
     "url_list/url_scheme_list_policy_handler_unittest.cc",
     "webui/json_generation_unittest.cc",
+    "webui/policy_status_provider_unittest.cc",
     "webui/statistics_collector_unittest.cc",
   ]
   deps = [
diff --git a/components/policy/core/browser/webui/policy_status_provider_unittest.cc b/components/policy/core/browser/webui/policy_status_provider_unittest.cc
new file mode 100644
index 0000000..c9231e8
--- /dev/null
+++ b/components/policy/core/browser/webui/policy_status_provider_unittest.cc
@@ -0,0 +1,142 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/browser/webui/policy_status_provider.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/values.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+#include "components/policy/resources/webui/mojom/policy.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+namespace {
+
+constexpr char kTestClientId[] = "test-client-id";
+constexpr char kTestUsername[] = "user@example.com";
+constexpr char kTestAssetId[] = "asset-123";
+constexpr char kTestLocation[] = "location-xyz";
+constexpr char kTestDirectoryApiId[] = "dir-api-456";
+constexpr char kTestGaiaId[] = "gaia-789";
+constexpr char kTestStatus[] = "OK";
+
+class MockPolicyStatusObserver : public PolicyStatusProvider::Observer {
+ public:
+  MOCK_METHOD(void, OnPolicyStatusChanged, (), (override));
+};
+
+class TestPolicyStatusProvider : public PolicyStatusProvider {
+ public:
+  TestPolicyStatusProvider() = default;
+  ~TestPolicyStatusProvider() override = default;
+
+  void TriggerNotifyStatusChange() { NotifyStatusChange(); }
+
+  using PolicyStatusProvider::DictStatusToMojo;
+};
+
+TEST(PolicyStatusProviderTest, DefaultStatus) {
+  PolicyStatusProvider provider;
+  EXPECT_TRUE(provider.GetStatus().empty());
+
+  policy::mojom::StatusPtr status_mojo = provider.GetStatusMojo();
+  ASSERT_FALSE(status_mojo.is_null());
+  EXPECT_TRUE(status_mojo->client_id.empty());
+}
+
+TEST(PolicyStatusProviderTest, ObserverNotification) {
+  TestPolicyStatusProvider provider;
+  testing::StrictMock<MockPolicyStatusObserver> observer;
+
+  provider.AddObserver(&observer);
+  EXPECT_CALL(observer, OnPolicyStatusChanged()).Times(1);
+  provider.TriggerNotifyStatusChange();
+  testing::Mock::VerifyAndClearExpectations(&observer);
+
+  provider.RemoveObserver(&observer);
+  EXPECT_CALL(observer, OnPolicyStatusChanged()).Times(0);
+  provider.TriggerNotifyStatusChange();
+}
+
+TEST(PolicyStatusProviderTest, GetStatusFromPolicyDataNull) {
+  base::DictValue status =
+      PolicyStatusProvider::GetStatusFromPolicyData(nullptr);
+  const std::string* client_id = status.FindString(kClientIdKey);
+  ASSERT_TRUE(client_id);
+  EXPECT_EQ(*client_id, std::string());
+
+  const std::string* username = status.FindString(kUsernameKey);
+  ASSERT_TRUE(username);
+  EXPECT_EQ(*username, std::string());
+}
+
+TEST(PolicyStatusProviderTest, GetStatusFromPolicyDataPopulated) {
+  enterprise_management::PolicyData policy_data;
+  policy_data.set_device_id(kTestClientId);
+  policy_data.set_username(kTestUsername);
+  policy_data.set_annotated_asset_id(kTestAssetId);
+  policy_data.set_annotated_location(kTestLocation);
+  policy_data.set_directory_api_id(kTestDirectoryApiId);
+  policy_data.set_gaia_id(kTestGaiaId);
+
+  base::DictValue status =
+      PolicyStatusProvider::GetStatusFromPolicyData(&policy_data);
+
+  EXPECT_EQ(*status.FindString(kClientIdKey), kTestClientId);
+  EXPECT_EQ(*status.FindString(kUsernameKey), kTestUsername);
+  EXPECT_EQ(*status.FindString(kAssetIdKey), kTestAssetId);
+  EXPECT_EQ(*status.FindString(kLocationKey), kTestLocation);
+  EXPECT_EQ(*status.FindString(kDirectoryApiIdKey), kTestDirectoryApiId);
+  EXPECT_EQ(*status.FindString(kGaiaIdKey), kTestGaiaId);
+}
+
+TEST(PolicyStatusProviderTest, PopulateStatusFromPolicyDataNull) {
+  auto status = policy::mojom::Status::New();
+  PolicyStatusProvider::PopulateStatusFromPolicyData(nullptr, status);
+  EXPECT_EQ(status->client_id, std::string());
+  EXPECT_EQ(status->username, std::string());
+}
+
+TEST(PolicyStatusProviderTest, PopulateStatusFromPolicyDataPopulated) {
+  enterprise_management::PolicyData policy_data;
+  policy_data.set_device_id(kTestClientId);
+  policy_data.set_username(kTestUsername);
+  policy_data.set_annotated_asset_id(kTestAssetId);
+  policy_data.set_annotated_location(kTestLocation);
+  policy_data.set_directory_api_id(kTestDirectoryApiId);
+  policy_data.set_gaia_id(kTestGaiaId);
+
+  auto status = policy::mojom::Status::New();
+  PolicyStatusProvider::PopulateStatusFromPolicyData(&policy_data, status);
+
+  EXPECT_EQ(status->client_id, kTestClientId);
+  EXPECT_EQ(status->username, kTestUsername);
+  EXPECT_EQ(status->asset_id, kTestAssetId);
+  EXPECT_EQ(status->location, kTestLocation);
+  EXPECT_EQ(status->directory_api_id, kTestDirectoryApiId);
+  EXPECT_EQ(status->gaia_id, kTestGaiaId);
+}
+
+TEST(PolicyStatusProviderTest, DictStatusToMojo) {
+  base::DictValue dict;
+  dict.Set(kClientIdKey, kTestClientId);
+  dict.Set("status", kTestStatus);
+  dict.Set("error", false);
+  dict.Set("policiesPushAvailable", true);
+
+  policy::mojom::StatusPtr status =
+      TestPolicyStatusProvider::DictStatusToMojo(dict);
+
+  EXPECT_EQ(status->client_id, kTestClientId);
+  EXPECT_EQ(status->status, kTestStatus);
+  EXPECT_FALSE(status->error);
+  EXPECT_TRUE(status->policies_push_available);
+}
+
+}  // namespace
+}  // namespace policy
diff --git a/components/policy/core/common/features.cc b/components/policy/core/common/features.cc
index 93c486d..9a468b05 100644
--- a/components/policy/core/common/features.cc
+++ b/components/policy/core/common/features.cc
@@ -99,4 +99,11 @@
              "ExportPlatformPoliciesJson",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// When enabled, migrates user cloud management status and sign-in interception
+// policy fetching from the legacy SecureConnect endpoint to Device Management
+// Server.
+BASE_FEATURE(kMigrateSecureConnectApiToDmServer,
+             "MigrateSecureConnectApiToDmServer",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 }  // namespace policy::features
diff --git a/components/policy/core/common/features.h b/components/policy/core/common/features.h
index d9ee50e..8702a483 100644
--- a/components/policy/core/common/features.h
+++ b/components/policy/core/common/features.h
@@ -101,6 +101,11 @@
 // Enables the export of platform policies as JSON on the chrome://policy page.
 POLICY_EXPORT BASE_DECLARE_FEATURE(kExportPlatformPoliciesJson);
 
+// When enabled, migrates user cloud management status and sign-in interception
+// policy fetching from the legacy SecureConnect endpoint to Device Management
+// Server.
+POLICY_EXPORT BASE_DECLARE_FEATURE(kMigrateSecureConnectApiToDmServer);
+
 }  // namespace policy::features
 
 #endif  // COMPONENTS_POLICY_CORE_COMMON_FEATURES_H_
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index b5d76e4..1df32173 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -5312,7 +5312,13 @@
   optional GenerateChromeProfileChallengeRequest
       generate_chrome_profile_challenge_request = 44;
 
-  // Next id: 45.
+  // Request to check whether a user account is managed and optionally fetch
+  // sign-in interception policies.
+  // HTTP query parameter 'request': 'user_management_status_and_policies'
+  optional UserManagementStatusAndPoliciesRequest
+      user_management_status_and_policies_request = 45;
+
+  // Next id: 46.
 }
 
 // Response from server to device.
@@ -5483,7 +5489,59 @@
   optional GenerateChromeProfileChallengeResponse
       generate_chrome_profile_challenge_response = 44;
 
-  // Next id: 45.
+  // Response to check user management status and policies request
+  // (`UserManagementStatusAndPoliciesRequest`).
+  optional UserManagementStatusAndPoliciesResponse
+      user_management_status_and_policies_response = 45;
+
+  // Next id: 46.
+}
+
+// Request from device to server to check if a user is managed and retrieve
+// sign-in experience policies.
+message UserManagementStatusAndPoliciesRequest {
+  // Whether to fetch sign-in experience policies for the user. Policies are
+  // fetched only if the user is managed and has management enabled.
+  optional bool should_fetch_policies = 1;
+}
+
+// Enterprise policies needed to customize the sign-in experience (such as
+// profile separation and branding).
+message SigninExperiencePolicies {
+  // Value for the `SyncDisabled` policy.
+  optional bool sync_disabled = 1;
+
+  // Value for the `ProfileSeparationSettings` policy.
+  optional int32 profile_separation_settings = 2;
+
+  // Value for the `ProfileSeparationDataMigrationSettings` policy.
+  optional int32 profile_separation_data_migration_settings = 3;
+
+  // Value for the `BrowserThemeColor` policy.
+  optional string browser_theme_color = 4;
+
+  // Value for the `EnterpriseLogoUrl` policy.
+  optional string enterprise_logo_url = 5;
+
+  // Value for the `EnterpriseCustomLabel` policy.
+  optional string enterprise_custom_label = 6;
+
+  // Value for the `ManagedAccountsSigninRestriction` policy.
+  optional string managed_accounts_signin_restrictions = 7;
+}
+
+// Response from server to device for check user management status and policies
+// request (`UserManagementStatusAndPoliciesRequest`).
+message UserManagementStatusAndPoliciesResponse {
+  // Whether the user account is managed by an enterprise/domain.
+  optional bool is_managed = 1;
+
+  // Whether Chrome management is enabled for this user.
+  optional bool is_management_enabled = 2;
+
+  // Sign-in experience policies for the user, populated when
+  // `should_fetch_policies` is true and management is enabled.
+  optional SigninExperiencePolicies signin_experience_policies = 3;
 }
 
 // Device State Information stored in the server is retrieval at
diff --git a/components/prefs/pref_member.cc b/components/prefs/pref_member.cc
index 30461e9..8e181f80 100644
--- a/components/prefs/pref_member.cc
+++ b/components/prefs/pref_member.cc
@@ -9,6 +9,8 @@
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
+#include "base/i18n/language_tag.h"
+#include "base/i18n/language_tag_value_converters.h"
 #include "base/json/values_util.h"
 #include "base/location.h"
 #include "base/task/sequenced_task_runner.h"
@@ -239,6 +241,28 @@
 }
 
 template <>
+PrefMember<base::i18n::LanguageTag>::Internal::Internal()
+    : value_(base::i18n::GetKnownLanguageTag("und")) {}
+
+template <>
+void PrefMember<base::i18n::LanguageTag>::UpdatePref(
+    const base::i18n::LanguageTag& value) {
+  prefs()->SetLanguageTag(pref_name(), value);
+}
+
+template <>
+bool PrefMember<base::i18n::LanguageTag>::Internal::UpdateValueInternal(
+    const base::Value& value) const {
+  std::optional<base::i18n::LanguageTag> tag =
+      base::i18n::ValueToLanguageTag(value);
+  if (!tag) {
+    return false;
+  }
+  value_ = *tag;
+  return true;
+}
+
+template <>
 void PrefMember<std::vector<std::string> >::UpdatePref(
     const std::vector<std::string>& value) {
   base::ListValue list_value;
diff --git a/components/prefs/pref_member.h b/components/prefs/pref_member.h
index aac7de1..382ee59 100644
--- a/components/prefs/pref_member.h
+++ b/components/prefs/pref_member.h
@@ -31,6 +31,7 @@
 #include "base/files/file_path.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_forward.h"
+#include "base/i18n/language_tag.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/task/sequenced_task_runner.h"
@@ -352,6 +353,19 @@
     const base::Value& value) const;
 
 template <>
+COMPONENTS_PREFS_EXPORT
+PrefMember<base::i18n::LanguageTag>::Internal::Internal();
+
+template <>
+COMPONENTS_PREFS_EXPORT void PrefMember<base::i18n::LanguageTag>::UpdatePref(
+    const base::i18n::LanguageTag& value);
+
+template <>
+COMPONENTS_PREFS_EXPORT bool
+PrefMember<base::i18n::LanguageTag>::Internal::UpdateValueInternal(
+    const base::Value& value) const;
+
+template <>
 COMPONENTS_PREFS_EXPORT void PrefMember<std::vector<std::string>>::UpdatePref(
     const std::vector<std::string>& value);
 
@@ -365,6 +379,7 @@
 typedef PrefMember<double> DoublePrefMember;
 typedef PrefMember<std::string> StringPrefMember;
 typedef PrefMember<base::FilePath> FilePathPrefMember;
+typedef PrefMember<base::i18n::LanguageTag> LanguageTagPrefMember;
 // This preference member is expensive for large string arrays.
 typedef PrefMember<std::vector<std::string>> StringListPrefMember;
 
diff --git a/components/prefs/pref_member_unittest.cc b/components/prefs/pref_member_unittest.cc
index 352c31e..6a935ed6 100644
--- a/components/prefs/pref_member_unittest.cc
+++ b/components/prefs/pref_member_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/functional/bind.h"
+#include "base/i18n/language_tag.h"
 #include "base/location.h"
 #include "base/memory/raw_ptr.h"
 #include "base/synchronization/waitable_event.h"
@@ -348,3 +349,24 @@
   helper->FetchValue();
   EXPECT_TRUE(helper->value());
 }
+
+TEST_F(PrefMemberTest, InitGetAndSetLanguageTagPrefMember) {
+  TestingPrefServiceSimple prefs;
+  constexpr char kLanguageTagPref[] = "language_tag";
+  constexpr base::i18n::LanguageTag default_tag =
+      base::i18n::GetKnownLanguageTag("en");
+  constexpr base::i18n::LanguageTag new_tag =
+      base::i18n::GetKnownLanguageTag("es");
+
+  prefs.registry()->RegisterLanguageTagPref(kLanguageTagPref, default_tag);
+
+  LanguageTagPrefMember member;
+  member.Init(kLanguageTagPref, &prefs);
+  EXPECT_EQ(default_tag, member.GetValue());
+
+  prefs.SetLanguageTag(kLanguageTagPref, new_tag);
+  EXPECT_EQ(new_tag, member.GetValue());
+
+  member.SetValue(default_tag);
+  EXPECT_EQ(default_tag, prefs.GetLanguageTag(kLanguageTagPref));
+}
diff --git a/components/prefs/pref_registry_simple.cc b/components/prefs/pref_registry_simple.cc
index 5a6c252..38579a2 100644
--- a/components/prefs/pref_registry_simple.cc
+++ b/components/prefs/pref_registry_simple.cc
@@ -8,6 +8,8 @@
 #include <utility>
 
 #include "base/files/file_path.h"
+#include "base/i18n/language_tag.h"
+#include "base/i18n/language_tag_value_converters.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
@@ -47,6 +49,14 @@
   RegisterPreference(path, base::Value(default_value.AsUTF8Unsafe()), flags);
 }
 
+void PrefRegistrySimple::RegisterLanguageTagPref(
+    std::string_view path,
+    const base::i18n::LanguageTag& default_value,
+    uint32_t flags) {
+  RegisterPreference(path, base::i18n::LanguageTagToValue(default_value),
+                     flags);
+}
+
 void PrefRegistrySimple::RegisterListPref(std::string_view path,
                                           uint32_t flags) {
   RegisterPreference(path, base::Value(base::Value::Type::LIST), flags);
diff --git a/components/prefs/pref_registry_simple.h b/components/prefs/pref_registry_simple.h
index cf71c1b..4170b69 100644
--- a/components/prefs/pref_registry_simple.h
+++ b/components/prefs/pref_registry_simple.h
@@ -19,6 +19,10 @@
 class FilePath;
 }
 
+namespace base::i18n {
+class LanguageTag;
+}
+
 // A simple implementation of PrefRegistry.
 class COMPONENTS_PREFS_EXPORT PrefRegistrySimple : public PrefRegistry {
  public:
@@ -49,6 +53,10 @@
                             const base::FilePath& default_value,
                             uint32_t flags = NO_REGISTRATION_FLAGS);
 
+  void RegisterLanguageTagPref(std::string_view path,
+                               const base::i18n::LanguageTag& default_value,
+                               uint32_t flags = NO_REGISTRATION_FLAGS);
+
   void RegisterListPref(std::string_view path,
                         uint32_t flags = NO_REGISTRATION_FLAGS);
 
diff --git a/components/prefs/pref_service.cc b/components/prefs/pref_service.cc
index 27e6f92a..51349822 100644
--- a/components/prefs/pref_service.cc
+++ b/components/prefs/pref_service.cc
@@ -16,6 +16,8 @@
 #include "base/debug/dump_without_crashing.h"
 #include "base/files/file_path.h"
 #include "base/functional/bind.h"
+#include "base/i18n/language_tag.h"
+#include "base/i18n/language_tag_value_converters.h"
 #include "base/json/values_util.h"
 #include "base/location.h"
 #include "base/logging.h"
@@ -156,6 +158,12 @@
   return *result;
 }
 
+base::i18n::LanguageTag PrefService::GetLanguageTag(
+    std::string_view path) const {
+  return base::i18n::ValueToLanguageTag(GetValue(path))
+      .value_or(base::i18n::GetKnownLanguageTag("und"));
+}
+
 bool PrefService::HasPrefPath(std::string_view path) const {
   const Preference* pref = FindPreference(path);
   return pref && !pref->IsDefaultValue();
@@ -402,6 +410,11 @@
   SetUserPrefValue(path, base::FilePathToValue(value));
 }
 
+void PrefService::SetLanguageTag(std::string_view path,
+                                 const base::i18n::LanguageTag& value) {
+  SetUserPrefValue(path, base::i18n::LanguageTagToValue(value));
+}
+
 void PrefService::SetInt64(std::string_view path, int64_t value) {
   CHECK_EQ(pref_registry_->GetRegisteredPrefType(path).value_or(
                PrefRegistry::RegisteredPrefType::kInt64),
diff --git a/components/prefs/pref_service.h b/components/prefs/pref_service.h
index 77f00e7..7ac0cd79 100644
--- a/components/prefs/pref_service.h
+++ b/components/prefs/pref_service.h
@@ -51,6 +51,10 @@
 class FilePath;
 }
 
+namespace base::i18n {
+class LanguageTag;
+}
+
 namespace prefs {
 class ScopedDictionaryPrefUpdate;
 }
@@ -237,6 +241,7 @@
   double GetDouble(std::string_view path) const;
   const std::string& GetString(std::string_view path) const;
   base::FilePath GetFilePath(std::string_view path) const;
+  base::i18n::LanguageTag GetLanguageTag(std::string_view path) const;
 
   // Returns the branch if it exists, or the registered default value otherwise.
   // `path` must point to a registered preference (DCHECK).
@@ -273,6 +278,8 @@
   void SetDict(std::string_view path, base::DictValue dict);
   void SetList(std::string_view path, base::ListValue list);
   void SetFilePath(std::string_view path, const base::FilePath& value);
+  void SetLanguageTag(std::string_view path,
+                      const base::i18n::LanguageTag& value);
 
   // Int64 helper methods that actually store the given value as a string.
   // Note that if obtaining the named value via GetDictionary or GetList, the
diff --git a/components/prefs/pref_service_unittest.cc b/components/prefs/pref_service_unittest.cc
index f9c39ac..cd5e210 100644
--- a/components/prefs/pref_service_unittest.cc
+++ b/components/prefs/pref_service_unittest.cc
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/functional/callback_helpers.h"
+#include "base/i18n/language_tag.h"
 #include "base/test/gtest_util.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -29,6 +30,7 @@
 
 namespace {
 
+using base::i18n::GetKnownLanguageTag;
 const char kPrefName[] = "pref.name";
 
 }  // namespace
@@ -539,3 +541,32 @@
   EXPECT_CHECK_DEATH(prefs.SetTime(kInt64Pref, base::Time::Now()));
   EXPECT_CHECK_DEATH(prefs.GetTime(kInt64Pref));
 }
+
+TEST(PrefServiceTest, RegisterSetAndClearLanguageTagPref) {
+  TestingPrefServiceSimple prefs;
+  constexpr char kLanguageTagPref[] = "language_tag_pref";
+
+  prefs.registry()->RegisterLanguageTagPref(kLanguageTagPref,
+                                            GetKnownLanguageTag("en"));
+  EXPECT_EQ(prefs.GetLanguageTag(kLanguageTagPref), GetKnownLanguageTag("en"));
+
+  prefs.SetLanguageTag(kLanguageTagPref, GetKnownLanguageTag("fr"));
+  EXPECT_EQ(prefs.GetLanguageTag(kLanguageTagPref), GetKnownLanguageTag("fr"));
+
+  prefs.ClearPref(kLanguageTagPref);
+  EXPECT_EQ(prefs.GetLanguageTag(kLanguageTagPref), GetKnownLanguageTag("en"));
+}
+
+TEST(PrefServiceTest, GetLanguageTagFallback) {
+  TestingPrefServiceSimple prefs;
+  constexpr char kIntPref[] = "int_pref";
+  constexpr char kStringPref[] = "string_pref";
+
+  // Fallback to "und" when pref path holds an unexpected value type.
+  prefs.registry()->RegisterIntegerPref(kIntPref, 42);
+  EXPECT_EQ(prefs.GetLanguageTag(kIntPref), GetKnownLanguageTag("und"));
+
+  // Fallback to "und" when pref path holds an invalid language tag string.
+  prefs.registry()->RegisterStringPref(kStringPref, "invalid---tag");
+  EXPECT_EQ(prefs.GetLanguageTag(kStringPref), GetKnownLanguageTag("und"));
+}
diff --git a/components/privacy_sandbox/mock_privacy_sandbox_settings.h b/components/privacy_sandbox/mock_privacy_sandbox_settings.h
index e7ddba74..7875fc4 100644
--- a/components/privacy_sandbox/mock_privacy_sandbox_settings.h
+++ b/components/privacy_sandbox/mock_privacy_sandbox_settings.h
@@ -17,32 +17,6 @@
   ~MockPrivacySandboxSettings() override;
 
   // PrivacySandboxSettings:
-  MOCK_METHOD(bool, IsTopicsAllowed, (), (override, const));
-  MOCK_METHOD(bool,
-              IsTopicsAllowedForContext,
-              (const url::Origin&, const GURL&, content::RenderFrameHost*),
-              (override, const));
-  MOCK_METHOD(void,
-              SetTopicAllowed,
-              (const privacy_sandbox::CanonicalTopic&, bool),
-              (override));
-  MOCK_METHOD(void, ClearTopicSettings, (base::Time, base::Time), (override));
-  MOCK_METHOD(base::Time, TopicsDataAccessibleSince, (), (override, const));
-  MOCK_METHOD(void,
-              SetFledgeJoiningAllowed,
-              (const std::string&, bool),
-              (override));
-  MOCK_METHOD(void,
-              ClearFledgeJoiningAllowedSettings,
-              (base::Time, base::Time),
-              (override));
-  MOCK_METHOD(bool,
-              IsFledgeAllowed,
-              (const url::Origin&,
-               const url::Origin&,
-               privacy_sandbox::InterestGroupApiOperation,
-               content::RenderFrameHost*),
-              (override, const));
   MOCK_METHOD(
       bool,
       IsEventReportingDestinationAttested,
@@ -61,22 +35,11 @@
               IsSharedStorageSelectURLAllowed,
               (const url::Origin&, const url::Origin&, std::string*, bool*),
               (override, const));
-  MOCK_METHOD(bool,
-              IsPrivateAggregationAllowed,
-              (const url::Origin&, const url::Origin&, bool*),
-              (override, const));
-  MOCK_METHOD(bool,
-              IsPrivateAggregationDebugModeAllowed,
-              (const url::Origin&, const url::Origin&),
-              (override, const));
-  MOCK_METHOD(void, SetAllPrivacySandboxAllowedForTesting, (), (override));
-  MOCK_METHOD(void, SetTopicsBlockedForTesting, (), (override));
   MOCK_METHOD(bool, IsPrivacySandboxRestricted, (), (override, const));
   MOCK_METHOD(bool,
               IsPrivacySandboxCurrentlyUnrestricted,
               (),
               (override, const));
-  MOCK_METHOD(void, OnCookiesCleared, (), (override));
   MOCK_METHOD(void, AddObserver, (Observer*), (override));
   MOCK_METHOD(void, RemoveObserver, (Observer*), (override));
   MOCK_METHOD(void,
diff --git a/components/privacy_sandbox/privacy_sandbox_prefs.cc b/components/privacy_sandbox/privacy_sandbox_prefs.cc
index c58d4cb..96185dd5 100644
--- a/components/privacy_sandbox/privacy_sandbox_prefs.cc
+++ b/components/privacy_sandbox/privacy_sandbox_prefs.cc
@@ -28,10 +28,6 @@
                                 false);
   registry->RegisterBooleanPref(prefs::kPrivacySandboxM1Restricted, false);
 
-  registry->RegisterTimePref(prefs::kPrivacySandboxTopicsDataAccessibleSince,
-                             base::Time());
-  registry->RegisterListPref(prefs::kPrivacySandboxBlockedTopics);
-  registry->RegisterDictionaryPref(prefs::kPrivacySandboxFledgeJoinBlocked);
   registry->RegisterBooleanPref(prefs::kPrivacySandboxNoticeDisplayed, false);
   registry->RegisterBooleanPref(prefs::kPrivacySandboxConsentDecisionMade,
                                 false);
diff --git a/components/privacy_sandbox/privacy_sandbox_prefs.h b/components/privacy_sandbox/privacy_sandbox_prefs.h
index 5cbbb70..af64ee6 100644
--- a/components/privacy_sandbox/privacy_sandbox_prefs.h
+++ b/components/privacy_sandbox/privacy_sandbox_prefs.h
@@ -49,23 +49,6 @@
 inline constexpr char kPrivacySandboxM1Restricted[] =
     "privacy_sandbox.m1.restricted";
 
-// The point in time from which history is eligible to be used when calculating
-// a user's Topics API topics.
-inline constexpr char kPrivacySandboxTopicsDataAccessibleSince[] =
-    "privacy_sandbox.topics_data_accessible_since";
-
-// List of entries representing Topics API topics which are blocked for
-// the profile. Blocked topics cannot be provided to site, or considered as
-// part of the profile's "top topics". Entries in the list are dictionaries
-// containing the CanonicalTopic and the time the setting was created.
-inline constexpr char kPrivacySandboxBlockedTopics[] =
-    "privacy_sandbox.blocked_topics";
-
-// Dictionary of entries representing top frame origins on which the profile
-// cannot be joined to an interest group. Keys are the blocked origins, and
-// values are the time the setting was applied.
-inline constexpr char kPrivacySandboxFledgeJoinBlocked[] =
-    "privacy_sandbox.fledge_join_blocked";
 
 // Boolean that indicates that the Privacy Sandbox notice was shown to the
 // profile.
diff --git a/components/privacy_sandbox/privacy_sandbox_settings.h b/components/privacy_sandbox/privacy_sandbox_settings.h
index cbcea9b..7e64f73c 100644
--- a/components/privacy_sandbox/privacy_sandbox_settings.h
+++ b/components/privacy_sandbox/privacy_sandbox_settings.h
@@ -8,8 +8,6 @@
 #include "base/time/time.h"
 #include "components/keyed_service/core/keyed_service.h"
 
-class GURL;
-
 namespace content {
 class RenderFrameHost;
 }
@@ -20,8 +18,6 @@
 
 namespace privacy_sandbox {
 
-class CanonicalTopic;
-
 // When a new enum value is added:
 // 1. Update kMaxValue to match it.
 // 2. Update `PrivacySandboxAttestationsGatedAPIProto` in
@@ -58,8 +54,6 @@
  public:
   class Observer {
    public:
-    virtual void OnTopicsDataAccessibleSinceUpdated() {}
-
     // Fired when the Related Website Sets changes to being `enabled` as a
     // result of the kPrivacySandboxRelatedWebsiteSets preference changing.
     virtual void OnRelatedWebsiteSetsEnabledChanged(bool enabled) {}
@@ -85,65 +79,6 @@
     virtual bool IsIncognitoProfile() const = 0;
   };
 
-  // Returns whether the Topics API is allowed at all. If false, Topics API
-  // calculations should not occur. If true, the more specific function,
-  // IsTopicsApiAllowedForContext(), should be consulted for the relevant
-  // context.
-  virtual bool IsTopicsAllowed() const = 0;
-
-  // Determines whether the Topics API is allowable in a particular context.
-  // |top_frame_origin| is used to check for content settings which could both
-  // affect 1P and 3P contexts.
-  // If provided, `console_frame` is used to log errors to the console upon
-  // attestation failure.
-  virtual bool IsTopicsAllowedForContext(
-      const url::Origin& top_frame_origin,
-      const GURL& url,
-      content::RenderFrameHost* console_frame = nullptr) const = 0;
-
-  // Sets |topic| to |allowed|.
-  //
-  // NOTE: This function has no observable effect because the Topics API is
-  // deprecated and its underlying data is hidden. This will be removed soon.
-  virtual void SetTopicAllowed(const CanonicalTopic& topic, bool allowed) = 0;
-
-  // Removes all Topic settings with creation times between |start_time|
-  // and |end_time|. This allows for integration with the existing browsing data
-  // remover, such as the one powering Clear Browser Data.
-  virtual void ClearTopicSettings(base::Time start_time,
-                                  base::Time end_time) = 0;
-
-  // Returns the point in time from which history is eligible to be used when
-  // calculating a user's Topics API topics. Reset when a user clears all
-  // cookies, or when the browser restarts with "Clear on exit" enabled. The
-  // returned time will have been fuzzed for local privacy, and so may be in the
-  // future, in which case no history is eligible.
-  virtual base::Time TopicsDataAccessibleSince() const = 0;
-
-  // Sets the ability for |top_frame_etld_plus1| to join the profile to interest
-  // groups to |allowed|. This information is stored in preferences, and is made
-  // available to the API via IsFledgeJoiningAllowed(). |top_frame_etld_plus1|
-  // should in most circumstances be a valid eTLD+1, but hosts are accepted to
-  // allow for shifts in private registries. Entries are converted into wildcard
-  // subdomain ContentSettingsPattern before comparison.
-  virtual void SetFledgeJoiningAllowed(const std::string& top_frame_etld_plus1,
-                                       bool allowed) = 0;
-
-  // Clears any FLEDGE joining block settings with creation times between
-  // |start_time| and |end_time|.
-  virtual void ClearFledgeJoiningAllowedSettings(base::Time start_time,
-                                                 base::Time end_time) = 0;
-
-  // Determine whether |auction_party| can register an interest group, or sell
-  // buy in an auction, on |top_frame_origin|.
-  // If provided, `console_frame` is used to log errors to the console upon
-  // attestation failure.
-  virtual bool IsFledgeAllowed(
-      const url::Origin& top_frame_origin,
-      const url::Origin& auction_party,
-      InterestGroupApiOperation interest_group_api_operation,
-      content::RenderFrameHost* console_frame = nullptr) const = 0;
-
   // Determine whether |destination_origin| is allowed to receive events
   // (reportEvent(), automatic beacons) reported by an API like Protected
   // Audience or Shared Storage. This does not check if the API itself is
@@ -194,38 +129,6 @@
       std::string* out_debug_message,
       bool* out_block_is_site_setting_specific) const = 0;
 
-  // Determines whether the Private Aggregation API is allowable in a particular
-  // context. `top_frame_origin` is the associated top-frame origin of the
-  // calling context. Applicable to all uses of Private Aggregation.
-  //
-  // The out parameter `out_block_is_site_setting_specific` will be set to true
-  // in the case that the return value is false and the failure to be allowed is
-  // due to site-settings. Otherwise the parameter will be set to false (because
-  // either the return value is true, or the failure is due to a
-  // non-site-setting-specific reason).
-  virtual bool IsPrivateAggregationAllowed(
-      const url::Origin& top_frame_origin,
-      const url::Origin& reporting_origin,
-      bool* out_block_is_site_setting_specific) const = 0;
-
-  // Determines whether the Private Aggregation API's debug mode is allowable in
-  // a particular context. Note that if IsPrivateAggregationAllowed() is false,
-  // this will always be false too. `top_frame_origin` is the associated
-  // top-frame origin of the calling context. Applicable to all uses of Private
-  // Aggregation.
-  virtual bool IsPrivateAggregationDebugModeAllowed(
-      const url::Origin& top_frame_origin,
-      const url::Origin& reporting_origin) const = 0;
-
-  // Allows all Privacy Sandbox prefs for testing. This should be used if tests
-  // don't depend on specific access control and just would like to have Privacy
-  // Sandbox allowed. Doesn't affect other non-default settings which might
-  // disallow APIs e.g. site data exceptions.
-  virtual void SetAllPrivacySandboxAllowedForTesting() = 0;
-
-  // Blocks Topics pref for testing.
-  virtual void SetTopicsBlockedForTesting() = 0;
-
   // Returns whether the Privacy Sandbox is being restricted by the associated
   // delegate. Forwards directly to the corresponding delegate function.
   // Virtual to allow mocking in tests.
@@ -237,11 +140,6 @@
   // this method always return the current restriction status.
   virtual bool IsPrivacySandboxCurrentlyUnrestricted() const = 0;
 
-  // Called when there's a broad cookies clearing action. For example, this
-  // should be called on "Clear browsing data", but shouldn't be called on the
-  // Clear-Site-Data header, as it's restricted to a specific site.
-  virtual void OnCookiesCleared() = 0;
-
   virtual void AddObserver(Observer* observer) = 0;
   virtual void RemoveObserver(Observer* observer) = 0;
 
diff --git a/components/privacy_sandbox/privacy_sandbox_settings_impl.cc b/components/privacy_sandbox/privacy_sandbox_settings_impl.cc
index 0dc712df..30170421 100644
--- a/components/privacy_sandbox/privacy_sandbox_settings_impl.cc
+++ b/components/privacy_sandbox/privacy_sandbox_settings_impl.cc
@@ -41,105 +41,8 @@
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 #include "url/gurl.h"
-#include "url/origin.h"
-
 namespace privacy_sandbox {
 
-namespace {
-
-using ::content_settings::CookieControlsMode;
-
-constexpr char kBlockedTopicsTopicKey[] = "topic";
-constexpr char kBlockedTopicsBlockTimeKey[] = "blockedOn";
-
-constexpr char kIsTopicsAllowedHistogram[] = "PrivacySandbox.IsTopicsAllowed";
-constexpr char kIsTopicsAllowedForContextHistogram[] =
-    "PrivacySandbox.IsTopicsAllowedForContext";
-constexpr char kIsFledgeJoinAllowedHistogram[] =
-    "PrivacySandbox.IsFledgeJoinAllowed";
-constexpr char kIsFledgeLeaveAllowedHistogram[] =
-    "PrivacySandbox.IsFledgeLeaveAllowed";
-constexpr char kIsFledgeUpdateAllowedHistogram[] =
-    "PrivacySandbox.IsFledgeUpdateAllowed";
-constexpr char kIsFledgeSellAllowedHistogram[] =
-    "PrivacySandbox.IsFledgeSellAllowed";
-constexpr char kIsFledgeBuyAllowedHistogram[] =
-    "PrivacySandbox.IsFledgeBuyAllowed";
-constexpr char kIsFledgeReadAllowedHistogram[] =
-    "PrivacySandbox.IsFledgeReadAllowed";
-constexpr char kIsPrivacySandboxReportingDestinationAttestedHistogram[] =
-    "PrivacySandbox.IsPrivacySandboxReportingDestinationAttested";
-constexpr char kIsSharedStorageAllowedHistogram[] =
-    "PrivacySandbox.IsSharedStorageAllowed";
-constexpr char kIsSharedStorageSelectURLAllowedHistogram[] =
-    "PrivacySandbox.IsSharedStorageSelectURLAllowed";
-constexpr char kIsPrivateAggregationAllowedHistogram[] =
-    "PrivacySandbox.IsPrivateAggregationAllowed";
-
-bool IsCookiesClearOnExitEnabled(HostContentSettingsMap* map) {
-  return map->GetDefaultContentSetting(ContentSettingsType::COOKIES) ==
-         ContentSetting::CONTENT_SETTING_SESSION_ONLY;
-}
-
-// Convert a stored FLEDGE block eTLD+1 into applicable content settings
-// patterns. This ensures that if Public Suffix List membership changes, the
-// stored item continues to match as when it was set. Multiple patterns are set
-// to support IP address fallbacks, which do not support [*.] prefixes.
-// TODO (crbug.com/1287153): This is somewhat hacky and can be removed when
-// FLEDGE is controlled by a content setting directly.
-std::vector<ContentSettingsPattern> FledgeBlockToContentSettingsPatterns(
-    const std::string& entry) {
-  return {ContentSettingsPattern::FromString("[*.]" + entry),
-          ContentSettingsPattern::FromString(entry)};
-}
-
-// Returns a base::DictValue for storage in prefs that represents |topic|
-// blocked at the current time.
-base::DictValue CreateBlockedTopicEntry(const CanonicalTopic& topic) {
-  return base::DictValue()
-      .Set(kBlockedTopicsTopicKey, topic.ToValue())
-      .Set(kBlockedTopicsBlockTimeKey, base::TimeToValue(base::Time::Now()));
-}
-
-}  // namespace
-
-// static
-bool PrivacySandboxSettingsImpl::IsAllowed(Status status) {
-  return status == Status::kAllowed;
-}
-
-// static
-void PrivacySandboxSettingsImpl::JoinHistogram(const char* name,
-                                               Status status) {
-  base::UmaHistogramEnumeration(name, status);
-}
-
-// static
-void PrivacySandboxSettingsImpl::JoinFledgeHistogram(
-    InterestGroupApiOperation interest_group_api_operation,
-    Status status) {
-  switch (interest_group_api_operation) {
-    case InterestGroupApiOperation::kJoin:
-      JoinHistogram(kIsFledgeJoinAllowedHistogram, status);
-      break;
-    case InterestGroupApiOperation::kLeave:
-      JoinHistogram(kIsFledgeLeaveAllowedHistogram, status);
-      break;
-    case InterestGroupApiOperation::kUpdate:
-      JoinHistogram(kIsFledgeUpdateAllowedHistogram, status);
-      break;
-    case InterestGroupApiOperation::kSell:
-      JoinHistogram(kIsFledgeSellAllowedHistogram, status);
-      break;
-    case InterestGroupApiOperation::kBuy:
-      JoinHistogram(kIsFledgeBuyAllowedHistogram, status);
-      break;
-    case InterestGroupApiOperation::kRead:
-      JoinHistogram(kIsFledgeReadAllowedHistogram, status);
-      break;
-  }
-}
-
 PrivacySandboxSettingsImpl::PrivacySandboxSettingsImpl(
     std::unique_ptr<Delegate> delegate,
     HostContentSettingsMap* host_content_settings_map,
@@ -152,13 +55,6 @@
   CHECK(pref_service_);
   CHECK(host_content_settings_map_);
   CHECK(cookie_settings_);
-  // "Clear on exit" causes a cookie deletion on shutdown. But for practical
-  // purposes, we're notifying the observers on startup (which should be
-  // equivalent, as no cookie operations could have happened while the profile
-  // was shut down).
-  if (IsCookiesClearOnExitEnabled(host_content_settings_map_)) {
-    OnCookiesCleared();
-  }
 
   pref_change_registrar_.Init(pref_service_);
   pref_change_registrar_.Add(
@@ -179,253 +75,10 @@
   pref_change_registrar_.Reset();
 }
 
-PrivacySandboxSettingsImpl::Status
-PrivacySandboxSettingsImpl::GetM1TopicAllowedStatus() const {
-  return Status::kApisDisabled;
-}
-
-bool PrivacySandboxSettingsImpl::IsTopicsAllowed() const {
-  Status status = GetM1TopicAllowedStatus();
-  JoinHistogram(kIsTopicsAllowedHistogram, status);
-  return IsAllowed(status);
-}
-
-bool PrivacySandboxSettingsImpl::IsTopicsAllowedForContext(
-    const url::Origin& top_frame_origin,
-    const GURL& url,
-    content::RenderFrameHost* console_frame) const {
-  // Check for attestation on the calling context's site.
-  Status attestation_status =
-      PrivacySandboxAttestations::GetInstance()->IsSiteAttested(
-          net::SchemefulSite(url), PrivacySandboxAttestationsGatedAPI::kTopics);
-  if (!IsAllowed(attestation_status)) {
-    JoinHistogram(kIsTopicsAllowedForContextHistogram, attestation_status);
-    if (console_frame) {
-      console_frame->AddMessageToConsole(
-          blink::mojom::ConsoleMessageLevel::kError,
-          "Attestation check for Topics on " + url.spec() + " failed.");
-    }
-    return false;
-  }
-
-  Status status = GetM1TopicAllowedStatus();
-  if (IsAllowed(status)) {
-    status = GetSiteAccessAllowedStatus(top_frame_origin, url);
-  }
-  JoinHistogram(kIsTopicsAllowedForContextHistogram, status);
-  return IsAllowed(status);
-}
-
-void PrivacySandboxSettingsImpl::SetTopicAllowed(const CanonicalTopic& topic,
-                                                 bool allowed) {
-  ScopedListPrefUpdate scoped_pref_update(pref_service_,
-                                          prefs::kPrivacySandboxBlockedTopics);
-
-  // Presence in the preference list indicates that a topic is blocked, as
-  // there is no concept of explicitly allowed topics. Thus, allowing a topic
-  // is the same as removing it, if it exists, from the blocklist. Blocking
-  // a topic is the same as adding it to the blocklist, but as duplicate entries
-  // are undesireable, removing any existing reference first is desireable.
-  // Thus, regardless of |allowed|, removing any existing reference is the
-  // first step.
-  scoped_pref_update->EraseIf([&](const base::Value& value) {
-    auto* blocked_topic_value = value.GetDict().Find(kBlockedTopicsTopicKey);
-    auto converted_topic = CanonicalTopic::FromValue(*blocked_topic_value);
-    return converted_topic && *converted_topic == topic;
-  });
-
-  // If the topic is being blocked, it can be (re)added to the blocklist. If the
-  // topic was removed from the blocklist above, this is equivalent to updating
-  // the modified time associated with the entry to the current time. As data
-  // deletions are typically from the current time backwards, this makes it
-  // more likely to be removed - a privacy improvement.
-  if (!allowed) {
-    scoped_pref_update->Append(CreateBlockedTopicEntry(topic));
-  }
-}
-
-void PrivacySandboxSettingsImpl::ClearTopicSettings(base::Time start_time,
-                                                    base::Time end_time) {
-  ScopedListPrefUpdate scoped_pref_update(pref_service_,
-                                          prefs::kPrivacySandboxBlockedTopics);
-
-  // Shortcut for maximum time range deletion.
-  if (start_time == base::Time() && end_time == base::Time::Max()) {
-    scoped_pref_update->clear();
-    return;
-  }
-
-  scoped_pref_update->EraseIf([&](const base::Value& value) {
-    auto blocked_time =
-        base::ValueToTime(value.GetDict().Find(kBlockedTopicsBlockTimeKey));
-    return start_time <= blocked_time && blocked_time <= end_time;
-  });
-}
-
-base::Time PrivacySandboxSettingsImpl::TopicsDataAccessibleSince() const {
-  return pref_service_->GetTime(
-      prefs::kPrivacySandboxTopicsDataAccessibleSince);
-}
-
-PrivacySandboxSettingsImpl::Status
-PrivacySandboxSettingsImpl::GetM1AdMeasurementAllowedStatus(
-    const url::Origin& top_frame_origin,
-    const url::Origin& reporting_origin) const {
-  Status status = GetM1PrivacySandboxApiEnabledStatus(
-      prefs::kPrivacySandboxM1AdMeasurementEnabled);
-  if (!IsAllowed(status)) {
-    return status;
-  }
-
-  return GetSiteAccessAllowedStatus(top_frame_origin,
-                                    reporting_origin.GetURL());
-}
-
-void PrivacySandboxSettingsImpl::SetFledgeJoiningAllowed(
-    const std::string& top_frame_etld_plus1,
-    bool allowed) {
-  ScopedDictPrefUpdate scoped_pref_update(
-      pref_service_, prefs::kPrivacySandboxFledgeJoinBlocked);
-
-  // Ensure that the provided etld_plus1 actually is an etld+1.
-  auto effective_top_frame_etld_plus1 =
-      net::registry_controlled_domains::GetDomainAndRegistry(
-          top_frame_etld_plus1,
-          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
-
-  // Hosts are also accepted as a fallback. This may occur if the private
-  // registry has changed, and what the caller may be assuming is an eTLD+1 no
-  // longer is. Simply ignoring non-eTLD+1's may thus result in unexpected
-  // access.
-  if (effective_top_frame_etld_plus1 != top_frame_etld_plus1) {
-    // Add a dummy scheme and use GURL to confirm the provided string is a valid
-    // host.
-    const GURL url("https://" + top_frame_etld_plus1);
-    effective_top_frame_etld_plus1 = url.GetHost();
-  }
-
-  // Ignore attempts to configure an empty etld+1. This will also catch the
-  // case where the eTLD+1 was not even a host, as GURL will have canonicalised
-  // it to empty.
-  if (effective_top_frame_etld_plus1.length() == 0) {
-    NOTREACHED() << "Cannot control FLEDGE joining for empty eTLD+1";
-  }
-
-  if (allowed) {
-    // Existence of the key implies blocking, so simply removing the key is
-    // sufficient. If the key wasn't already present, the following is a no-op.
-    scoped_pref_update->Remove(effective_top_frame_etld_plus1);
-  } else {
-    // Overriding the creation date for keys which already exist is acceptable.
-    // Time range based deletions are typically started from the current time,
-    // and so this will be more aggressively removed. This decreases the chance
-    // a potentially sensitive website remains in preferences.
-    scoped_pref_update->Set(effective_top_frame_etld_plus1,
-                            base::TimeToValue(base::Time::Now()));
-  }
-}
-
-void PrivacySandboxSettingsImpl::ClearFledgeJoiningAllowedSettings(
-    base::Time start_time,
-    base::Time end_time) {
-  ScopedDictPrefUpdate scoped_pref_update(
-      pref_service_, prefs::kPrivacySandboxFledgeJoinBlocked);
-  auto& pref_data = scoped_pref_update.Get();
-
-  // Shortcut for maximum time range deletion
-  if (start_time == base::Time() && end_time == base::Time::Max()) {
-    pref_data.clear();
-    return;
-  }
-
-  std::vector<std::string> keys_to_remove;
-  for (auto entry : pref_data) {
-    std::optional<base::Time> created_time = base::ValueToTime(entry.second);
-    if (created_time.has_value() && start_time <= created_time &&
-        created_time <= end_time) {
-      keys_to_remove.push_back(entry.first);
-    }
-  }
-
-  for (const auto& key : keys_to_remove) {
-    pref_data.Remove(key);
-  }
-}
-
-bool PrivacySandboxSettingsImpl::IsFledgeJoiningAllowed(
-    const url::Origin& top_frame_origin) const {
-  ScopedDictPrefUpdate scoped_pref_update(
-      pref_service_, prefs::kPrivacySandboxFledgeJoinBlocked);
-  auto& pref_data = scoped_pref_update.Get();
-  for (auto entry : pref_data) {
-    if (std::ranges::any_of(FledgeBlockToContentSettingsPatterns(entry.first),
-                            [&](const auto& pattern) {
-                              return pattern.Matches(top_frame_origin.GetURL());
-                            })) {
-      return false;
-    }
-  }
-  return true;
-}
-
-PrivacySandboxSettingsImpl::Status
-PrivacySandboxSettingsImpl::GetM1FledgeAllowedStatus(
-    const url::Origin& top_frame_origin,
-    const url::Origin& auction_party) const {
-  Status status = GetM1PrivacySandboxApiEnabledStatus(
-      prefs::kPrivacySandboxM1FledgeEnabled);
-  if (!IsAllowed(status)) {
-    return status;
-  }
-
-  return GetSiteAccessAllowedStatus(top_frame_origin, auction_party.GetURL());
-}
-
 bool PrivacySandboxSettingsImpl::IsEventReportingDestinationAttested(
     const url::Origin& destination_origin,
     privacy_sandbox::PrivacySandboxAttestationsGatedAPI invoking_api) const {
-  // Check for attestation on the event recipient's site with whichever API
-  // created the frame that invoked the event reporting.
-  Status attestation_status =
-      PrivacySandboxAttestations::GetInstance()->IsSiteAttested(
-          net::SchemefulSite(destination_origin), invoking_api);
-  JoinHistogram(kIsPrivacySandboxReportingDestinationAttestedHistogram,
-                attestation_status);
-  return IsAllowed(attestation_status);
-}
-
-bool PrivacySandboxSettingsImpl::IsFledgeAllowed(
-    const url::Origin& top_frame_origin,
-    const url::Origin& auction_party,
-    InterestGroupApiOperation interest_group_api_operation,
-    content::RenderFrameHost* console_frame) const {
-  // Check for attestation on the auction party's site. The auction party is a
-  // variety of entities during the auction, all of which need to be attested.
-  Status attestation_status =
-      PrivacySandboxAttestations::GetInstance()->IsSiteAttested(
-          net::SchemefulSite(auction_party),
-          PrivacySandboxAttestationsGatedAPI::kProtectedAudience);
-  if (!IsAllowed(attestation_status)) {
-    JoinFledgeHistogram(interest_group_api_operation, attestation_status);
-    if (console_frame) {
-      console_frame->AddMessageToConsole(
-          blink::mojom::ConsoleMessageLevel::kError,
-          "Attestation check for Protected Audience on " +
-              auction_party.Serialize() + " failed.");
-    }
-    return false;
-  }
-
-  if (interest_group_api_operation == InterestGroupApiOperation::kJoin &&
-      !IsFledgeJoiningAllowed(top_frame_origin)) {
-    JoinFledgeHistogram(interest_group_api_operation,
-                        Status::kJoiningTopFrameBlocked);
-    return false;
-  }
-
-  Status status = GetM1FledgeAllowedStatus(top_frame_origin, auction_party);
-  JoinFledgeHistogram(interest_group_api_operation, status);
-  return IsAllowed(status);
+  return false;
 }
 
 bool PrivacySandboxSettingsImpl::IsSharedStorageAllowed(
@@ -434,61 +87,7 @@
     std::string* out_debug_message,
     content::RenderFrameHost* console_frame,
     bool* out_block_is_site_setting_specific) const {
-  // Check for attestation on the caller's site.
-  Status attestation_status =
-      PrivacySandboxAttestations::GetInstance()->IsSiteAttested(
-          net::SchemefulSite(accessing_origin),
-          PrivacySandboxAttestationsGatedAPI::kSharedStorage);
-  SetOutBlockIsSiteSettingSpecificFromStatus(
-      attestation_status, out_block_is_site_setting_specific);
-  if (!IsAllowed(attestation_status)) {
-    JoinHistogram(kIsSharedStorageAllowedHistogram, attestation_status);
-    std::string error_message =
-        base::StrCat({"Attestation check for Shared Storage on ",
-                      accessing_origin.Serialize(), " failed."});
-    if (out_debug_message) {
-      *out_debug_message = base::StrCat(
-          {error_message, "\nReturned status ",
-           base::NumberToString(int(attestation_status)),
-           "; see `PrivacySandboxSettingsImpl::Status` at ",
-           "https://chromium.googlesource.com/chromium/src/+/refs/heads/main/",
-           "components/privacy_sandbox/privacy_sandbox_settings_impl.h."});
-    }
-    if (console_frame) {
-      console_frame->AddMessageToConsole(
-          blink::mojom::ConsoleMessageLevel::kError, error_message);
-    }
-    return false;
-  }
-
-  Status status = GetPrivacySandboxAllowedStatus();
-  SetOutBlockIsSiteSettingSpecificFromStatus(
-      status, out_block_is_site_setting_specific);
-  if (IsAllowed(status)) {
-    status =
-        GetSiteAccessAllowedStatus(top_frame_origin, accessing_origin.GetURL());
-    SetOutBlockIsSiteSettingSpecificFromStatus(
-        status, out_block_is_site_setting_specific);
-    if (out_debug_message) {
-      *out_debug_message = base::StrCat(
-          {"Site access settings returned status ",
-           base::NumberToString(int(status)), " for accessing origin ",
-           accessing_origin.Serialize(), " and top-frame origin ",
-           top_frame_origin.Serialize(),
-           "; see `PrivacySandboxSettingsImpl::Status` at ",
-           "https://chromium.googlesource.com/chromium/src/+/refs/heads/main/",
-           "components/privacy_sandbox/privacy_sandbox_settings_impl.h."});
-    }
-  } else if (out_debug_message) {
-    *out_debug_message = base::StrCat(
-        {"Privacy Sandbox settings returned status ",
-         base::NumberToString(int(status)),
-         "; see `PrivacySandboxSettingsImpl::Status` at ",
-         "https://chromium.googlesource.com/chromium/src/+/refs/heads/main/",
-         "components/privacy_sandbox/privacy_sandbox_settings_impl.h."});
-  }
-  JoinHistogram(kIsSharedStorageAllowedHistogram, status);
-  return IsAllowed(status);
+  return false;
 }
 
 bool PrivacySandboxSettingsImpl::IsSharedStorageSelectURLAllowed(
@@ -496,109 +95,9 @@
     const url::Origin& accessing_origin,
     std::string* out_debug_message,
     bool* out_block_is_site_setting_specific) const {
-  Status status = GetM1FledgeAllowedStatus(top_frame_origin, accessing_origin);
-  SetOutBlockIsSiteSettingSpecificFromStatus(
-      status, out_block_is_site_setting_specific);
-  JoinHistogram(kIsSharedStorageSelectURLAllowedHistogram, status);
-  if (out_debug_message) {
-    *out_debug_message = base::StrCat(
-        {"M1 measurement settings returned status ",
-         base::NumberToString(int(status)), " for accessing origin ",
-         accessing_origin.Serialize(), " and top-frame origin ",
-         top_frame_origin.Serialize(),
-         "; see `PrivacySandboxSettingsImpl::Status` at ",
-         "https://chromium.googlesource.com/chromium/src/+/refs/heads/main/",
-         "components/privacy_sandbox/privacy_sandbox_settings_impl.h."});
-  }
-  return IsAllowed(status);
-}
-
-bool PrivacySandboxSettingsImpl::IsPrivateAggregationAllowed(
-    const url::Origin& top_frame_origin,
-    const url::Origin& reporting_origin,
-    bool* out_block_is_site_setting_specific) const {
-  // Check for attestation on the worklet's site.
-  Status attestation_status =
-      PrivacySandboxAttestations::GetInstance()->IsSiteAttested(
-          net::SchemefulSite(reporting_origin),
-          PrivacySandboxAttestationsGatedAPI::kPrivateAggregation);
-  SetOutBlockIsSiteSettingSpecificFromStatus(
-      attestation_status, out_block_is_site_setting_specific);
-  if (!IsAllowed(attestation_status)) {
-    JoinHistogram(kIsPrivateAggregationAllowedHistogram, attestation_status);
-    dwa::builders::PrivacySandbox_IsPrivateAggregationAllowed()
-        .SetContent(reporting_origin.Serialize())
-        .SetStatus(static_cast<int64_t>(attestation_status))
-        .Record(metrics::dwa::DwaRecorder::Get());
-    return false;
-  }
-
-  Status status =
-      GetM1AdMeasurementAllowedStatus(top_frame_origin, reporting_origin);
-  SetOutBlockIsSiteSettingSpecificFromStatus(
-      status, out_block_is_site_setting_specific);
-  JoinHistogram(kIsPrivateAggregationAllowedHistogram, status);
-  dwa::builders::PrivacySandbox_IsPrivateAggregationAllowed()
-      .SetContent(reporting_origin.Serialize())
-      .SetStatus(static_cast<int64_t>(status))
-      .Record(metrics::dwa::DwaRecorder::Get());
-  return IsAllowed(status);
-}
-
-bool PrivacySandboxSettingsImpl::IsPrivateAggregationDebugModeAllowed(
-    const url::Origin& top_frame_origin,
-    const url::Origin& reporting_origin) const {
-  if (!IsPrivateAggregationAllowed(
-          top_frame_origin, reporting_origin,
-          /*out_block_is_site_setting_specific=*/nullptr)) {
-    return false;
-  }
-
-  // If this feature is disabled, provide a top-frame origin anyway to match
-  // previous behavior.
-  std::optional<url::Origin> top_frame_origin_to_query;
-  if (!base::FeatureList::IsEnabled(
-          kPrivateAggregationDebugReportingIgnoreSiteExceptions)) {
-    top_frame_origin_to_query = top_frame_origin;
-  }
-  // With what is available here, we can create a cookie_partition_key for the
-  // given top_frame_origin and we can assume the ancestor chain bit is
-  // cross_site since the `IsFullCookieAccessAllowed` call below uses a null
-  // `SiteForCookies`, which means that we will always be in a cross site
-  // context.
-  net::SchemefulSite top_frame_site(top_frame_origin);
-  std::optional<net::CookiePartitionKey> cookie_partition_key =
-      net::CookiePartitionKey::FromStorageKeyComponents(
-          top_frame_site,
-          net::CookiePartitionKey::BoolToAncestorChainBit(/*cross_site=*/true),
-          /*nonce=*/std::nullopt);
-
-  // Third party cookies must also be available for this context. An empty site
-  // for cookies and empty top-frame origin is provided so the context is always
-  // treated as a third party. That is, we ignore any top-level site cookie
-  // exceptions (see crbug.com/364318217).
-  content_settings::CookieSettingsBase::CookieSettingWithMetadata
-      cookie_setting_with_metadata;
-  if (cookie_settings_->IsFullCookieAccessAllowed(
-          reporting_origin.GetURL(), net::SiteForCookies(),
-          top_frame_origin_to_query, net::CookieSettingOverrides(),
-          cookie_partition_key, &cookie_setting_with_metadata)) {
-    return true;
-  }
-
   return false;
 }
 
-void PrivacySandboxSettingsImpl::SetAllPrivacySandboxAllowedForTesting() {
-  pref_service_->SetBoolean(prefs::kPrivacySandboxM1FledgeEnabled, true);
-  pref_service_->SetBoolean(prefs::kPrivacySandboxM1TopicsEnabled, true);
-  pref_service_->SetBoolean(prefs::kPrivacySandboxM1AdMeasurementEnabled, true);
-}
-
-void PrivacySandboxSettingsImpl::SetTopicsBlockedForTesting() {
-  pref_service_->SetBoolean(prefs::kPrivacySandboxM1TopicsEnabled, false);
-}
-
 bool PrivacySandboxSettingsImpl::IsPrivacySandboxRestricted() const {
   return delegate_->IsPrivacySandboxRestricted();
 }
@@ -607,9 +106,6 @@
   return delegate_->IsPrivacySandboxCurrentlyUnrestricted();
 }
 
-void PrivacySandboxSettingsImpl::OnCookiesCleared() {
-  SetTopicsDataAccessibleFromNow();
-}
 
 void PrivacySandboxSettingsImpl::OnRelatedWebsiteSetsEnabledPrefChanged() {
   for (auto& observer : observers_) {
@@ -630,62 +126,9 @@
   delegate_ = std::move(delegate);
 }
 
-void PrivacySandboxSettingsImpl::SetTopicsDataAccessibleFromNow() const {
-  pref_service_->SetTime(prefs::kPrivacySandboxTopicsDataAccessibleSince,
-                         base::Time::Now());
-
-  for (auto& observer : observers_) {
-    observer.OnTopicsDataAccessibleSinceUpdated();
-  }
-}
-
-PrivacySandboxSettingsImpl::Status
-PrivacySandboxSettingsImpl::GetSiteAccessAllowedStatus(
-    const url::Origin& top_frame_origin,
-    const GURL& url) const {
-  // Relying on |host_content_settings_map_| instead of |cookie_settings_|
-  // allows to query whether the site associated with the |url| is allowed to
-  // access Site data (aka ContentSettingsType::COOKIES) without considering any
-  // 3P cookie blocking setting.
-  return content_settings::CookieSettingsBase::IsAllowed(
-             host_content_settings_map_->GetContentSetting(
-                 url, top_frame_origin.GetURL(), ContentSettingsType::COOKIES))
-             ? Status::kAllowed
-             : Status::kSiteDataAccessBlocked;
-}
-
-PrivacySandboxSettingsImpl::Status
-PrivacySandboxSettingsImpl::GetPrivacySandboxAllowedStatus(
-    bool should_ignore_restriction /*=false*/) const {
-  if (delegate_->IsIncognitoProfile()) {
-    return Status::kIncognitoProfile;
-  }
-
-  if (IsPrivacySandboxRestricted() && !should_ignore_restriction) {
-    return Status::kRestricted;
-  }
-
-  return Status::kAllowed;
-}
-
-PrivacySandboxSettingsImpl::Status
-PrivacySandboxSettingsImpl::GetM1PrivacySandboxApiEnabledStatus(
-    const std::string& pref_name) const {
-  return Status::kApisDisabled;
-}
-
 bool PrivacySandboxSettingsImpl::AreRelatedWebsiteSetsEnabled() const {
   return pref_service_->GetBoolean(
       prefs::kPrivacySandboxRelatedWebsiteSetsEnabled);
 }
 
-void PrivacySandboxSettingsImpl::SetOutBlockIsSiteSettingSpecificFromStatus(
-    Status status,
-    bool* out_block_is_site_setting_specific) const {
-  if (out_block_is_site_setting_specific != nullptr) {
-    *out_block_is_site_setting_specific =
-        status == Status::kSiteDataAccessBlocked;
-  }
-}
-
 }  // namespace privacy_sandbox
diff --git a/components/privacy_sandbox/privacy_sandbox_settings_impl.h b/components/privacy_sandbox/privacy_sandbox_settings_impl.h
index 97bee6a..3872d8a 100644
--- a/components/privacy_sandbox/privacy_sandbox_settings_impl.h
+++ b/components/privacy_sandbox/privacy_sandbox_settings_impl.h
@@ -44,23 +44,6 @@
   void Shutdown() override;
 
   // PrivacySandboxSettings:
-  bool IsTopicsAllowed() const override;
-  bool IsTopicsAllowedForContext(
-      const url::Origin& top_frame_origin,
-      const GURL& url,
-      content::RenderFrameHost* console_frame = nullptr) const override;
-  void SetTopicAllowed(const CanonicalTopic& topic, bool allowed) override;
-  void ClearTopicSettings(base::Time start_time, base::Time end_time) override;
-  base::Time TopicsDataAccessibleSince() const override;
-  void SetFledgeJoiningAllowed(const std::string& top_frame_etld_plus1,
-                               bool allowed) override;
-  void ClearFledgeJoiningAllowedSettings(base::Time start_time,
-                                         base::Time end_time) override;
-  bool IsFledgeAllowed(
-      const url::Origin& top_frame_origin,
-      const url::Origin& auction_party,
-      InterestGroupApiOperation interest_group_api_operation,
-      content::RenderFrameHost* console_frame = nullptr) const override;
   bool IsEventReportingDestinationAttested(
       const url::Origin& destination_origin,
       privacy_sandbox::PrivacySandboxAttestationsGatedAPI invoking_api)
@@ -76,19 +59,9 @@
       const url::Origin& accessing_origin,
       std::string* out_debug_message,
       bool* out_block_is_site_setting_specific) const override;
-  bool IsPrivateAggregationAllowed(
-      const url::Origin& top_frame_origin,
-      const url::Origin& reporting_origin,
-      bool* out_block_is_site_setting_specific) const override;
-  bool IsPrivateAggregationDebugModeAllowed(
-      const url::Origin& top_frame_origin,
-      const url::Origin& reporting_origin) const override;
 
-  void SetAllPrivacySandboxAllowedForTesting() override;
-  void SetTopicsBlockedForTesting() override;
   bool IsPrivacySandboxRestricted() const override;
   bool IsPrivacySandboxCurrentlyUnrestricted() const override;
-  void OnCookiesCleared() override;
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
   void SetDelegateForTesting(std::unique_ptr<Delegate> delegate) override;
@@ -104,8 +77,6 @@
   // Called when the Related Website Sets enabled preference is changed.
   void OnRelatedWebsiteSetsEnabledPrefChanged();
 
-  void SetTopicsDataAccessibleFromNow() const;
-
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
   enum class Status {
@@ -125,54 +96,6 @@
     kMaxValue = kAttestationsFileNotPresent,
   };
 
-  static bool IsAllowed(Status status);
-
-  static void JoinHistogram(const char* name, Status status);
-  static void JoinFledgeHistogram(
-      InterestGroupApiOperation interest_group_api_operation,
-      Status status);
-
-  // Whether the site associated with the URL is allowed to access privacy
-  // sandbox APIs within the context of |top_frame_origin|.
-  Status GetSiteAccessAllowedStatus(const url::Origin& top_frame_origin,
-                                    const GURL& url) const;
-
-  // Whether the privacy sandbox APIs can be allowed given the current
-  // environment. For example, the privacy sandbox is always disabled in
-  // Incognito and for restricted accounts.
-  Status GetPrivacySandboxAllowedStatus(
-      bool should_ignore_restriction = false) const;
-
-  // Whether the privacy sandbox associated with  the |pref_name| is enabled.
-  // For individual sites, check as well with GetSiteAccessAllowedStatus.
-  Status GetM1PrivacySandboxApiEnabledStatus(
-      const std::string& pref_name) const;
-
-  // Whether the Topics API can be allowed given the current
-  // environment or the reason why it is not allowed.
-  Status GetM1TopicAllowedStatus() const;
-
-  // Whether ad measurement APIs can be allowed given the current environment or
-  // the reason why it is not allowed.
-  Status GetM1AdMeasurementAllowedStatus(
-      const url::Origin& top_frame_origin,
-      const url::Origin& reporting_origin) const;
-
-  // Whether Fledge can be allowed given the current environment or the reason
-  // why it is not allowed.
-  Status GetM1FledgeAllowedStatus(const url::Origin& top_frame_origin,
-                                  const url::Origin& accessing_origin) const;
-
-  // Internal helper for `IsFledgeAllowed`. Used only when
-  // `interest_group_api_operation` is `kJoin`.
-  bool IsFledgeJoiningAllowed(const url::Origin& top_frame_origin) const;
-
-  // Sets the out parameter `out_block_is_site_setting_specific` if it is
-  // non-null, based on the given `status`.
-  void SetOutBlockIsSiteSettingSpecificFromStatus(
-      Status status,
-      bool* out_block_is_site_setting_specific) const;
-
   base::ObserverList<Observer>::Unchecked observers_;
 
   std::unique_ptr<Delegate> delegate_;
diff --git a/components/privacy_sandbox/privacy_sandbox_settings_impl_unittest.cc b/components/privacy_sandbox/privacy_sandbox_settings_impl_unittest.cc
index d64fd3d..5a3e44fb 100644
--- a/components/privacy_sandbox/privacy_sandbox_settings_impl_unittest.cc
+++ b/components/privacy_sandbox/privacy_sandbox_settings_impl_unittest.cc
@@ -41,25 +41,17 @@
 namespace {
 
 using ::privacy_sandbox_test_util::MockPrivacySandboxSettingsDelegate;
-using ::privacy_sandbox_test_util::MultipleInputKeys;
-using ::privacy_sandbox_test_util::MultipleOutputKeys;
 using ::privacy_sandbox_test_util::MultipleStateKeys;
-using ::privacy_sandbox_test_util::PrivacySandboxSettingsTestPeer;
 using ::privacy_sandbox_test_util::SiteDataExceptions;
 using ::privacy_sandbox_test_util::TestCase;
 using ::privacy_sandbox_test_util::TestInput;
 using ::privacy_sandbox_test_util::TestOutput;
 using ::privacy_sandbox_test_util::TestState;
-using ::testing::Return;
-
-using Status = PrivacySandboxSettingsTestPeer::Status;
 
 using enum privacy_sandbox_test_util::StateKey;
 using enum privacy_sandbox_test_util::InputKey;
 using enum privacy_sandbox_test_util::OutputKey;
 
-constexpr int kTestTaxonomyVersion = 1;
-
 }  // namespace
 
 class PrivacySandboxSettingsTest : public testing::Test {
@@ -126,10 +118,6 @@
   PrivacySandboxSettingsImpl* privacy_sandbox_settings_impl() {
     return privacy_sandbox_settings_.get();
   }
-  bool IsFledgeJoiningAllowed(const std::string& url) {
-    return PrivacySandboxSettingsTestPeer(privacy_sandbox_settings_impl())
-        .IsFledgeJoiningAllowed(url::Origin::Create(GURL(url)));
-  }
   content::BrowserTaskEnvironment* task_environment() {
     return &browser_task_environment_;
   }
@@ -149,105 +137,6 @@
   std::unique_ptr<PrivacySandboxSettingsImpl> privacy_sandbox_settings_;
 };
 
-TEST_F(PrivacySandboxSettingsTest, TopicsDataAccessibleSince) {
-  ASSERT_NE(base::Time(), base::Time::Now());
-
-  EXPECT_EQ(base::Time(),
-            privacy_sandbox_settings()->TopicsDataAccessibleSince());
-
-  privacy_sandbox_settings()->OnCookiesCleared();
-
-  EXPECT_EQ(base::Time::Now(),
-            privacy_sandbox_settings()->TopicsDataAccessibleSince());
-}
-
-TEST_F(PrivacySandboxSettingsTest, FledgeJoiningAllowed) {
-  // Whether or not a site can join a user to an interest group is independent
-  // of any other profile state.
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://example.com"));
-
-  // Settings should match at the eTLD + 1 level.
-  privacy_sandbox_settings()->SetFledgeJoiningAllowed("example.com", false);
-
-  EXPECT_FALSE(IsFledgeJoiningAllowed("https://subsite.example.com"));
-  EXPECT_FALSE(IsFledgeJoiningAllowed("http://example.com"));
-  EXPECT_FALSE(IsFledgeJoiningAllowed("https://example.com:888"));
-  EXPECT_FALSE(IsFledgeJoiningAllowed("https://example.com"));
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://example.com.au"));
-
-  privacy_sandbox_settings()->SetFledgeJoiningAllowed("example.com", true);
-
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://example.com"));
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://subsite.example.com"));
-  EXPECT_TRUE(IsFledgeJoiningAllowed("http://example.com"));
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://example.com:888"));
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://example.com"));
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://example.com.au"));
-}
-
-TEST_F(PrivacySandboxSettingsTest, NonEtldPlusOneBlocked) {
-  // Confirm that, as a fallback, hosts are accepted by SetFledgeJoiningAllowed.
-  privacy_sandbox_settings()->SetFledgeJoiningAllowed("subsite.example.com",
-                                                      false);
-
-  // Applied setting should affect subdomaings.
-  EXPECT_FALSE(IsFledgeJoiningAllowed("https://subsite.example.com"));
-  EXPECT_FALSE(IsFledgeJoiningAllowed("http://another.subsite.example.com"));
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://example.com"));
-
-  // When removing the setting, only an exact match, and not the associated
-  // eTLD+1, should remove a setting.
-  privacy_sandbox_settings()->SetFledgeJoiningAllowed("example.com", true);
-  EXPECT_FALSE(IsFledgeJoiningAllowed("https://subsite.example.com"));
-  EXPECT_FALSE(IsFledgeJoiningAllowed("http://another.subsite.example.com"));
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://example.com"));
-
-  privacy_sandbox_settings()->SetFledgeJoiningAllowed("subsite.example.com",
-                                                      true);
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://subsite.example.com"));
-  EXPECT_TRUE(IsFledgeJoiningAllowed("http://another.subsite.example.com"));
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://example.com"));
-
-  // IP addresses should also be accepted as a fallback.
-  privacy_sandbox_settings()->SetFledgeJoiningAllowed("10.1.1.100", false);
-  EXPECT_FALSE(IsFledgeJoiningAllowed("https://10.1.1.100"));
-  EXPECT_FALSE(IsFledgeJoiningAllowed("http://10.1.1.100:8080"));
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://10.2.2.200"));
-}
-
-TEST_F(PrivacySandboxSettingsTest, FledgeJoinSettingTimeRangeDeletion) {
-  // Confirm that time range deletions work appropriately for FLEDGE join
-  // settings.
-  privacy_sandbox_settings()->SetFledgeJoiningAllowed("first.com", false);
-  task_environment()->AdvanceClock(base::Hours(1));
-
-  const base::Time kSecondSettingTime = base::Time::Now();
-  privacy_sandbox_settings()->SetFledgeJoiningAllowed("second.com", false);
-
-  task_environment()->AdvanceClock(base::Hours(1));
-  privacy_sandbox_settings()->SetFledgeJoiningAllowed("third.com", false);
-
-  EXPECT_FALSE(IsFledgeJoiningAllowed("https://first.com"));
-  EXPECT_FALSE(IsFledgeJoiningAllowed("https://second.com"));
-  EXPECT_FALSE(IsFledgeJoiningAllowed("https://third.com"));
-
-  // Construct a deletion which only targets the second setting.
-  privacy_sandbox_settings()->ClearFledgeJoiningAllowedSettings(
-      kSecondSettingTime - base::Seconds(1),
-      kSecondSettingTime + base::Seconds(1));
-  EXPECT_FALSE(IsFledgeJoiningAllowed("https://first.com"));
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://second.com"));
-  EXPECT_FALSE(IsFledgeJoiningAllowed("https://third.com"));
-
-  // Perform a maximmal time range deletion, which should remove the two
-  // remaining settings.
-  privacy_sandbox_settings()->ClearFledgeJoiningAllowedSettings(
-      base::Time(), base::Time::Max());
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://first.com"));
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://second.com"));
-  EXPECT_TRUE(IsFledgeJoiningAllowed("https://third.com"));
-}
-
 TEST_F(PrivacySandboxSettingsTest, OnRelatedWebsiteSetsEnabledChanged) {
   // OnRelatedWebsiteSetsEnabledChanged() should only call observers when the
   // pref changes.
@@ -258,159 +147,7 @@
   prefs()->SetBoolean(prefs::kPrivacySandboxRelatedWebsiteSetsEnabled, true);
   testing::Mock::VerifyAndClearExpectations(&observer);
 
-  EXPECT_CALL(observer, OnRelatedWebsiteSetsEnabledChanged(/*enabled=*/false));
-  prefs()->SetBoolean(prefs::kPrivacySandboxRelatedWebsiteSetsEnabled, false);
-  testing::Mock::VerifyAndClearExpectations(&observer);
-}
-
-TEST_F(PrivacySandboxSettingsTest, ClearingTopicSettings) {
-  // Confirm that time range deletions affect the correct settings.
-  CanonicalTopic topic_a(1, kTestTaxonomyVersion);
-  CanonicalTopic topic_b(57, kTestTaxonomyVersion);
-  CanonicalTopic topic_c(86, kTestTaxonomyVersion);
-
-  privacy_sandbox_settings()->SetTopicAllowed(topic_a, false);
-  task_environment()->AdvanceClock(base::Hours(1));
-
-  const auto kSecondSettingTime = base::Time::Now();
-  privacy_sandbox_settings()->SetTopicAllowed(topic_b, false);
-
-  task_environment()->AdvanceClock(base::Hours(1));
-  privacy_sandbox_settings()->SetTopicAllowed(topic_c, false);
-
-  EXPECT_EQ(3u, prefs()->GetList(prefs::kPrivacySandboxBlockedTopics).size());
-
-  // Construct a deletion which only targets the second setting.
-  privacy_sandbox_settings()->ClearTopicSettings(
-      kSecondSettingTime - base::Seconds(1),
-      kSecondSettingTime + base::Seconds(1));
-
-  EXPECT_EQ(2u, prefs()->GetList(prefs::kPrivacySandboxBlockedTopics).size());
-
-  // Perform a maximmal time range deletion, which should remove the two
-  // remaining settings.
-  privacy_sandbox_settings()->ClearTopicSettings(base::Time(),
-                                                 base::Time::Max());
-  EXPECT_EQ(0u, prefs()->GetList(prefs::kPrivacySandboxBlockedTopics).size());
-}
-
-struct PrivateAggregationDebugModeTestCase {
-  using TupleT = std::tuple<bool, bool>;
-
-  explicit PrivateAggregationDebugModeTestCase(TupleT t)
-      : site_exception_user_setting_defined(std::get<0>(t)),
-        ignore_site_exception_feature_enabled(std::get<1>(t)) {}
-
-  bool site_exception_user_setting_defined = false;
-  bool ignore_site_exception_feature_enabled = false;
-};
-
-// This test class relies on setting the cookie controls mode pref, which is not
-// used on iOS.
-#if !BUILDFLAG(IS_IOS)
-class PrivacySandboxSettingsPrivateAggregationDebugModeTest
-    : public PrivacySandboxSettingsTest,
-      public testing::WithParamInterface<PrivateAggregationDebugModeTestCase> {
-};
-
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    PrivacySandboxSettingsPrivateAggregationDebugModeTest,
-    testing::ConvertGenerator<PrivateAggregationDebugModeTestCase::TupleT>(
-        testing::Combine(testing::Bool(),
-                         testing::Bool())),
-    // Creates a human-readable name for each test. Per gtest docs, test names
-    // must contain only alphanumeric characters.
-    [](const testing::TestParamInfo<PrivateAggregationDebugModeTestCase>& info)
-        -> std::string {
-      return base::StringPrintf(
-          "AndSiteExceptionUserSetting%s"
-          "AndIgnoreSiteException%s",
-          info.param.site_exception_user_setting_defined ? "Defined"
-                                                         : "NotDefined",
-          info.param.ignore_site_exception_feature_enabled ? "On" : "Off");
-    });
-
-// Test that Private Aggregation Debug Mode can be enabled in some circumstances
-// even though third-party cookies are blocked.
-TEST_P(PrivacySandboxSettingsPrivateAggregationDebugModeTest,
-       IsPrivateAggregationDebugModeAllowed) {
-  // Debug Mode should be disabled when third-party cookies are blocked,
-  // unless they are allowed due to an explicit user site exception.
-  //
-  // Additionally, if third-party cookies are re-enabled with a top-level site
-  // exception, that will allow for debug mode unless the ignore site exception
-  // feature is enabled.
-  const PrivateAggregationDebugModeTestCase& test_case = GetParam();
-
-  base::test::ScopedFeatureList feature_list;
-  std::vector<base::test::FeatureRef> enabled_features, disabled_features = {};
-  if (test_case.ignore_site_exception_feature_enabled) {
-    enabled_features.emplace_back(
-        kPrivateAggregationDebugReportingIgnoreSiteExceptions);
-  } else {
-    disabled_features.emplace_back(
-        kPrivateAggregationDebugReportingIgnoreSiteExceptions);
   }
-  feature_list.InitWithFeatures(enabled_features, disabled_features);
-
-  // Enable ad measurement pref. Otherwise, Private Aggregation will not be
-  // allowed by PrivacySandboxSettingsImpl::IsPrivateAggregationAllowed().
-  prefs()->SetUserPref(prefs::kPrivacySandboxM1AdMeasurementEnabled,
-                       base::Value(true));
-
-  prefs()->SetUserPref(
-      prefs::kCookieControlsMode,
-      std::make_unique<base::Value>(static_cast<int>(
-          content_settings::CookieControlsMode::kBlockThirdParty)));
-  if (test_case.site_exception_user_setting_defined) {
-    host_content_settings_map()->SetContentSettingCustomScope(
-        ContentSettingsPattern::FromString("https://embedded.com"),
-        ContentSettingsPattern::FromString("https://test.com"),
-        ContentSettingsType::COOKIES, ContentSetting::CONTENT_SETTING_ALLOW);
-  }
-
-  EXPECT_FALSE(privacy_sandbox_settings()->IsPrivateAggregationDebugModeAllowed(
-      url::Origin::Create(GURL("https://test.com")),
-      url::Origin::Create(GURL("https://embedded.com"))));
-}
-#endif
-
-class PrivacySandboxSettingsTestCookiesClearOnExitTurnedOff
-    : public PrivacySandboxSettingsTest {
- public:
-  void InitializePrefsBeforeStart() override {
-    prefs()->SetUserPref(prefs::kPrivacySandboxTopicsDataAccessibleSince,
-                         std::make_unique<base::Value>(::base::TimeToValue(
-                             base::Time::FromTimeT(12345))));
-  }
-};
-
-TEST_F(PrivacySandboxSettingsTestCookiesClearOnExitTurnedOff,
-       UseLastTopicsDataAccessibleSince) {
-  EXPECT_EQ(base::Time::FromTimeT(12345),
-            privacy_sandbox_settings()->TopicsDataAccessibleSince());
-}
-
-class PrivacySandboxSettingsTestCookiesClearOnExitTurnedOn
-    : public PrivacySandboxSettingsTest {
- public:
-  void InitializePrefsBeforeStart() override {
-    host_content_settings_map()->SetDefaultContentSetting(
-        ContentSettingsType::COOKIES,
-        ContentSetting::CONTENT_SETTING_SESSION_ONLY);
-
-    prefs()->SetUserPref(prefs::kPrivacySandboxTopicsDataAccessibleSince,
-                         std::make_unique<base::Value>(::base::TimeToValue(
-                             base::Time::FromTimeT(12345))));
-  }
-};
-
-TEST_F(PrivacySandboxSettingsTestCookiesClearOnExitTurnedOn,
-       UpdateTopicsDataAccessibleSince) {
-  EXPECT_EQ(base::Time::Now(),
-            privacy_sandbox_settings()->TopicsDataAccessibleSince());
-}
 
 class PrivacySandboxSettingsMockDelegateTest
     : public PrivacySandboxSettingsTest {
@@ -458,292 +195,9 @@
 
   bool actual_out_shared_storage_block_is_site_setting_specific_ = false;
   bool actual_out_select_url_block_is_site_setting_specific_ = false;
-  bool actual_out_private_aggregation_block_is_site_setting_specific_ = false;
 
  private:
   bool test_case_run_ = false;
 };
 
-class PrivacySandboxAttestationsTest : public base::test::WithFeatureOverride,
-                                       public PrivacySandboxSettingsM1Test {
- public:
-  PrivacySandboxAttestationsTest()
-      : base::test::WithFeatureOverride(
-            kDefaultAllowPrivacySandboxAttestations) {
-    // This test suite tests Privacy Sandbox Attestations related behaviors,
-    // turn off the setting that makes all APIs considered attested.
-    PrivacySandboxAttestations::GetInstance()
-        ->SetAllPrivacySandboxAttestedForTesting(false);
-  }
-
-  bool IsAttestationsDefaultAllowed() const { return IsParamFeatureEnabled(); }
-};
-
-// When the browser hasn't yet confirmed that the attestations file is present
-// in the filesystem. An attestation check:
-// 1. succeeds if `kDefaultAllowPrivacySandboxAttestations` is on.
-// 2. fails otherwise.
-TEST_P(PrivacySandboxAttestationsTest, AttestationsFileNotYetChecked) {
-  GURL top_frame_url("https://top-frame.com");
-  GURL enrollee_url("https://embedded.com");
-  RunTestCase(
-      TestState{
-          {MultipleStateKeys{kM1TopicsEnabledUserPrefValue,
-                             kM1FledgeEnabledUserPrefValue,
-                             kM1AdMeasurementEnabledUserPrefValue},
-           true},
-          {kAttestationsMap, std::nullopt},
-      },
-      TestInput{
-          {kTopicsURL, enrollee_url},
-          {kTopFrameOrigin, url::Origin::Create(top_frame_url)},
-          {kAdMeasurementReportingOrigin, url::Origin::Create(enrollee_url)},
-          {kFledgeAuctionPartyOrigin, url::Origin::Create(enrollee_url)},
-          {kEventReportingDestinationOrigin, url::Origin::Create(enrollee_url)},
-          {kAccessingOrigin, url::Origin::Create(enrollee_url)},
-
-          {kOutSharedStorageBlockIsSiteSettingSpecific,
-           &actual_out_shared_storage_block_is_site_setting_specific_},
-          {kOutPrivateAggregationBlockIsSiteSettingSpecific,
-           &actual_out_private_aggregation_block_is_site_setting_specific_}},
-      TestOutput{
-          {MultipleOutputKeys{
-               kIsEventReportingDestinationAttestedForFledge,
-               kIsEventReportingDestinationAttestedForSharedStorage,
-               kIsSharedStorageAllowed},
-           IsAttestationsDefaultAllowed()},
-          {MultipleOutputKeys{
-               kIsSharedStorageAllowedMetric,
-               kIsEventReportingDestinationAttestedForSharedStorageMetric,
-               kIsEventReportingDestinationAttestedForFledgeMetric},
-           static_cast<int>(IsAttestationsDefaultAllowed()
-                                ? Status::kAllowed
-                                : Status::kAttestationsFileNotYetChecked)},
-          {MultipleOutputKeys{kIsSharedStorageBlockSiteSettingSpecific,
-                              kIsPrivateAggregationBlockSiteSettingSpecific},
-           &kFalse_}});
-}
-
-// When the attestations map has no enrollments at all (i.e., no enrollment
-// for the site in question), attestation fails.
-TEST_P(PrivacySandboxAttestationsTest, NoEnrollments) {
-  GURL top_frame_url("https://top-frame.com");
-  GURL enrollee_url("https://embedded.com");
-  RunTestCase(
-      TestState{{MultipleStateKeys{kM1TopicsEnabledUserPrefValue,
-                                   kM1FledgeEnabledUserPrefValue,
-                                   kM1AdMeasurementEnabledUserPrefValue},
-                 true},
-                {kAttestationsMap, PrivacySandboxAttestationsMap{}}},
-      TestInput{
-          {kTopicsURL, enrollee_url},
-          {kTopFrameOrigin, url::Origin::Create(top_frame_url)},
-          {kAdMeasurementReportingOrigin, url::Origin::Create(enrollee_url)},
-          {kFledgeAuctionPartyOrigin, url::Origin::Create(enrollee_url)},
-          {kEventReportingDestinationOrigin, url::Origin::Create(enrollee_url)},
-          {kAccessingOrigin, url::Origin::Create(enrollee_url)},
-
-          {kOutSharedStorageBlockIsSiteSettingSpecific,
-           &actual_out_shared_storage_block_is_site_setting_specific_},
-          {kOutPrivateAggregationBlockIsSiteSettingSpecific,
-           &actual_out_private_aggregation_block_is_site_setting_specific_}},
-      TestOutput{
-          {MultipleOutputKeys{
-               kIsTopicsAllowedForContext, kIsFledgeJoinAllowed,
-               kIsFledgeLeaveAllowed, kIsFledgeUpdateAllowed,
-               kIsFledgeSellAllowed, kIsFledgeBuyAllowed,
-               kIsEventReportingDestinationAttestedForFledge,
-               kIsEventReportingDestinationAttestedForSharedStorage,
-               kIsSharedStorageAllowed, kIsPrivateAggregationAllowed,
-               kIsPrivateAggregationDebugModeAllowed},
-           false},
-          {MultipleOutputKeys{
-               kIsSharedStorageAllowedMetric,
-               kIsEventReportingDestinationAttestedForSharedStorageMetric,
-               kIsEventReportingDestinationAttestedForFledgeMetric},
-           static_cast<int>(Status::kAttestationFailed)},
-          {MultipleOutputKeys{kIsSharedStorageBlockSiteSettingSpecific,
-                              kIsPrivateAggregationBlockSiteSettingSpecific},
-           &kFalse_}});
-}
-
-// When the site in question is enrolled but has no attestations at all (i.e.,
-// no attestation for the API in question), attestation fails.
-TEST_P(PrivacySandboxAttestationsTest, EnrollmentWithoutAttestations) {
-  GURL top_frame_url("https://top-frame.com");
-  GURL enrollee_url("https://embedded.com");
-  RunTestCase(
-      TestState{{MultipleStateKeys{kM1TopicsEnabledUserPrefValue,
-                                   kM1FledgeEnabledUserPrefValue,
-                                   kM1AdMeasurementEnabledUserPrefValue},
-                 true},
-                {kAttestationsMap,
-                 PrivacySandboxAttestationsMap{
-                     {net::SchemefulSite(enrollee_url), {}}}}},
-      TestInput{
-          {kTopicsURL, enrollee_url},
-          {kTopFrameOrigin, url::Origin::Create(top_frame_url)},
-          {kAdMeasurementReportingOrigin, url::Origin::Create(enrollee_url)},
-          {kFledgeAuctionPartyOrigin, url::Origin::Create(enrollee_url)},
-          {kEventReportingDestinationOrigin, url::Origin::Create(enrollee_url)},
-          {kAccessingOrigin, url::Origin::Create(enrollee_url)}},
-      TestOutput{
-          {MultipleOutputKeys{
-               kIsTopicsAllowedForContext, kIsFledgeJoinAllowed,
-               kIsFledgeLeaveAllowed, kIsFledgeUpdateAllowed,
-               kIsFledgeSellAllowed, kIsFledgeBuyAllowed,
-               kIsEventReportingDestinationAttestedForFledge,
-               kIsEventReportingDestinationAttestedForSharedStorage,
-               kIsSharedStorageAllowed, kIsPrivateAggregationAllowed,
-               kIsPrivateAggregationDebugModeAllowed},
-           false},
-          {MultipleOutputKeys{
-               kIsSharedStorageAllowedMetric,
-               kIsEventReportingDestinationAttestedForSharedStorageMetric,
-               kIsEventReportingDestinationAttestedForFledgeMetric},
-           static_cast<int>(Status::kAttestationFailed)}});
-}
-
-TEST_P(PrivacySandboxAttestationsTest, SetOverrideFromDevtools) {
-  prefs()->SetUserPref(prefs::kCookieControlsMode,
-                       std::make_unique<base::Value>(static_cast<int>(
-                           content_settings::CookieControlsMode::kOff)));
-  privacy_sandbox_settings()->SetAllPrivacySandboxAllowedForTesting();
-
-  // Set an empty attestations map to prevent the API being default allowed
-  // when feature `kDefaultAllowPrivacySandboxAttestations` is on.
-  PrivacySandboxAttestations::GetInstance()->SetAttestationsForTesting(
-      PrivacySandboxAttestationsMap{});
-
-  GURL top_level_url("https://top-level-origin.com");
-  GURL caller_url("https://embedded.com");
-
-  // With an empty attestation map, Topics is not allowed.
-  EXPECT_FALSE(privacy_sandbox_settings()->IsTopicsAllowedForContext(
-      url::Origin::Create(top_level_url), caller_url));
-  EXPECT_FALSE(privacy_sandbox_settings()->IsEventReportingDestinationAttested(
-      url::Origin::Create(GURL("https://embedded.com")),
-      PrivacySandboxAttestationsGatedAPI::kProtectedAudience));
-
-  // With an override of the site from a devtools call, attestation passes, but
-  // Topics is disabled.
-  PrivacySandboxAttestations::GetInstance()->AddOverride(
-      net::SchemefulSite(GURL("https://embedded.com")));
-  EXPECT_FALSE(privacy_sandbox_settings()->IsTopicsAllowedForContext(
-      url::Origin::Create(top_level_url), caller_url));
-  EXPECT_TRUE(privacy_sandbox_settings()->IsEventReportingDestinationAttested(
-      url::Origin::Create(GURL("https://embedded.com")),
-      PrivacySandboxAttestationsGatedAPI::kProtectedAudience));
-}
-
-TEST_P(PrivacySandboxAttestationsTest, SetOverrideFromFlags) {
-  static const struct TestCase {
-    std::string name;
-    std::string flags;
-    GURL report_url;
-    bool expected;
-  } kTestCases[] = {
-      {"Basic", "https://embedded.com", GURL("https://embedded.com"), true},
-      {"Empty", "", GURL("https://embedded.com"), false},
-      {"Different", "https://other.com", GURL("https://embedded.com"), false},
-      {"Multiple", "https://other.com, https://embedded.com",
-       GURL("https://embedded.com"), true},
-      {"Invalid", "embedded.com", GURL("https://embedded.com"), false},
-      {"Extra Comma", "https://a.com,,https://embedded.com",
-       GURL("https://embedded.com"), true},
-      {"www", "https://www.embedded.com", GURL("https://embedded.com"), true},
-  };
-  prefs()->SetUserPref(prefs::kCookieControlsMode,
-                       std::make_unique<base::Value>(static_cast<int>(
-                           content_settings::CookieControlsMode::kOff)));
-  privacy_sandbox_settings()->SetAllPrivacySandboxAllowedForTesting();
-  base::test::ScopedCommandLine scoped_command_line;
-
-  // Set an empty attestations map to prevent the API being default allowed
-  // when feature `kDefaultAllowPrivacySandboxAttestations` is on.
-  PrivacySandboxAttestations::GetInstance()->SetAttestationsForTesting(
-      PrivacySandboxAttestationsMap{});
-
-  for (const auto& test : kTestCases) {
-    // Reset the overrides flags from the previous test loop.
-    scoped_command_line.GetProcessCommandLine()->RemoveSwitch(
-        kPrivacySandboxEnrollmentOverrides);
-
-    // Event reporting for Protected Audience should not be allowed at first.
-    EXPECT_FALSE(
-        privacy_sandbox_settings()->IsEventReportingDestinationAttested(
-            url::Origin::Create(test.report_url),
-            PrivacySandboxAttestationsGatedAPI::kProtectedAudience));
-
-    scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
-        kPrivacySandboxEnrollmentOverrides, test.flags);
-
-    // Check reporting for Protected Audience after setting the flag.
-    EXPECT_EQ(privacy_sandbox_settings()->IsEventReportingDestinationAttested(
-                  url::Origin::Create(test.report_url),
-                  PrivacySandboxAttestationsGatedAPI::kProtectedAudience),
-              test.expected)
-        << test.name;
-  }
-}
-
-INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(PrivacySandboxAttestationsTest);
-
-namespace {
-
-constexpr char kAttestationFailedTemplate[] =
-    "Attestation check for Shared Storage on %s failed.\nReturned status %d; "
-    "see `PrivacySandboxSettingsImpl::Status` at "
-    "https://chromium.googlesource.com/chromium/src/+/refs/heads/main/"
-    "components/privacy_sandbox/privacy_sandbox_settings_impl.h.";
-
-class PrivacySandboxSettingsSharedStorageDebugTest
-    : public PrivacySandboxSettingsM1Test {
- public:
-  PrivacySandboxSettingsSharedStorageDebugTest() {
-    default_allow_attestations_feature_list_.InitAndEnableFeature(
-        kDefaultAllowPrivacySandboxAttestations);
-
-    // This test suite tests Privacy Sandbox Attestations related behaviors,
-    // turn off the setting that makes all APIs considered attested.
-    PrivacySandboxAttestations::GetInstance()
-        ->SetAllPrivacySandboxAttestedForTesting(false);
-  }
-
- protected:
-  std::string actual_out_shared_storage_debug_message_;
-  std::string actual_out_select_url_debug_message_;
-
- private:
-  base::test::ScopedFeatureList default_allow_attestations_feature_list_;
-};
-
-}  // namespace
-
-TEST_F(PrivacySandboxSettingsSharedStorageDebugTest, NoEnrollments) {
-  std::string expected_out_shared_storage_debug_message =
-      base::StringPrintf(kAttestationFailedTemplate, "https://embedded.com",
-                         static_cast<int>(Status::kAttestationFailed));
-
-  // Confirm that the expected debug message is received when attestation fails
-  // due to no enrollments.
-  RunTestCase(
-      TestState{{kM1FledgeEnabledUserPrefValue, true},
-                {kAttestationsMap, PrivacySandboxAttestationsMap{}}},
-      TestInput{
-          {kTopFrameOrigin, url::Origin::Create(GURL("https://top-frame.com"))},
-          {kAccessingOrigin, url::Origin::Create(GURL("https://embedded.com"))},
-          {kOutSharedStorageDebugMessage,
-           &actual_out_shared_storage_debug_message_},
-
-          {kOutSharedStorageBlockIsSiteSettingSpecific,
-           &actual_out_shared_storage_block_is_site_setting_specific_}},
-      TestOutput{{kIsSharedStorageAllowed, false},
-                 {kIsSharedStorageAllowedMetric,
-                  static_cast<int>(Status::kAttestationFailed)},
-                 {kIsSharedStorageAllowedDebugMessage,
-                  &expected_out_shared_storage_debug_message},
-                 {kIsSharedStorageBlockSiteSettingSpecific, &kFalse_}});
-}
-
 }  // namespace privacy_sandbox
diff --git a/components/privacy_sandbox/privacy_sandbox_test_util.cc b/components/privacy_sandbox/privacy_sandbox_test_util.cc
index d70b1b0..7330d78 100644
--- a/components/privacy_sandbox/privacy_sandbox_test_util.cc
+++ b/components/privacy_sandbox/privacy_sandbox_test_util.cc
@@ -241,12 +241,6 @@
                   privacy_sandbox::PrivacySandboxAttestationsMap>>(value));
       return;
     }
-    case (StateKey::kBlockFledgeJoiningForEtldplus1): {
-      SCOPED_TRACE("State Setup: Disable FLEDGE joining for eTLD+1");
-      privacy_sandbox_settings->SetFledgeJoiningAllowed(
-          GetItemValue<std::string>(value), false);
-      return;
-    }
     default:
       NOTREACHED();
   }
@@ -275,157 +269,7 @@
     sync_preferences::TestingPrefServiceSyncable* testing_pref_service) {
   auto [output_key, output_value] = output;
   switch (output_key) {
-    case (OutputKey::kIsTopicsAllowed): {
-      SCOPED_TRACE("Check Output: IsTopicsAllowed()");
-      auto return_value = GetItemValue<bool>(output_value);
-      ASSERT_EQ(return_value, privacy_sandbox_settings->IsTopicsAllowed());
-      return;
-    }
-    case (OutputKey::kIsTopicsAllowedForContext): {
-      SCOPED_TRACE("Check Output: IsTopicsAllowedForContext()");
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto topics_url = GetItemValueForKey<GURL>(InputKey::kTopicsURL, input);
-      auto return_value = GetItemValue<bool>(output_value);
-      ASSERT_EQ(return_value,
-                privacy_sandbox_settings->IsTopicsAllowedForContext(
-                    top_frame_origin, topics_url));
-      return;
-    }
-    case (OutputKey::kIsFledgeJoinAllowed): {
-      SCOPED_TRACE("Check Output: IsFledgeAllowed(kJoin)");
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto fledge_auction_party_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kFledgeAuctionPartyOrigin, input);
-      auto return_value = GetItemValue<bool>(output_value);
-      ASSERT_EQ(return_value,
-                privacy_sandbox_settings->IsFledgeAllowed(
-                    top_frame_origin, fledge_auction_party_origin,
-                    privacy_sandbox::InterestGroupApiOperation::kJoin));
-      return;
-    }
-    case (OutputKey::kIsFledgeLeaveAllowed): {
-      SCOPED_TRACE("Check Output: IsFledgeAllowed(kLeave)");
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto fledge_auction_party_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kFledgeAuctionPartyOrigin, input);
-      auto return_value = GetItemValue<bool>(output_value);
-      ASSERT_EQ(return_value,
-                privacy_sandbox_settings->IsFledgeAllowed(
-                    top_frame_origin, fledge_auction_party_origin,
-                    privacy_sandbox::InterestGroupApiOperation::kLeave));
-      return;
-    }
-    case (OutputKey::kIsFledgeUpdateAllowed): {
-      SCOPED_TRACE("Check Output: IsFledgeAllowed(kUpdate)");
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto fledge_auction_party_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kFledgeAuctionPartyOrigin, input);
-      auto return_value = GetItemValue<bool>(output_value);
-      ASSERT_EQ(return_value,
-                privacy_sandbox_settings->IsFledgeAllowed(
-                    top_frame_origin, fledge_auction_party_origin,
-                    privacy_sandbox::InterestGroupApiOperation::kUpdate));
-      return;
-    }
-    case (OutputKey::kIsFledgeSellAllowed): {
-      SCOPED_TRACE("Check Output: IsFledgeAllowed(kSell)");
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto fledge_auction_party_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kFledgeAuctionPartyOrigin, input);
-      auto return_value = GetItemValue<bool>(output_value);
-      ASSERT_EQ(return_value,
-                privacy_sandbox_settings->IsFledgeAllowed(
-                    top_frame_origin, fledge_auction_party_origin,
-                    privacy_sandbox::InterestGroupApiOperation::kSell));
-      return;
-    }
-    case (OutputKey::kIsFledgeBuyAllowed): {
-      SCOPED_TRACE("Check Output: IsFledgeAllowed(kBuy)");
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto fledge_auction_party_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kFledgeAuctionPartyOrigin, input);
-      auto return_value = GetItemValue<bool>(output_value);
-      ASSERT_EQ(return_value,
-                privacy_sandbox_settings->IsFledgeAllowed(
-                    top_frame_origin, fledge_auction_party_origin,
-                    privacy_sandbox::InterestGroupApiOperation::kBuy));
-      return;
-    }
-    case (OutputKey::kIsEventReportingDestinationAttestedForFledge): {
-      SCOPED_TRACE(
-          "Check Output: IsEventReportingDestinationAttestedForFledge()");
-      auto event_reporting_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kEventReportingDestinationOrigin, input);
-      auto return_value = GetItemValue<bool>(output_value);
-      ASSERT_EQ(return_value,
-                privacy_sandbox_settings->IsEventReportingDestinationAttested(
-                    event_reporting_origin,
-                    privacy_sandbox::PrivacySandboxAttestationsGatedAPI::
-                        kProtectedAudience));
-      return;
-    }
-    case (OutputKey::kIsEventReportingDestinationAttestedForFledgeMetric): {
-      SCOPED_TRACE(
-          "Check Output: "
-          "PrivacySandbox.IsPrivacySandboxReportingDestinationAttested "
-          "(FLEDGE)");
-      base::HistogramTester histogram_tester;
-      auto event_reporting_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kEventReportingDestinationOrigin, input);
-      std::ignore =
-          privacy_sandbox_settings->IsEventReportingDestinationAttested(
-              event_reporting_origin,
-              privacy_sandbox::PrivacySandboxAttestationsGatedAPI::
-                  kProtectedAudience);
-      auto histogram_value = GetItemValue<int>(output_value);
-      histogram_tester.ExpectUniqueSample(
-          "PrivacySandbox.IsPrivacySandboxReportingDestinationAttested",
-          histogram_value, 1);
-      return;
-    }
-    case (OutputKey::kIsEventReportingDestinationAttestedForSharedStorage): {
-      SCOPED_TRACE(
-          "Check Output: "
-          "IsEventReportingDestinationAttestedForSharedStorage()");
-      auto event_reporting_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kEventReportingDestinationOrigin, input);
-      auto return_value = GetItemValue<bool>(output_value);
-      ASSERT_EQ(return_value,
-                privacy_sandbox_settings->IsEventReportingDestinationAttested(
-                    event_reporting_origin,
-                    privacy_sandbox::PrivacySandboxAttestationsGatedAPI::
-                        kSharedStorage));
-      return;
-    }
-    case (OutputKey::
-              kIsEventReportingDestinationAttestedForSharedStorageMetric): {
-      SCOPED_TRACE(
-          "Check Output: "
-          "PrivacySandbox.IsPrivacySandboxReportingDestinationAttested "
-          "(SharedStorage)");
-      base::HistogramTester histogram_tester;
-      auto event_reporting_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kEventReportingDestinationOrigin, input);
-      std::ignore =
-          privacy_sandbox_settings->IsEventReportingDestinationAttested(
-              event_reporting_origin,
-              privacy_sandbox::PrivacySandboxAttestationsGatedAPI::
-                  kSharedStorage);
-      auto histogram_value = GetItemValue<int>(output_value);
-      histogram_tester.ExpectUniqueSample(
-          "PrivacySandbox.IsPrivacySandboxReportingDestinationAttested",
-          histogram_value, 1);
-      return;
-    }
-
     case (OutputKey::kIsSharedStorageAllowed): {
-      SCOPED_TRACE("Check Output: kIsSharedStorageAllowed()");
       auto top_frame_origin =
           GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
       auto accessing_origin =
@@ -454,130 +298,6 @@
       return;
     }
 
-    case (OutputKey::kIsPrivateAggregationAllowed): {
-      SCOPED_TRACE("Check Output: IsPrivateAggregationAllowed()");
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto reporting_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kAdMeasurementReportingOrigin, input);
-      auto return_value = GetItemValue<bool>(output_value);
-      ASSERT_EQ(return_value,
-                privacy_sandbox_settings->IsPrivateAggregationAllowed(
-                    top_frame_origin, reporting_origin,
-                    /*out_block_is_site_setting_specific=*/nullptr));
-      return;
-    }
-
-    case (OutputKey::kIsPrivateAggregationDebugModeAllowed): {
-      SCOPED_TRACE("Check Output: IsPrivateAggregationDebugModeAllowed()");
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto reporting_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kAdMeasurementReportingOrigin, input);
-      auto return_value = GetItemValue<bool>(output_value);
-      ASSERT_EQ(return_value,
-                privacy_sandbox_settings->IsPrivateAggregationDebugModeAllowed(
-                    top_frame_origin, reporting_origin));
-      return;
-    }
-
-    case (OutputKey::kIsTopicsAllowedMetric): {
-      SCOPED_TRACE("Check Output: PrivacySandbox.IsTopicsAllowed");
-      base::HistogramTester histogram_tester;
-      std::ignore = privacy_sandbox_settings->IsTopicsAllowed();
-      auto histogram_value = GetItemValue<int>(output_value);
-      histogram_tester.ExpectUniqueSample("PrivacySandbox.IsTopicsAllowed",
-                                          histogram_value, 1);
-      return;
-    }
-    case (OutputKey::kIsTopicsAllowedForContextMetric): {
-      SCOPED_TRACE("Check Output: PrivacySandbox.IsTopicsAllowedForContext");
-      base::HistogramTester histogram_tester;
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto topics_url = GetItemValueForKey<GURL>(InputKey::kTopicsURL, input);
-      std::ignore = privacy_sandbox_settings->IsTopicsAllowedForContext(
-          top_frame_origin, topics_url);
-      auto histogram_value = GetItemValue<int>(output_value);
-      histogram_tester.ExpectUniqueSample(
-          "PrivacySandbox.IsTopicsAllowedForContext", histogram_value, 1);
-      return;
-    }
-    case (OutputKey::kIsFledgeJoinAllowedMetric): {
-      SCOPED_TRACE("Check Output: PrivacySandbox.IsFledgeJoinAllowed");
-      base::HistogramTester histogram_tester;
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto fledge_auction_party_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kFledgeAuctionPartyOrigin, input);
-      std::ignore = privacy_sandbox_settings->IsFledgeAllowed(
-          top_frame_origin, fledge_auction_party_origin,
-          privacy_sandbox::InterestGroupApiOperation::kJoin);
-      auto histogram_value = GetItemValue<int>(output_value);
-      histogram_tester.ExpectUniqueSample("PrivacySandbox.IsFledgeJoinAllowed",
-                                          histogram_value, 1);
-      return;
-    }
-    case (OutputKey::kIsFledgeLeaveAllowedMetric): {
-      SCOPED_TRACE("Check Output: PrivacySandbox.IsFledgeLeaveAllowed");
-      base::HistogramTester histogram_tester;
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto fledge_auction_party_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kFledgeAuctionPartyOrigin, input);
-      std::ignore = privacy_sandbox_settings->IsFledgeAllowed(
-          top_frame_origin, fledge_auction_party_origin,
-          privacy_sandbox::InterestGroupApiOperation::kLeave);
-      auto histogram_value = GetItemValue<int>(output_value);
-      histogram_tester.ExpectUniqueSample("PrivacySandbox.IsFledgeLeaveAllowed",
-                                          histogram_value, 1);
-      return;
-    }
-    case (OutputKey::kIsFledgeUpdateAllowedMetric): {
-      SCOPED_TRACE("Check Output: PrivacySandbox.IsFledgeUpdateAllowed");
-      base::HistogramTester histogram_tester;
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto fledge_auction_party_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kFledgeAuctionPartyOrigin, input);
-      std::ignore = privacy_sandbox_settings->IsFledgeAllowed(
-          top_frame_origin, fledge_auction_party_origin,
-          privacy_sandbox::InterestGroupApiOperation::kUpdate);
-      auto histogram_value = GetItemValue<int>(output_value);
-      histogram_tester.ExpectUniqueSample(
-          "PrivacySandbox.IsFledgeUpdateAllowed", histogram_value, 1);
-      return;
-    }
-    case (OutputKey::kIsFledgeSellAllowedMetric): {
-      SCOPED_TRACE("Check Output: PrivacySandbox.IsFledgeSellAllowed");
-      base::HistogramTester histogram_tester;
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto fledge_auction_party_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kFledgeAuctionPartyOrigin, input);
-      std::ignore = privacy_sandbox_settings->IsFledgeAllowed(
-          top_frame_origin, fledge_auction_party_origin,
-          privacy_sandbox::InterestGroupApiOperation::kSell);
-      auto histogram_value = GetItemValue<int>(output_value);
-      histogram_tester.ExpectUniqueSample("PrivacySandbox.IsFledgeSellAllowed",
-                                          histogram_value, 1);
-      return;
-    }
-    case (OutputKey::kIsFledgeBuyAllowedMetric): {
-      SCOPED_TRACE("Check Output: PrivacySandbox.IsFledgeBuyAllowed");
-      base::HistogramTester histogram_tester;
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto fledge_auction_party_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kFledgeAuctionPartyOrigin, input);
-      std::ignore = privacy_sandbox_settings->IsFledgeAllowed(
-          top_frame_origin, fledge_auction_party_origin,
-          privacy_sandbox::InterestGroupApiOperation::kBuy);
-      auto histogram_value = GetItemValue<int>(output_value);
-      histogram_tester.ExpectUniqueSample("PrivacySandbox.IsFledgeBuyAllowed",
-                                          histogram_value, 1);
-      return;
-    }
     case (OutputKey::kIsSharedStorageAllowedMetric): {
       SCOPED_TRACE("Check Output: PrivacySandbox.IsSharedStorageAllowed");
       base::HistogramTester histogram_tester;
@@ -610,56 +330,6 @@
           "PrivacySandbox.IsSharedStorageSelectURLAllowed", histogram_value, 1);
       return;
     }
-    case (OutputKey::kIsPrivateAggregationAllowedMetric): {
-      SCOPED_TRACE("Check Output: PrivacySandbox.IsPrivateAggregationAllowed");
-      base::HistogramTester histogram_tester;
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto reporting_origin = GetItemValueForKey<url::Origin>(
-          InputKey::kAdMeasurementReportingOrigin, input);
-
-      base::test::ScopedFeatureList scoped_feature_list_{
-          metrics::dwa::kDwaFeature};
-
-      // Ensures that metrics are only counted for this call.
-      // TODO(crbug.com/403946431): Consider implementing a scoped object to
-      // improve ergonomics.
-      metrics::dwa::DwaRecorder::Get()->EnableRecording();
-      metrics::dwa::DwaRecorder::Get()->Purge();
-      ASSERT_THAT(metrics::dwa::DwaRecorder::Get()->GetEntriesForTesting(),
-                  testing::IsEmpty());
-
-      std::ignore = privacy_sandbox_settings->IsPrivateAggregationAllowed(
-          top_frame_origin, reporting_origin,
-          /*out_block_is_site_setting_specific=*/nullptr);
-      auto histogram_value = GetItemValue<int>(output_value);
-      histogram_tester.ExpectUniqueSample(
-          "PrivacySandbox.IsPrivateAggregationAllowed", histogram_value, 1);
-
-      ASSERT_THAT(metrics::dwa::DwaRecorder::Get()->GetEntriesForTesting(),
-                  testing::SizeIs(1));
-      EXPECT_THAT(
-          metrics::dwa::DwaRecorder::Get()
-              ->GetEntriesForTesting()[0]
-              ->event_hash,
-          base::HashMetricName("PrivacySandbox.IsPrivateAggregationAllowed"));
-
-      // DWA content sanitization extracts the eTLD+1 from the provided
-      // reporting origin.
-      EXPECT_THAT(
-          metrics::dwa::DwaRecorder::Get()
-              ->GetEntriesForTesting()[0]
-              ->content_hash,
-          base::HashMetricName(
-              net::registry_controlled_domains::GetDomainAndRegistry(
-                  reporting_origin.GetURL(), net::registry_controlled_domains::
-                                                 INCLUDE_PRIVATE_REGISTRIES)));
-      EXPECT_THAT(
-          metrics::dwa::DwaRecorder::Get()->GetEntriesForTesting()[0]->metrics,
-          testing::UnorderedElementsAre(
-              testing::Pair(base::HashMetricName("Status"), histogram_value)));
-      return;
-    }
     case (OutputKey::kPromptType): {
       // OutputKey::kPromptType is not used.
       // TODO(crbug.com/474716334): Remove this case when the enum is removed.
@@ -816,29 +486,6 @@
       }
       return;
     }
-    case (OutputKey::kIsPrivateAggregationBlockSiteSettingSpecific): {
-      SCOPED_TRACE(
-          "Check Output: Verify out_is_block_site_specific in "
-          "IsPrivateAggregationAllowed()");
-      auto top_frame_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kTopFrameOrigin, input);
-      auto accessing_origin =
-          GetItemValueForKey<url::Origin>(InputKey::kAccessingOrigin, input);
-      bool* actual_out_is_block_site_specific = GetItemValueForKey<bool*>(
-          InputKey::kOutPrivateAggregationBlockIsSiteSettingSpecific, input);
-      privacy_sandbox_settings->IsPrivateAggregationAllowed(
-          top_frame_origin, accessing_origin,
-          actual_out_is_block_site_specific);
-      bool* expected_out_is_block_site_specific =
-          GetItemValue<bool*>(output_value);
-      ASSERT_EQ(!!actual_out_is_block_site_specific,
-                !!expected_out_is_block_site_specific);
-      if (expected_out_is_block_site_specific) {
-        ASSERT_EQ(*actual_out_is_block_site_specific,
-                  *expected_out_is_block_site_specific);
-      }
-      return;
-    }
   }
 }
 
@@ -890,14 +537,4 @@
   }
 }
 
-// static
-bool PrivacySandboxSettingsTestPeer::IsAllowed(Status status) {
-  return privacy_sandbox::PrivacySandboxSettingsImpl::IsAllowed(status);
-}
-
-bool PrivacySandboxSettingsTestPeer::IsFledgeJoiningAllowed(
-    const url::Origin& top_frame_origin) const {
-  return pss_impl_->IsFledgeJoiningAllowed(top_frame_origin);
-}
-
 }  // namespace privacy_sandbox_test_util
diff --git a/components/privacy_sandbox/privacy_sandbox_test_util.h b/components/privacy_sandbox/privacy_sandbox_test_util.h
index 8b471bf..1d0b640 100644
--- a/components/privacy_sandbox/privacy_sandbox_test_util.h
+++ b/components/privacy_sandbox/privacy_sandbox_test_util.h
@@ -44,10 +44,6 @@
 
   using Status = privacy_sandbox::PrivacySandboxSettingsImpl::Status;
 
-  static bool IsAllowed(Status status);
-
-  bool IsFledgeJoiningAllowed(const url::Origin& top_frame_origin) const;
-
  private:
   raw_ptr<privacy_sandbox::PrivacySandboxSettingsImpl> pss_impl_;
 };
@@ -57,7 +53,6 @@
  public:
   MockPrivacySandboxObserver();
   ~MockPrivacySandboxObserver();
-  MOCK_METHOD(void, OnTopicsDataAccessibleSinceUpdated, (), (override));
   MOCK_METHOD1(OnRelatedWebsiteSetsEnabledChanged, void(bool));
 };
 
@@ -139,22 +134,15 @@
   kOutSharedStorageSelectURLDebugMessage = 13,
   kOutSharedStorageBlockIsSiteSettingSpecific = 14,
   kOutSharedStorageSelectURLBlockIsSiteSettingSpecific = 15,
-  kOutPrivateAggregationBlockIsSiteSettingSpecific = 16,
 };
 
 // Defines the expected output of the functions under test, when the profile is
 // setup as per defined state, and they are provided the defined inputs.
 enum class OutputKey {
-  kIsTopicsAllowed = 1,
-  kIsTopicsAllowedForContext = 2,
   kIsSharedStorageAllowed = 6,
   kIsSharedStorageSelectURLAllowed = 7,
-  kIsPrivateAggregationAllowed = 8,
-  kIsTopicsAllowedMetric = 9,
-  kIsTopicsAllowedForContextMetric = 10,
   kIsSharedStorageAllowedMetric = 14,
   kIsSharedStorageSelectURLAllowedMetric = 15,
-  kIsPrivateAggregationAllowedMetric = 16,
   // kPromptType and kM1PromptSuppressedReason are Obsolete.
   // TODO(crbug.com/474716334): Remove obsolete enums.
   kPromptType = 21,
@@ -166,26 +154,10 @@
   kM1FledgeEnabled = 27,
   kM1AdMeasurementEnabled = 28,
   kM1RestrictedNoticeAcknowledged = 31,
-  kIsEventReportingDestinationAttestedForFledge = 32,
-  kIsEventReportingDestinationAttestedForSharedStorage = 33,
-  kIsEventReportingDestinationAttestedForFledgeMetric = 34,
-  kIsEventReportingDestinationAttestedForSharedStorageMetric = 35,
-  kIsFledgeJoinAllowed = 36,
-  kIsFledgeLeaveAllowed = 37,
-  kIsFledgeUpdateAllowed = 38,
-  kIsFledgeSellAllowed = 39,
-  kIsFledgeBuyAllowed = 40,
-  kIsFledgeJoinAllowedMetric = 41,
-  kIsFledgeLeaveAllowedMetric = 42,
-  kIsFledgeUpdateAllowedMetric = 43,
-  kIsFledgeSellAllowedMetric = 44,
-  kIsFledgeBuyAllowedMetric = 45,
-  kIsPrivateAggregationDebugModeAllowed = 47,
   kIsSharedStorageAllowedDebugMessage = 48,
   kIsSharedStorageSelectURLAllowedDebugMessage = 49,
   kIsSharedStorageBlockSiteSettingSpecific = 50,
   kIsSharedStorageSelectURLBlockSiteSettingSpecific = 51,
-  kIsPrivateAggregationBlockSiteSettingSpecific = 52,
 };
 
 // To allow multiple input keys to map to the same value, without having to
diff --git a/components/privacy_sandbox/privacy_sandbox_test_util_unittest.cc b/components/privacy_sandbox/privacy_sandbox_test_util_unittest.cc
index 190ea55..332d22c 100644
--- a/components/privacy_sandbox/privacy_sandbox_test_util_unittest.cc
+++ b/components/privacy_sandbox/privacy_sandbox_test_util_unittest.cc
@@ -288,40 +288,6 @@
   EXPECT_EQ(start_time + base::Hours(1), base::Time::Now());
 }
 
-TEST_F(PrivacySandboxBaseTestUtilTest,
-       VerifyIsTopicsAllowedForContextOutputKey) {
-  GURL kTopicsURL = GURL("https://topics.com");
-
-  EXPECT_CALL(*mock_privacy_sandbox_settings(),
-              IsTopicsAllowedForContext(TopFrameOrigin(), kTopicsURL, nullptr))
-      .WillOnce(testing::Return(true));
-
-  CheckOutput({{InputKey::kTopicsURL, kTopicsURL},
-               {InputKey::kTopFrameOrigin, TopFrameOrigin()}},
-              {OutputKey::kIsTopicsAllowedForContext, true});
-}
-
-TEST_F(PrivacySandboxBaseTestUtilTest, VerifyIsTopicsAllowedOutputKey) {
-  EXPECT_CALL(*mock_privacy_sandbox_settings(), IsTopicsAllowed())
-      .WillOnce(testing::Return(true));
-  CheckOutput({}, {OutputKey::kIsTopicsAllowed, true});
-}
-
-TEST_F(PrivacySandboxBaseTestUtilTest, VerifyIsFledgeAllowedOutputKey) {
-  url::Origin kFledgeAuctionPartyOrigin =
-      url::Origin::Create(GURL("https://fledge.com"));
-
-  EXPECT_CALL(*mock_privacy_sandbox_settings(),
-              IsFledgeAllowed(TopFrameOrigin(), kFledgeAuctionPartyOrigin,
-                              privacy_sandbox::InterestGroupApiOperation::kJoin,
-                              nullptr))
-      .WillOnce(testing::Return(true));
-
-  CheckOutput({{InputKey::kFledgeAuctionPartyOrigin, kFledgeAuctionPartyOrigin},
-               {InputKey::kTopFrameOrigin, TopFrameOrigin()}},
-              {OutputKey::kIsFledgeJoinAllowed, true});
-}
-
 TEST_F(PrivacySandboxBaseTestUtilTest, VerifyIsSharedStorageAllowedOutputKey) {
   EXPECT_CALL(
       *mock_privacy_sandbox_settings(),
@@ -351,23 +317,6 @@
 }
 
 TEST_F(PrivacySandboxBaseTestUtilTest,
-       VerifyIsPrivateAggregationAllowedOutputKey) {
-  url::Origin kAdMeasurementReportingOrigin =
-      url::Origin::Create(GURL("https://reporting.com"));
-
-  EXPECT_CALL(*mock_privacy_sandbox_settings(),
-              IsPrivateAggregationAllowed(
-                  TopFrameOrigin(), kAdMeasurementReportingOrigin,
-                  /*out_block_is_site_setting_specific=*/nullptr))
-      .WillOnce(testing::Return(true));
-
-  CheckOutput(
-      {{InputKey::kAdMeasurementReportingOrigin, kAdMeasurementReportingOrigin},
-       {InputKey::kTopFrameOrigin, TopFrameOrigin()}},
-      {OutputKey::kIsPrivateAggregationAllowed, true});
-}
-
-TEST_F(PrivacySandboxBaseTestUtilTest,
        VerifyIsSharedStorageAllowedDebugMessageOutputKey) {
   std::string actual_out_debug_message;
   EXPECT_CALL(
@@ -457,26 +406,4 @@
                &expected_out_block_is_site_setting_specific});
 }
 
-TEST_F(PrivacySandboxBaseTestUtilTest,
-       VerifyIsPrivateAggregationBlockSiteSettingSpecificOutputKey) {
-  bool actual_out_block_is_site_setting_specific = true;
-  EXPECT_CALL(
-      *mock_privacy_sandbox_settings(),
-      IsPrivateAggregationAllowed(TopFrameOrigin(), AccessingOrigin(),
-                                  /*out_block_is_site_setting_specific=*/
-                                  &actual_out_block_is_site_setting_specific))
-      .WillOnce(testing::DoAll(testing::SetArgPointee<2>(false),
-                               testing::Return(true)));
-
-  // The expected value for `out_block_is_site_setting_specific` here is false
-  // because we are using a mock method that sets it to false.
-  bool expected_out_block_is_site_setting_specific = false;
-  CheckOutput({{InputKey::kAccessingOrigin, AccessingOrigin()},
-               {InputKey::kTopFrameOrigin, TopFrameOrigin()},
-               {InputKey::kOutPrivateAggregationBlockIsSiteSettingSpecific,
-                &actual_out_block_is_site_setting_specific}},
-              {OutputKey::kIsPrivateAggregationBlockSiteSettingSpecific,
-               &expected_out_block_is_site_setting_specific});
-}
-
 }  // namespace privacy_sandbox_test_util
diff --git a/components/private_ai/client.cc b/components/private_ai/client.cc
index 6c9a182..281921a5 100644
--- a/components/private_ai/client.cc
+++ b/components/private_ai/client.cc
@@ -16,6 +16,7 @@
 #include "components/private_ai/connection_factory_impl.h"
 #include "components/private_ai/features.h"
 #include "components/private_ai/phosphor/token_manager.h"
+#include "net/base/url_util.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "url/gurl.h"
 #include "url/url_constants.h"
@@ -63,12 +64,21 @@
 
 // static
 GURL Client::FormatUrl(const std::string& url) {
-  return GURL(base::StrCat({"wss://", url}));
+  GURL base_url(url);
+  if (base_url.is_valid()) {
+    GURL::Replacements replacements;
+    replacements.SetSchemeStr(url::kWssScheme);
+    base_url = base_url.ReplaceComponents(replacements);
+  } else {
+    base_url = GURL(base::StrCat({"wss://", url}));
+  }
+  return base_url;
 }
 
 // static
 GURL Client::FormatUrl(const std::string& url, const std::string& api_key) {
-  return GURL(base::StrCat({"wss://", url, "?key=", api_key}));
+  GURL formatted_url = FormatUrl(url);
+  return net::AppendOrReplaceQueryParameter(formatted_url, "key", api_key);
 }
 
 }  // namespace private_ai
diff --git a/components/private_ai/client_impl_unittest.cc b/components/private_ai/client_impl_unittest.cc
index 3d7d714..f1570fe 100644
--- a/components/private_ai/client_impl_unittest.cc
+++ b/components/private_ai/client_impl_unittest.cc
@@ -18,6 +18,7 @@
 #include "components/private_ai/proto/private_ai.pb.h"
 #include "components/private_ai/status_code.h"
 #include "components/private_ai/testing/fake_connection.h"
+#include "net/base/url_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -256,4 +257,24 @@
   EXPECT_TRUE(destroyed_future.Wait());
 }
 
+TEST(ClientTest, FormatUrl) {
+  std::string api_key;
+
+  GURL url1 = Client::FormatUrl("example.com", "test-key");
+  EXPECT_TRUE(net::GetValueForKeyInQuery(url1, "key", &api_key));
+  EXPECT_EQ(api_key, "test-key");
+
+  GURL url2 = Client::FormatUrl("wss://example.com", "test-key");
+  EXPECT_TRUE(net::GetValueForKeyInQuery(url2, "key", &api_key));
+  EXPECT_EQ(api_key, "test-key");
+
+  GURL url3 = Client::FormatUrl("example.com?foo=bar", "test-key");
+  EXPECT_TRUE(net::GetValueForKeyInQuery(url3, "key", &api_key));
+  EXPECT_EQ(api_key, "test-key");
+
+  GURL url4 = Client::FormatUrl("wss://example.com?foo=bar", "test-key");
+  EXPECT_TRUE(net::GetValueForKeyInQuery(url4, "key", &api_key));
+  EXPECT_EQ(api_key, "test-key");
+}
+
 }  // namespace private_ai
diff --git a/components/safe_browsing/core/common/phishing_classifier/scorer.cc b/components/safe_browsing/core/common/phishing_classifier/scorer.cc
index e5b9860..1c038c2d 100644
--- a/components/safe_browsing/core/common/phishing_classifier/scorer.cc
+++ b/components/safe_browsing/core/common/phishing_classifier/scorer.cc
@@ -439,13 +439,17 @@
   std::unique_ptr<Scorer> scorer =
       Create(std::move(region), std::move(visual_tflite_model));
 
+  if (!scorer) {
+    return nullptr;
+  }
+
   if (image_embedding_model.IsValid()) {
-    if (scorer && !scorer->image_embedding_model_.Initialize(
-                      std::move(image_embedding_model))) {
+    if (!scorer->image_embedding_model_.Initialize(
+            std::move(image_embedding_model))) {
       RecordScorerCreationStatus(
           SCORER_FAIL_FLATBUFFER_INVALID_IMAGE_EMBEDDING_TFLITE_MODEL);
       return nullptr;
-    } else if (scorer) {
+    } else {
       if (!scorer->flatbuffer_model_->img_embedding_metadata()) {
         RecordScorerCreationStatus(SCORER_FAIL_MODEL_MISSING_FIELDS);
         return nullptr;
@@ -523,6 +527,10 @@
     }
   }
 
+  if (!flatbuffer_model_ || !flatbuffer_model_->img_embedding_metadata()) {
+    return;
+  }
+
   SetImageEmbeddingDimensions(
       flatbuffer_model_->img_embedding_metadata()->input_width(),
       flatbuffer_model_->img_embedding_metadata()->input_height());
@@ -583,8 +591,13 @@
   }
 }
 
+bool Scorer::HasVisualTfLiteModel() const {
+  return flatbuffer_model_ != nullptr;
+}
+
 int Scorer::tflite_model_version() const {
-  return flatbuffer_model_->tflite_metadata()->version();
+  return flatbuffer_model_ ? flatbuffer_model_->tflite_metadata()->version()
+                           : 0;
 }
 const google::protobuf::RepeatedPtrField<TfLiteModelMetadata::Threshold>&
 Scorer::tflite_thresholds() const {
@@ -604,7 +617,9 @@
 }
 
 int Scorer::image_embedding_tflite_model_version() const {
-  return flatbuffer_model_->img_embedding_metadata()->version();
+  return (flatbuffer_model_ && flatbuffer_model_->img_embedding_metadata())
+             ? flatbuffer_model_->img_embedding_metadata()->version()
+             : 0;
 }
 
 void ScorerStorage::SetScorer(std::unique_ptr<Scorer> scorer) {
diff --git a/components/safe_browsing/core/common/phishing_classifier/scorer_unittest.cc b/components/safe_browsing/core/common/phishing_classifier/scorer_unittest.cc
index dc32921..585adb36 100644
--- a/components/safe_browsing/core/common/phishing_classifier/scorer_unittest.cc
+++ b/components/safe_browsing/core/common/phishing_classifier/scorer_unittest.cc
@@ -175,4 +175,37 @@
   EXPECT_FALSE(scorer.get());
 }
 
+TEST_F(PhishingScorerTest, ImageEmbeddingModelDefensiveChecks) {
+  // Verify CreateScorerWithImageEmbeddingModel handles invalid region/null
+  // scorer gracefully.
+  std::unique_ptr<Scorer> scorer = Scorer::CreateScorerWithImageEmbeddingModel(
+      base::ReadOnlySharedMemoryRegion(), base::File(), base::File());
+  EXPECT_FALSE(scorer);
+
+  // Verify AttachImageEmbeddingModel handles null flatbuffer_model_
+  // gracefully.
+  scorer = Scorer::Create(/*classification_input_width=*/0,
+                          /*classification_input_height=*/0, base::File());
+  ASSERT_TRUE(scorer);
+  EXPECT_FALSE(scorer->HasVisualTfLiteModel());
+  EXPECT_EQ(0, scorer->image_embedding_tflite_model_version());
+
+  // This should not crash despite flatbuffer_model_ being null.
+  scorer->AttachImageEmbeddingModel(base::File());
+
+  // Verify AttachImageEmbeddingModel and image_embedding_tflite_model_version
+  // handle a valid flatbuffer_model_ that lacks img_embedding_metadata
+  // gracefully.
+  std::string flatbuffer = GetFlatBufferString();
+  base::MappedReadOnlyRegion mapped_region =
+      GetMappedReadOnlyRegionWithData(flatbuffer);
+  scorer = Scorer::Create(mapped_region.region.Duplicate(), base::File());
+  ASSERT_TRUE(scorer);
+  EXPECT_EQ(0, scorer->image_embedding_tflite_model_version());
+
+  // This should not crash despite img_embedding_metadata being null in the
+  // flatbuffer model.
+  scorer->AttachImageEmbeddingModel(base::File());
+}
+
 }  // namespace safe_browsing
diff --git a/components/sampling_profiler/call_stack_profile_params.h b/components/sampling_profiler/call_stack_profile_params.h
index 87ada0bb..c3eb330 100644
--- a/components/sampling_profiler/call_stack_profile_params.h
+++ b/components/sampling_profiler/call_stack_profile_params.h
@@ -22,8 +22,7 @@
     kThreadHung,
     kPeriodicCollection,
     kPeriodicHeapCollection,
-    kPeriodicHeapChurnCollection,
-    kLast = kPeriodicHeapChurnCollection
+    kLast = kPeriodicHeapCollection
   };
 
   // The default constructor is required for mojo and should not be used
diff --git a/components/saved_tab_groups/public/android/java/src/org/chromium/components/tab_group_sync/EitherIdTest.java b/components/saved_tab_groups/public/android/java/src/org/chromium/components/tab_group_sync/EitherIdTest.java
index bc8f1c2..b41dcd8 100644
--- a/components/saved_tab_groups/public/android/java/src/org/chromium/components/tab_group_sync/EitherIdTest.java
+++ b/components/saved_tab_groups/public/android/java/src/org/chromium/components/tab_group_sync/EitherIdTest.java
@@ -4,8 +4,6 @@
 
 package org.chromium.components.tab_group_sync;
 
-import static org.chromium.base.test.util.Batch.PER_CLASS;
-
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -13,14 +11,12 @@
 
 import org.chromium.base.Token;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.components.tab_group_sync.EitherId.EitherGroupId;
 import org.chromium.components.tab_group_sync.EitherId.EitherTabId;
 
 /** Test for both sub classes of EitherId. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(value = PER_CLASS)
 public class EitherIdTest {
     @Test
     public void testCreatingLocalTabId() {
diff --git a/components/search/ntp_features.cc b/components/search/ntp_features.cc
index 7fd5dfe..8809342d7 100644
--- a/components/search/ntp_features.cc
+++ b/components/search/ntp_features.cc
@@ -203,7 +203,6 @@
 BASE_FEATURE(kNtpScaledActionChips, base::FEATURE_DISABLED_BY_DEFAULT);
 
 // If enabled, scaled merchandising action chips will be in a smaller format.
-// Requires #ntp-scaled-action-chips to be enabled too.
 BASE_FEATURE(kNtpScaledActionChipsSmall, base::FEATURE_DISABLED_BY_DEFAULT);
 
 // If enabled, sharepoint module will be shown.
@@ -435,6 +434,14 @@
     &ntp_features::kNtpScaledActionChipsSmall,
     "kNtpMaxSmallChips",
     6);
+const base::FeatureParam<bool> kNtpScaledActionChipsSmallInTestMode(
+    &ntp_features::kNtpScaledActionChipsSmall,
+    "kNtpScaledActionChipsSmallInTestMode",
+    false);
+const base::FeatureParam<bool> kNtpScaledActionChipsShowFallback(
+    &ntp_features::kNtpScaledActionChips,
+    "kNtpScaledActionChipsShowFallback",
+    false);
 
 const base::FeatureParam<int> kNtpCustomizeChromeAutoShownMaxCount(
     &ntp_features::kNtpCustomizeChromeAutoOpen,
diff --git a/components/search/ntp_features.h b/components/search/ntp_features.h
index 9d81a39e..b44a31f 100644
--- a/components/search/ntp_features.h
+++ b/components/search/ntp_features.h
@@ -282,6 +282,13 @@
 // Parameter determining the maximum number of small action chips to show.
 extern const base::FeatureParam<int> kNtpMaxSmallChips;
 
+// Parameter determining if in test mode for small action chips.
+extern const base::FeatureParam<bool> kNtpScaledActionChipsSmallInTestMode;
+
+// Parameter determining if fallback action chips should be shown.
+// Used for local testing.
+extern const base::FeatureParam<bool> kNtpScaledActionChipsShowFallback;
+
 // Parameter determining the minimum amount of time that must pass before
 // shortcuts staleness counters will be incremented.
 extern const base::FeatureParam<base::TimeDelta>
diff --git a/components/signin/core/browser/account_preview_data_service.h b/components/signin/core/browser/account_preview_data_service.h
index 92618d4..93fda8b 100644
--- a/components/signin/core/browser/account_preview_data_service.h
+++ b/components/signin/core/browser/account_preview_data_service.h
@@ -8,6 +8,7 @@
 #include <optional>
 #include <vector>
 
+#include "base/functional/callback.h"
 #include "build/build_config.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/signin/core/browser/account_preview_data.h"
@@ -48,6 +49,16 @@
   virtual std::optional<AccountPreviewPreference> GetPreferredAccountForPromo()
       const = 0;
 
+  // Computes the preview preference for a single account specified by
+  // `gaia_id`. If data is already cached or the account is invalid/tokens
+  // unloaded, `callback` is invoked synchronously with the result. Otherwise, a
+  // network fetch is started and `callback` is invoked asynchronously upon
+  // completion.
+  virtual void GetPreviewPreferenceForAccount(
+      const GaiaId& gaia_id,
+      base::OnceCallback<void(std::optional<AccountPreviewPreference>)>
+          callback) = 0;
+
 #if BUILDFLAG(IS_ANDROID)
   // Updates the account currently used by the external 1P app. A null/empty
   // value means that no account is signed-in in the app.
diff --git a/components/signin/core/browser/account_preview_data_service_impl.cc b/components/signin/core/browser/account_preview_data_service_impl.cc
index 8125af4..58d4d58 100644
--- a/components/signin/core/browser/account_preview_data_service_impl.cc
+++ b/components/signin/core/browser/account_preview_data_service_impl.cc
@@ -76,6 +76,21 @@
   }
 }
 
+AccountPreviewDataService::AccountPreviewPreference
+ComputeAccountPreviewPreference(const GaiaId& gaia_id,
+                                const AccountPreviewData& data) {
+  // TODO(crbug.com/530144650): Align with the heuristic used by
+  // `AccountPreviewDataServiceImpl::ComputePreferredAccount()`.
+  AccountPreviewDataService::AccountPreviewPreference preference;
+  preference.gaia_id = gaia_id;
+  for (const auto& [data_type, count] : data.counts) {
+    if (count > 0) {
+      preference.preferred_data_types.push_back(data_type);
+    }
+  }
+  return preference;
+}
+
 }  // namespace
 
 AccountPreviewDataServiceImpl::AccountPreviewDataServiceImpl(
@@ -113,6 +128,29 @@
   return ReadPreferredAccountFromPrefs();
 }
 
+void AccountPreviewDataServiceImpl::GetPreviewPreferenceForAccount(
+    const GaiaId& gaia_id,
+    base::OnceCallback<void(std::optional<AccountPreviewPreference>)>
+        callback) {
+  auto it = cached_data_.find(gaia_id);
+  if (it != cached_data_.end()) {
+    std::move(callback).Run(
+        ComputeAccountPreviewPreference(gaia_id, it->second));
+    return;
+  }
+
+  if (!identity_manager_ || !identity_manager_->AreRefreshTokensLoaded()) {
+    std::move(callback).Run(std::nullopt);
+    return;
+  }
+
+  single_pending_requests_[gaia_id].push_back(std::move(callback));
+
+  if (!active_fetchers_.contains(gaia_id)) {
+    FetchAccountPreviewData(gaia_id);
+  }
+}
+
 std::optional<AccountPreviewData>
 AccountPreviewDataServiceImpl::GetAccountPreviewData(
     const GaiaId& gaia_id) const {
@@ -161,10 +199,10 @@
 
   cached_data_.erase(gaia_id);
   if (active_fetchers_.contains(gaia_id)) {
+    MaybeNotifySinglePendingRequests(gaia_id);
     // `all_accounts_fetched_barrier_` relies on fetcher results, so it should
     // be called before clearing the active fetcher.
-    CHECK(all_accounts_fetched_barrier_);
-    all_accounts_fetched_barrier_.Run();
+    NotifyBatchBarrierOnFetchCompleted(gaia_id);
     active_fetchers_.erase(gaia_id);
   }
 
@@ -202,18 +240,18 @@
 void AccountPreviewDataServiceImpl::OnSingleFetchCompleted(
     const GaiaId& gaia_id,
     std::optional<AccountPreviewData> data) {
-  bool loaded = data.has_value();
-  if (loaded) {
+  if (data.has_value()) {
     auto [it, inserted] =
         cached_data_.insert_or_assign(gaia_id, std::move(*data));
     metrics_recorder_.RecordMetrics(gaia_id, it->second);
   }
+
+  MaybeNotifySinglePendingRequests(gaia_id);
+  NotifyBatchBarrierOnFetchCompleted(gaia_id);
+
   active_fetchers_.erase(gaia_id);
   // `gaia_id` is owned by the fetcher and should not be used beyond this point.
 
-  CHECK(all_accounts_fetched_barrier_);
-  all_accounts_fetched_barrier_.Run();
-
   if (fetch_complete_callback_for_testing_) {
     std::move(fetch_complete_callback_for_testing_).Run();
   }
@@ -340,6 +378,14 @@
   }
 
   CHECK_GT(target_fetcher_count, 0u);
+  batch_gaia_ids_.clear();
+  for (const auto& [id, fetcher] : active_fetchers_) {
+    batch_gaia_ids_.insert(id);
+  }
+  for (const auto& id : gaia_ids_to_fetch) {
+    batch_gaia_ids_.insert(id);
+  }
+
   all_accounts_fetched_barrier_ = base::BarrierClosure(
       target_fetcher_count,
       base::BindOnce(&AccountPreviewDataServiceImpl::OnAllFetchesCompleted,
@@ -433,6 +479,7 @@
 void AccountPreviewDataServiceImpl::OnAllFetchesCompleted(
     bool should_reset_periodic_timer) {
   all_accounts_fetched_barrier_.Reset();
+  batch_gaia_ids_.clear();
 
   RecordAccountsUsedForLastFetch();
 
@@ -540,11 +587,50 @@
   repeating_timer_->Start();
 }
 
+void AccountPreviewDataServiceImpl::NotifyBatchBarrierOnFetchCompleted(
+    const GaiaId& gaia_id) {
+  if (batch_gaia_ids_.erase(gaia_id)) {
+    CHECK(all_accounts_fetched_barrier_);
+    all_accounts_fetched_barrier_.Run();
+  }
+}
+
+void AccountPreviewDataServiceImpl::MaybeNotifySinglePendingRequests(
+    const GaiaId& gaia_id) {
+  auto it = single_pending_requests_.find(gaia_id);
+  if (it == single_pending_requests_.end() || it->second.empty()) {
+    return;
+  }
+
+  std::optional<AccountPreviewPreference> preference;
+  auto cache_it = cached_data_.find(gaia_id);
+  if (cache_it != cached_data_.end()) {
+    preference = ComputeAccountPreviewPreference(gaia_id, cache_it->second);
+  }
+
+  auto callbacks = std::move(it->second);
+  single_pending_requests_.erase(it);
+  for (auto& cb : callbacks) {
+    std::move(cb).Run(preference);
+  }
+}
+
+void AccountPreviewDataServiceImpl::ClearAllSinglePendingRequests() {
+  for (auto& [gaia_id, callbacks] : single_pending_requests_) {
+    for (auto& cb : callbacks) {
+      std::move(cb).Run(std::nullopt);
+    }
+  }
+  single_pending_requests_.clear();
+}
+
 void AccountPreviewDataServiceImpl::ClearMemoryData() {
   cached_data_.clear();
   active_fetchers_.clear();
-  account_id_to_gaia_id_.clear();
+  ClearAllSinglePendingRequests();
   all_accounts_fetched_barrier_.Reset();
+  batch_gaia_ids_.clear();
+  account_id_to_gaia_id_.clear();
   deferred_fetch_on_loaded_tokens_callback_.Reset();
 }
 
diff --git a/components/signin/core/browser/account_preview_data_service_impl.h b/components/signin/core/browser/account_preview_data_service_impl.h
index 25e6bff7..a53fd15 100644
--- a/components/signin/core/browser/account_preview_data_service_impl.h
+++ b/components/signin/core/browser/account_preview_data_service_impl.h
@@ -23,6 +23,7 @@
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "google_apis/gaia/gaia_id.h"
 #include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
+#include "third_party/abseil-cpp/absl/container/flat_hash_set.h"
 
 class PrefService;
 
@@ -70,6 +71,10 @@
   // AccountPreviewDataService implementation:
   std::optional<AccountPreviewPreference> GetPreferredAccountForPromo()
       const override;
+  void GetPreviewPreferenceForAccount(
+      const GaiaId& gaia_id,
+      base::OnceCallback<void(std::optional<AccountPreviewPreference>)>
+          callback) override;
 
   // Retrieves the cached preview data. Exposed specifically for testing.
   // Note: This may not be available if the browser restarted and no fetch has
@@ -112,6 +117,10 @@
   void ResetTimer();
   std::optional<AccountPreviewPreference> ComputePreferredAccount() const;
 
+  void NotifyBatchBarrierOnFetchCompleted(const GaiaId& gaia_id);
+  void MaybeNotifySinglePendingRequests(const GaiaId& gaia_id);
+  void ClearAllSinglePendingRequests();
+
   void ClearMemoryData();
   void ClearStoredResults();
   void ClearAllDataAndResults();
@@ -140,6 +149,16 @@
                       std::unique_ptr<AccountPreviewDataFetcher>,
                       GaiaId::Hash>
       active_fetchers_;
+  // Used to track single account requests - originating from
+  // `GetPreviewPreferenceForAccount()`.
+  absl::flat_hash_map<GaiaId,
+                      std::vector<base::OnceCallback<void(
+                          std::optional<AccountPreviewPreference>)>>,
+                      GaiaId::Hash>
+      single_pending_requests_;
+  // Used to track batch requests - originating from any source that requires
+  // to compute the overall preferred account.
+  absl::flat_hash_set<GaiaId, GaiaId::Hash> batch_gaia_ids_;
   base::RepeatingClosure all_accounts_fetched_barrier_;
 
   // Mapping used to look up gaia_id based on account_id, used when an account
diff --git a/components/signin/core/browser/account_preview_data_service_impl_unittest.cc b/components/signin/core/browser/account_preview_data_service_impl_unittest.cc
index 190b0854..bbfa088c 100644
--- a/components/signin/core/browser/account_preview_data_service_impl_unittest.cc
+++ b/components/signin/core/browser/account_preview_data_service_impl_unittest.cc
@@ -41,6 +41,9 @@
 
 namespace signin {
 
+using AccountPreviewPreference =
+    AccountPreviewDataService::AccountPreviewPreference;
+
 class AllDataAvailableWaiter {
  public:
   explicit AllDataAvailableWaiter(AccountPreviewDataServiceImpl* service)
@@ -394,8 +397,8 @@
 TEST_F(AccountPreviewDataServiceTest, GetPreferredAccountForPromo) {
   // 1. Initially empty.
   {
-    std::optional<AccountPreviewDataService::AccountPreviewPreference>
-        preference = service_->GetPreferredAccountForPromo();
+    std::optional<AccountPreviewPreference> preference =
+        service_->GetPreferredAccountForPromo();
     EXPECT_FALSE(preference.has_value());
   }
 
@@ -420,8 +423,8 @@
   // TODO(crbug.com/530144650): When the heuristic is implemented, this test
   // should be updated to expect a non-empty preference.
   {
-    std::optional<AccountPreviewDataService::AccountPreviewPreference>
-        preference = service_->GetPreferredAccountForPromo();
+    std::optional<AccountPreviewPreference> preference =
+        service_->GetPreferredAccountForPromo();
     EXPECT_FALSE(preference.has_value());
   }
 }
@@ -812,8 +815,8 @@
   prefs_.SetDict(prefs::kAccountPreviewPreference, std::move(dict));
 
   // Verify that account1 is indeed preferred.
-  std::optional<AccountPreviewDataService::AccountPreviewPreference>
-      preferred_account = service_->GetPreferredAccountForPromo();
+  std::optional<AccountPreviewPreference> preferred_account =
+      service_->GetPreferredAccountForPromo();
   ASSERT_TRUE(preferred_account.has_value());
   ASSERT_EQ(preferred_account->gaia_id, account1.gaia);
 
@@ -825,8 +828,8 @@
   // uncached account1 and the preferred account pref remains intact.
   identity_test_env_.RemoveRefreshTokenForAccount(account2.account_id);
 
-  std::optional<AccountPreviewDataService::AccountPreviewPreference>
-      preferred_account_after = service_->GetPreferredAccountForPromo();
+  std::optional<AccountPreviewPreference> preferred_account_after =
+      service_->GetPreferredAccountForPromo();
   ASSERT_TRUE(preferred_account_after.has_value());
   EXPECT_EQ(account1.gaia, preferred_account_after->gaia_id);
 
@@ -863,8 +866,8 @@
 
   // Verify that the preferred account in prefs was NOT overwritten or
   // recomputed by OnAllFetchesCompleted() because the feature flag is disabled.
-  std::optional<AccountPreviewDataService::AccountPreviewPreference>
-      preference = service_->GetPreferredAccountForPromo();
+  std::optional<AccountPreviewPreference> preference =
+      service_->GetPreferredAccountForPromo();
   ASSERT_TRUE(preference.has_value());
   EXPECT_EQ(kFakeGaiaId, preference->gaia_id);
 }
@@ -881,8 +884,8 @@
 
   prefs_.SetDict(prefs::kAccountPreviewPreference, std::move(dict));
 
-  std::optional<AccountPreviewDataService::AccountPreviewPreference>
-      preference = service_->GetPreferredAccountForPromo();
+  std::optional<AccountPreviewPreference> preference =
+      service_->GetPreferredAccountForPromo();
   ASSERT_TRUE(preference.has_value());
   EXPECT_EQ(GaiaId("test_gaia_id"), preference->gaia_id);
   std::vector<syncer::DataType> expected_types = {syncer::BOOKMARKS,
@@ -1450,4 +1453,116 @@
             preference->other_device_form_factor);
 }
 
+TEST_F(AccountPreviewDataServiceTest,
+       GetPreviewPreferenceForAccountCachedData) {
+  AccountInfo account =
+      identity_test_env_.MakeAccountAvailable("user@gmail.com");
+
+  MockSuccessfulFetch(&test_url_loader_factory_);
+  base::RunLoop run_loop;
+  service_->SetFetchCompleteCallbackForTesting(run_loop.QuitClosure());
+  run_loop.Run();
+
+  ASSERT_TRUE(service_->GetAccountPreviewData(account.gaia).has_value());
+
+  base::RunLoop fetch_run_loop;
+  std::optional<AccountPreviewPreference> fetched_preference;
+  service_->GetPreviewPreferenceForAccount(
+      account.gaia, base::BindOnce(
+                        [](base::OnceClosure quit,
+                           std::optional<AccountPreviewPreference>* result,
+                           std::optional<AccountPreviewPreference> pref) {
+                          *result = std::move(pref);
+                          std::move(quit).Run();
+                        },
+                        fetch_run_loop.QuitClosure(), &fetched_preference));
+  fetch_run_loop.Run();
+
+  EXPECT_TRUE(fetched_preference.has_value());
+  EXPECT_EQ(account.gaia, fetched_preference->gaia_id);
+}
+
+TEST_F(AccountPreviewDataServiceTest,
+       GetPreviewPreferenceForAccountTriggersFetch) {
+  AccountInfo account =
+      identity_test_env_.MakeAccountAvailable("user@gmail.com");
+
+  ASSERT_FALSE(service_->GetAccountPreviewData(account.gaia).has_value());
+
+  base::RunLoop fetch_run_loop;
+  std::optional<AccountPreviewPreference> fetched_preference;
+  service_->GetPreviewPreferenceForAccount(
+      account.gaia, base::BindOnce(
+                        [](base::OnceClosure quit,
+                           std::optional<AccountPreviewPreference>* result,
+                           std::optional<AccountPreviewPreference> pref) {
+                          *result = std::move(pref);
+                          std::move(quit).Run();
+                        },
+                        fetch_run_loop.QuitClosure(), &fetched_preference));
+
+  MockSuccessfulFetch(&test_url_loader_factory_);
+  fetch_run_loop.Run();
+
+  EXPECT_TRUE(fetched_preference.has_value());
+  EXPECT_EQ(account.gaia, fetched_preference->gaia_id);
+  EXPECT_TRUE(service_->GetAccountPreviewData(account.gaia).has_value());
+}
+
+TEST_F(AccountPreviewDataServiceTest,
+       GetPreviewPreferenceForAccountInvalidAccount) {
+  base::RunLoop fetch_run_loop;
+  std::optional<AccountPreviewPreference> fetched_preference;
+  service_->GetPreviewPreferenceForAccount(
+      GaiaId("non_existent_gaia_id"),
+      base::BindOnce(
+          [](base::OnceClosure quit,
+             std::optional<AccountPreviewPreference>* result,
+             std::optional<AccountPreviewPreference> pref) {
+            *result = std::move(pref);
+            std::move(quit).Run();
+          },
+          fetch_run_loop.QuitClosure(), &fetched_preference));
+  fetch_run_loop.Run();
+
+  EXPECT_FALSE(fetched_preference.has_value());
+}
+
+TEST_F(AccountPreviewDataServiceTest,
+       GetPreviewPreferenceForAccountDoesNotInterfereWithAllAccountsBarrier) {
+  AccountInfo account1 =
+      identity_test_env_.MakeAccountAvailable("account1@gmail.com");
+
+  // An active fetcher is created for account1 during token update.
+  EXPECT_TRUE(service_->HasActiveFetcherForTesting(account1.gaia));
+
+  // Attach a single account fetch callback to the ongoing fetch for account1.
+  base::RunLoop single_fetch_run_loop;
+  std::optional<AccountPreviewPreference> single_fetch_preference;
+  service_->GetPreviewPreferenceForAccount(
+      account1.gaia,
+      base::BindOnce(
+          [](base::OnceClosure quit,
+             std::optional<AccountPreviewPreference>* result,
+             std::optional<AccountPreviewPreference> pref) {
+            *result = std::move(pref);
+            std::move(quit).Run();
+          },
+          single_fetch_run_loop.QuitClosure(), &single_fetch_preference));
+
+  AllDataAvailableWaiter waiter(service_.get());
+
+  // Complete fetch for account1.
+  MockSuccessfulFetch(&test_url_loader_factory_);
+  single_fetch_run_loop.Run();
+  waiter.Wait();
+
+  // Both single fetch callback and batch completion barrier should run as
+  // expected.
+  EXPECT_TRUE(single_fetch_preference.has_value());
+  EXPECT_EQ(account1.gaia, single_fetch_preference->gaia_id);
+  EXPECT_TRUE(waiter.is_all_data_available());
+  EXPECT_TRUE(service_->GetAccountPreviewData(account1.gaia).has_value());
+}
+
 }  // namespace signin
diff --git a/components/signin/internal/identity_manager/account_capabilities_list.h b/components/signin/internal/identity_manager/account_capabilities_list.h
index 0748e32..b631c4a 100644
--- a/components/signin/internal/identity_manager/account_capabilities_list.h
+++ b/components/signin/internal/identity_manager/account_capabilities_list.h
@@ -197,6 +197,12 @@
                      switches::kEnableFakeCapabilityForTesting)
 #endif
 
+ACCOUNT_CAPABILITY_F(
+    kIsSubjectToUniversalOptOutCapabilityName,
+    IS_SUBJECT_TO_UNIVERSAL_OPT_OUT_CAPABILITY_NAME,
+    "accountcapabilities/gmydemrnmnqxa",
+    switches::kReadIsSubjectToUniversalOptOutCapability)
+
 #if BUILDFLAG(IS_IOS)
 ACCOUNT_CAPABILITY_F(kMustFetchAppleAgeRangeInChromeCapabilityName,
                      MUST_FETCH_APPLE_AGE_RANGE_IN_CHROME_CAPABILITY_NAME,
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/base/AccountCapabilities.java b/components/signin/public/android/java/src/org/chromium/components/signin/base/AccountCapabilities.java
index b49fa06..67c1aee 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/base/AccountCapabilities.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/base/AccountCapabilities.java
@@ -200,6 +200,15 @@
     }
 
     /**
+     * @return isSubjectToUniversalOptOut capability value.
+     */
+    public @Tribool int isSubjectToUniversalOptOut() {
+        return getCapabilityByName(
+                AccountCapabilitiesConstants
+                        .IS_SUBJECT_TO_UNIVERSAL_OPT_OUT_CAPABILITY_NAME);
+    }
+
+    /**
      * @return supportsWalletPrivatePassesInAutofill capability value.
      */
     public @Tribool int supportsWalletPrivatePassesInAutofill() {
diff --git a/components/signin/public/android/junit/src/org/chromium/components/signin/base/AccountCapabilitiesTest.java b/components/signin/public/android/junit/src/org/chromium/components/signin/base/AccountCapabilitiesTest.java
index 1912483..8def491 100644
--- a/components/signin/public/android/junit/src/org/chromium/components/signin/base/AccountCapabilitiesTest.java
+++ b/components/signin/public/android/junit/src/org/chromium/components/signin/base/AccountCapabilitiesTest.java
@@ -78,6 +78,9 @@
                 return capabilities.isSubjectToEnterpriseFeatures();
             case AccountCapabilitiesConstants.IS_SUBJECT_TO_PARENTAL_CONTROLS_CAPABILITY_NAME:
                 return capabilities.isSubjectToParentalControls();
+            case AccountCapabilitiesConstants
+                    .IS_SUBJECT_TO_UNIVERSAL_OPT_OUT_CAPABILITY_NAME:
+                return capabilities.isSubjectToUniversalOptOut();
             case AccountCapabilitiesConstants.SUPPORTS_WALLET_PRIVATE_PASSES_IN_AUTOFILL_NAME:
                 return capabilities.supportsWalletPrivatePassesInAutofill();
                 /** keep-sorted end */
@@ -122,6 +125,8 @@
                             AccountCapabilitiesConstants.CAN_USE_GEMINI_IN_CHROME_CAPABILITY_NAME,
                             AccountCapabilitiesConstants
                                     .IS_SUBJECT_TO_PARENTAL_CONTROLS_CAPABILITY_NAME,
+                            AccountCapabilitiesConstants
+                                    .IS_SUBJECT_TO_UNIVERSAL_OPT_OUT_CAPABILITY_NAME,
                             AccountCapabilitiesConstants.CAN_USE_SPEAKER_LABEL_IN_RECORDER_APP,
                             AccountCapabilitiesConstants
                                     .SUPPORTS_WALLET_PRIVATE_PASSES_IN_AUTOFILL_NAME,
diff --git a/components/signin/public/base/signin_switches.cc b/components/signin/public/base/signin_switches.cc
index e7bdf03..88bcf98f 100644
--- a/components/signin/public/base/signin_switches.cc
+++ b/components/signin/public/base/signin_switches.cc
@@ -735,6 +735,9 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 #endif
 
+BASE_FEATURE(kReadIsSubjectToUniversalOptOutCapability,
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kReadSupportsWalletPrivatePassesInAutofillCapability,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
diff --git a/components/signin/public/base/signin_switches.h b/components/signin/public/base/signin_switches.h
index 768b525..8339d041 100644
--- a/components/signin/public/base/signin_switches.h
+++ b/components/signin/public/base/signin_switches.h
@@ -713,6 +713,10 @@
 
 // Enables fetching the capability of the same name on all platforms.
 COMPONENT_EXPORT(SIGNIN_SWITCHES)
+BASE_DECLARE_FEATURE(kReadIsSubjectToUniversalOptOutCapability);
+
+// Enables fetching the capability of the same name on all platforms.
+COMPONENT_EXPORT(SIGNIN_SWITCHES)
 BASE_DECLARE_FEATURE(kReadSupportsWalletPrivatePassesInAutofillCapability);
 
 #if !BUILDFLAG(IS_ANDROID)
diff --git a/components/signin/public/identity_manager/account_capabilities.cc b/components/signin/public/identity_manager/account_capabilities.cc
index 05c9418..a9615a8 100644
--- a/components/signin/public/identity_manager/account_capabilities.cc
+++ b/components/signin/public/identity_manager/account_capabilities.cc
@@ -311,6 +311,10 @@
   return GetCapabilityByName(kIsSubjectToParentalControlsCapabilityName);
 }
 
+signin::Tribool AccountCapabilities::is_subject_to_universal_opt_out() const {
+  return GetCapabilityByName(kIsSubjectToUniversalOptOutCapabilityName);
+}
+
 #if BUILDFLAG(IS_IOS)
 signin::Tribool AccountCapabilities::must_fetch_apple_age_range_in_chrome()
     const {
diff --git a/components/signin/public/identity_manager/account_capabilities.h b/components/signin/public/identity_manager/account_capabilities.h
index 34f2a9f7..73ee2c55 100644
--- a/components/signin/public/identity_manager/account_capabilities.h
+++ b/components/signin/public/identity_manager/account_capabilities.h
@@ -170,6 +170,9 @@
   // Chrome applies parental controls to accounts with this capability.
   signin::Tribool is_subject_to_parental_controls() const;
 
+  // Whether the user is subject to Universal Opt-Out.
+  signin::Tribool is_subject_to_universal_opt_out() const;
+
 #if BUILDFLAG(IS_IOS)
   // Whether the client must fetch Apple's age range in Chrome.
   signin::Tribool must_fetch_apple_age_range_in_chrome() const;
diff --git a/components/signin/public/identity_manager/account_capabilities_test_mutator.cc b/components/signin/public/identity_manager/account_capabilities_test_mutator.cc
index 3cc77dc4..9c931763 100644
--- a/components/signin/public/identity_manager/account_capabilities_test_mutator.cc
+++ b/components/signin/public/identity_manager/account_capabilities_test_mutator.cc
@@ -217,6 +217,12 @@
       value;
 }
 
+void AccountCapabilitiesTestMutator::set_is_subject_to_universal_opt_out(
+    bool value) {
+  capabilities_
+      ->capabilities_map_[kIsSubjectToUniversalOptOutCapabilityName] = value;
+}
+
 #if BUILDFLAG(IS_IOS)
 void AccountCapabilitiesTestMutator::set_must_fetch_apple_age_range_in_chrome(
     bool value) {
diff --git a/components/signin/public/identity_manager/account_capabilities_test_mutator.h b/components/signin/public/identity_manager/account_capabilities_test_mutator.h
index 6c9e2a8e..a03cf19 100644
--- a/components/signin/public/identity_manager/account_capabilities_test_mutator.h
+++ b/components/signin/public/identity_manager/account_capabilities_test_mutator.h
@@ -75,6 +75,7 @@
       bool value);
   void set_is_subject_to_enterprise_features(bool value);
   void set_is_subject_to_parental_controls(bool value);
+  void set_is_subject_to_universal_opt_out(bool value);
 #if BUILDFLAG(IS_IOS)
   void set_must_fetch_apple_age_range_in_chrome(bool value);
 #endif
diff --git a/components/signin/public/identity_manager/account_capabilities_unittest.cc b/components/signin/public/identity_manager/account_capabilities_unittest.cc
index 154a38f..6ae6d5e 100644
--- a/components/signin/public/identity_manager/account_capabilities_unittest.cc
+++ b/components/signin/public/identity_manager/account_capabilities_unittest.cc
@@ -451,6 +451,21 @@
       signin::Tribool::kFalse);
 }
 
+TEST_F(AccountCapabilitiesTest, IsSubjectToUniversalOptOut) {
+  AccountCapabilities capabilities;
+  EXPECT_EQ(capabilities.is_subject_to_universal_opt_out(),
+            signin::Tribool::kUnknown);
+
+  AccountCapabilitiesTestMutator mutator(&capabilities);
+  mutator.set_is_subject_to_universal_opt_out(true);
+  EXPECT_EQ(capabilities.is_subject_to_universal_opt_out(),
+            signin::Tribool::kTrue);
+
+  mutator.set_is_subject_to_universal_opt_out(false);
+  EXPECT_EQ(capabilities.is_subject_to_universal_opt_out(),
+            signin::Tribool::kFalse);
+}
+
 TEST_F(AccountCapabilitiesTest, SupportsWalletPrivatePassesInAutofill) {
   AccountCapabilities capabilities;
   EXPECT_EQ(capabilities.supports_wallet_private_passes_in_autofill(),
diff --git a/components/skills/internal/enterprise_skills_provider.cc b/components/skills/internal/enterprise_skills_provider.cc
index bd8b90a..f2fb8e3 100644
--- a/components/skills/internal/enterprise_skills_provider.cc
+++ b/components/skills/internal/enterprise_skills_provider.cc
@@ -4,6 +4,7 @@
 
 #include "components/skills/internal/enterprise_skills_provider.h"
 
+#include <iterator>
 #include <string_view>
 
 #include "base/barrier_closure.h"
@@ -35,6 +36,18 @@
 constexpr int kMaxNetworkRetries = 3;
 constexpr char kDefaultEnterpriseSkillIcon[] = "💼";
 
+// Static illustration URLs hosted on Google's static CDN (gstatic.com).
+// Assigned to enterprise skills in round-robin fashion (skill_index %
+// hosted_image_count).
+constexpr std::string_view kEnterpriseSkillImages[] = {
+    "https://www.gstatic.com/chrome/skills/images/enterprise_briefcase.png",
+    "https://www.gstatic.com/chrome/skills/images/enterprise_building_blue.png",
+    "https://www.gstatic.com/chrome/skills/images/"
+    "enterprise_building_yellow.png",
+    "https://www.gstatic.com/chrome/skills/images/enterprise_folder.png",
+    "https://www.gstatic.com/chrome/skills/images/enterprise_luggage.png",
+};
+
 bool IsFieldValid(std::string_view field, size_t max_length) {
   return !field.empty() && field.length() <= max_length;
 }
@@ -292,6 +305,13 @@
 
 void EnterpriseSkillsProvider::OnAllFetchesComplete() {
   // TODO(b/533517209): Record Enterprise.Skills.Count.
+  const size_t hosted_image_count = std::size(kEnterpriseSkillImages);
+  for (size_t skill_index = 0; skill_index < pending_skills_.size();
+       ++skill_index) {
+    pending_skills_[skill_index]->image_url =
+        GURL(kEnterpriseSkillImages[skill_index % hosted_image_count]);
+  }
+
   skills_ = std::move(pending_skills_);
   pending_skills_.clear();
   NotifyObservers();
diff --git a/components/skills/internal/enterprise_skills_provider_unittest.cc b/components/skills/internal/enterprise_skills_provider_unittest.cc
index 208ed25..d0dc70d 100644
--- a/components/skills/internal/enterprise_skills_provider_unittest.cc
+++ b/components/skills/internal/enterprise_skills_provider_unittest.cc
@@ -4,8 +4,10 @@
 
 #include "components/skills/internal/enterprise_skills_provider.h"
 
+#include <iterator>
 #include <memory>
 #include <string>
+#include <string_view>
 
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/string_number_conversions.h"
@@ -303,6 +305,9 @@
   EXPECT_EQ("Prompt content goes here.", skills[0]->prompt);
   EXPECT_FALSE(skills[0]->id.empty());
   EXPECT_EQ(skills[0]->id.length(), 36u);
+  EXPECT_EQ(
+      "https://www.gstatic.com/chrome/skills/images/enterprise_briefcase.png",
+      skills[0]->image_url.spec());
 }
 
 TEST_F(EnterpriseSkillsProviderFetchTest, ValidateResourceRequest) {
@@ -395,6 +400,53 @@
 }
 
 TEST_F(EnterpriseSkillsProviderFetchTest,
+       MultipleSkillsAssignsRoundRobinImages) {
+  std::vector<std::pair<std::string, std::string>> policy_entries;
+  std::vector<std::string> contents;
+  for (int i = 0; i < 6; ++i) {
+    std::string url =
+        "https://example.com/skill" + base::NumberToString(i) + ".yaml";
+    std::string content = "---\nname: Skill " + base::NumberToString(i) +
+                          "\ndescription: D\n---\nP";
+    std::string hash = base::HexEncode(crypto::hash::Sha256(content));
+    policy_entries.emplace_back(url, hash);
+    contents.push_back(content);
+  }
+
+  base::RunLoop run_loop;
+  auto sub = provider_->RegisterSkillsChangedCallback(run_loop.QuitClosure());
+  SetPolicyPref(policy_entries);
+
+  for (int i = 0; i < 6; ++i) {
+    test_url_loader_factory_.AddResponse(policy_entries[i].first, contents[i]);
+  }
+  run_loop.Run();
+
+  const auto& skills = provider_->GetSkills();
+  ASSERT_EQ(6u, skills.size());
+  EXPECT_EQ(
+      "https://www.gstatic.com/chrome/skills/images/enterprise_briefcase.png",
+      skills[0]->image_url.spec());
+  EXPECT_EQ(
+      "https://www.gstatic.com/chrome/skills/images/"
+      "enterprise_building_blue.png",
+      skills[1]->image_url.spec());
+  EXPECT_EQ(
+      "https://www.gstatic.com/chrome/skills/images/"
+      "enterprise_building_yellow.png",
+      skills[2]->image_url.spec());
+  EXPECT_EQ(
+      "https://www.gstatic.com/chrome/skills/images/enterprise_folder.png",
+      skills[3]->image_url.spec());
+  EXPECT_EQ(
+      "https://www.gstatic.com/chrome/skills/images/enterprise_luggage.png",
+      skills[4]->image_url.spec());
+  EXPECT_EQ(
+      "https://www.gstatic.com/chrome/skills/images/enterprise_briefcase.png",
+      skills[5]->image_url.spec());
+}
+
+TEST_F(EnterpriseSkillsProviderFetchTest,
        PolicyChangeMidFlightCancelsPreviousFetches) {
   std::string content1 = "---\nname: Skill 1\ndescription: D1\n---\nP1";
   std::string content2 = "---\nname: Skill 2\ndescription: D2\n---\nP2";
diff --git a/components/stylus_handwriting/android/junit/src/org/chromium/components/stylus_handwriting/StylusWritingControllerTest.java b/components/stylus_handwriting/android/junit/src/org/chromium/components/stylus_handwriting/StylusWritingControllerTest.java
index 0ae8abc..a45aea4 100644
--- a/components/stylus_handwriting/android/junit/src/org/chromium/components/stylus_handwriting/StylusWritingControllerTest.java
+++ b/components/stylus_handwriting/android/junit/src/org/chromium/components/stylus_handwriting/StylusWritingControllerTest.java
@@ -31,7 +31,6 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.RobolectricUtil;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Feature;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.ViewAndroidDelegate;
@@ -40,7 +39,6 @@
  * Tests for StylusWritingController. Specifically how it handles whether to show a stylus hover
  * icon or not.
  */
-@Batch(Batch.PER_CLASS)
 @RunWith(BaseRobolectricTestRunner.class)
 @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
 public class StylusWritingControllerTest {
diff --git a/components/tracing/DEPS b/components/tracing/DEPS
index 5ab6e38..58ca9b64 100644
--- a/components/tracing/DEPS
+++ b/components/tracing/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+absl/container/flat_hash_map.h",
   "+ipc",
   "+components/metrics",
   "+components/prefs",
diff --git a/components/tracing/common/active_processes_win.cc b/components/tracing/common/active_processes_win.cc
index dcd9754f..de8650c6 100644
--- a/components/tracing/common/active_processes_win.cc
+++ b/components/tracing/common/active_processes_win.cc
@@ -9,6 +9,7 @@
 #include "base/base_paths.h"
 #include "base/command_line.h"
 #include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/version.h"
 
 namespace tracing {
@@ -42,6 +43,19 @@
 
 }  // namespace
 
+ActiveProcesses::Image::Image(base::FilePath path,
+                              uint64_t base_address,
+                              uint64_t size,
+                              std::optional<std::string> debug_id)
+    : path_(std::move(path)),
+      base_address_(base_address),
+      size_(size),
+      debug_id_(debug_id) {}
+
+ActiveProcesses::Image::Image() = default;
+ActiveProcesses::Image::Image(const Image& other) = default;
+ActiveProcesses::Image::~Image() = default;
+
 ActiveProcesses::Process::Process(uint32_t pid,
                                   uint32_t parent_pid,
                                   uint32_t session_id,
@@ -212,6 +226,61 @@
   threads_.erase(iter);
 }
 
+void ActiveProcesses::AddLoadedImage(uint32_t pid,
+                                     uint64_t base_address,
+                                     uint64_t size,
+                                     base::FilePath path) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto iter = processes_.find(pid);
+  if (iter == processes_.end()) {
+    // Cannot track the address space of a process that is unknown. Its start
+    // event must have been lost.
+    return;
+  }
+  std::optional<std::string> debug_id;
+  const auto debug_id_iter = known_debug_ids_.find(path);
+  if (debug_id_iter != known_debug_ids_.end()) {
+    debug_id = debug_id_iter->second;
+  }
+  const Image image(std::move(path), base_address, size, debug_id);
+  iter->second.loaded_images.insert_or_assign(base_address, std::move(image));
+}
+
+void ActiveProcesses::RemoveLoadedImage(uint32_t pid,
+                                        uint64_t base_address,
+                                        uint64_t size,
+                                        base::FilePath path) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto iter = processes_.find(pid);
+  if (iter == processes_.end()) {
+    // Cannot track the address space of a process that is unknown. Its start
+    // event must have been lost.
+    return;
+  }
+  iter->second.loaded_images.erase(base_address);
+}
+
+std::optional<ActiveProcesses::Image> ActiveProcesses::GetImageForAddress(
+    uint32_t pid,
+    uint64_t address) {
+  auto iter = processes_.find(pid);
+  if (iter == processes_.end()) {
+    return std::nullopt;
+  }
+  const auto& loaded_images = iter->second.loaded_images;
+  auto upper_bound_iter = loaded_images.upper_bound(address);
+  if (upper_bound_iter != loaded_images.begin()) {
+    // Find the image with the highest base address that's below `address`, and
+    // return it if `address` is within its address range.
+    const auto& image = (upper_bound_iter - 1)->second;
+    if (address - image.base_address_ < image.size_) {
+      return image;
+    }
+  }
+  // An image containing `address` was not found.
+  return std::nullopt;
+}
+
 ActiveProcesses::Category ActiveProcesses::GetThreadCategory(
     uint32_t tid) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -222,6 +291,13 @@
   return Category::kOther;
 }
 
+ActiveProcesses::Category ActiveProcesses::GetProcessCategory(
+    uint32_t pid) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  const auto iter = processes_.find(pid);
+  return iter != processes_.end() ? iter->second.category : Category::kOther;
+}
+
 std::wstring_view ActiveProcesses::GetThreadName(uint32_t tid) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
diff --git a/components/tracing/common/active_processes_win.h b/components/tracing/common/active_processes_win.h
index a091690..d21a44d0 100644
--- a/components/tracing/common/active_processes_win.h
+++ b/components/tracing/common/active_processes_win.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_TRACING_COMMON_ACTIVE_PROCESSES_WIN_H_
 #define COMPONENTS_TRACING_COMMON_ACTIVE_PROCESSES_WIN_H_
 
+#include <windows.h>
+
 #include <stdint.h>
 
 #include <optional>
@@ -14,6 +16,8 @@
 #include <unordered_set>
 #include <utility>
 
+#include "absl/container/flat_hash_map.h"
+#include "base/containers/flat_map.h"
 #include "base/files/file_path.h"
 #include "base/memory/raw_ptr.h"
 #include "base/process/process_handle.h"
@@ -43,6 +47,23 @@
     kOther = 2,
   };
 
+  // Details about an image (a.k.a. executable, module, or DLL) loaded in a
+  // process's memory.
+  struct Image {
+    Image();
+    Image(base::FilePath path,
+          uint64_t base_address,
+          uint64_t size,
+          std::optional<std::string> debug_id);
+    Image(const Image&);
+    ~Image();
+
+    base::FilePath path_;
+    uint64_t base_address_ = 0;
+    uint64_t size_ = 0;
+    std::optional<std::string> debug_id_;
+  };
+
   // Constructs an instance for a trace initiated on behalf of the process
   // identified by `client_pid`. This and 1) any of its direct children of it
   // with the same image filename and 2) any other program residing in the same
@@ -75,10 +96,30 @@
   // Remove's a process's thread from the collection, if it is present.
   void RemoveThread(uint32_t pid, uint32_t tid);
 
+  // Adds a loaded image to the collection.
+  void AddLoadedImage(uint32_t pid,
+                      uint64_t base_address,
+                      uint64_t size,
+                      base::FilePath path);
+
+  // Removes a loaded image from the collection if present.
+  void RemoveLoadedImage(uint32_t process_id,
+                         uint64_t base_address,
+                         uint64_t size,
+                         base::FilePath path);
+
+  // Returns the image in the given process's address space that contains
+  // `address`.
+  std::optional<Image> GetImageForAddress(uint32_t pid, uint64_t address);
+
   // Returns the category for the process to which the thread `tid` belongs.
   // Returns kOther if `tid` is unknown.
   Category GetThreadCategory(uint32_t tid) const;
 
+  // Returns the category for the given process. Returns `kOther` if the process
+  // ID is unknown.
+  Category GetProcessCategory(uint32_t pid) const;
+
   // Returns a thread's name, or an empty view if not found or unset. The
   // returned view may become invalidated following any other operation on this
   // instance.
@@ -107,6 +148,7 @@
     std::wstring command_line;
     Category category;
     std::unordered_set<uint32_t> threads;
+    base::flat_map<uint64_t, Image> loaded_images;
   };
 
   // Handles addition of the `Process` corresponding to the tracing client. All
@@ -149,6 +191,12 @@
   // not yet been added or has been removed.
   raw_ptr<Process> client_process_ = nullptr;
 
+  // A mapping of image path to debug ID, for loaded images (i.e., running
+  // executables and loaded DLLs) of interest. Enables symbolization of call
+  // stacks for ETW events.
+  // TODO(crbug.com/400769265): fill `known_debug_ids_` from the browser.
+  absl::flat_hash_map<base::FilePath, std::string> known_debug_ids_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
diff --git a/components/tracing/common/etw_consumer_win.cc b/components/tracing/common/etw_consumer_win.cc
index 463cb0b..f198518 100644
--- a/components/tracing/common/etw_consumer_win.cc
+++ b/components/tracing/common/etw_consumer_win.cc
@@ -15,6 +15,7 @@
 #include "base/containers/buffer_iterator.h"
 #include "base/containers/fixed_flat_map.h"
 #include "base/containers/span.h"
+#include "base/files/file_util.h"
 #include "base/hash/hash.h"
 #include "base/location.h"
 #include "base/logging.h"
@@ -183,6 +184,13 @@
       0x11d0,
       {0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c}};
 
+  // ImageLoad event provider GUID, 2cb15d1d-5fc1-11d2-abe1-00a0c911f518
+  static constexpr GUID kImageLoadGuid = {
+      0x2cb15d1d,
+      0x5fc1,
+      0x11d2,
+      {0xab, 0xe1, 0x00, 0xa0, 0xc9, 0x11, 0xf5, 0x18}};
+
   // StackWalk event provider GUID, def2fe46-7bd6-4b80-bd94-f57fe20d0ce3
   static constexpr GUID kStackWalkGuid = {
       0xdef2fe46,
@@ -200,6 +208,7 @@
            {kMemInfoGuid, &EtwConsumer::HandleMemInfoEvent},
            {kFileIoGuid, &EtwConsumer::HandleFileIoEvent},
            {kDiskIoGuid, &EtwConsumer::HandleDiskIoEvent},
+           {kImageLoadGuid, &EtwConsumer::HandleImageLoadEvent},
            {kStackWalkGuid, &EtwConsumer::HandleStackWalkEvent}});
 
   auto* const self = reinterpret_cast<EtwConsumer*>(event_record->UserContext);
@@ -414,6 +423,66 @@
   }
 }
 
+void EtwConsumer::HandleImageLoadEvent(const EVENT_HEADER& header,
+                                       const ETW_BUFFER_CONTEXT& buffer_context,
+                                       size_t pointer_size,
+                                       base::span<const uint8_t> packet_data) {
+  // Size of `ImageLoad` event:
+  //   3 pointers + 8 `uint32`s + wide string contents + wide string terminator.
+  // Check that `packet_data` is large enough to hold at least the pointers,
+  // integers, and wide string terminator.
+  const size_t kMinimumSize =
+      3 * pointer_size + 8 * sizeof(uint32_t) + sizeof(wchar_t);
+  if (packet_data.size() < kMinimumSize) {
+    return;
+  }
+
+  if (!inclusion_policy_.ShouldRecordCallStacks(header.ThreadId)) {
+    return;
+  }
+
+  // Read the contents of `packet_data`.
+  base::BufferIterator<const uint8_t> iterator{packet_data};
+  const auto image_base = CopyPointer(iterator, pointer_size);
+  const auto image_size = CopyPointer(iterator, pointer_size);
+  auto process_id = *iterator.CopyObject<uint32_t>();
+  if (header.ProcessId != static_cast<DWORD>(-1)) {
+    process_id = header.ProcessId;
+  }
+  (void)iterator.CopyObject<uint32_t>();      // ImageChecksum
+  (void)iterator.CopyObject<uint32_t>();      // TimeDateStamp
+  (void)iterator.CopyObject<uint32_t>();      // Reserved0
+  (void)CopyPointer(iterator, pointer_size);  // DefaultBase
+  (void)iterator.CopyObject<uint32_t>();      // Reserved1
+  (void)iterator.CopyObject<uint32_t>();      // Reserved2
+  (void)iterator.CopyObject<uint32_t>();      // Reserved3
+  (void)iterator.CopyObject<uint32_t>();      // Reserved4
+  const auto file_name = CopyWString(iterator);
+  if (!file_name.has_value()) {
+    return;
+  }
+  base::FilePath path;
+  if (!base::DevicePathToDriveLetterPath(base::FilePath(*file_name), &path)) {
+    return;
+  }
+  switch (header.EventDescriptor.Opcode) {
+    // Emitted when an image is loaded.
+    case EVENT_TRACE_TYPE_LOAD:
+    // Emitted when tracing starts, once per image loaded at the time.
+    case EVENT_TRACE_TYPE_DC_START:
+      active_processes_.AddLoadedImage(process_id, image_base, image_size,
+                                       path);
+      break;
+    // Emitted when an image is unloaded.
+    case EVENT_TRACE_TYPE_END:
+    // Emitted when tracing ends, once per image loaded at the time.
+    case EVENT_TRACE_TYPE_DC_END:
+      active_processes_.RemoveLoadedImage(process_id, image_base, image_size,
+                                          path);
+      break;
+  }
+}
+
 void EtwConsumer::HandleStackWalkEvent(const EVENT_HEADER& header,
                                        const ETW_BUFFER_CONTEXT& buffer_context,
                                        size_t pointer_size,
@@ -432,7 +501,7 @@
     return;
   }
   const auto qpc_timestamp = *iterator.CopyObject<uint64_t>();
-  (void)*iterator.CopyObject<uint32_t>();  // StackProcess
+  const auto stack_process = *iterator.CopyObject<uint32_t>();
   const auto stack_thread = *iterator.CopyObject<uint32_t>();
   if (!inclusion_policy_.ShouldRecordCallStacks(stack_thread)) {
     return;
@@ -454,8 +523,8 @@
 
   // Use a hash of the call stack as a unique identifier for interning.
   size_t ip_hash = 0;
-  for (const auto& instruction_pointer : call_stack) {
-    ip_hash = base::HashInts(ip_hash, instruction_pointer);
+  for (const auto& ip : call_stack) {
+    ip_hash = base::HashInts(ip_hash, ip);
   }
   InterningIndexEntry interned_callstack =
       interned_callstacks_.LookupOrAdd(ip_hash);
@@ -483,15 +552,60 @@
 
   // Intern each stack frame not seen before.
   std::vector<InterningID> frame_ids;
-  for (const auto& instruction_pointer : call_stack) {
+  for (const auto& ip : call_stack) {
+    // Intern the debug ID (unique identifier) for the module this stack frame
+    // belongs to, and the relative address (i.e., offset within the module), to
+    // enable it to be symbolized.
+    std::optional<ActiveProcesses::Image> module =
+        active_processes_.GetImageForAddress(stack_process, ip);
     InterningIndexEntry interned_frame =
-        interned_frames_.LookupOrAdd(instruction_pointer);
-    if (!interned_frame.was_emitted) {
-      auto* frame_entry = interned_data->add_frames();
-      frame_entry->set_iid(interned_frame.id);
-      frame_entry->set_rel_pc(instruction_pointer);
-    }
+        interned_frames_.LookupOrAdd(std::make_pair(stack_process, ip));
     frame_ids.push_back(interned_frame.id);
+    if (interned_frame.was_emitted) {
+      continue;
+    }
+
+    auto* frame_proto = interned_data->add_frames();
+    frame_proto->set_iid(interned_frame.id);
+    frame_proto->set_rel_pc(module.has_value() ? ip - module->base_address_
+                                               : ip);
+    if (!module.has_value()) {
+      continue;
+    }
+
+    // Intern the module this stack frame belongs to if needed.
+    InterningIndexEntry interned_module = interned_modules_.LookupOrAdd(
+        std::make_pair(stack_process, module->base_address_));
+    frame_proto->set_mapping_id(interned_module.id);
+    if (interned_module.was_emitted) {
+      continue;
+    }
+
+    // Intern the module's debug ID if needed.
+    const auto debug_id_str = module->debug_id_.value_or("");
+    InterningIndexEntry interned_module_debug_id =
+        interned_module_debug_ids_.LookupOrAdd(debug_id_str);
+    if (!interned_module_debug_id.was_emitted) {
+      auto* module_id_proto = interned_data->add_build_ids();
+      module_id_proto->set_iid(interned_module_debug_id.id);
+      module_id_proto->set_str(debug_id_str.data());
+    }
+
+    // Intern the module's filename if needed.
+    const auto module_name = module->path_.BaseName().value();
+    InterningIndexEntry interned_module_name =
+        interned_module_names_.LookupOrAdd(module_name);
+    if (!interned_module_name.was_emitted) {
+      auto* module_name_proto = interned_data->add_mapping_paths();
+      module_name_proto->set_iid(interned_module_name.id);
+      module_name_proto->set_str(base::WideToUTF8(module_name.data()));
+    }
+
+    // Intern the module.
+    auto* module_proto = interned_data->add_mappings();
+    module_proto->set_iid(interned_module.id);
+    module_proto->set_build_id(interned_module_debug_id.id);
+    module_proto->add_path_string_ids(interned_module_name.id);
   }
 
   auto* callstack_entry = interned_data->add_callstacks();
diff --git a/components/tracing/common/etw_consumer_win.h b/components/tracing/common/etw_consumer_win.h
index 146cd3ff..e1342b6 100644
--- a/components/tracing/common/etw_consumer_win.h
+++ b/components/tracing/common/etw_consumer_win.h
@@ -9,6 +9,7 @@
 
 #include <atomic>
 #include <memory>
+#include <string>
 #include <unordered_map>
 
 #include "base/containers/span.h"
@@ -23,6 +24,7 @@
 #include "services/tracing/public/cpp/perfetto/interning_index.h"
 #include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
 #include "third_party/perfetto/include/perfetto/tracing/trace_writer_base.h"
+#include "third_party/perfetto/protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 
 namespace perfetto::protos::pbzero {
 class EtwTraceEvent;
@@ -119,6 +121,11 @@
                           size_t pointer_size,
                           base::span<const uint8_t> packet_data)
       VALID_CONTEXT_REQUIRED(sequence_checker_);
+  void HandleImageLoadEvent(const EVENT_HEADER& header,
+                            const ETW_BUFFER_CONTEXT& buffer_context,
+                            size_t pointer_size,
+                            base::span<const uint8_t> packet_data)
+      VALID_CONTEXT_REQUIRED(sequence_checker_);
   void HandleStackWalkEvent(const EVENT_HEADER& header,
                             const ETW_BUFFER_CONTEXT& buffer_context,
                             size_t pointer_size,
@@ -320,8 +327,17 @@
 
   // Call stacks, each interned by a hash of the entire call stack.
   InterningIndex<TypeList<size_t>, SizeList<1024>> interned_callstacks_;
-  // Stack frames, each interned by the address on the stack.
-  InterningIndex<TypeList<uint64_t>, SizeList<1024>> interned_frames_;
+  // Stack frames, each interned by a [process ID, address] pair.
+  InterningIndex<TypeList<std::pair<uint32_t, uint64_t>>, SizeList<1024>>
+      interned_frames_;
+  // Filenames for loaded modules.
+  InterningIndex<TypeList<std::wstring>, SizeList<1024>> interned_module_names_;
+  // Debug IDs for loaded modules.
+  InterningIndex<TypeList<std::string>, SizeList<1024>>
+      interned_module_debug_ids_;
+  // Loaded modules, each interned by a [process ID, base address] pair.
+  InterningIndex<TypeList<std::pair<uint32_t, uint64_t>>, SizeList<1024>>
+      interned_modules_;
 
   // If true, interned data will be reset before the next event is processed.
   std::atomic<bool> reset_emitted_state_{true};
diff --git a/components/ukm/observers/ukm_consent_state_observer.cc b/components/ukm/observers/ukm_consent_state_observer.cc
index 50cab4b..6a14458 100644
--- a/components/ukm/observers/ukm_consent_state_observer.cc
+++ b/components/ukm/observers/ukm_consent_state_observer.cc
@@ -152,23 +152,32 @@
     // persistent preference state to avoid migrating them to a disabled state.
     // Once migrated, advanced reporting consent is completely decoupled from
     // sync preferences, meaning this is a one-time initialization.
-    bool apps_supported =
-        sync_service->GetUserSettings()->GetRegisteredSelectableTypes().Has(
-            syncer::UserSelectableType::kApps);
     bool extensions_supported =
         sync_service->GetUserSettings()->GetRegisteredSelectableTypes().Has(
             syncer::UserSelectableType::kExtensions);
-
-    bool apps_sync_enabled =
-        !apps_supported ||
-        sync_service->GetUserSettings()->GetSelectedTypes().Has(
-            syncer::UserSelectableType::kApps);
-
     bool extensions_sync_enabled =
         !extensions_supported ||
         sync_service->GetUserSettings()->GetSelectedTypes().Has(
             syncer::UserSelectableType::kExtensions);
 
+#if BUILDFLAG(IS_CHROMEOS)
+    bool apps_supported =
+        sync_service->GetUserSettings()->GetRegisteredSelectableOsTypes().Has(
+            syncer::UserSelectableOsType::kOsApps);
+    bool apps_sync_enabled =
+        !apps_supported ||
+        sync_service->GetUserSettings()->GetSelectedOsTypes().Has(
+            syncer::UserSelectableOsType::kOsApps);
+#else
+    bool apps_supported =
+        sync_service->GetUserSettings()->GetRegisteredSelectableTypes().Has(
+            syncer::UserSelectableType::kApps);
+    bool apps_sync_enabled =
+        !apps_supported ||
+        sync_service->GetUserSettings()->GetSelectedTypes().Has(
+            syncer::UserSelectableType::kApps);
+#endif
+
     advanced_reporting_enabled =
         msbb_enabled && apps_sync_enabled && extensions_sync_enabled;
   }
diff --git a/components/ukm/observers/ukm_consent_state_observer_unittest.cc b/components/ukm/observers/ukm_consent_state_observer_unittest.cc
index 15aff76c..45ba470a 100644
--- a/components/ukm/observers/ukm_consent_state_observer_unittest.cc
+++ b/components/ukm/observers/ukm_consent_state_observer_unittest.cc
@@ -542,14 +542,23 @@
   MockSyncService sync;
   sync.SetSignedIn(signin::ConsentLevel::kSignin);
 
+#if BUILDFLAG(IS_CHROMEOS)
+  sync.SetAppSync(true);
+#endif
+
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
   // Mark apps and extensions sync as supported (registered) and enabled.
-  sync.GetUserSettings()->SetRegisteredSelectableTypes(
-      {syncer::UserSelectableType::kApps,
-       syncer::UserSelectableType::kExtensions});
-  sync.GetUserSettings()->SetSelectedTypes(
-      /*sync_everything=*/false, {syncer::UserSelectableType::kApps,
-                                  syncer::UserSelectableType::kExtensions});
+  syncer::UserSelectableTypeSet registered_types = {
+      syncer::UserSelectableType::kExtensions};
+  syncer::UserSelectableTypeSet selected_types = {
+      syncer::UserSelectableType::kExtensions};
+#if !BUILDFLAG(IS_CHROMEOS)
+  registered_types.Put(syncer::UserSelectableType::kApps);
+  selected_types.Put(syncer::UserSelectableType::kApps);
+#endif
+  sync.GetUserSettings()->SetRegisteredSelectableTypes(registered_types);
+  sync.GetUserSettings()->SetSelectedTypes(/*sync_everything=*/false,
+                                           selected_types);
 #endif
 
   sync_preferences::TestingPrefServiceSyncable prefs;
@@ -575,9 +584,12 @@
 
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
   // Mark apps and extensions sync as supported, but only extensions enabled.
-  sync.GetUserSettings()->SetRegisteredSelectableTypes(
-      {syncer::UserSelectableType::kApps,
-       syncer::UserSelectableType::kExtensions});
+  syncer::UserSelectableTypeSet registered_types = {
+      syncer::UserSelectableType::kExtensions};
+#if !BUILDFLAG(IS_CHROMEOS)
+  registered_types.Put(syncer::UserSelectableType::kApps);
+#endif
+  sync.GetUserSettings()->SetRegisteredSelectableTypes(registered_types);
   sync.GetUserSettings()->SetSelectedTypes(
       /*sync_everything=*/false, {syncer::UserSelectableType::kExtensions});
 #endif
@@ -613,13 +625,22 @@
   MockSyncService sync;
   sync.SetSignedIn(signin::ConsentLevel::kSignin);
 
+#if BUILDFLAG(IS_CHROMEOS)
+  sync.SetAppSync(true);
+#endif
+
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
-  sync.GetUserSettings()->SetRegisteredSelectableTypes(
-      {syncer::UserSelectableType::kApps,
-       syncer::UserSelectableType::kExtensions});
-  sync.GetUserSettings()->SetSelectedTypes(
-      /*sync_everything=*/false, {syncer::UserSelectableType::kApps,
-                                  syncer::UserSelectableType::kExtensions});
+  syncer::UserSelectableTypeSet registered_types = {
+      syncer::UserSelectableType::kExtensions};
+  syncer::UserSelectableTypeSet selected_types = {
+      syncer::UserSelectableType::kExtensions};
+#if !BUILDFLAG(IS_CHROMEOS)
+  registered_types.Put(syncer::UserSelectableType::kApps);
+  selected_types.Put(syncer::UserSelectableType::kApps);
+#endif
+  sync.GetUserSettings()->SetRegisteredSelectableTypes(registered_types);
+  sync.GetUserSettings()->SetSelectedTypes(/*sync_everything=*/false,
+                                           selected_types);
 #endif
 
   sync_preferences::TestingPrefServiceSyncable prefs;
diff --git a/components/viz/common/frame_sinks/begin_frame_source.h b/components/viz/common/frame_sinks/begin_frame_source.h
index 418e2efb..33c5ffb6 100644
--- a/components/viz/common/frame_sinks/begin_frame_source.h
+++ b/components/viz/common/frame_sinks/begin_frame_source.h
@@ -245,10 +245,7 @@
   // Notifies the source that it may need to reconnect to a VSync source (e.g.,
   // DisplayLinkMac) for the specified display. This is typically triggered by
   // display configuration changes in the browser process.
-  // |is_browser_vsync_supported| indicates whether the browser-side
-  // CADisplayLink is valid.
-  virtual void UpdateVSyncDisplay(int64_t display_id,
-                                  bool is_browser_vsync_supported) {}
+  virtual void UpdateVSyncDisplay(int64_t display_id) {}
 
 #endif
 
diff --git a/components/viz/service/frame_sinks/external_begin_frame_source_mac.cc b/components/viz/service/frame_sinks/external_begin_frame_source_mac.cc
index 40aec862..23508f42 100644
--- a/components/viz/service/frame_sinks/external_begin_frame_source_mac.cc
+++ b/components/viz/service/frame_sinks/external_begin_frame_source_mac.cc
@@ -122,20 +122,8 @@
 // Forces an update of the DisplayLinkMac for the specified display. This is
 // called when the browser-side CADisplayLink state changes (e.g., becomes
 // valid or invalid) or when a display is added or removed.
-void ExternalBeginFrameSourceMac::UpdateVSyncDisplay(
-    int64_t display_id,
-    bool is_browser_vsync_supported) {
+void ExternalBeginFrameSourceMac::UpdateVSyncDisplay(int64_t display_id) {
   if (display_id_ == display_id) {
-    // Defer the transition to the browser-side DisplayLink if we are currently
-    // active (needing begin frames) to avoid rendering jank.
-    if (is_browser_vsync_supported && needs_begin_frames_) {
-      vsync_display_id_update_deferred_ = true;
-      return;
-    }
-
-    // If the browser-side DisplayLink is unsupported, or active rendering
-    // has stopped, apply the VSync display id update immediately.
-    vsync_display_id_update_deferred_ = false;
     SetVSyncDisplayID(display_id_, /*force_update=*/true);
   }
 }
@@ -152,7 +140,6 @@
   // Forward the |display_id| to output surface for frame presentation.
   output_surface_->SetVSyncDisplayID(display_id, force_update);
 
-  vsync_display_id_update_deferred_ = false;
   // Remove the current callback from display_link_mac_ or from the timer.
   if (needs_begin_frames_ || vsync_callback_mac_) {
     StopBeginFrame(/*force_stop=*/true);
@@ -311,14 +298,6 @@
     StartBeginFrame(/*display_link_keep_alive_only=*/false);
   } else {
     StopBeginFrame(/*force_stop=*/false);
-
-    // |update_vsync_params_callback_| is set in RootCompositorFrameSinkImpl().
-    // A null update_vsync_params_callback_ indicates
-    // RootCompositorFrameSinkImpl is being destroyed.
-    if (vsync_display_id_update_deferred_ && update_vsync_params_callback_) {
-      vsync_display_id_update_deferred_ = false;
-      SetVSyncDisplayID(display_id_, /*force_update=*/true);
-    }
   }
 }
 
diff --git a/components/viz/service/frame_sinks/external_begin_frame_source_mac.h b/components/viz/service/frame_sinks/external_begin_frame_source_mac.h
index 39e38e9..80e958d6 100644
--- a/components/viz/service/frame_sinks/external_begin_frame_source_mac.h
+++ b/components/viz/service/frame_sinks/external_begin_frame_source_mac.h
@@ -45,8 +45,7 @@
   // BeginFrameSource implementation.
   void SetVSyncDisplayID(int64_t display_id, bool force_update) override;
 
-  void UpdateVSyncDisplay(int64_t display_id,
-                          bool is_browser_vsync_supported) override;
+  void UpdateVSyncDisplay(int64_t display_id) override;
 
   // ExternalBeginFrameSourceClient implementation.
   void OnNeedsBeginFrames(bool needs_begin_frames) override;
@@ -85,11 +84,6 @@
   void StartBeginFrame(bool display_link_keep_alive_only);
   void StopBeginFrame(bool force_stop);
 
-  // Defer `UpdateVSyncDisplay()` calls while `needs_begin_frames_` is true
-  // to ensure a jank-free transition when switching to the browser-side
-  // DisplayLink. (Used for the kCADisplayLinkInBrowser feature only).
-  void UpdateDeferredVSyncDisplayIfNeeded();
-
   void RecordFirstFrameHistograms(bool is_timer);
 
   // Implements base::PowerSuspendObserver.
@@ -126,12 +120,6 @@
 
   bool just_started_begin_frame_ = false;
 
-  // Indicates that a VSync display update is deferred until active rendering
-  // (`needs_begin_frames_`) stops, ensuring a smooth transition to the
-  // browser-side DisplayLink. (Used for the kCADisplayLinkInBrowser feature
-  // only).
-  bool vsync_display_id_update_deferred_ = false;
-
   // To prevent the DisplayLink from constantly toggling on and off, allow it
   // to continue running for this many consecutive VSyncs after it is no longer
   // needed.
diff --git a/components/viz/service/frame_sinks/external_begin_frame_source_mac_unittest.cc b/components/viz/service/frame_sinks/external_begin_frame_source_mac_unittest.cc
index f280d40..e61cf13 100644
--- a/components/viz/service/frame_sinks/external_begin_frame_source_mac_unittest.cc
+++ b/components/viz/service/frame_sinks/external_begin_frame_source_mac_unittest.cc
@@ -287,8 +287,7 @@
   EXPECT_FALSE(source_->display_link_mac());
   EXPECT_TRUE(source_->time_source());
 
-  source_->UpdateVSyncDisplay(/*display_id=*/1,
-                              /*is_browser_vsync_supported=*/true);
+  source_->UpdateVSyncDisplay(/*display_id=*/1);
   EXPECT_TRUE(source_->display_link_mac());
   EXPECT_FALSE(source_->time_source());
 }
diff --git a/components/viz/service/frame_sinks/external_begin_frame_source_mojo_mac.cc b/components/viz/service/frame_sinks/external_begin_frame_source_mojo_mac.cc
index 7a45736b..4e92e02 100644
--- a/components/viz/service/frame_sinks/external_begin_frame_source_mojo_mac.cc
+++ b/components/viz/service/frame_sinks/external_begin_frame_source_mojo_mac.cc
@@ -18,7 +18,7 @@
         controller_receiver,
     mojo::PendingRemote<mojom::ExternalBeginFrameControllerClient>
         controller_remote_client,
-    base::RepeatingCallback<void(int64_t, bool)> update_vsync_displays_cb)
+    base::RepeatingCallback<void(int64_t)> update_vsync_displays_cb)
     : receiver_(this),
       remote_client_(std::move(controller_remote_client)),
       update_vsync_displays_cb_(std::move(update_vsync_displays_cb)) {
@@ -78,7 +78,7 @@
   // When ExternalBeginFrameSourceMac is using DisplayLink in Browser, destroy
   // and recreate a DisplayLinkMac in every ExternalBeginFrameSourceMac if
   // needed.
-  update_vsync_displays_cb_.Run(display_id, is_browser_vsync_supported);
+  update_vsync_displays_cb_.Run(display_id);
 }
 
 void ExternalBeginFrameSourceMojoMac::IssueExternalBeginFrame(
diff --git a/components/viz/service/frame_sinks/external_begin_frame_source_mojo_mac.h b/components/viz/service/frame_sinks/external_begin_frame_source_mojo_mac.h
index 6078ca3..3daf297 100644
--- a/components/viz/service/frame_sinks/external_begin_frame_source_mojo_mac.h
+++ b/components/viz/service/frame_sinks/external_begin_frame_source_mojo_mac.h
@@ -26,7 +26,7 @@
           controller_receiver,
       mojo::PendingRemote<mojom::ExternalBeginFrameControllerClient>
           controller_remote_client,
-      base::RepeatingCallback<void(int64_t, bool)> update_vsync_displays_cb);
+      base::RepeatingCallback<void(int64_t)> update_vsync_displays_cb);
   ~ExternalBeginFrameSourceMojoMac() override;
 
   // mojom::ExternalBeginFrameController implementation.
@@ -55,7 +55,7 @@
   // Callback to FrameSinkManagerImpl::UpdateVSyncDisplays(), used to notify
   // all root frame sinks on the specified display to refresh their vsync
   // sources.
-  base::RepeatingCallback<void(int64_t, bool)> update_vsync_displays_cb_;
+  base::RepeatingCallback<void(int64_t)> update_vsync_displays_cb_;
 
   base::WeakPtrFactory<ExternalBeginFrameSourceMojoMac> weak_factory_{this};
 };
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
index d5de44d..91aae28 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -298,13 +298,11 @@
           update_vsync_displays_cb);
 }
 
-void FrameSinkManagerImpl::UpdateVSyncDisplays(
-    int64_t display_id,
-    bool is_browser_vsync_supported) {
+void FrameSinkManagerImpl::UpdateVSyncDisplays(int64_t display_id) {
   for (auto& root_frame_sink : root_sink_map_) {
     if (root_frame_sink.second->external_begin_frame_source()) {
       root_frame_sink.second->external_begin_frame_source()->UpdateVSyncDisplay(
-          display_id, is_browser_vsync_supported);
+          display_id);
     }
   }
 }
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.h b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
index 61aac945..c88844c 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.h
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
@@ -450,7 +450,7 @@
   // Forces every RootCompositorFrameSink associated with the specified display
   // to update its DisplayLinkMac. This ensures that frame sinks stay in sync
   // with the display configuration when displays are added or removed.
-  void UpdateVSyncDisplays(int64_t display_id, bool is_browser_vsync_supported);
+  void UpdateVSyncDisplays(int64_t display_id);
 #endif
 
  private:
diff --git a/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorImplPasswordOnlyTest.java b/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorImplPasswordOnlyTest.java
index 0696608..a5720988 100644
--- a/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorImplPasswordOnlyTest.java
+++ b/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorImplPasswordOnlyTest.java
@@ -24,7 +24,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.blink.mojom.Authenticator;
 import org.chromium.blink.mojom.GetCredentialOptions;
 import org.chromium.blink.mojom.Mediation;
@@ -39,7 +38,6 @@
 /** Tests for {@link AuthenticatorImpl} with password-only requests. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 @SmallTest
 public class AuthenticatorImplPasswordOnlyTest {
     private AuthenticatorImpl mAuthenticator;
diff --git a/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorImplTest.java b/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorImplTest.java
index eb6c4c8..ca2e26a7 100644
--- a/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorImplTest.java
+++ b/components/webauthn/android/junit/src/org/chromium/components/webauthn/AuthenticatorImplTest.java
@@ -31,7 +31,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.blink.mojom.Authenticator;
 import org.chromium.blink.mojom.AuthenticatorStatus;
 import org.chromium.blink.mojom.GetCredentialOptions;
@@ -51,7 +50,6 @@
 /** Tests for {@link AuthenticatorImpl}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 @SmallTest
 public class AuthenticatorImplTest {
     private AuthenticatorImpl mAuthenticator;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index fa5dd12..42c59c8 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2401,6 +2401,8 @@
       "memory_pressure/user_level_memory_pressure_signal_generator.h",
       "renderer_host/input/touch_selection_controller_input_observer.cc",
       "renderer_host/input/touch_selection_controller_input_observer.h",
+      "renderer_host/unbounded_surface_window_android.cc",
+      "renderer_host/unbounded_surface_window_android.h",
     ]
   }
 
diff --git a/content/browser/accessibility/browser_accessibility_state_impl_auralinux.cc b/content/browser/accessibility/browser_accessibility_state_impl_auralinux.cc
index 9a71b53..a5eced3b 100644
--- a/content/browser/accessibility/browser_accessibility_state_impl_auralinux.cc
+++ b/content/browser/accessibility/browser_accessibility_state_impl_auralinux.cc
@@ -14,16 +14,12 @@
 #include <sstream>
 #include <string>
 
-#include "base/callback_list.h"
 #include "base/debug/crash_logging.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task/thread_pool.h"
 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
-#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/scoped_accessibility_mode.h"
-#include "ui/gfx/animation/animation.h"
-#include "ui/linux/linux_ui.h"
 
 namespace content {
 
@@ -114,8 +110,7 @@
 class BrowserAccessibilityStateImplAuralinux
     : public BrowserAccessibilityStateImpl {
  public:
-  BrowserAccessibilityStateImplAuralinux();
-  ~BrowserAccessibilityStateImplAuralinux() override;
+  BrowserAccessibilityStateImplAuralinux() = default;
 
   // BrowserAccessibilityStateImpl:
   void RefreshAssistiveTech() override;
@@ -123,7 +118,6 @@
 
  private:
   void OnDiscoveredOrca(bool is_orca_active);
-  void OnAnimationsEnabledChanged();
 
   // A ScopedAccessibilityMode that holds AXMode::kScreenReader when
   // an active screen reader has been detected.
@@ -132,27 +126,8 @@
   // The presence of an AssistiveTech is currently being recomputed.
   // Will be updated via DiscoverOrca().
   bool awaiting_known_assistive_tech_computation_ = false;
-
-  base::CallbackListSubscription animations_enabled_subscription_;
 };
 
-BrowserAccessibilityStateImplAuralinux::
-    BrowserAccessibilityStateImplAuralinux() {
-  animations_enabled_subscription_ =
-      ui::LinuxUi::RegisterAnimationsEnabledCallback(base::BindRepeating(
-          &BrowserAccessibilityStateImplAuralinux::OnAnimationsEnabledChanged,
-          base::Unretained(this)));
-}
-
-BrowserAccessibilityStateImplAuralinux::
-    ~BrowserAccessibilityStateImplAuralinux() = default;
-
-void BrowserAccessibilityStateImplAuralinux::OnAnimationsEnabledChanged() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  gfx::Animation::UpdatePrefersReducedMotion();
-  NotifyWebContentsPreferencesChanged();
-}
-
 void BrowserAccessibilityStateImplAuralinux::RefreshAssistiveTech() {
   if (!awaiting_known_assistive_tech_computation_) {
     awaiting_known_assistive_tech_computation_ = true;
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 92f6e62..e4757f7 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -77,6 +77,7 @@
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
+#include "content/browser/renderer_host/unbounded_surface_window_android.h"
 #include "content/browser/renderer_host/visible_time_request_trigger.h"
 #include "content/browser/screen_orientation/screen_orientation_provider.h"
 #include "content/common/features.h"
@@ -1877,6 +1878,18 @@
   delete this;
 }
 
+void RenderWidgetHostViewAndroid::CreateUnboundedSurface(
+    mojo::PendingAssociatedReceiver<blink::mojom::UnboundedSurfaceHost> host,
+    mojo::PendingAssociatedRemote<blink::mojom::UnboundedSurfaceClient> client,
+    const gfx::Rect& bounds_in_dips,
+    base::WeakPtr<RenderWidgetHostViewBase> subframe_view) {
+  gfx::Rect bounds_in_screen =
+      ConvertSubframeBoundsToScreen(bounds_in_dips, subframe_view.get());
+  unbounded_surface_window_ = UnboundedSurfaceWindowAndroid::Create(
+      this, std::move(host), std::move(client), bounds_in_screen,
+      std::move(subframe_view));
+}
+
 void RenderWidgetHostViewAndroid::UpdateTooltipUnderCursor(
     const std::u16string& tooltip_text) {
   if (!IsTooltipsEnabled()) {
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index a499fd8..8f2180e 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -212,6 +212,12 @@
   void ShowWithVisibility(PageVisibilityState page_visibility) final;
   void WasOccluded() override;
   void Destroy() override;
+  void CreateUnboundedSurface(
+      mojo::PendingAssociatedReceiver<blink::mojom::UnboundedSurfaceHost> host,
+      mojo::PendingAssociatedRemote<blink::mojom::UnboundedSurfaceClient>
+          client,
+      const gfx::Rect& bounds_in_dips,
+      base::WeakPtr<RenderWidgetHostViewBase> subframe_view) override;
   void UpdateTooltipUnderCursor(const std::u16string& tooltip_text) override;
   void UpdateTooltip(const std::u16string& tooltip_text) override;
   void UpdateTooltipFromKeyboard(const std::u16string& tooltip_text,
diff --git a/content/browser/renderer_host/unbounded_element_browsertest.cc b/content/browser/renderer_host/unbounded_element_browsertest.cc
index 07ced2b..a7dc1de4 100644
--- a/content/browser/renderer_host/unbounded_element_browsertest.cc
+++ b/content/browser/renderer_host/unbounded_element_browsertest.cc
@@ -9,9 +9,11 @@
 #include "base/test/test_future.h"
 #include "build/build_config.h"
 #include "cc/base/features.h"
+#include "components/input/render_widget_host_input_event_router.h"
 #include "components/viz/common/frame_sinks/copy_output_result.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
 #include "content/browser/renderer_host/unbounded_surface_window.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/input/synthetic_gesture_target.h"
@@ -40,8 +42,29 @@
 #include "ui/ozone/public/ozone_platform.h"
 #endif
 
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/android_info.h"
+#endif
+
 namespace content {
 
+namespace {
+
+void SkipTestsForUnsupportedPlatforms() {
+#if BUILDFLAG(IS_IOS)
+  // TODO(crbug.com/508672616): Not yet implemented on iOS.
+  GTEST_SKIP();
+#elif BUILDFLAG(IS_ANDROID)
+  if (base::android::android_info::sdk_int() <
+      base::android::android_info::SDK_VERSION_U) {
+    GTEST_SKIP()
+        << "Unbounded elements require Android U (API 34+ / Android 14+).";
+  }
+#endif
+}
+
+}  // namespace
+
 class UnboundedElementBrowserTestBase : public ContentBrowserTest {
  public:
   UnboundedElementBrowserTestBase() = default;
@@ -137,10 +160,7 @@
   UnboundedElementBrowserTest() = default;
   ~UnboundedElementBrowserTest() override = default;
   void SetUp() override {
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-    // TODO(crbug.com/508672616): Not yet implemented on Android/iOS.
-    GTEST_SKIP();
-#else
+    SkipTestsForUnsupportedPlatforms();
     std::vector<base::test::FeatureRef> enabled_features = {
         blink::features::kUnboundedElement,
         blink::features::kUnboundedElementOnTheOpenWeb};
@@ -152,7 +172,6 @@
     }
     feature_list_.InitWithFeatures(enabled_features, disabled_features);
     UnboundedElementBrowserTestBase::SetUp();
-#endif
   }
 
  private:
@@ -303,7 +322,13 @@
   EXPECT_EQ(50, EvalJs(primary_main_frame_host(), "window.__touch_y"));
 }
 
-IN_PROC_BROWSER_TEST_P(UnboundedElementBrowserTest, LightDismissEscKey) {
+// TODO(crbug.com/534380085): Flaky/failing on Android.
+#if BUILDFLAG(IS_ANDROID)
+#define MAYBE_LightDismissEscKey DISABLED_LightDismissEscKey
+#else
+#define MAYBE_LightDismissEscKey LightDismissEscKey
+#endif
+IN_PROC_BROWSER_TEST_P(UnboundedElementBrowserTest, MAYBE_LightDismissEscKey) {
   GURL url(embedded_test_server()->GetURL("/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), url));
 
@@ -552,12 +577,17 @@
 }
 
 IN_PROC_BROWSER_TEST_P(UnboundedElementBrowserTest, PopupInputEventRouting) {
-#if BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_ANDROID)
+  // Mouse events are not routed through UnboundedSurfaceWindow on Android, as
+  // native touch/pointer events are handled by the regular Android View
+  // hierarchy.
+  GTEST_SKIP();
+#elif BUILDFLAG(IS_CHROMEOS)
   // TODO(crbug.com/508672616): Not yet working on ChromeOS due to Aura/Ash
   // popup container positioning and coordinate conversion issues.
   GTEST_SKIP();
 #endif
-#if !BUILDFLAG(IS_CHROMEOS)
+#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
   GURL url(embedded_test_server()->GetURL("/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), url));
 
@@ -604,12 +634,17 @@
 
 IN_PROC_BROWSER_TEST_P(UnboundedElementBrowserTest,
                        PopupOutsideViewportInputEventRouting) {
-#if BUILDFLAG(IS_CHROMEOS)
+#if BUILDFLAG(IS_ANDROID)
+  // Mouse events are not routed through UnboundedSurfaceWindow on Android, as
+  // native touch/pointer events are handled by the regular Android View
+  // hierarchy.
+  GTEST_SKIP();
+#elif BUILDFLAG(IS_CHROMEOS)
   // TODO(crbug.com/508672616): Not yet working on ChromeOS due to Aura/Ash
   // popup container positioning and coordinate conversion issues.
   GTEST_SKIP();
 #endif
-#if !BUILDFLAG(IS_CHROMEOS)
+#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
   GURL url(embedded_test_server()->GetURL("/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), url));
 
@@ -801,8 +836,16 @@
   }
 }
 
+// TODO(crbug.com/534380085): Flaky/failing on Android.
+#if BUILDFLAG(IS_ANDROID)
+#define MAYBE_NestedChildBoundsExpansionTriggersRedraw \
+  DISABLED_NestedChildBoundsExpansionTriggersRedraw
+#else
+#define MAYBE_NestedChildBoundsExpansionTriggersRedraw \
+  NestedChildBoundsExpansionTriggersRedraw
+#endif
 IN_PROC_BROWSER_TEST_P(UnboundedElementBrowserTest,
-                       NestedChildBoundsExpansionTriggersRedraw) {
+                       MAYBE_NestedChildBoundsExpansionTriggersRedraw) {
   GURL url(embedded_test_server()->GetURL("/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), url));
 
@@ -845,8 +888,16 @@
   EXPECT_EQ(200, window->GetBounds().width());
 }
 
+// TODO(crbug.com/534380085): Flaky/failing on Android.
+#if BUILDFLAG(IS_ANDROID)
+#define MAYBE_AnimatedChildWithBoxShadowSubmitsFrame \
+  DISABLED_AnimatedChildWithBoxShadowSubmitsFrame
+#else
+#define MAYBE_AnimatedChildWithBoxShadowSubmitsFrame \
+  AnimatedChildWithBoxShadowSubmitsFrame
+#endif
 IN_PROC_BROWSER_TEST_P(UnboundedElementBrowserTest,
-                       AnimatedChildWithBoxShadowSubmitsFrame) {
+                       MAYBE_AnimatedChildWithBoxShadowSubmitsFrame) {
   GURL url(embedded_test_server()->GetURL("/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), url));
 
@@ -1095,9 +1146,7 @@
   ~UnboundedElementPermutationBrowserTest() override = default;
 
   void SetUp() override {
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
-    GTEST_SKIP();
-#else
+    SkipTestsForUnsupportedPlatforms();
     const auto& params = GetParam();
     std::vector<base::test::FeatureRef> enabled_features;
     std::vector<base::test::FeatureRef> disabled_features;
@@ -1120,7 +1169,6 @@
 
     feature_list_.InitWithFeatures(enabled_features, disabled_features);
     UnboundedElementBrowserTestBase::SetUp();
-#endif
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -1130,7 +1178,8 @@
     std::vector<std::string> enabled_blink_features;
     std::vector<std::string> disabled_blink_features;
 
-    if (params.unbounded_element_runtime_feature) {
+    if (params.unbounded_element_runtime_feature ||
+        params.open_web_runtime_feature) {
       enabled_blink_features.push_back("UnboundedElement");
     } else {
       disabled_blink_features.push_back("UnboundedElement");
@@ -1160,7 +1209,7 @@
 
 IN_PROC_BROWSER_TEST_P(UnboundedElementPermutationBrowserTest,
                        CheckPermutation) {
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+#if BUILDFLAG(IS_IOS)
   GTEST_SKIP();
 #else
   const auto& params = GetParam();
diff --git a/content/browser/renderer_host/unbounded_surface_window.h b/content/browser/renderer_host/unbounded_surface_window.h
index 45a3c27..ee376824 100644
--- a/content/browser/renderer_host/unbounded_surface_window.h
+++ b/content/browser/renderer_host/unbounded_surface_window.h
@@ -13,7 +13,6 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom.h"
-#include "third_party/blink/public/common/input/web_input_event.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/native_ui_types.h"
 
diff --git a/content/browser/renderer_host/unbounded_surface_window_android.cc b/content/browser/renderer_host/unbounded_surface_window_android.cc
new file mode 100644
index 0000000..42af1552
--- /dev/null
+++ b/content/browser/renderer_host/unbounded_surface_window_android.cc
@@ -0,0 +1,410 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/unbounded_surface_window_android.h"
+
+#include <cmath>
+
+#include "base/android/jni_android.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/notreached.h"
+#include "base/task/single_thread_task_runner.h"
+#include "base/threading/platform_thread.h"
+#include "cc/slim/frame_sink.h"
+#include "cc/slim/layer.h"
+#include "cc/slim/layer_tree.h"
+#include "cc/slim/surface_layer.h"
+#include "components/input/render_widget_host_input_event_router.h"
+#include "components/viz/common/frame_sinks/copy_output_request.h"
+#include "components/viz/common/frame_sinks/copy_output_result.h"
+#include "components/viz/host/host_display_client.h"
+#include "components/viz/host/host_frame_sink_manager.h"
+#include "content/browser/compositor/surface_utils.h"
+#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
+#include "content/browser/renderer_host/compositor_dependencies_android.h"
+#include "content/browser/renderer_host/render_widget_host_delegate.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_view_android.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
+#include "content/public/android/content_jni_headers/UnboundedSurfacePopupWindow_jni.h"
+#include "content/public/browser/browser_thread.h"
+#include "gpu/ipc/common/gpu_surface_tracker.h"
+#include "services/viz/privileged/mojom/compositing/display_private.mojom.h"
+#include "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom.h"
+#include "ui/android/window_android.h"
+#include "ui/display/screen.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
+#include "ui/gfx/native_ui_types.h"
+#include "ui/gl/android/scoped_java_surface_control.h"
+
+namespace content {
+
+// static
+std::unique_ptr<UnboundedSurfaceWindowAndroid>
+UnboundedSurfaceWindowAndroid::Create(
+    RenderWidgetHostViewAndroid* parent_view,
+    mojo::PendingAssociatedReceiver<blink::mojom::UnboundedSurfaceHost> host,
+    mojo::PendingAssociatedRemote<blink::mojom::UnboundedSurfaceClient> client,
+    const gfx::Rect& bounds_in_dips,
+    base::WeakPtr<RenderWidgetHostViewBase> subframe_view) {
+  auto window = base::WrapUnique(new UnboundedSurfaceWindowAndroid(
+      parent_view, std::move(host), std::move(client),
+      std::move(subframe_view)));
+  if (!window->InitWindow(bounds_in_dips)) {
+    return nullptr;
+  }
+  return window;
+}
+
+UnboundedSurfaceWindowAndroid::UnboundedSurfaceWindowAndroid(
+    RenderWidgetHostViewAndroid* parent_view,
+    mojo::PendingAssociatedReceiver<blink::mojom::UnboundedSurfaceHost> host,
+    mojo::PendingAssociatedRemote<blink::mojom::UnboundedSurfaceClient> client,
+    base::WeakPtr<RenderWidgetHostViewBase> subframe_view)
+    : HostDisplayClient(gfx::kNullAcceleratedWidget),
+      parent_view_(parent_view),
+      subframe_view_(std::move(subframe_view)) {
+  if (host.is_valid() && client.is_valid()) {
+    receiver_.Bind(std::move(host));
+    receiver_.set_disconnect_handler(
+        base::BindOnce(&UnboundedSurfaceWindowAndroid::OnConnectionError,
+                       base::Unretained(this)));
+    client_remote_.Bind(std::move(client));
+  }
+}
+
+UnboundedSurfaceWindowAndroid::~UnboundedSurfaceWindowAndroid() {
+  Dismiss();
+  display_private_.reset();
+  if (frame_sink_id_.is_valid()) {
+    GetHostFrameSinkManager()->InvalidateFrameSinkId(
+        frame_sink_id_, this,
+        base::BindOnce(
+            [](gpu::SurfaceHandle surface_handle) {
+              if (surface_handle != gpu::kNullSurfaceHandle) {
+                gpu::GpuSurfaceTracker::Get()->RemoveSurface(surface_handle);
+              }
+            },
+            surface_handle_));
+  }
+}
+
+base::WeakPtr<UnboundedSurfaceWindow>
+UnboundedSurfaceWindowAndroid::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
+bool UnboundedSurfaceWindowAndroid::is_valid() const {
+  return !j_popup_window_.is_null();
+}
+
+gfx::NativeWindow UnboundedSurfaceWindowAndroid::GetNativeWindow() const {
+  return window_android_;
+}
+
+void UnboundedSurfaceWindowAndroid::SetBounds(const gfx::Rect& bounds_in_dips) {
+  if (!is_valid()) {
+    return;
+  }
+  bounds_in_dips_ = bounds_in_dips;
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  float dsf = parent_view_->GetDeviceScaleFactor();
+  gfx::Point origin_pixels =
+      gfx::ScaleToRoundedPoint(bounds_in_dips.origin(), dsf);
+  gfx::Size size_pixels = gfx::ScaleToRoundedSize(bounds_in_dips.size(), dsf);
+
+  Java_UnboundedSurfacePopupWindow_resize(env, j_popup_window_,
+                                          origin_pixels.x(), origin_pixels.y());
+
+  local_surface_id_allocator_.GenerateId();
+
+  if (display_private_) {
+    display_private_->Resize(size_pixels);
+  }
+  if (layer_tree_) {
+    layer_tree_->SetViewportRectAndScale(
+        gfx::Rect(size_pixels), dsf,
+        local_surface_id_allocator_.GetCurrentLocalSurfaceId());
+  }
+  if (surface_layer_) {
+    surface_layer_->SetBounds(size_pixels);
+    surface_layer_->SetSurfaceId(
+        viz::SurfaceId(frame_sink_id_, GetLocalSurfaceId()),
+        cc::DeadlinePolicy::UseDefaultDeadline());
+  }
+
+  if (client_remote_.is_bound()) {
+    client_remote_->OnSurfaceAllocated(GetFrameSinkId(), GetLocalSurfaceId());
+  }
+}
+
+viz::FrameSinkId UnboundedSurfaceWindowAndroid::GetFrameSinkId() const {
+  return frame_sink_id_;
+}
+
+viz::LocalSurfaceId UnboundedSurfaceWindowAndroid::GetLocalSurfaceId() const {
+  return local_surface_id_allocator_.GetCurrentLocalSurfaceId();
+}
+
+void UnboundedSurfaceWindowAndroid::GetCompositorFrameSink(
+    mojo::PendingReceiver<viz::mojom::CompositorFrameSink> sink,
+    mojo::PendingRemote<viz::mojom::CompositorFrameSinkClient> client) {
+  GetHostFrameSinkManager()->CreateCompositorFrameSink(
+      frame_sink_id_, std::move(sink), std::move(client),
+      /*render_input_router_config=*/nullptr);
+}
+
+void UnboundedSurfaceWindowAndroid::RouteMouseEvent(
+    const blink::WebMouseEvent& event) {
+  NOTREACHED()
+      << "Mouse events are not routed through UnboundedSurfaceWindow on "
+         "Android.";
+}
+
+gfx::Rect UnboundedSurfaceWindowAndroid::GetBounds() const {
+  return bounds_in_dips_;
+}
+
+void UnboundedSurfaceWindowAndroid::CopyFromSurface(
+    const gfx::Rect& src_subrect,
+    const gfx::Size& dst_size,
+    base::TimeDelta timeout,
+    base::OnceCallback<void(const content::CopyFromSurfaceResult&)> callback) {
+  if (!surface_layer_) {
+    std::move(callback).Run(content::CopyFromSurfaceResult());
+    return;
+  }
+  auto request = std::make_unique<viz::CopyOutputRequest>(
+      viz::CopyOutputRequest::ResultFormat::RGBA,
+      viz::CopyOutputRequest::ResultDestination::kSystemMemory,
+      base::BindOnce(
+          [](base::OnceCallback<void(const content::CopyFromSurfaceResult&)>
+                 callback,
+             std::unique_ptr<viz::CopyOutputResult> result) {
+            std::move(callback).Run(
+                ToCopyFromSurfaceResult(result->ScopedAccessSkBitmap()
+                                            .GetOutScopedBitmapAndMetadata()));
+          },
+          std::move(callback)));
+  request->set_result_task_runner(
+      base::SingleThreadTaskRunner::GetCurrentDefault());
+
+  viz::SurfaceId surface_id(frame_sink_id_, GetLocalSurfaceId());
+  GetHostFrameSinkManager()->RequestCopyOfOutput(
+      surface_id, std::move(request), /*capture_exact_surface_id=*/false,
+      timeout);
+}
+
+void UnboundedSurfaceWindowAndroid::EnsureSurfaceSynchronizedForWebTest() {
+  if (surface_layer_) {
+    surface_layer_->SetSurfaceId(
+        viz::SurfaceId(frame_sink_id_, GetLocalSurfaceId()),
+        cc::DeadlinePolicy::UseInfiniteDeadline());
+  }
+}
+
+void UnboundedSurfaceWindowAndroid::DidFailToInitializeLayerTreeFrameSink() {
+  Dismiss();
+}
+
+void UnboundedSurfaceWindowAndroid::DidLoseLayerTreeFrameSink() {
+  Dismiss();
+}
+
+void UnboundedSurfaceWindowAndroid::Dismiss() {
+  if (is_dismissed_) {
+    return;
+  }
+  is_dismissed_ = true;
+  display_private_.reset();
+  layer_tree_.reset();
+  surface_layer_ = nullptr;
+  window_android_ = nullptr;
+  if (client_remote_.is_bound()) {
+    client_remote_->OnDismissed();
+    client_remote_.reset();
+  }
+  if (is_valid()) {
+    JNIEnv* env = base::android::AttachCurrentThread();
+    Java_UnboundedSurfacePopupWindow_dismissPopup(env, j_popup_window_);
+    j_popup_window_.Reset();
+  }
+  if (parent_view_) {
+    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&RenderWidgetHostViewBase::DestroyUnboundedSurface,
+                       parent_view_->GetWeakPtr(), GetWeakPtr()));
+  }
+}
+
+void UnboundedSurfaceWindowAndroid::UpdateBounds(const gfx::Rect& bounds) {
+  if (!parent_view_) {
+    return;
+  }
+  parent_view_->UpdateUnboundedSurfaceBoundsInSubframeContext(
+      bounds, subframe_view_.get());
+  if (client_remote_.is_bound()) {
+    client_remote_->OnSurfaceAllocated(GetFrameSinkId(), GetLocalSurfaceId());
+  }
+}
+
+bool UnboundedSurfaceWindowAndroid::InitWindow(
+    const gfx::Rect& bounds_in_dips) {
+  if (!parent_view_ || !parent_view_->GetNativeView()) {
+    return false;
+  }
+  ui::WindowAndroid* parent_window_android =
+      parent_view_->GetNativeView()->GetWindowAndroid();
+  if (!parent_window_android) {
+    return false;
+  }
+
+  frame_sink_id_ = AllocateFrameSinkId();
+  GetHostFrameSinkManager()->RegisterFrameSinkId(
+      frame_sink_id_, this, viz::ReportFirstSurfaceActivation::kNo);
+  GetHostFrameSinkManager()->SetFrameSinkDebugLabel(frame_sink_id_,
+                                                    "UnboundedSurfaceWindow");
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  bounds_in_dips_ = bounds_in_dips;
+  float dsf = parent_view_->GetDeviceScaleFactor();
+  gfx::Point origin_pixels =
+      gfx::ScaleToRoundedPoint(bounds_in_dips.origin(), dsf);
+  gfx::Size size_pixels = gfx::ScaleToRoundedSize(bounds_in_dips.size(), dsf);
+
+  base::android::ScopedJavaLocalRef<jobject> java_window =
+      Java_UnboundedSurfacePopupWindow_create(
+          env, parent_window_android->GetJavaObject(),
+          parent_view_->GetNativeView()->GetContainerView(), origin_pixels.x(),
+          origin_pixels.y());
+
+  if (java_window.is_null()) {
+    return false;
+  }
+  j_popup_window_ = base::android::ScopedJavaGlobalRef<jobject>(java_window);
+
+  base::android::ScopedJavaLocalRef<jobject> j_window_android =
+      Java_UnboundedSurfacePopupWindow_getWindowAndroid(env, j_popup_window_);
+  window_android_ = ui::WindowAndroid::FromJavaWindowAndroid(j_window_android);
+  if (!window_android_) {
+    return false;
+  }
+
+  base::android::ScopedJavaLocalRef<jobject> j_surface_control =
+      Java_UnboundedSurfacePopupWindow_getSurfaceControl(env, j_popup_window_);
+  if (j_surface_control.is_null()) {
+    return false;
+  }
+
+  bool release_on_destroy = true;
+  gl::ScopedJavaSurfaceControl scoped_java_surface_control(j_surface_control,
+                                                           release_on_destroy);
+  gpu::GpuSurfaceTracker* tracker = gpu::GpuSurfaceTracker::Get();
+  surface_handle_ = tracker->AddSurfaceForNativeWidget(
+      gpu::SurfaceRecord(std::move(scoped_java_surface_control)));
+
+  local_surface_id_allocator_.GenerateId();
+
+  layer_tree_ = cc::slim::LayerTree::Create(this);
+  layer_tree_->set_background_color(SkColors::kTransparent);
+  layer_tree_->SetViewportRectAndScale(
+      gfx::Rect(size_pixels), dsf,
+      local_surface_id_allocator_.GetCurrentLocalSurfaceId());
+
+  CreateDisplayAndFrameSink(size_pixels);
+
+  surface_layer_ = cc::slim::SurfaceLayer::Create();
+  surface_layer_->SetIsDrawable(true);
+  surface_layer_->SetBackgroundColor(SkColors::kTransparent);
+  surface_layer_->SetBounds(size_pixels);
+  surface_layer_->SetSurfaceId(
+      viz::SurfaceId(frame_sink_id_, GetLocalSurfaceId()),
+      cc::DeadlinePolicy::UseDefaultDeadline());
+
+  layer_tree_->SetRoot(surface_layer_);
+  layer_tree_->SetVisible(true);
+
+  if (client_remote_.is_bound()) {
+    client_remote_->OnSurfaceAllocated(GetFrameSinkId(), GetLocalSurfaceId());
+  }
+
+  return true;
+}
+
+void UnboundedSurfaceWindowAndroid::CreateDisplayAndFrameSink(
+    const gfx::Size& surface_size) {
+  if (!window_android_) {
+    return;
+  }
+
+  scoped_refptr<gpu::GpuChannelHost> gpu_channel_host =
+      BrowserGpuChannelHostFactory::instance()->GetGpuChannel();
+  if (!gpu_channel_host) {
+    return;
+  }
+
+  CompositorDependenciesAndroid::Get().TryEstablishVizConnectionIfNeeded();
+
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+      GetUIThreadTaskRunner({BrowserTaskType::kUserInput});
+
+  auto root_params = viz::mojom::RootCompositorFrameSinkParams::New();
+
+  mojo::PendingAssociatedRemote<viz::mojom::CompositorFrameSink> sink_remote;
+  root_params->compositor_frame_sink =
+      sink_remote.InitWithNewEndpointAndPassReceiver();
+  mojo::PendingReceiver<viz::mojom::CompositorFrameSinkClient> client_receiver =
+      root_params->compositor_frame_sink_client
+          .InitWithNewPipeAndPassReceiver();
+  display_private_.reset();
+  root_params->display_private =
+      display_private_.BindNewEndpointAndPassReceiver();
+
+  root_params->display_client = GetBoundRemote(task_runner);
+
+  gfx::DisplayColorSpaces display_color_spaces =
+      display::Screen::Get()
+          ->GetDisplayNearestWindow(window_android_)
+          .GetColorSpaces();
+
+  viz::RendererSettings renderer_settings;
+  renderer_settings.partial_swap_enabled = true;
+  renderer_settings.allow_antialiasing = false;
+  renderer_settings.highp_threshold_min = 2048;
+  renderer_settings.requires_alpha_channel = true;
+
+  root_params->frame_sink_id = frame_sink_id_;
+  root_params->widget = surface_handle_;
+  root_params->gpu_compositing = true;
+  root_params->renderer_settings = renderer_settings;
+  root_params->refresh_rate = window_android_->GetRefreshRate();
+
+  GetHostFrameSinkManager()->CreateRootCompositorFrameSink(
+      std::move(root_params), /*maybe_wait_on_destruction=*/false);
+
+  display_private_->SetDisplayVisible(true);
+  display_private_->Resize(surface_size);
+  display_private_->SetDisplayColorSpaces(display_color_spaces);
+  display_private_->SetSupportedRefreshRates(
+      window_android_->GetSupportedRefreshRates());
+
+  layer_tree_->SetFrameSink(cc::slim::FrameSink::Create(
+      std::move(sink_remote), std::move(client_receiver), nullptr,
+      GetUIThreadTaskRunner({BrowserTaskType::kUserInput}),
+      base::kInvalidThreadId));
+  layer_tree_->SetVisible(true);
+}
+
+void UnboundedSurfaceWindowAndroid::OnConnectionError() {
+  Dismiss();
+}
+
+}  // namespace content
+
+DEFINE_JNI(UnboundedSurfacePopupWindow)
diff --git a/content/browser/renderer_host/unbounded_surface_window_android.h b/content/browser/renderer_host/unbounded_surface_window_android.h
new file mode 100644
index 0000000..d93c6fd
--- /dev/null
+++ b/content/browser/renderer_host/unbounded_surface_window_android.h
@@ -0,0 +1,133 @@
+// Copyright 2026 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_BROWSER_RENDERER_HOST_UNBOUNDED_SURFACE_WINDOW_ANDROID_H_
+#define CONTENT_BROWSER_RENDERER_HOST_UNBOUNDED_SURFACE_WINDOW_ANDROID_H_
+
+#include <jni.h>
+
+#include <memory>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/slim/layer_tree.h"
+#include "cc/slim/layer_tree_client.h"
+#include "cc/slim/surface_layer.h"
+#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "components/viz/host/host_display_client.h"
+#include "components/viz/host/host_frame_sink_client.h"
+#include "content/browser/renderer_host/unbounded_surface_window.h"
+#include "gpu/ipc/common/surface_handle.h"
+#include "mojo/public/cpp/bindings/associated_receiver.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "services/viz/privileged/mojom/compositing/display_private.mojom.h"
+#include "third_party/blink/public/mojom/unbounded_element/unbounded_element.mojom.h"
+#include "ui/android/window_android.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace content {
+
+class RenderWidgetHostViewAndroid;
+class RenderWidgetHostViewBase;
+
+class UnboundedSurfaceWindowAndroid
+    : public UnboundedSurfaceWindow,
+      public viz::HostDisplayClient,
+      public viz::HostFrameSinkClient,
+      public cc::slim::LayerTreeClient,
+      public blink::mojom::UnboundedSurfaceHost {
+ public:
+  static std::unique_ptr<UnboundedSurfaceWindowAndroid> Create(
+      RenderWidgetHostViewAndroid* parent_view,
+      mojo::PendingAssociatedReceiver<blink::mojom::UnboundedSurfaceHost> host,
+      mojo::PendingAssociatedRemote<blink::mojom::UnboundedSurfaceClient>
+          client,
+      const gfx::Rect& bounds_in_dips,
+      base::WeakPtr<RenderWidgetHostViewBase> subframe_view);
+
+  ~UnboundedSurfaceWindowAndroid() override;
+
+  // UnboundedSurfaceWindow overrides:
+  base::WeakPtr<UnboundedSurfaceWindow> GetWeakPtr() override;
+  bool is_valid() const override;
+  gfx::NativeWindow GetNativeWindow() const override;
+  void SetBounds(const gfx::Rect& bounds_in_dips) override;
+  viz::FrameSinkId GetFrameSinkId() const override;
+  viz::LocalSurfaceId GetLocalSurfaceId() const override;
+  void GetCompositorFrameSink(
+      mojo::PendingReceiver<viz::mojom::CompositorFrameSink> sink,
+      mojo::PendingRemote<viz::mojom::CompositorFrameSinkClient> client)
+      override;
+
+  void RouteMouseEvent(const blink::WebMouseEvent& event) override;
+  gfx::Rect GetBounds() const override;
+  void CopyFromSurface(
+      const gfx::Rect& src_subrect,
+      const gfx::Size& dst_size,
+      base::TimeDelta timeout,
+      base::OnceCallback<void(const content::CopyFromSurfaceResult&)> callback)
+      override;
+  void EnsureSurfaceSynchronizedForWebTest() override;
+  void Dismiss() override;
+
+  // blink::mojom::UnboundedSurfaceHost overrides:
+  void UpdateBounds(const gfx::Rect& bounds) override;
+
+  // viz::mojom::DisplayClient implementation:
+  void DidCompleteSwapWithSize(const gfx::Size& pixel_size) override {}
+  void OnContextCreationResult(gpu::ContextResult context_result) override {}
+  void SetWideColorEnabled(bool enabled) override {}
+  void SetPreferredRefreshRate(float refresh_rate) override {}
+
+  // viz::HostFrameSinkClient overrides:
+  void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override {
+  }
+  void OnFrameTokenChanged(uint32_t frame_token,
+                           base::TimeTicks activation_time) override {}
+
+  // cc::slim::LayerTreeClient overrides:
+  void BeginFrame(const viz::BeginFrameArgs& args) override {}
+  void DidReceiveCompositorFrameAck() override {}
+  void RequestNewFrameSink() override {}
+  void DidInitializeLayerTreeFrameSink() override {}
+  void DidFailToInitializeLayerTreeFrameSink() override;
+  void DidSubmitCompositorFrame() override {}
+  void DidLoseLayerTreeFrameSink() override;
+
+ private:
+  UnboundedSurfaceWindowAndroid(
+      RenderWidgetHostViewAndroid* parent_view,
+      mojo::PendingAssociatedReceiver<blink::mojom::UnboundedSurfaceHost> host,
+      mojo::PendingAssociatedRemote<blink::mojom::UnboundedSurfaceClient>
+          client,
+      base::WeakPtr<RenderWidgetHostViewBase> subframe_view);
+
+  bool InitWindow(const gfx::Rect& bounds_in_dips);
+  void CreateDisplayAndFrameSink(const gfx::Size& surface_size);
+  void OnConnectionError();
+
+  raw_ptr<RenderWidgetHostViewAndroid> parent_view_;
+  base::WeakPtr<RenderWidgetHostViewBase> subframe_view_;
+  viz::FrameSinkId frame_sink_id_;
+  viz::ParentLocalSurfaceIdAllocator local_surface_id_allocator_;
+  mojo::AssociatedReceiver<blink::mojom::UnboundedSurfaceHost> receiver_{this};
+  mojo::AssociatedRemote<blink::mojom::UnboundedSurfaceClient> client_remote_;
+
+  raw_ptr<ui::WindowAndroid> window_android_ = nullptr;
+  gpu::SurfaceHandle surface_handle_ = gpu::kNullSurfaceHandle;
+  mojo::AssociatedRemote<viz::mojom::DisplayPrivate> display_private_;
+  std::unique_ptr<cc::slim::LayerTree> layer_tree_;
+  scoped_refptr<cc::slim::SurfaceLayer> surface_layer_;
+
+  base::android::ScopedJavaGlobalRef<jobject> j_popup_window_;
+  gfx::Rect bounds_in_dips_;
+  bool is_dismissed_ = false;
+
+  base::WeakPtrFactory<UnboundedSurfaceWindow> weak_ptr_factory_{this};
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_UNBOUNDED_SURFACE_WINDOW_ANDROID_H_
diff --git a/content/browser/webauth/virtual_authenticator.cc b/content/browser/webauth/virtual_authenticator.cc
index e9f4415f..9013963 100644
--- a/content/browser/webauth/virtual_authenticator.cc
+++ b/content/browser/webauth/virtual_authenticator.cc
@@ -7,11 +7,11 @@
 #include <optional>
 #include <utility>
 
+#include "base/containers/to_vector.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/uuid.h"
 #include "crypto/hash.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/public/fido_constants.h"
 #include "device/fido/public/public_key_credential_rp_entity.h"
 #include "device/fido/public/public_key_credential_user_entity.h"
@@ -301,7 +301,8 @@
   if (result.has_value())
     value = std::move(*result);
 
-  std::move(callback).Run(device::fido_parsing_utils::MaterializeOrNull(value));
+  std::move(callback).Run(value ? std::make_optional(base::ToVector(*value))
+                                : std::nullopt);
 }
 
 void VirtualAuthenticator::OnLargeBlobCompressed(
@@ -317,8 +318,7 @@
   if (result.has_value()) {
     state_->InjectLargeBlob(
         &registration->second,
-        device::LargeBlob(device::fido_parsing_utils::Materialize(*result),
-                          original_size));
+        device::LargeBlob(base::ToVector(*result), original_size));
   }
   std::move(callback).Run(result.has_value());
 }
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index 93144ef2..412c529 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -286,6 +286,7 @@
     "java/src/org/chromium/content/browser/SpeechRecognitionImpl.java",
     "java/src/org/chromium/content/browser/SyntheticGestureTarget.java",
     "java/src/org/chromium/content/browser/TtsPlatformImpl.java",
+    "java/src/org/chromium/content/browser/UnboundedSurfacePopupWindow.java",
     "java/src/org/chromium/content/browser/ViewEventSinkImpl.java",
     "java/src/org/chromium/content/browser/WindowEventObserver.java",
     "java/src/org/chromium/content/browser/WindowEventObserverManager.java",
@@ -530,6 +531,7 @@
     "java/src/org/chromium/content/browser/SpeechRecognitionImpl.java",
     "java/src/org/chromium/content/browser/SyntheticGestureTarget.java",
     "java/src/org/chromium/content/browser/TtsPlatformImpl.java",
+    "java/src/org/chromium/content/browser/UnboundedSurfacePopupWindow.java",
     "java/src/org/chromium/content/browser/accessibility/AccessibilityNodeInfoBuilder.java",
     "java/src/org/chromium/content/browser/accessibility/AccessibilityNodeInfoUtils.java",
     "java/src/org/chromium/content/browser/accessibility/AssistDataBuilder.java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/UnboundedSurfacePopupWindow.java b/content/public/android/java/src/org/chromium/content/browser/UnboundedSurfacePopupWindow.java
new file mode 100644
index 0000000..ed008e1c9
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/UnboundedSurfacePopupWindow.java
@@ -0,0 +1,120 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser;
+
+import android.content.Context;
+import android.os.Build;
+import android.view.AttachedSurfaceControl;
+import android.view.SurfaceControl;
+import android.view.View;
+
+import androidx.annotation.RequiresApi;
+
+import org.jni_zero.CalledByNative;
+import org.jni_zero.JNINamespace;
+
+import org.chromium.base.lifetime.LifetimeAssert;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.ui.base.WindowAndroid;
+
+/** A popup window for hosting unbounded visual surfaces on Android U+. */
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+@JNINamespace("content")
+@NullMarked
+public class UnboundedSurfacePopupWindow {
+    private final WindowAndroid mWindowAndroid;
+    private final View mParentView;
+    private final @Nullable LifetimeAssert mLifetimeAssert = LifetimeAssert.create(this);
+    private @Nullable SurfaceControl mSurfaceControl;
+
+    @CalledByNative
+    private static @Nullable UnboundedSurfacePopupWindow create(
+            WindowAndroid parentWindowAndroid, @Nullable View parentView, int x, int y) {
+        // Unbounded elements rely on AttachedSurfaceControl.buildReparentTransaction(),
+        // which requires Android U (API level 34) or higher.
+        // See HTMLElement::showUnboundedElement() in
+        // third_party/blink/renderer/core/html/html_element.cc
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+            return null;
+        }
+        Context context = parentWindowAndroid.getContext().get();
+        if (context == null || parentView == null) {
+            return null;
+        }
+        AttachedSurfaceControl attachedSurfaceControl = parentView.getRootSurfaceControl();
+        if (attachedSurfaceControl == null) {
+            return null;
+        }
+
+        SurfaceControl surfaceControl =
+                new SurfaceControl.Builder().setName("UnboundedSurfaceControl").build();
+        SurfaceControl.Transaction transaction =
+                attachedSurfaceControl.buildReparentTransaction(surfaceControl);
+        if (transaction == null) {
+            surfaceControl.release();
+            return null;
+        }
+
+        transaction.setVisibility(surfaceControl, true);
+        transaction.apply();
+        transaction.close();
+
+        WindowAndroid windowAndroid = new WindowAndroid(context, false);
+        UnboundedSurfacePopupWindow popupWindow =
+                new UnboundedSurfacePopupWindow(windowAndroid, parentView, surfaceControl);
+        popupWindow.updatePosition(x, y);
+        return popupWindow;
+    }
+
+    private UnboundedSurfacePopupWindow(
+            WindowAndroid windowAndroid, View parentView, SurfaceControl surfaceControl) {
+        mWindowAndroid = windowAndroid;
+        mParentView = parentView;
+        mSurfaceControl = surfaceControl;
+    }
+
+    @CalledByNative
+    private @Nullable SurfaceControl getSurfaceControl() {
+        return mSurfaceControl;
+    }
+
+    @CalledByNative
+    private WindowAndroid getWindowAndroid() {
+        return mWindowAndroid;
+    }
+
+    private void updatePosition(int x, int y) {
+        if (mSurfaceControl == null) {
+            return;
+        }
+        int[] viewOriginInSurface = new int[2];
+        mParentView.getLocationInSurface(viewOriginInSurface);
+        SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+        transaction.setPosition(
+                mSurfaceControl, x + viewOriginInSurface[0], y + viewOriginInSurface[1]);
+        transaction.apply();
+        transaction.close();
+    }
+
+    @CalledByNative
+    private void resize(int x, int y) {
+        updatePosition(x, y);
+    }
+
+    @CalledByNative
+    private void dismissPopup() {
+        LifetimeAssert.setSafeToGc(mLifetimeAssert, true);
+        if (mSurfaceControl != null) {
+            SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+            transaction.reparent(mSurfaceControl, null);
+            transaction.apply();
+            transaction.close();
+            mSurfaceControl.release();
+            mSurfaceControl = null;
+        }
+        mWindowAndroid.destroy();
+    }
+}
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityE2ETest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityE2ETest.java
index ee01964..21a4118 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityE2ETest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityE2ETest.java
@@ -48,7 +48,6 @@
 import org.chromium.ui.accessibility.testservice.EventMatcher;
 import org.chromium.ui.accessibility.testservice.IAccessibilityTestHelperService;
 import org.chromium.ui.accessibility.testservice.NodeMatcher;
-import org.chromium.ui.accessibility.testservice.WaitForParams;
 import org.chromium.ui.test.util.DeviceRestriction;
 
 import java.io.IOException;
@@ -76,7 +75,7 @@
     private static final String ACCESSIBILITY_TEST_SERVICE_NAME =
             ACCESSIBILITY_TEST_SERVICE_COMPONENT_NAME.flattenToString();
     private static final long BIND_TIMEOUT_MS = 5000;
-    private static final long EVENT_TIMEOUT_MS = 5000;
+    private static final long DEFAULT_TIMEOUT_MS = 10000;
     private static final String TAG = "WebContentsAXTest";
 
     private static final String EXTRA_SELECTION_START_OFFSET_TYPE =
@@ -147,8 +146,7 @@
 
     private boolean waitForEvent(EventMatcher matcher) {
         try {
-            return getAccessibilityHelperService()
-                    .waitFor(new WaitForParamsBuilder().setEventMatcher(matcher).build());
+            return getAccessibilityHelperService().waitForEvent(matcher, DEFAULT_TIMEOUT_MS);
         } catch (Exception e) {
             Log.e(TAG, "Error waiting for event", e);
             return false;
@@ -157,8 +155,7 @@
 
     private boolean waitForNode(NodeMatcher matcher) {
         try {
-            return getAccessibilityHelperService()
-                    .waitFor(new WaitForParamsBuilder().setNodeMatcher(matcher).build());
+            return getAccessibilityHelperService().waitForNode(matcher, DEFAULT_TIMEOUT_MS);
         } catch (Exception e) {
             Log.e(TAG, "Error waiting for node", e);
             return false;
@@ -167,12 +164,10 @@
 
     private boolean waitForNodeOnEvent(EventMatcher eventMatcher, NodeMatcher nodeMatcher) {
         try {
-            return getAccessibilityHelperService()
-                    .waitFor(
-                            new WaitForParamsBuilder()
-                                    .setEventMatcher(eventMatcher)
-                                    .setNodeMatcher(nodeMatcher)
-                                    .build());
+            boolean eventReceived =
+                    getAccessibilityHelperService().waitForEvent(eventMatcher, DEFAULT_TIMEOUT_MS);
+            if (!eventReceived) return false;
+            return getAccessibilityHelperService().waitForNode(nodeMatcher, DEFAULT_TIMEOUT_MS);
         } catch (Exception e) {
             Log.e(TAG, "Error waiting for node on event", e);
             return false;
@@ -1039,36 +1034,8 @@
         Assert.assertTrue("Tree dump should contain CLICK action", treeDump.contains("CLICK"));
     }
 
-    private static class WaitForParamsBuilder {
-        private static final long DEFAULT_TIMEOUT_MS = 5000;
-
-        @Nullable private EventMatcher mEventMatcher;
-        @Nullable private NodeMatcher mNodeMatcher;
-        private final long mTimeoutMs = DEFAULT_TIMEOUT_MS;
-
-        public WaitForParamsBuilder setEventMatcher(EventMatcher eventMatcher) {
-            mEventMatcher = eventMatcher;
-            return this;
-        }
-
-        public WaitForParamsBuilder setNodeMatcher(NodeMatcher nodeMatcher) {
-            mNodeMatcher = nodeMatcher;
-            return this;
-        }
-
-        public WaitForParams build() {
-            WaitForParams matcher = new WaitForParams();
-            matcher.eventMatcher = mEventMatcher;
-            matcher.nodeMatcher = mNodeMatcher;
-            matcher.timeoutMs = mTimeoutMs;
-            return matcher;
-        }
-    }
-
     @SuppressWarnings("unused")
     private static class EventMatcherBuilder {
-        private static final long DEFAULT_TIMEOUT_MS = 5000;
-
         private int mEventType;
         private int mContentChangeTypes;
         @Nullable private NodeMatcher mSourceMatcher;
@@ -1099,8 +1066,6 @@
 
     @SuppressWarnings("unused")
     private static class NodeMatcherBuilder {
-        private static final long DEFAULT_TIMEOUT_MS = 5000;
-
         private String mClassName = "";
         private String mText = "";
         private Boolean mInputFocused;
diff --git a/content/public/android/junit/src/org/chromium/content/browser/input/ImeUtilsTest.java b/content/public/android/junit/src/org/chromium/content/browser/input/ImeUtilsTest.java
index 327ed248..b22e5a5 100644
--- a/content/public/android/junit/src/org/chromium/content/browser/input/ImeUtilsTest.java
+++ b/content/public/android/junit/src/org/chromium/content/browser/input/ImeUtilsTest.java
@@ -14,14 +14,12 @@
 
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
-import org.chromium.base.test.util.Batch;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.util.Base64;
 
 @RunWith(JUnit4.class)
-@Batch(Batch.UNIT_TESTS)
 public class ImeUtilsTest {
     @Test
     @SmallTest
diff --git a/content/public/android/junit/src/org/chromium/content/browser/input/StylusGestureConverterTest.java b/content/public/android/junit/src/org/chromium/content/browser/input/StylusGestureConverterTest.java
index 67ce3e8..9c4d27c 100644
--- a/content/public/android/junit/src/org/chromium/content/browser/input/StylusGestureConverterTest.java
+++ b/content/public/android/junit/src/org/chromium/content/browser/input/StylusGestureConverterTest.java
@@ -29,7 +29,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.blink.mojom.StylusWritingGestureAction;
 import org.chromium.blink.mojom.StylusWritingGestureData;
@@ -40,7 +39,6 @@
  * converter to convert them into gesture data. The gesture data is then checked for accuracy.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.PER_CLASS)
 @Config(sdk = VERSION_CODES.UPSIDE_DOWN_CAKE)
 public class StylusGestureConverterTest {
     private static final String GESTURE_TYPE_HISTOGRAM = "InputMethod.StylusHandwriting.Gesture";
diff --git a/content/public/android/junit/src/org/chromium/content_public/browser/ContactsDialogHostTest.java b/content/public/android/junit/src/org/chromium/content_public/browser/ContactsDialogHostTest.java
index 1b2c59c..1abed4b4 100644
--- a/content/public/android/junit/src/org/chromium/content_public/browser/ContactsDialogHostTest.java
+++ b/content/public/android/junit/src/org/chromium/content_public/browser/ContactsDialogHostTest.java
@@ -12,11 +12,9 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 
 /** Tests for the ContactsDialogHost. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class ContactsDialogHostTest {
 
diff --git a/content/public/android/junit/src/org/chromium/content_public/browser/ViewFocusChangeSuppressionTest.java b/content/public/android/junit/src/org/chromium/content_public/browser/ViewFocusChangeSuppressionTest.java
index deeab8c..179d7b7d 100644
--- a/content/public/android/junit/src/org/chromium/content_public/browser/ViewFocusChangeSuppressionTest.java
+++ b/content/public/android/junit/src/org/chromium/content_public/browser/ViewFocusChangeSuppressionTest.java
@@ -22,11 +22,9 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 
 /** Tests for {@link ViewFocusChangeSuppression}. */
 @RunWith(BaseRobolectricTestRunner.class)
-@Batch(Batch.UNIT_TESTS)
 @Config(manifest = Config.NONE)
 public class ViewFocusChangeSuppressionTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc
index 8f73a29..ad8392b3 100644
--- a/content/public/test/mock_render_process_host.cc
+++ b/content/public/test/mock_render_process_host.cc
@@ -13,6 +13,7 @@
 #include "base/functional/callback_helpers.h"
 #include "base/lazy_instance.h"
 #include "base/location.h"
+#include "base/no_destructor.h"
 #include "base/notimplemented.h"
 #include "base/process/process_handle.h"
 #include "base/task/single_thread_task_runner.h"
@@ -299,6 +300,13 @@
 }
 
 const base::Process& MockRenderProcessHost::GetProcess() {
+  if (process_still_launching_) {
+    // Matches RenderProcessHostImpl::GetProcess() while the launcher is still
+    // starting: a sentinel for "no process".
+    static const base::NoDestructor<base::Process> null_process;
+    return *null_process;
+  }
+
   static const base::Process current_process(base::Process::Current());
   return current_process;
 }
diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h
index 6af040e..a78272f 100644
--- a/content/public/test/mock_render_process_host.h
+++ b/content/public/test/mock_render_process_host.h
@@ -304,6 +304,13 @@
     is_for_top_chrome_web_ui_ = is_for_top_chrome_web_ui;
   }
 
+  // Makes GetProcess() report an invalid Process, as RenderProcessHostImpl
+  // does between Init() and OnProcessLaunched(). Lets tests cover the window
+  // in which a host is initialized but its renderer has not launched yet.
+  void SimulateProcessStillLaunchingForTesting(bool still_launching) {
+    process_still_launching_ = still_launching;
+  }
+
   void SetProcessLaunchedTime(base::TimeTicks time) {
     process_launched_time_ = time;
   }
@@ -349,6 +356,7 @@
   bool is_for_top_chrome_web_ui_ = false;
   bool has_immersive_xr_session_ = false;
   bool is_ready_ = false;
+  bool process_still_launching_ = false;
   base::TimeTicks process_launched_time_;
   int pending_view_count_;
   int worker_ref_count_;
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test.py b/content/test/gpu/gpu_tests/gpu_integration_test.py
index f3e6996..21726a0 100644
--- a/content/test/gpu/gpu_tests/gpu_integration_test.py
+++ b/content/test/gpu/gpu_tests/gpu_integration_test.py
@@ -1396,6 +1396,7 @@
         'arm-mali-g78',  # android-pixel-6
         'nvidia-nvidia-tegra',  # android-shield-android-tv
         'imagination-technologies-0x71061212',  # android-pixel-10
+        'samsung-electronics-co.-ltd.-0x2600200',  # android-sm-s926b
         'vmware,',  # VMs
         'vmware,-0x1050',  # ChromeOS VMs
         'mesa/x.org',  # ChromeOS VMs
diff --git a/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt
index 0f16e926..7fa5e03 100644
--- a/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
@@ -43,7 +43,8 @@
 #             intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912 intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783 nvidia-0x2c02
-#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330 ]
+#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330
+#         samsung-electronics-co.-ltd. ]
 # Architecture
 # tags: [ arch-arm64 arch-x86_64 ]
 # Decoder
diff --git a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
index cffb7dbf..bf1dc30 100644
--- a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
@@ -43,7 +43,8 @@
 #             intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912 intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783 nvidia-0x2c02
-#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330 ]
+#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330
+#         samsung-electronics-co.-ltd. ]
 # Architecture
 # tags: [ arch-arm64 arch-x86_64 ]
 # Decoder
diff --git a/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt
index bc065fd..9f5f95c 100644
--- a/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
@@ -43,7 +43,8 @@
 #             intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912 intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783 nvidia-0x2c02
-#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330 ]
+#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330
+#         samsung-electronics-co.-ltd. ]
 # Architecture
 # tags: [ arch-arm64 arch-x86_64 ]
 # Decoder
diff --git a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
index 49d9b41..2e6ec16 100644
--- a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
@@ -43,7 +43,8 @@
 #             intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912 intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783 nvidia-0x2c02
-#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330 ]
+#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330
+#         samsung-electronics-co.-ltd. ]
 # Architecture
 # tags: [ arch-arm64 arch-x86_64 ]
 # Decoder
diff --git a/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
index 5b8f5de..e39e5d4 100644
--- a/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
@@ -43,7 +43,8 @@
 #             intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912 intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783 nvidia-0x2c02
-#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330 ]
+#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330
+#         samsung-electronics-co.-ltd. ]
 # Architecture
 # tags: [ arch-arm64 arch-x86_64 ]
 # Decoder
diff --git a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
index e5ec8b8c..fa4aae2f 100644
--- a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
@@ -43,7 +43,8 @@
 #             intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912 intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783 nvidia-0x2c02
-#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330 ]
+#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330
+#         samsung-electronics-co.-ltd. ]
 # Architecture
 # tags: [ arch-arm64 arch-x86_64 ]
 # Decoder
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index fde66186..e8a53f12 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
@@ -43,7 +43,8 @@
 #             intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912 intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783 nvidia-0x2c02
-#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330 ]
+#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330
+#         samsung-electronics-co.-ltd. ]
 # Architecture
 # tags: [ arch-arm64 arch-x86_64 ]
 # Decoder
diff --git a/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
index d5dc2c6..c26fa9b2 100644
--- a/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
@@ -43,7 +43,8 @@
 #             intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912 intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783 nvidia-0x2c02
-#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330 ]
+#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330
+#         samsung-electronics-co.-ltd. ]
 # Architecture
 # tags: [ arch-arm64 arch-x86_64 ]
 # Decoder
diff --git a/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
index 2e9946e4..11ba303 100644
--- a/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
@@ -43,7 +43,8 @@
 #             intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912 intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783 nvidia-0x2c02
-#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330 ]
+#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330
+#         samsung-electronics-co.-ltd. ]
 # Architecture
 # tags: [ arch-arm64 arch-x86_64 ]
 # Decoder
diff --git a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
index fcdef76b..877d5dd 100644
--- a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
@@ -43,7 +43,8 @@
 #             intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912 intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783 nvidia-0x2c02
-#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330 ]
+#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330
+#         samsung-electronics-co.-ltd. ]
 # Architecture
 # tags: [ arch-arm64 arch-x86_64 ]
 # Decoder
diff --git a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
index 880827d..74480ba 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
@@ -43,7 +43,8 @@
 #             intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912 intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783 nvidia-0x2c02
-#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330 ]
+#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330
+#         samsung-electronics-co.-ltd. ]
 # Architecture
 # tags: [ arch-arm64 arch-x86_64 ]
 # Decoder
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 573062a..749749a 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
@@ -43,7 +43,8 @@
 #             intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912 intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783 nvidia-0x2c02
-#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330 ]
+#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330
+#         samsung-electronics-co.-ltd. ]
 # Architecture
 # tags: [ arch-arm64 arch-x86_64 ]
 # Decoder
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 339ff98..5217072 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
@@ -43,7 +43,8 @@
 #             intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912 intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783 nvidia-0x2c02
-#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330 ]
+#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330
+#         samsung-electronics-co.-ltd. ]
 # Architecture
 # tags: [ arch-arm64 arch-x86_64 ]
 # Decoder
diff --git a/content/test/gpu/gpu_tests/test_expectations/webrtc_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webrtc_expectations.txt
index ae27805..f0a809b 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webrtc_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webrtc_expectations.txt
@@ -11,7 +11,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
@@ -43,7 +43,8 @@
 #             intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912 intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783 nvidia-0x2c02
-#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330 ]
+#         qualcomm qualcomm-0x36333630 qualcomm-0x36334330
+#         samsung-electronics-co.-ltd. ]
 # Architecture
 # tags: [ arch-arm64 arch-x86_64 ]
 # Decoder
diff --git a/content/test/gpu/validate_tag_consistency.py b/content/test/gpu/validate_tag_consistency.py
index 686e110..e8b4e803e 100755
--- a/content/test/gpu/validate_tag_consistency.py
+++ b/content/test/gpu/validate_tag_consistency.py
@@ -130,6 +130,7 @@
             'qualcomm-0x36333630',
             'qualcomm-0x36334330',
         ],
+        'samsung-electronics-co.-ltd.': [],
     },
 }
 
@@ -194,7 +195,7 @@
 # Devices
 # tags: [ android-pixel-2 android-pixel-4 android-pixel-6 android-pixel-10
 #             android-shield-android-tv android-sm-a137f android-sm-a236b
-#             android-sm-s911u1
+#             android-sm-s911u1 android-sm-s926b
 #         android-brya android-corsola
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus
diff --git a/device/fido/attestation_statement_formats_unittest.cc b/device/fido/attestation_statement_formats_unittest.cc
index 234e29e..5245ec4 100644
--- a/device/fido/attestation_statement_formats_unittest.cc
+++ b/device/fido/attestation_statement_formats_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <vector>
 
+#include "base/containers/to_vector.h"
 #include "components/cbor/reader.h"
 #include "components/cbor/writer.h"
 #include "device/fido/fido_parsing_utils.h"
@@ -85,18 +86,17 @@
 TEST(PackedAttestationStatementTest, CBOR) {
   EXPECT_THAT(
       *cbor::Writer::Write(AsCBOR(PackedAttestationStatement(
-          CoseAlgorithmIdentifier::kEs256,
-          fido_parsing_utils::Materialize(kSignature),
-          {fido_parsing_utils::Materialize(kCertificates)}))),
+          CoseAlgorithmIdentifier::kEs256, base::ToVector(kSignature),
+          {base::ToVector(kCertificates)}))),
       testing::ElementsAreArray(test_data::kPackedAttestationStatementCBOR));
 }
 
 TEST(PackedAttestationStatementTest, CBOR_NoCerts) {
-  EXPECT_THAT(*cbor::Writer::Write(AsCBOR(PackedAttestationStatement(
-                  CoseAlgorithmIdentifier::kEs256,
-                  fido_parsing_utils::Materialize(kSignature), {}))),
-              testing::ElementsAreArray(
-                  test_data::kPackedAttestationStatementCBORNoCerts));
+  EXPECT_THAT(
+      *cbor::Writer::Write(AsCBOR(PackedAttestationStatement(
+          CoseAlgorithmIdentifier::kEs256, base::ToVector(kSignature), {}))),
+      testing::ElementsAreArray(
+          test_data::kPackedAttestationStatementCBORNoCerts));
 }
 
 TEST(OpaqueAttestationStatementTest, GetLeafCertificate) {
diff --git a/device/fido/attested_credential_data.cc b/device/fido/attested_credential_data.cc
index 211b1337..6815037 100644
--- a/device/fido/attested_credential_data.cc
+++ b/device/fido/attested_credential_data.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/containers/span_reader.h"
+#include "base/containers/to_array.h"
 #include "base/containers/to_vector.h"
 #include "base/memory/raw_ptr_exclusion.h"
 #include "base/numerics/safe_math.h"
@@ -164,7 +165,7 @@
 
   return std::make_pair(
       AttestedCredentialData(aaguid, credential_id_length_span,
-                             fido_parsing_utils::Materialize(credential_id),
+                             base::ToVector(credential_id),
                              std::move(public_key)),
       buffer);
 }
@@ -213,9 +214,8 @@
         credential_id_length_bytes,
     std::vector<uint8_t> credential_id,
     std::unique_ptr<PublicKey> public_key)
-    : aaguid_(fido_parsing_utils::Materialize(aaguid)),
-      credential_id_length_(
-          fido_parsing_utils::Materialize(credential_id_length_bytes)),
+    : aaguid_(base::ToArray(aaguid)),
+      credential_id_length_(base::ToArray(credential_id_length_bytes)),
       credential_id_(std::move(credential_id)),
       public_key_(std::move(public_key)) {
   const size_t credential_id_length =
@@ -233,7 +233,7 @@
           std::array<uint8_t, kCredentialIdLengthLength>{
               base::checked_cast<uint8_t>((0xff00 & credential_id.size()) >> 8),
               base::checked_cast<uint8_t>(0xff & credential_id.size())},
-          fido_parsing_utils::Materialize(credential_id),
+          base::ToVector(credential_id),
           std::move(public_key)) {
   CHECK_LE(credential_id.size(), 0xffffu);
 }
diff --git a/device/fido/authenticator_data.cc b/device/fido/authenticator_data.cc
index c1a6e94..b71aed4 100644
--- a/device/fido/authenticator_data.cc
+++ b/device/fido/authenticator_data.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/containers/to_array.h"
 #include "base/strings/string_number_conversions.h"
 #include "components/cbor/diagnostic_writer.h"
 #include "components/cbor/reader.h"
@@ -106,8 +107,8 @@
     std::optional<AttestedCredentialData> data,
     std::optional<cbor::Value> extensions)
     : flags_(flags),
-      application_parameter_(fido_parsing_utils::Materialize(rp_id_hash)),
-      counter_(fido_parsing_utils::Materialize(counter)),
+      application_parameter_(base::ToArray(rp_id_hash)),
+      counter_(base::ToArray(counter)),
       attested_data_(std::move(data)),
       extensions_(std::move(extensions)) {
   ValidateAuthenticatorDataStateOrCrash();
@@ -128,7 +129,7 @@
                                     backup_state,
                                     attested_credential_data.has_value(),
                                     extensions.has_value())),
-      application_parameter_(fido_parsing_utils::Materialize(rp_id_hash)),
+      application_parameter_(base::ToArray(rp_id_hash)),
       counter_(std::array<uint8_t, kSignCounterLength>{
           static_cast<uint8_t>(sign_counter >> 24),
           static_cast<uint8_t>(sign_counter >> 16),
diff --git a/device/fido/authenticator_get_assertion_response.cc b/device/fido/authenticator_get_assertion_response.cc
index c72910ba..c809a1e8 100644
--- a/device/fido/authenticator_get_assertion_response.cc
+++ b/device/fido/authenticator_get_assertion_response.cc
@@ -7,10 +7,10 @@
 #include <optional>
 #include <utility>
 
+#include "base/containers/to_vector.h"
 #include "components/cbor/values.h"
 #include "components/device_event_log/device_event_log.h"
 #include "device/fido/authenticator_data.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "third_party/boringssl/src/include/openssl/ecdsa.h"
 
 namespace device {
@@ -51,8 +51,7 @@
   AuthenticatorData authenticator_data(relying_party_id_hash, flags, counter,
                                        std::nullopt);
 
-  auto signature =
-      fido_parsing_utils::Materialize(u2f_data.subspan(kSignatureIndex));
+  auto signature = base::ToVector(u2f_data.subspan(kSignatureIndex));
 
   bssl::UniquePtr<ECDSA_SIG> parsed_sig(
       ECDSA_SIG_from_bytes(signature.data(), signature.size()));
@@ -65,7 +64,7 @@
   AuthenticatorGetAssertionResponse response(
       std::move(authenticator_data), std::move(signature), transport_used);
   response.credential = PublicKeyCredentialDescriptor(
-      CredentialType::kPublicKey, fido_parsing_utils::Materialize(key_handle));
+      CredentialType::kPublicKey, base::ToVector(key_handle));
   return std::move(response);
 }
 
diff --git a/device/fido/authenticator_get_info_response.cc b/device/fido/authenticator_get_info_response.cc
index 4592007c..0b0f451 100644
--- a/device/fido/authenticator_get_info_response.cc
+++ b/device/fido/authenticator_get_info_response.cc
@@ -7,10 +7,10 @@
 #include <algorithm>
 #include <utility>
 
+#include "base/containers/to_array.h"
 #include "base/numerics/safe_conversions.h"
 #include "components/cbor/values.h"
 #include "components/cbor/writer.h"
-#include "device/fido/fido_parsing_utils.h"
 
 namespace device {
 
@@ -34,7 +34,7 @@
     base::span<const uint8_t, kAaguidLength> in_aaguid)
     : versions(std::move(in_versions)),
       ctap2_versions(std::move(in_ctap2_versions)),
-      aaguid(fido_parsing_utils::Materialize(in_aaguid)) {
+      aaguid(base::ToArray(in_aaguid)) {
   DCHECK_NE(versions.contains(ProtocolVersion::kCtap2), ctap2_versions.empty());
 }
 
diff --git a/device/fido/authenticator_make_credential_response.cc b/device/fido/authenticator_make_credential_response.cc
index 5e195f2..7afb468 100644
--- a/device/fido/authenticator_make_credential_response.cc
+++ b/device/fido/authenticator_make_credential_response.cc
@@ -11,7 +11,6 @@
 #include "device/fido/attestation_statement_formats.h"
 #include "device/fido/attested_credential_data.h"
 #include "device/fido/authenticator_data.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/large_blob.h"
 #include "device/fido/p256_public_key.h"
 #include "device/fido/public/fido_constants.h"
diff --git a/device/fido/cable/fido_tunnel_device.cc b/device/fido/cable/fido_tunnel_device.cc
index 4540c5e..c5312c9 100644
--- a/device/fido/cable/fido_tunnel_device.cc
+++ b/device/fido/cable/fido_tunnel_device.cc
@@ -6,6 +6,7 @@
 
 #include <variant>
 
+#include "base/containers/to_array.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
@@ -20,7 +21,6 @@
 #include "device/fido/cable/v2_constants.h"
 #include "device/fido/cbor_extract.h"
 #include "device/fido/fido_device.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/network_context_factory.h"
 #include "device/fido/public/features.h"
 #include "device/fido/public/fido_constants.h"
@@ -145,8 +145,7 @@
 
   QRInfo& info = std::get<QRInfo>(info_);
   info.pairing_callback = std::move(pairing_callback);
-  info.local_identity_seed =
-      fido_parsing_utils::Materialize(local_identity_seed);
+  info.local_identity_seed = base::ToArray(local_identity_seed);
   info.tunnel_server_domain = components.tunnel_server_domain;
 
   info.psk =
diff --git a/device/fido/cable/pairing.cc b/device/fido/cable/pairing.cc
index a5f51ecf..7086904f 100644
--- a/device/fido/cable/pairing.cc
+++ b/device/fido/cable/pairing.cc
@@ -5,45 +5,13 @@
 #include "device/fido/cable/pairing.h"
 
 #include <algorithm>
-#include <cstring>
+#include <tuple>
 
-#include "base/check_op.h"
-#include "base/compiler_specific.h"
-#include "base/i18n/string_compare.h"
-#include "base/notreached.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
 #include "components/cbor/values.h"
-#include "crypto/random.h"
 #include "device/fido/cable/v2_handshake.h"
-#include "device/fido/fido_parsing_utils.h"
-#include "third_party/boringssl/src/include/openssl/aes.h"
-#include "third_party/boringssl/src/include/openssl/digest.h"
-#include "third_party/boringssl/src/include/openssl/ec.h"
-#include "third_party/boringssl/src/include/openssl/hkdf.h"
-#include "third_party/boringssl/src/include/openssl/mem.h"
-#include "third_party/boringssl/src/include/openssl/obj.h"
-#include "third_party/icu/source/common/unicode/locid.h"
-#include "third_party/icu/source/i18n/unicode/coll.h"
 
 namespace device::cablev2 {
 
-Pairing::NameComparator::NameComparator(const icu::Locale* locale) {
-  UErrorCode error = U_ZERO_ERROR;
-  collator_.reset(icu::Collator::createInstance(*locale, error));
-}
-
-Pairing::NameComparator::NameComparator(NameComparator&&) = default;
-
-Pairing::NameComparator::~NameComparator() = default;
-
-bool Pairing::NameComparator::operator()(const std::unique_ptr<Pairing>& a,
-                                         const std::unique_ptr<Pairing>& b) {
-  return base::i18n::CompareString16WithCollator(
-             *collator_, base::UTF8ToUTF16(a->name),
-             base::UTF8ToUTF16(b->name)) == UCOL_LESS;
-}
-
 Pairing::Pairing() = default;
 Pairing::~Pairing() = default;
 
@@ -102,30 +70,6 @@
 }
 
 // static
-bool Pairing::CompareByMostRecentFirst(const std::unique_ptr<Pairing>& a,
-                                       const std::unique_ptr<Pairing>& b) {
-  return a->last_updated > b->last_updated;
-}
-
-// static
-bool Pairing::CompareByLeastStableChannelFirst(
-    const std::unique_ptr<Pairing>& a,
-    const std::unique_ptr<Pairing>& b) {
-  return a->channel_priority > b->channel_priority;
-}
-
-// static
-bool Pairing::CompareByPublicKey(const std::unique_ptr<Pairing>& a,
-                                 const std::unique_ptr<Pairing>& b) {
-  return a->peer_public_key_x962 < b->peer_public_key_x962;
-}
-
-// static
-Pairing::NameComparator Pairing::CompareByName(const icu::Locale* locale) {
-  return NameComparator(locale);
-}
-
-// static
 bool Pairing::EqualPublicKeys(const std::unique_ptr<Pairing>& a,
                               const std::unique_ptr<Pairing>& b) {
   return a->peer_public_key_x962 == b->peer_public_key_x962;
diff --git a/device/fido/cable/pairing.h b/device/fido/cable/pairing.h
index 9bd765f..04a6da4 100644
--- a/device/fido/cable/pairing.h
+++ b/device/fido/cable/pairing.h
@@ -11,7 +11,6 @@
 #include <memory>
 #include <optional>
 #include <string>
-#include <variant>
 #include <vector>
 
 #include "base/component_export.h"
@@ -19,40 +18,16 @@
 #include "base/time/time.h"
 #include "device/fido/cable/v2_constants.h"
 #include "device/fido/public/fido_constants.h"
-#include "third_party/icu/source/common/unicode/uversion.h"
 
 namespace cbor {
 class Value;
 }
 
-// third_party/icu/source/common/unicode/uversion.h will set namespace icu.
-namespace U_ICU_NAMESPACE {
-class Collator;
-class Locale;
-}  // namespace U_ICU_NAMESPACE
-
 namespace device::cablev2 {
 
 // Pairing represents information previously received from a caBLEv2
 // authenticator that enables future interactions to skip scanning a QR code.
 struct COMPONENT_EXPORT(DEVICE_FIDO) Pairing {
-  // NameComparator is a less-than operation for sorting `Pairing` by name.
-  // See `CompareByName`.
-  class COMPONENT_EXPORT(DEVICE_FIDO) NameComparator {
-   public:
-    explicit NameComparator(const icu::Locale* locale);
-    NameComparator(NameComparator&&);
-    NameComparator(const NameComparator&) = delete;
-    NameComparator& operator=(const NameComparator&) = delete;
-    ~NameComparator();
-
-    bool operator()(const std::unique_ptr<Pairing>&,
-                    const std::unique_ptr<Pairing>&);
-
-   private:
-    std::unique_ptr<icu::Collator> collator_;
-  };
-
   Pairing();
   ~Pairing();
   Pairing(const Pairing&);
@@ -67,13 +42,6 @@
       base::span<const uint8_t, kQRSeedSize> local_identity_seed,
       base::span<const uint8_t, 32> handshake_hash);
 
-  static bool CompareByMostRecentFirst(const std::unique_ptr<Pairing>&,
-                                       const std::unique_ptr<Pairing>&);
-  static bool CompareByLeastStableChannelFirst(const std::unique_ptr<Pairing>&,
-                                               const std::unique_ptr<Pairing>&);
-  static bool CompareByPublicKey(const std::unique_ptr<Pairing>&,
-                                 const std::unique_ptr<Pairing>&);
-  static NameComparator CompareByName(const icu::Locale* locale);
   static bool EqualPublicKeys(const std::unique_ptr<Pairing>&,
                               const std::unique_ptr<Pairing>&);
 
diff --git a/device/fido/cable/v2_authenticator.cc b/device/fido/cable/v2_authenticator.cc
index 677891b..f2221639 100644
--- a/device/fido/cable/v2_authenticator.cc
+++ b/device/fido/cable/v2_authenticator.cc
@@ -9,6 +9,8 @@
 #include <variant>
 
 #include "base/containers/flat_set.h"
+#include "base/containers/to_array.h"
+#include "base/containers/to_vector.h"
 #include "base/feature_list.h"
 #include "base/json/json_reader.h"
 #include "base/memory/raw_ptr.h"
@@ -28,7 +30,6 @@
 #include "device/fido/cable/v2_handshake.h"
 #include "device/fido/cable/websocket_adapter.h"
 #include "device/fido/cbor_extract.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/network_context_factory.h"
 #include "device/fido/public/features.h"
 #include "device/fido/public/fido_constants.h"
@@ -287,9 +288,9 @@
             base::span<const uint8_t>(),
             device::cablev2::DerivedValueType::kEIDKey)),
         network_context_factory_(std::move(network_context_factory)),
-        peer_identity_(device::fido_parsing_utils::Materialize(peer_identity)),
+        peer_identity_(base::ToArray(peer_identity)),
         generate_pairing_data_(std::move(generate_pairing_data)),
-        secret_(fido_parsing_utils::Materialize(secret)),
+        secret_(base::ToVector(secret)),
         features_(std::move(features)) {
     DCHECK_EQ(state_, State::kNone);
     state_ = State::kConnecting;
@@ -311,13 +312,13 @@
       base::span<const uint8_t, 16> tunnel_id,
       bssl::UniquePtr<EC_KEY> local_identity)
       : platform_(platform),
-        tunnel_id_(fido_parsing_utils::Materialize(tunnel_id)),
+        tunnel_id_(base::ToArray(tunnel_id)),
         eid_key_(device::cablev2::Derive<kEIDKeySize>(
             secret,
             client_nonce,
             device::cablev2::DerivedValueType::kEIDKey)),
         network_context_factory_(network_context_factory),
-        secret_(fido_parsing_utils::Materialize(secret)),
+        secret_(base::ToVector(secret)),
         features_({Feature::kCTAP}),
         local_identity_(std::move(local_identity)) {
     DCHECK_EQ(state_, State::kNone);
@@ -1236,7 +1237,7 @@
   PairingDataGenerator(base::span<const uint8_t, kRootSecretSize> root_secret,
                        const std::string& name,
                        std::optional<std::vector<uint8_t>> contact_id)
-      : root_secret_(fido_parsing_utils::Materialize(root_secret)),
+      : root_secret_(base::ToArray(root_secret)),
         name_(name),
         contact_id_(std::move(contact_id)) {}
 
diff --git a/device/fido/cable/v2_discovery.cc b/device/fido/cable/v2_discovery.cc
index 839102f..454ccc23 100644
--- a/device/fido/cable/v2_discovery.cc
+++ b/device/fido/cable/v2_discovery.cc
@@ -4,6 +4,7 @@
 
 #include "device/fido/cable/v2_discovery.h"
 
+#include "base/containers/to_array.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
@@ -20,7 +21,6 @@
 #include "device/fido/cable/fido_tunnel_device.h"
 #include "device/fido/cable/pairing.h"
 #include "device/fido/cable/v2_handshake.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/public/features.h"
 #include "third_party/boringssl/src/include/openssl/aes.h"
 
@@ -133,8 +133,7 @@
 }
 
 void Discovery::OnBLEAdvertSeen(base::span<const uint8_t, kAdvertSize> advert) {
-  const std::array<uint8_t, kAdvertSize> advert_array =
-      fido_parsing_utils::Materialize<kAdvertSize>(advert);
+  const std::array<uint8_t, kAdvertSize> advert_array = base::ToArray(advert);
 
   if (device_committed_) {
     // A device has already been accepted. Ignore other adverts.
@@ -231,12 +230,13 @@
     return std::nullopt;
   }
 
-  UnpairedKeys ret;
   static_assert(kQRKeySize == kQRSeedSize + kQRSecretSize);
-  ret.local_identity_seed = fido_parsing_utils::Materialize(
-      qr_generator_key->subspan<0, kQRSeedSize>());
-  ret.qr_secret = fido_parsing_utils::Materialize(
-      qr_generator_key->subspan<kQRSeedSize, kQRSecretSize>());
+  UnpairedKeys ret{
+      .local_identity_seed =
+          base::ToArray(qr_generator_key->subspan<0, kQRSeedSize>()),
+      .qr_secret = base::ToArray(
+          qr_generator_key->subspan<kQRSeedSize, kQRSecretSize>()),
+  };
   ret.eid_key = Derive<ret.eid_key.size()>(
       ret.qr_secret, base::span<const uint8_t>(), DerivedValueType::kEIDKey);
   return ret;
diff --git a/device/fido/cable/v2_handshake.cc b/device/fido/cable/v2_handshake.cc
index 857664f..5ce83720 100644
--- a/device/fido/cable/v2_handshake.cc
+++ b/device/fido/cable/v2_handshake.cc
@@ -18,6 +18,7 @@
 #include "base/containers/span.h"
 #include "base/containers/span_reader.h"
 #include "base/containers/span_writer.h"
+#include "base/containers/to_array.h"
 #include "base/feature_list.h"
 #include "base/numerics/byte_conversions.h"
 #include "base/numerics/safe_conversions.h"
@@ -32,7 +33,6 @@
 #include "components/device_event_log/device_event_log.h"
 #include "crypto/aead.h"
 #include "device/fido/cable/v2_constants.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/public/features.h"
 #include "device/fido/public/fido_constants.h"
 #include "third_party/abseil-cpp/absl/functional/overload.h"
@@ -784,8 +784,8 @@
 
 Crypter::Crypter(base::span<const uint8_t, 32> read_key,
                  base::span<const uint8_t, 32> write_key)
-    : read_key_(fido_parsing_utils::Materialize(read_key)),
-      write_key_(fido_parsing_utils::Materialize(write_key)) {}
+    : read_key_(base::ToArray(read_key)),
+      write_key_(base::ToArray(write_key)) {}
 
 Crypter::~Crypter() = default;
 
@@ -879,11 +879,10 @@
     : local_identity_(identity_seed ? ECKeyFromSeed(*identity_seed) : nullptr) {
   DCHECK(peer_identity.has_value() ^ static_cast<bool>(local_identity_));
   if (peer_identity) {
-    peer_identity_ =
-        fido_parsing_utils::Materialize<kP256X962Length>(*peer_identity);
+    peer_identity_ = base::ToArray(*peer_identity);
   }
   if (psk) {
-    psk_ = fido_parsing_utils::Materialize(*psk);
+    psk_ = base::ToArray(*psk);
   }
 }
 
diff --git a/device/fido/cable/v2_test_util.cc b/device/fido/cable/v2_test_util.cc
index 70ec833..74adbb1 100644
--- a/device/fido/cable/v2_test_util.cc
+++ b/device/fido/cable/v2_test_util.cc
@@ -13,6 +13,8 @@
 #include "base/check.h"
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
+#include "base/containers/to_array.h"
+#include "base/containers/to_vector.h"
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
@@ -484,9 +486,8 @@
       base::span<const uint8_t, kAdvertSize> payload) override {
     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
         FROM_HERE,
-        base::BindOnce(
-            &TestPlatform::DoSendBLEAdvert, weak_factory_.GetWeakPtr(),
-            device::fido_parsing_utils::Materialize<payload.size()>(payload)));
+        base::BindOnce(&TestPlatform::DoSendBLEAdvert,
+                       weak_factory_.GetWeakPtr(), base::ToArray(payload)));
     return std::make_unique<DummyBLEAdvert>();
   }
 
@@ -657,8 +658,8 @@
             qr_secret,
             {},
             device::cablev2::DerivedValueType::kEIDKey)),
-        peer_identity_(device::fido_parsing_utils::Materialize(peer_identity)),
-        secret_(fido_parsing_utils::Materialize(qr_secret)) {
+        peer_identity_(base::ToArray(peer_identity)),
+        secret_(base::ToVector(qr_secret)) {
     websocket_client_ = std::make_unique<device::cablev2::WebSocketAdapter>(
         base::BindOnce(&LateLinkingDevice::OnTunnelReady,
                        base::Unretained(this)),
@@ -882,7 +883,7 @@
             qr_secret,
             {},
             device::cablev2::DerivedValueType::kEIDKey)),
-        secret_(fido_parsing_utils::Materialize(qr_secret)) {
+        secret_(base::ToVector(qr_secret)) {
     websocket_client_ = std::make_unique<device::cablev2::WebSocketAdapter>(
         base::BindOnce(&HandshakeErrorDevice::OnTunnelReady,
                        base::Unretained(this)),
diff --git a/device/fido/credential_management.cc b/device/fido/credential_management.cc
index 56725a3b..7450c8c 100644
--- a/device/fido/credential_management.cc
+++ b/device/fido/credential_management.cc
@@ -6,9 +6,9 @@
 
 #include "base/check_op.h"
 #include "base/containers/span.h"
+#include "base/containers/to_array.h"
 #include "components/cbor/values.h"
 #include "components/cbor/writer.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/pin.h"
 #include "device/fido/public/fido_constants.h"
 #include "third_party/boringssl/src/include/openssl/hmac.h"
@@ -321,7 +321,7 @@
     if (!sized_large_blob_span) {
       return std::nullopt;
     }
-    large_blob_key = fido_parsing_utils::Materialize(*sized_large_blob_span);
+    large_blob_key = base::ToArray(*sized_large_blob_span);
   }
 
   size_t credential_count = 0;
diff --git a/device/fido/credential_management_handler_unittest.cc b/device/fido/credential_management_handler_unittest.cc
index 0346515..248c626 100644
--- a/device/fido/credential_management_handler_unittest.cc
+++ b/device/fido/credential_management_handler_unittest.cc
@@ -16,6 +16,7 @@
 
 #include "base/check_op.h"
 #include "base/containers/flat_set.h"
+#include "base/containers/to_vector.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/strings/strcat.h"
@@ -97,8 +98,8 @@
   virtual_device_factory_.mutable_state()->pin_retries = device::kMaxPinRetries;
 
   PublicKeyCredentialRpEntity rp(kRPID, kRPName);
-  PublicKeyCredentialUserEntity user(fido_parsing_utils::Materialize(kUserID),
-                                     kUserName, kUserDisplayName);
+  PublicKeyCredentialUserEntity user(base::ToVector(kUserID), kUserName,
+                                     kUserDisplayName);
 
   ASSERT_TRUE(virtual_device_factory_.mutable_state()->InjectResidentKey(
       kCredentialID, rp, user));
@@ -150,13 +151,12 @@
       virtual_device_factory_.mutable_state()->large_blob;
 
   PublicKeyCredentialRpEntity rp(kRPID, kRPName);
-  PublicKeyCredentialUserEntity user(fido_parsing_utils::Materialize(kUserID),
-                                     kUserName, kUserDisplayName);
+  PublicKeyCredentialUserEntity user(base::ToVector(kUserID), kUserName,
+                                     kUserDisplayName);
   ASSERT_TRUE(virtual_device_factory_.mutable_state()->InjectResidentKey(
       kCredentialID, rp, user));
 
-  std::vector<uint8_t> credential_id =
-      fido_parsing_utils::Materialize(kCredentialID);
+  std::vector<uint8_t> credential_id = base::ToVector(kCredentialID);
   LargeBlob blob(std::vector<uint8_t>{'b', 'l', 'o', 'b'}, 4);
   virtual_device_factory_.mutable_state()->InjectLargeBlob(
       &virtual_device_factory_.mutable_state()->registrations.at(credential_id),
@@ -194,13 +194,12 @@
   EXPECT_TRUE(ready_future_.Wait());
 
   PublicKeyCredentialRpEntity rp(kRPID, kRPName);
-  PublicKeyCredentialUserEntity user(fido_parsing_utils::Materialize(kUserID),
-                                     kUserName, kUserDisplayName);
+  PublicKeyCredentialUserEntity user(base::ToVector(kUserID), kUserName,
+                                     kUserDisplayName);
   ASSERT_TRUE(virtual_device_factory_.mutable_state()->InjectResidentKey(
       kCredentialID, rp, user));
 
-  std::vector<uint8_t> credential_id =
-      fido_parsing_utils::Materialize(kCredentialID);
+  std::vector<uint8_t> credential_id = base::ToVector(kCredentialID);
   LargeBlob blob(std::vector<uint8_t>{'b', 'l', 'o', 'b'}, 4);
   virtual_device_factory_.mutable_state()->InjectLargeBlob(
       &virtual_device_factory_.mutable_state()->registrations.at(credential_id),
@@ -240,12 +239,11 @@
   EXPECT_TRUE(ready_future_.Wait());
 
   PublicKeyCredentialRpEntity rp(kRPID, kRPName);
-  PublicKeyCredentialUserEntity user(fido_parsing_utils::Materialize(kUserID),
-                                     kUserName, kUserDisplayName);
+  PublicKeyCredentialUserEntity user(base::ToVector(kUserID), kUserName,
+                                     kUserDisplayName);
   ASSERT_TRUE(virtual_device_factory_.mutable_state()->InjectResidentKey(
       kCredentialID, rp, user));
-  std::vector<uint8_t> credential_id =
-      fido_parsing_utils::Materialize(kCredentialID);
+  std::vector<uint8_t> credential_id = base::ToVector(kCredentialID);
   LargeBlob blob(std::vector<uint8_t>{'b', 'l', 'o', 'b'}, 4);
   virtual_device_factory_.mutable_state()->InjectLargeBlob(
       &virtual_device_factory_.mutable_state()->registrations.at(credential_id),
@@ -279,12 +277,11 @@
   virtual_device_factory_.SetSupportedProtocol(device::ProtocolVersion::kCtap2);
   virtual_device_factory_.mutable_state()->pin = kPIN;
   virtual_device_factory_.mutable_state()->pin_retries = device::kMaxPinRetries;
-  std::vector<uint8_t> credential_id =
-      fido_parsing_utils::Materialize(kCredentialID);
+  std::vector<uint8_t> credential_id = base::ToVector(kCredentialID);
 
   PublicKeyCredentialRpEntity rp(kRPID, kRPName);
-  PublicKeyCredentialUserEntity user(fido_parsing_utils::Materialize(kUserID),
-                                     kUserName, kUserDisplayName);
+  PublicKeyCredentialUserEntity user(base::ToVector(kUserID), kUserName,
+                                     kUserDisplayName);
 
   ASSERT_TRUE(virtual_device_factory_.mutable_state()->InjectResidentKey(
       kCredentialID, rp, user));
@@ -293,8 +290,7 @@
   EXPECT_TRUE(ready_future_.Wait());
 
   PublicKeyCredentialUserEntity updated_user(
-      fido_parsing_utils::Materialize(kUserID), "bobbyr@example.com",
-      "Bobby R. Smith");
+      base::ToVector(kUserID), "bobbyr@example.com", "Bobby R. Smith");
 
   handler->UpdateUserInformation(
       device::PublicKeyCredentialDescriptor(device::CredentialType::kPublicKey,
@@ -365,8 +361,7 @@
       PublicKeyCredentialRpEntity(kRPID,
                                   base::StrCat({rp_name, kTruncatedUTF8})),
       PublicKeyCredentialUserEntity(
-          fido_parsing_utils::Materialize(kUserID),
-          base::StrCat({user_name, kTruncatedUTF8}),
+          base::ToVector(kUserID), base::StrCat({user_name, kTruncatedUTF8}),
           base::StrCat({display_name, kTruncatedUTF8}))));
 
   auto handler = MakeHandler();
@@ -382,10 +377,9 @@
   ASSERT_EQ(opt_response->front().credentials.size(), 1u);
   EXPECT_EQ(opt_response->front().rp,
             PublicKeyCredentialRpEntity(kRPID, rp_name));
-  EXPECT_EQ(
-      opt_response->front().credentials.front().user,
-      PublicKeyCredentialUserEntity(fido_parsing_utils::Materialize(kUserID),
-                                    user_name, display_name));
+  EXPECT_EQ(opt_response->front().credentials.front().user,
+            PublicKeyCredentialUserEntity(base::ToVector(kUserID), user_name,
+                                          display_name));
 }
 
 TEST_F(CredentialManagementHandlerTest, EnumerateCredentialsMultipleRPs) {
diff --git a/device/fido/ctap_get_assertion_request.cc b/device/fido/ctap_get_assertion_request.cc
index eb7b9c91..5912928b 100644
--- a/device/fido/ctap_get_assertion_request.cc
+++ b/device/fido/ctap_get_assertion_request.cc
@@ -8,10 +8,10 @@
 #include <limits>
 #include <utility>
 
+#include "base/containers/to_array.h"
 #include "base/feature_list.h"
 #include "crypto/hash.h"
 #include "device/fido/device_response_converter.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/pin.h"
 #include "device/fido/public/features.h"
 #include "device/fido/public/fido_constants.h"
@@ -80,7 +80,7 @@
 
   CtapGetAssertionRequest request(rp_id_it->second.GetString(),
                                   /*client_data_json=*/std::string());
-  request.client_data_hash = fido_parsing_utils::Materialize(client_data_hash);
+  request.client_data_hash = base::ToArray(client_data_hash);
 
   const auto allow_list_it = request_map.find(cbor::Value(3));
   if (allow_list_it != request_map.end()) {
diff --git a/device/fido/ctap_make_credential_request.cc b/device/fido/ctap_make_credential_request.cc
index 90ab4384..314996e 100644
--- a/device/fido/ctap_make_credential_request.cc
+++ b/device/fido/ctap_make_credential_request.cc
@@ -8,11 +8,11 @@
 #include <limits>
 #include <utility>
 
+#include "base/containers/to_array.h"
 #include "base/numerics/safe_conversions.h"
 #include "components/cbor/values.h"
 #include "crypto/hash.h"
 #include "device/fido/device_response_converter.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/public/fido_constants.h"
 
 namespace device {
@@ -88,7 +88,7 @@
   CtapMakeCredentialRequest request(
       /*client_data_json=*/std::string(), std::move(*rp_entity),
       std::move(*user_entity), std::move(*credential_params));
-  request.client_data_hash = fido_parsing_utils::Materialize(client_data_hash);
+  request.client_data_hash = base::ToArray(client_data_hash);
 
   const auto exclude_list_it = request_map.find(cbor::Value(5));
   if (exclude_list_it != request_map.end()) {
diff --git a/device/fido/ctap_request_common.cc b/device/fido/ctap_request_common.cc
index ce744530..c0c86298 100644
--- a/device/fido/ctap_request_common.cc
+++ b/device/fido/ctap_request_common.cc
@@ -4,9 +4,10 @@
 
 #include "device/fido/ctap_request_common.h"
 
+#include "base/containers/to_array.h"
+#include "base/containers/to_vector.h"
 #include "base/logging.h"
 #include "device/fido/device_response_converter.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/pin.h"
 
 namespace device {
@@ -16,9 +17,9 @@
     base::span<const uint8_t> in_encrypted_salts,
     base::span<const uint8_t> in_salts_auth,
     std::optional<PINUVAuthProtocol> in_pin_protocol)
-    : public_key_x962(fido_parsing_utils::Materialize(in_public_key_x962)),
-      encrypted_salts(fido_parsing_utils::Materialize(in_encrypted_salts)),
-      salts_auth(fido_parsing_utils::Materialize(in_salts_auth)),
+    : public_key_x962(base::ToArray(in_public_key_x962)),
+      encrypted_salts(base::ToVector(in_encrypted_salts)),
+      salts_auth(base::ToVector(in_salts_auth)),
       pin_protocol(in_pin_protocol) {}
 
 HMACSecret::HMACSecret(const HMACSecret&) = default;
diff --git a/device/fido/ctap_request_unittest.cc b/device/fido/ctap_request_unittest.cc
index cb313f85..78214231 100644
--- a/device/fido/ctap_request_unittest.cc
+++ b/device/fido/ctap_request_unittest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/containers/to_vector.h"
 #include "components/cbor/reader.h"
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/ctap_make_credential_request.h"
@@ -26,8 +27,7 @@
   PublicKeyCredentialRpEntity rp("acme.com");
   rp.name = "Acme";
 
-  PublicKeyCredentialUserEntity user(
-      fido_parsing_utils::Materialize(test_data::kUserId));
+  PublicKeyCredentialUserEntity user(base::ToVector(test_data::kUserId));
   user.name = "johnpsmith@example.com";
   user.display_name = "John P. Smith";
 
@@ -202,8 +202,7 @@
 
     PublicKeyCredentialRpEntity rp("acme.com");
     rp.name = "Acme";
-    PublicKeyCredentialUserEntity user(
-        fido_parsing_utils::Materialize(test_data::kUserId));
+    PublicKeyCredentialUserEntity user(base::ToVector(test_data::kUserId));
     user.name = "johnpsmith@example.com";
     user.display_name = "John P. Smith";
     CtapMakeCredentialRequest make_credential_request(
diff --git a/device/fido/ctap_response_unittest.cc b/device/fido/ctap_response_unittest.cc
index 80ec5416..7c5df49 100644
--- a/device/fido/ctap_response_unittest.cc
+++ b/device/fido/ctap_response_unittest.cc
@@ -7,6 +7,7 @@
 #include <string_view>
 
 #include "base/compiler_specific.h"
+#include "base/containers/to_vector.h"
 #include "components/cbor/reader.h"
 #include "components/cbor/values.h"
 #include "components/cbor/writer.h"
@@ -461,8 +462,7 @@
 
 std::vector<uint8_t> GetTestAttestedCredentialDataBytes() {
   // Combine kTestAttestedCredentialDataPrefix and kTestECPublicKeyCOSE.
-  auto test_attested_data =
-      fido_parsing_utils::Materialize(kTestAttestedCredentialDataPrefix);
+  auto test_attested_data = base::ToVector(kTestAttestedCredentialDataPrefix);
   fido_parsing_utils::Append(&test_attested_data,
                              test_data::kTestECPublicKeyCOSE);
   return test_attested_data;
@@ -470,16 +470,14 @@
 
 std::vector<uint8_t> GetTestAuthenticatorDataBytes() {
   // Build the test authenticator data.
-  auto test_authenticator_data =
-      fido_parsing_utils::Materialize(kTestAuthenticatorDataPrefix);
+  auto test_authenticator_data = base::ToVector(kTestAuthenticatorDataPrefix);
   auto test_attested_data = GetTestAttestedCredentialDataBytes();
   fido_parsing_utils::Append(&test_authenticator_data, test_attested_data);
   return test_authenticator_data;
 }
 
 std::vector<uint8_t> GetTestAttestationObjectBytes() {
-  auto test_authenticator_object =
-      fido_parsing_utils::Materialize(kFormatFidoU2fCBOR);
+  auto test_authenticator_object = base::ToVector(kFormatFidoU2fCBOR);
   fido_parsing_utils::Append(&test_authenticator_object, kAttStmtCBOR);
   fido_parsing_utils::Append(&test_authenticator_object,
                              test_data::kU2fAttestationStatementCBOR);
@@ -491,19 +489,19 @@
 }
 
 std::vector<uint8_t> GetTestSignResponse() {
-  return fido_parsing_utils::Materialize(test_data::kTestU2fSignResponse);
+  return base::ToVector(test_data::kTestU2fSignResponse);
 }
 
 // Get a subset of the response for testing error handling.
 std::vector<uint8_t> GetTestCorruptedSignResponse(size_t length) {
   DCHECK_LE(length, std::size(test_data::kTestU2fSignResponse));
-  return fido_parsing_utils::Materialize(fido_parsing_utils::ExtractSpan(
+  return base::ToVector(fido_parsing_utils::ExtractSpan(
       test_data::kTestU2fSignResponse, 0, length));
 }
 
 // Return a key handle used for GetAssertion request.
 std::vector<uint8_t> GetTestCredentialRawIdBytes() {
-  return fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle);
+  return base::ToVector(test_data::kU2fSignKeyHandle);
 }
 
 // DecodeCBOR parses a CBOR structure, ignoring the first byte of |in|, which is
@@ -967,8 +965,7 @@
       kTestDeviceAaguid,
       std::array<uint8_t, kCredentialIdLengthLength>{
           {0x00, 0x10}} /* credential_id_length */,
-      fido_parsing_utils::Materialize(
-          test_data::kCtap2MakeCredentialCredentialId),
+      base::ToVector(test_data::kCtap2MakeCredentialCredentialId),
       std::make_unique<PublicKey>(
           static_cast<int32_t>(CoseAlgorithmIdentifier::kEs256),
           kCoseEncodedPublicKey, std::nullopt));
@@ -978,11 +975,11 @@
 
   cbor::Value::MapValue attestation_map;
   attestation_map.emplace("alg", -7);
-  attestation_map.emplace("sig", fido_parsing_utils::Materialize(
-                                     test_data::kCtap2MakeCredentialSignature));
+  attestation_map.emplace(
+      "sig", base::ToVector(test_data::kCtap2MakeCredentialSignature));
   cbor::Value::ArrayValue certificate_chain;
-  certificate_chain.emplace_back(fido_parsing_utils::Materialize(
-      test_data::kCtap2MakeCredentialCertificate));
+  certificate_chain.emplace_back(
+      base::ToVector(test_data::kCtap2MakeCredentialCertificate));
   attestation_map.emplace("x5c", std::move(certificate_chain));
   AuthenticatorMakeCredentialResponse response(
       FidoTransportProtocol::kUsbHumanInterfaceDevice,
diff --git a/device/fido/enclave/enclave_protocol_utils.cc b/device/fido/enclave/enclave_protocol_utils.cc
index 3e67122..d4763de 100644
--- a/device/fido/enclave/enclave_protocol_utils.cc
+++ b/device/fido/enclave/enclave_protocol_utils.cc
@@ -9,6 +9,7 @@
 #include <variant>
 
 #include "base/compiler_specific.h"
+#include "base/containers/to_vector.h"
 #include "base/feature_list.h"
 #include "base/functional/callback.h"
 #include "base/json/json_reader.h"
@@ -324,8 +325,7 @@
   }
 
   response->credential = PublicKeyCredentialDescriptor(
-      CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(credential_id));
+      CredentialType::kPublicKey, base::ToVector(credential_id));
   response->hmac_secret = std::move(prf_results);
 
   const std::vector<uint8_t>* updated_encrypted_passkey =
diff --git a/device/fido/enclave/enclave_protocol_utils_unittest.cc b/device/fido/enclave/enclave_protocol_utils_unittest.cc
index 54d5bbf24..52e135e 100644
--- a/device/fido/enclave/enclave_protocol_utils_unittest.cc
+++ b/device/fido/enclave/enclave_protocol_utils_unittest.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/containers/span.h"
+#include "base/containers/to_vector.h"
 #include "base/functional/bind.h"
 #include "base/json/json_reader.h"
 #include "base/memory/scoped_refptr.h"
@@ -179,12 +180,12 @@
     base::OnceCallback<void(std::optional<enclave::ClientSignature>)>
         callback) {
   base::span<const uint8_t> message_span = to_be_signed;
-  EXPECT_EQ(fido_parsing_utils::Materialize(message_span.first(32u)),
-            fido_parsing_utils::Materialize(kHandshakeHash));
+  EXPECT_EQ(base::ToVector(message_span.first(32u)),
+            base::ToVector(kHandshakeHash));
 
   enclave::ClientSignature ret;
-  ret.device_id = fido_parsing_utils::Materialize(kDeviceId);
-  ret.signature = fido_parsing_utils::Materialize(kSignature);
+  ret.device_id = base::ToVector(kDeviceId);
+  ret.signature = base::ToVector(kSignature);
   ret.key_type = enclave::ClientKeyType::kHardware;
   std::move(callback).Run(std::move(ret));
 }
@@ -239,8 +240,8 @@
   ~EnclaveProtocolUtilsTest() override = default;
 
   void SetUp() override {
-    device_id_ = fido_parsing_utils::Materialize(kDeviceId);
-    user_id_ = fido_parsing_utils::Materialize(kUserId);
+    device_id_ = base::ToVector(kDeviceId);
+    user_id_ = base::ToVector(kUserId);
   }
 
   // This checks the outer map values of a request, which are common to all
diff --git a/device/fido/enclave/enclave_websocket_client.cc b/device/fido/enclave/enclave_websocket_client.cc
index ef6897f..4dac7291 100644
--- a/device/fido/enclave/enclave_websocket_client.cc
+++ b/device/fido/enclave/enclave_websocket_client.cc
@@ -7,6 +7,7 @@
 #include <limits>
 #include <utility>
 
+#include "base/containers/to_vector.h"
 #include "base/feature_list.h"
 #include "base/functional/callback_helpers.h"
 #include "base/metrics/histogram_functions.h"
@@ -114,7 +115,7 @@
   }
 
   if (state_ != State::kOpen) {
-    pending_write_data_ = fido_parsing_utils::Materialize(data);
+    pending_write_data_ = base::ToVector(data);
     return;
   }
 
diff --git a/device/fido/fido_device_authenticator.cc b/device/fido/fido_device_authenticator.cc
index 787157c1..f05d8056 100644
--- a/device/fido/fido_device_authenticator.cc
+++ b/device/fido/fido_device_authenticator.cc
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/containers/to_vector.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
@@ -325,8 +326,7 @@
       DCHECK(request.large_blob_key);
       destination = &large_blob_;
     }
-    destination->emplace(fido_parsing_utils::Materialize(result.value()),
-                         original_size);
+    destination->emplace(base::ToVector(result.value()), original_size);
   }
 
   MaybeGetEphemeralKeyForGetAssertion(std::move(request), std::move(options),
@@ -1186,7 +1186,7 @@
     bool set_blob = false;
     for (auto& response : responses) {
       if (response.large_blob_key == uncompressed_key) {
-        response.large_blob = fido_parsing_utils::Materialize(result.value());
+        response.large_blob = base::ToVector(result.value());
         set_blob = true;
         break;
       }
@@ -1216,8 +1216,7 @@
     base::expected<mojo_base::BigBuffer, std::string> result) {
   DCHECK_EQ(responses.size(), 1u);
   if (result.has_value()) {
-    responses.at(0).large_blob =
-        fido_parsing_utils::Materialize(result.value());
+    responses.at(0).large_blob = base::ToVector(result.value());
   } else {
     FIDO_LOG(ERROR) << "Could not uncompress blob: " << result.error();
   }
diff --git a/device/fido/fido_parsing_utils.cc b/device/fido/fido_parsing_utils.cc
index 9f92986..bea54dd 100644
--- a/device/fido/fido_parsing_utils.cc
+++ b/device/fido/fido_parsing_utils.cc
@@ -6,6 +6,7 @@
 
 #include "base/check_op.h"
 #include "base/compiler_specific.h"
+#include "base/containers/to_vector.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/stringprintf.h"
 
@@ -55,17 +56,6 @@
 
 }  // namespace
 
-std::vector<uint8_t> Materialize(base::span<const uint8_t> span) {
-  return std::vector<uint8_t>(span.begin(), span.end());
-}
-
-std::optional<std::vector<uint8_t>> MaterializeOrNull(
-    std::optional<base::span<const uint8_t>> span) {
-  if (span)
-    return Materialize(*span);
-  return std::nullopt;
-}
-
 void Append(std::vector<uint8_t>* target, base::span<const uint8_t> in_values) {
   CHECK(AreSpansDisjoint(*target, in_values));
   target->insert(target->end(), in_values.begin(), in_values.end());
@@ -74,7 +64,7 @@
 std::vector<uint8_t> Extract(base::span<const uint8_t> span,
                              size_t pos,
                              size_t length) {
-  return Materialize(ExtractSpan(span, pos, length));
+  return base::ToVector(ExtractSpan(span, pos, length));
 }
 
 base::span<const uint8_t> ExtractSpan(base::span<const uint8_t> span,
@@ -86,7 +76,7 @@
 }
 
 std::vector<uint8_t> ExtractSuffix(base::span<const uint8_t> span, size_t pos) {
-  return Materialize(ExtractSuffixSpan(span, pos));
+  return base::ToVector(ExtractSuffixSpan(span, pos));
 }
 
 base::span<const uint8_t> ExtractSuffixSpan(base::span<const uint8_t> span,
diff --git a/device/fido/fido_parsing_utils.h b/device/fido/fido_parsing_utils.h
index 02681d1..fc80f80a 100644
--- a/device/fido/fido_parsing_utils.h
+++ b/device/fido/fido_parsing_utils.h
@@ -37,23 +37,6 @@
   using is_transparent = void;
 };
 
-// Returns a materialized copy of |span|, that is, a vector with the same
-// elements.
-COMPONENT_EXPORT(DEVICE_FIDO)
-std::vector<uint8_t> Materialize(base::span<const uint8_t> span);
-COMPONENT_EXPORT(DEVICE_FIDO)
-std::optional<std::vector<uint8_t>> MaterializeOrNull(
-    std::optional<base::span<const uint8_t>> span);
-
-// Returns a materialized copy of the static |span|, that is, an array with the
-// same elements.
-template <size_t N>
-std::array<uint8_t, N> Materialize(base::span<const uint8_t, N> span) {
-  std::array<uint8_t, N> array;
-  std::ranges::copy(span, array.begin());
-  return array;
-}
-
 // Appends |in_values| to the end of |target|. The underlying container for
 // |in_values| should *not* be |target|.
 COMPONENT_EXPORT(DEVICE_FIDO)
diff --git a/device/fido/fido_parsing_utils_unittest.cc b/device/fido/fido_parsing_utils_unittest.cc
index 698664e..ec12864 100644
--- a/device/fido/fido_parsing_utils_unittest.cc
+++ b/device/fido/fido_parsing_utils_unittest.cc
@@ -80,45 +80,6 @@
   EXPECT_FALSE(RangeLess()(kOneTwoThreeFour, kOneTwoThreeFour));
 }
 
-TEST(U2fParsingUtils, Materialize) {
-  const std::vector<uint8_t> empty;
-  EXPECT_THAT(Materialize(empty), ::testing::IsEmpty());
-  EXPECT_THAT(Materialize(base::span<const uint8_t>()), ::testing::IsEmpty());
-
-  EXPECT_THAT(Materialize(base::span<const uint8_t>(kOne)),
-              ::testing::ElementsAreArray(kOne));
-  EXPECT_THAT(Materialize(base::span<const uint8_t>(kOneTwoThree)),
-              ::testing::ElementsAreArray(kOneTwoThree));
-
-  static_assert(std::is_same<std::vector<uint8_t>,
-                             decltype(Materialize(
-                                 base::span<const uint8_t>(kOne)))>::value,
-                "Materialize with a dynamic span should yield a std::vector.");
-}
-
-TEST(U2fParsingUtils, StaticMaterialize) {
-  std::array<uint8_t, 0> empty;
-  EXPECT_THAT(Materialize(empty), ::testing::IsEmpty());
-  EXPECT_THAT(Materialize(base::span<const uint8_t, 0>()),
-              ::testing::IsEmpty());
-
-  EXPECT_THAT(Materialize(base::span(kOne)), ::testing::ElementsAreArray(kOne));
-  EXPECT_THAT(Materialize(base::span(kOneTwoThree)),
-              ::testing::ElementsAreArray(kOneTwoThree));
-
-  static_assert(std::is_same<std::array<uint8_t, 1>,
-                             decltype(Materialize(base::span(kOne)))>::value,
-                "Materialize with a static span should yield a std::array.");
-}
-
-TEST(U2fParsingUtils, MaterializeOrNull) {
-  auto result = MaterializeOrNull(kOneTwoThree);
-  ASSERT_TRUE(result.has_value());
-  EXPECT_THAT(*result, ::testing::ElementsAreArray(kOneTwoThree));
-
-  EXPECT_EQ(MaterializeOrNull(std::nullopt), std::nullopt);
-}
-
 TEST(U2fParsingUtils, Append) {
   std::vector<uint8_t> target;
 
diff --git a/device/fido/get_assertion_handler_unittest.cc b/device/fido/get_assertion_handler_unittest.cc
index 477861c0..bf51fd7 100644
--- a/device/fido/get_assertion_handler_unittest.cc
+++ b/device/fido/get_assertion_handler_unittest.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/containers/flat_set.h"
+#include "base/containers/to_vector.h"
 #include "base/functional/bind.h"
 #include "base/location.h"
 #include "base/memory/raw_ptr.h"
@@ -150,7 +151,7 @@
                                     test_data::kClientDataJson);
     request.allow_list = {PublicKeyCredentialDescriptor(
         CredentialType::kPublicKey,
-        fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle))};
+        base::ToVector(test_data::kU2fSignKeyHandle))};
     return CreateGetAssertionHandlerWithRequest(std::move(request));
   }
 
@@ -159,8 +160,7 @@
                                     test_data::kClientDataJson);
     request.allow_list = {PublicKeyCredentialDescriptor(
         CredentialType::kPublicKey,
-        fido_parsing_utils::Materialize(
-            test_data::kTestGetAssertionCredentialId))};
+        base::ToVector(test_data::kTestGetAssertionCredentialId))};
     return CreateGetAssertionHandlerWithRequest(std::move(request));
   }
 
@@ -430,7 +430,7 @@
                                          test_data::kClientDataJson);
   request.allow_list = {PublicKeyCredentialDescriptor(
       CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle))};
+      base::ToVector(test_data::kU2fSignKeyHandle))};
   request.user_verification = UserVerificationRequirement::kRequired;
   auto request_handler =
       CreateGetAssertionHandlerWithRequest(std::move(request));
@@ -470,8 +470,7 @@
   CtapGetAssertionRequest request(test_data::kRelyingPartyId,
                                   test_data::kClientDataJson);
   request.allow_list = {PublicKeyCredentialDescriptor(
-      CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha))};
+      CredentialType::kPublicKey, base::ToVector(test_data::kKeyHandleAlpha))};
   auto request_handler =
       CreateGetAssertionHandlerWithRequest(std::move(request));
   discovery()->WaitForCallToStartAndSimulateSuccess();
@@ -703,8 +702,7 @@
                                   test_data::kClientDataJson);
   request.allow_list = {PublicKeyCredentialDescriptor(
       CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(
-          test_data::kTestGetAssertionCredentialId))};
+      base::ToVector(test_data::kTestGetAssertionCredentialId))};
   request.user_verification = UserVerificationRequirement::kRequired;
   auto request_handler =
       CreateGetAssertionHandlerWithRequest(std::move(request));
@@ -788,12 +786,12 @@
       test_data::kTestGetAssertionResponseWithUserEntity, base::TimeDelta(),
       GetAssertionRequestWithAllowlist(/*empty_list=*/false));
 
-  PublicKeyCredentialUserEntity user_entity(
-      fido_parsing_utils::Materialize(test_data::kUserId), test_data::kUsername,
-      test_data::kUserDisplayName);
+  PublicKeyCredentialUserEntity user_entity(base::ToVector(test_data::kUserId),
+                                            test_data::kUsername,
+                                            test_data::kUserDisplayName);
   DiscoverableCredentialMetadata preselected_account(
       AuthenticatorType::kOther, test_data::kRelyingPartyId,
-      fido_parsing_utils::Materialize(test_data::kTestGetAssertionCredentialId),
+      base::ToVector(test_data::kTestGetAssertionCredentialId),
       std::move(user_entity),
       /*provider_name=*/std::nullopt);
   request_handler->PreselectAccount(std::move(preselected_account));
@@ -820,12 +818,12 @@
       test_data::kTestGetAssertionResponseWithUserEntity, base::TimeDelta(),
       GetAssertionRequestWithAllowlist(/*empty_list=*/true));
 
-  PublicKeyCredentialUserEntity user_entity(
-      fido_parsing_utils::Materialize(test_data::kUserId), test_data::kUsername,
-      test_data::kUserDisplayName);
+  PublicKeyCredentialUserEntity user_entity(base::ToVector(test_data::kUserId),
+                                            test_data::kUsername,
+                                            test_data::kUserDisplayName);
   DiscoverableCredentialMetadata preselected_account(
       AuthenticatorType::kEnclave, test_data::kRelyingPartyId,
-      fido_parsing_utils::Materialize(test_data::kTestGetAssertionCredentialId),
+      base::ToVector(test_data::kTestGetAssertionCredentialId),
       std::move(user_entity),
       /*provider_name=*/std::nullopt);
   request_handler->PreselectAccount(std::move(preselected_account));
@@ -863,8 +861,7 @@
                                     test_data::kClientDataJson);
     request.allow_list = {PublicKeyCredentialDescriptor(
         CredentialType::kPublicKey,
-        fido_parsing_utils::Materialize(
-            test_data::kTestGetAssertionCredentialId))};
+        base::ToVector(test_data::kTestGetAssertionCredentialId))};
     auto handler = std::make_unique<GetAssertionRequestHandler>(
         &fido_discovery_factory,
         std::vector<std::unique_ptr<FidoDiscoveryBase>>(),
@@ -890,8 +887,7 @@
                                          test_data::kClientDataJson);
   request.allow_list = {PublicKeyCredentialDescriptor(
       CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(
-          test_data::kTestGetAssertionCredentialId))};
+      base::ToVector(test_data::kTestGetAssertionCredentialId))};
   request.cross_device_fallback_url = "https://example.com/fallback";
 
   auto request_handler =
@@ -916,8 +912,7 @@
                                          test_data::kClientDataJson);
   request.allow_list = {PublicKeyCredentialDescriptor(
       CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(
-          test_data::kTestGetAssertionCredentialId))};
+      base::ToVector(test_data::kTestGetAssertionCredentialId))};
   request.cross_device_fallback_url = "https://example.com/fallback";
 
   auto request_handler =
diff --git a/device/fido/get_assertion_task_unittest.cc b/device/fido/get_assertion_task_unittest.cc
index 5a2bd998..803c9d3 100644
--- a/device/fido/get_assertion_task_unittest.cc
+++ b/device/fido/get_assertion_task_unittest.cc
@@ -15,6 +15,8 @@
 
 #include "base/base64.h"
 #include "base/containers/span.h"
+#include "base/containers/to_array.h"
+#include "base/containers/to_vector.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
@@ -22,7 +24,6 @@
 #include "device/fido/authenticator_get_assertion_response.h"
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/device_response_converter.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/fido_test_data.h"
 #include "device/fido/mock_fido_device.h"
 #include "device/fido/public/fido_constants.h"
@@ -62,8 +63,7 @@
                                         test_data::kClientDataJson);
   request_param.allow_list.emplace_back(
       CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(
-          test_data::kTestGetAssertionCredentialId));
+      base::ToVector(test_data::kTestGetAssertionCredentialId));
 
   auto task = std::make_unique<GetAssertionTask>(
       device.get(), std::move(request_param), CtapGetAssertionOptions(),
@@ -131,8 +131,7 @@
   CtapGetAssertionRequest request_param(test_data::kRelyingPartyId,
                                         test_data::kClientDataJson);
   request_param.allow_list.emplace_back(
-      CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle));
+      CredentialType::kPublicKey, base::ToVector(test_data::kU2fSignKeyHandle));
 
   auto task = std::make_unique<GetAssertionTask>(
       device.get(), std::move(request_param), CtapGetAssertionOptions(),
@@ -149,8 +148,7 @@
   CtapGetAssertionRequest request_param(test_data::kRelyingPartyId,
                                         test_data::kClientDataJson);
   request_param.allow_list.emplace_back(PublicKeyCredentialDescriptor(
-      CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(kCredentialId)));
+      CredentialType::kPublicKey, base::ToVector(kCredentialId)));
 
   auto device = std::make_unique<VirtualCtap2Device>();
   ASSERT_TRUE(device->mutable_state()->InjectRegistration(
@@ -230,11 +228,10 @@
   std::vector<PublicKeyCredentialDescriptor> allowed_list;
   allowed_list.push_back(PublicKeyCredentialDescriptor(
       CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)));
+      base::ToVector(test_data::kU2fSignKeyHandle)));
   request.app_id = test_data::kAppId;
   request.alternative_application_parameter =
-      fido_parsing_utils::Materialize(base::span<const uint8_t, 32>(
-          test_data::kAlternativeApplicationParameter));
+      base::ToArray(test_data::kAlternativeApplicationParameter);
   request.allow_list = std::move(allowed_list);
 
   auto device = MockFidoDevice::MakeCtap();
@@ -259,11 +256,10 @@
   std::vector<PublicKeyCredentialDescriptor> allowed_list;
   allowed_list.push_back(PublicKeyCredentialDescriptor(
       CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)));
+      base::ToVector(test_data::kU2fSignKeyHandle)));
   request.app_id = test_data::kAppId;
   request.alternative_application_parameter =
-      fido_parsing_utils::Materialize(base::span<const uint8_t, 32>(
-          test_data::kAlternativeApplicationParameter));
+      base::ToArray(test_data::kAlternativeApplicationParameter);
   request.allow_list = std::move(allowed_list);
 
   ::testing::InSequence s;
@@ -302,12 +298,11 @@
   std::vector<PublicKeyCredentialDescriptor> allowed_list;
   allowed_list.push_back(PublicKeyCredentialDescriptor(
       CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)));
+      base::ToVector(test_data::kU2fSignKeyHandle)));
 
   request.app_id = test_data::kAppId;
   request.alternative_application_parameter =
-      fido_parsing_utils::Materialize(base::span<const uint8_t, 32>(
-          test_data::kAlternativeApplicationParameter));
+      base::ToArray(test_data::kAlternativeApplicationParameter);
   request.allow_list = std::move(allowed_list);
 
   auto device = MockFidoDevice::MakeCtap(ReadCTAPGetInfoResponse(
diff --git a/device/fido/hid/fido_hid_device_unittest.cc b/device/fido/hid/fido_hid_device_unittest.cc
index 2c79b15..2992eb0 100644
--- a/device/fido/hid/fido_hid_device_unittest.cc
+++ b/device/fido/hid/fido_hid_device_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/check_op.h"
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
+#include "base/containers/to_vector.h"
 #include "base/functional/bind.h"
 #include "base/location.h"
 #include "base/memory/raw_ptr.h"
@@ -102,7 +103,7 @@
     base::span<const uint8_t> nonce,
     base::span<const uint8_t> channel_id,
     base::span<const uint8_t> payload = base::span<const uint8_t>()) {
-  auto init_response = fido_parsing_utils::Materialize(kInitResponsePrefix);
+  auto init_response = base::ToVector(kInitResponsePrefix);
   fido_parsing_utils::Append(&init_response, nonce);
   fido_parsing_utils::Append(&init_response, channel_id);
   fido_parsing_utils::Append(&init_response, payload);
@@ -113,7 +114,7 @@
 // Returns HID keep alive message encoded into HID packet format.
 std::vector<uint8_t> GetKeepAliveHidMessage(
     base::span<const uint8_t> channel_id) {
-  auto response = fido_parsing_utils::Materialize(channel_id);
+  auto response = base::ToVector(channel_id);
   fido_parsing_utils::Append(&response, kMockKeepAliveResponseSuffix);
   response.resize(64);
   return response;
@@ -123,7 +124,7 @@
 std::vector<uint8_t> CreateMockResponseWithChannelId(
     base::span<const uint8_t> channel_id,
     base::span<const uint8_t> response_buffer) {
-  auto response = fido_parsing_utils::Materialize(channel_id);
+  auto response = base::ToVector(channel_id);
   fido_parsing_utils::Append(&response, response_buffer);
   response.resize(64);
   return response;
@@ -131,7 +132,7 @@
 
 // Returns a APDU encoded U2F version request for testing.
 std::vector<uint8_t> GetMockDeviceRequest() {
-  return fido_parsing_utils::Materialize(kMockU2fRequest);
+  return base::ToVector(kMockU2fRequest);
 }
 
 device::mojom::HidDeviceInfoPtr TestHidDevice() {
diff --git a/device/fido/hid/fido_hid_message.cc b/device/fido/hid/fido_hid_message.cc
index 53694d0..d93d5f8 100644
--- a/device/fido/hid/fido_hid_message.cc
+++ b/device/fido/hid/fido_hid_message.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/check_op.h"
+#include "base/containers/to_vector.h"
 #include "base/memory/ptr_util.h"
 #include "base/numerics/safe_conversions.h"
 #include "device/fido/fido_parsing_utils.h"
@@ -151,14 +152,13 @@
 
   auto init_data = data.first(std::min(init_packet_data_size, data.size()));
   packets_.push_back(std::make_unique<FidoHidInitPacket>(
-      channel_id, type,
-      std::vector<uint8_t>(init_data.begin(), init_data.end()), data.size()));
+      channel_id, type, base::ToVector(init_data), data.size()));
   data = data.subspan(init_data.size());
 
   for (auto cont_data :
        fido_parsing_utils::SplitSpan(data, continuation_packet_data_size)) {
     packets_.push_back(std::make_unique<FidoHidContinuationPacket>(
-        channel_id, sequence++, fido_parsing_utils::Materialize(cont_data)));
+        channel_id, sequence++, base::ToVector(cont_data)));
   }
 }
 
diff --git a/device/fido/large_blob.cc b/device/fido/large_blob.cc
index ff3ebce..9d6d713 100644
--- a/device/fido/large_blob.cc
+++ b/device/fido/large_blob.cc
@@ -9,13 +9,13 @@
 
 #include "base/containers/map_util.h"
 #include "base/containers/span.h"
+#include "base/containers/to_vector.h"
 #include "base/numerics/byte_conversions.h"
 #include "components/cbor/reader.h"
 #include "components/cbor/writer.h"
 #include "crypto/aead.h"
 #include "crypto/random.h"
 #include "crypto/sha2.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/pin.h"
 
 namespace device {
@@ -301,9 +301,7 @@
   length = std::min(length, bytes_.size() - offset_);
 
   LargeBlobArrayFragment fragment{
-      fido_parsing_utils::Materialize(
-          base::span(bytes_).subspan(offset_, length)),
-      offset_};
+      base::ToVector(base::span(bytes_).subspan(offset_, length)), offset_};
   offset_ += length;
   return fragment;
 }
diff --git a/device/fido/large_blob_unittest.cc b/device/fido/large_blob_unittest.cc
index 175cec55..35bcee20 100644
--- a/device/fido/large_blob_unittest.cc
+++ b/device/fido/large_blob_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <optional>
 
+#include "base/containers/to_vector.h"
 #include "device/fido/fido_parsing_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -36,49 +37,45 @@
 
 TEST_F(FidoLargeBlobTest, VerifyLargeBlobArrayIntegrityValid) {
   std::vector<uint8_t> large_blob_array =
-      fido_parsing_utils::Materialize(kValidEmptyLargeBlobArray);
+      base::ToVector(kValidEmptyLargeBlobArray);
   EXPECT_TRUE(VerifyLargeBlobArrayIntegrity(large_blob_array));
 }
 
 TEST_F(FidoLargeBlobTest, VerifyLargeBlobArrayIntegrityInvalid) {
   std::vector<uint8_t> large_blob_array =
-      fido_parsing_utils::Materialize(kValidEmptyLargeBlobArray);
+      base::ToVector(kValidEmptyLargeBlobArray);
   large_blob_array[0] += 1;
   EXPECT_FALSE(VerifyLargeBlobArrayIntegrity(large_blob_array));
 
-  large_blob_array = fido_parsing_utils::Materialize(kValidEmptyLargeBlobArray);
+  large_blob_array = base::ToVector(kValidEmptyLargeBlobArray);
   large_blob_array.erase(large_blob_array.begin());
   EXPECT_FALSE(VerifyLargeBlobArrayIntegrity(large_blob_array));
 }
 
 TEST_F(FidoLargeBlobTest, LargeBlobArrayReader_MaterializeEmpty) {
   LargeBlobArrayReader large_blob_array_reader;
-  large_blob_array_reader.Append(
-      fido_parsing_utils::Materialize(kValidEmptyLargeBlobArray));
+  large_blob_array_reader.Append(base::ToVector(kValidEmptyLargeBlobArray));
   EXPECT_EQ(0u, large_blob_array_reader.Materialize()->size());
 }
 
 TEST_F(FidoLargeBlobTest, LargeBlobArrayReader_MaterializeInvalidCbor) {
   LargeBlobArrayReader large_blob_array_reader;
-  large_blob_array_reader.Append(
-      fido_parsing_utils::Materialize(kInvalidLargeBlobArray));
+  large_blob_array_reader.Append(base::ToVector(kInvalidLargeBlobArray));
   EXPECT_FALSE(large_blob_array_reader.Materialize());
 }
 
 TEST_F(FidoLargeBlobTest, LargeBlobArrayReader_MaterializeInvalidHash) {
   std::vector<uint8_t> large_blob_array =
-      fido_parsing_utils::Materialize(kValidEmptyLargeBlobArray);
+      base::ToVector(kValidEmptyLargeBlobArray);
   large_blob_array[0] += 1;
   LargeBlobArrayReader large_blob_array_reader;
-  large_blob_array_reader.Append(
-      fido_parsing_utils::Materialize(large_blob_array));
+  large_blob_array_reader.Append(base::ToVector(large_blob_array));
   EXPECT_FALSE(large_blob_array_reader.Materialize());
 }
 
 TEST_F(FidoLargeBlobTest, LargeBlobArrayReader_MaterializeValid) {
   LargeBlobArrayReader large_blob_array_reader;
-  large_blob_array_reader.Append(
-      fido_parsing_utils::Materialize(kValidLargeBlobArray));
+  large_blob_array_reader.Append(base::ToVector(kValidLargeBlobArray));
   cbor::Value::ArrayValue vector = *large_blob_array_reader.Materialize();
   EXPECT_EQ(2u, vector.size());
 }
@@ -93,8 +90,7 @@
   ASSERT_NE(0u, kValidLargeBlobArray.size() % fragment_size);
 
   LargeBlobArrayWriter large_blob_array_writer({});
-  std::vector<uint8_t> large_blob_array =
-      fido_parsing_utils::Materialize(kValidLargeBlobArray);
+  std::vector<uint8_t> large_blob_array = base::ToVector(kValidLargeBlobArray);
   large_blob_array_writer.set_bytes_for_testing(large_blob_array);
   std::vector<uint8_t> reconstructed;
   EXPECT_TRUE(large_blob_array_writer.has_remaining_fragments());
@@ -121,8 +117,7 @@
   ASSERT_EQ(0u, kValidLargeBlobArray.size() % fragment_size);
 
   LargeBlobArrayWriter large_blob_array_writer({});
-  std::vector<uint8_t> large_blob_array =
-      fido_parsing_utils::Materialize(kValidLargeBlobArray);
+  std::vector<uint8_t> large_blob_array = base::ToVector(kValidLargeBlobArray);
   large_blob_array_writer.set_bytes_for_testing(large_blob_array);
   std::vector<uint8_t> reconstructed;
   EXPECT_TRUE(large_blob_array_writer.has_remaining_fragments());
diff --git a/device/fido/mac/credential_store.mm b/device/fido/mac/credential_store.mm
index f7f05094..0abc863 100644
--- a/device/fido/mac/credential_store.mm
+++ b/device/fido/mac/credential_store.mm
@@ -17,6 +17,7 @@
 #include "base/apple/foundation_util.h"
 #include "base/apple/osstatus_logging.h"
 #include "base/apple/scoped_cftyperef.h"
+#include "base/containers/to_vector.h"
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
@@ -25,7 +26,6 @@
 #include "crypto/apple/keychain_v2.h"
 #include "crypto/random.h"
 #include "device/fido/authenticator_data.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/mac/credential_metadata.h"
 #include "device/fido/mac/touch_id_context.h"
 
@@ -640,8 +640,7 @@
 bool TouchIdCredentialStore::UpdateCredential(
     base::span<uint8_t> credential_id_span,
     const std::string& username) {
-  std::vector<uint8_t> credential_id =
-      fido_parsing_utils::Materialize(credential_id_span);
+  std::vector<uint8_t> credential_id = base::ToVector(credential_id_span);
 
   std::optional<std::list<Credential>> credentials = FindCredentialsImpl(
       /*rp_id=*/std::nullopt, {credential_id});
diff --git a/device/fido/mac/fake_icloud_keychain_sys.mm b/device/fido/mac/fake_icloud_keychain_sys.mm
index 298b558..29dbe26 100644
--- a/device/fido/mac/fake_icloud_keychain_sys.mm
+++ b/device/fido/mac/fake_icloud_keychain_sys.mm
@@ -9,11 +9,11 @@
 
 #include <vector>
 
+#include "base/containers/to_vector.h"
 #include "base/functional/callback.h"
 #include "base/strings/sys_string_conversions.h"
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/ctap_make_credential_request.h"
-#include "device/fido/fido_parsing_utils.h"
 
 // Fake subclass to simulate a largeBlob registration output.
 
@@ -172,9 +172,8 @@
     base::span<const uint8_t> credential_id) {
   CHECK(!make_credential_error_code_);
   make_credential_attestation_object_bytes_ =
-      fido_parsing_utils::Materialize(attestation_object_bytes);
-  make_credential_credential_id_ =
-      fido_parsing_utils::Materialize(credential_id);
+      base::ToVector(attestation_object_bytes);
+  make_credential_credential_id_ = base::ToVector(credential_id);
 }
 
 void FakeSystemInterface::SetMakeCredentialError(int code) {
@@ -192,11 +191,10 @@
     base::span<const uint8_t> signature,
     base::span<const uint8_t> user_id,
     base::span<const uint8_t> credential_id) {
-  get_assertion_authenticator_data_ =
-      fido_parsing_utils::Materialize(authenticator_data);
-  get_assertion_signature_ = fido_parsing_utils::Materialize(signature);
-  get_assertion_user_id_ = fido_parsing_utils::Materialize(user_id);
-  get_assertion_credential_id_ = fido_parsing_utils::Materialize(credential_id);
+  get_assertion_authenticator_data_ = base::ToVector(authenticator_data);
+  get_assertion_signature_ = base::ToVector(signature);
+  get_assertion_user_id_ = base::ToVector(user_id);
+  get_assertion_credential_id_ = base::ToVector(credential_id);
 }
 
 void FakeSystemInterface::SetGetAssertionError(int code, std::string msg) {
diff --git a/device/fido/mac/icloud_keychain.mm b/device/fido/mac/icloud_keychain.mm
index 99d1f70..c62167f 100644
--- a/device/fido/mac/icloud_keychain.mm
+++ b/device/fido/mac/icloud_keychain.mm
@@ -12,6 +12,7 @@
 #include <utility>
 
 #include "base/apple/foundation_util.h"
+#include "base/containers/to_vector.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/memory/scoped_refptr.h"
@@ -34,7 +35,6 @@
 #include "device/fido/discoverable_credential_metadata.h"
 #include "device/fido/fido_authenticator.h"
 #include "device/fido/fido_discovery_base.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/large_blob.h"
 #include "device/fido/mac/icloud_keychain_sys.h"
 #include "device/fido/public/features.h"
@@ -531,13 +531,12 @@
 
     AuthenticatorGetAssertionResponse response(
         std::move(*authenticator_data),
-        fido_parsing_utils::Materialize(NSDataToSpan(result.signature)),
-        transport_used);
+        base::ToVector(NSDataToSpan(result.signature)), transport_used);
     response.user_entity = PublicKeyCredentialUserEntity(
-        fido_parsing_utils::Materialize(NSDataToSpan(result.userID)));
+        base::ToVector(NSDataToSpan(result.userID)));
     response.credential = PublicKeyCredentialDescriptor(
         CredentialType::kPublicKey,
-        fido_parsing_utils::Materialize(NSDataToSpan(result.credentialID)));
+        base::ToVector(NSDataToSpan(result.credentialID)));
     response.user_selected = true;
     if (@available(macOS 15.0, *)) {
       if ([result
@@ -559,8 +558,8 @@
         if (platform_result.largeBlob != nil) {
           auto* large_blob_out = platform_result.largeBlob;
           if (large_blob_out.readData != nil) {
-            response.large_blob = fido_parsing_utils::Materialize(
-                NSDataToSpan(large_blob_out.readData));
+            response.large_blob =
+                base::ToVector(NSDataToSpan(large_blob_out.readData));
           }
           response.large_blob_written = large_blob_out.didWrite;
         }
diff --git a/device/fido/make_credential_handler_unittest.cc b/device/fido/make_credential_handler_unittest.cc
index fa36c00..774b8dfe 100644
--- a/device/fido/make_credential_handler_unittest.cc
+++ b/device/fido/make_credential_handler_unittest.cc
@@ -11,6 +11,7 @@
 
 #include "base/containers/flat_set.h"
 #include "base/containers/span.h"
+#include "base/containers/to_vector.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/location.h"
@@ -98,9 +99,8 @@
       AuthenticatorSelectionCriteria authenticator_selection_criteria = {}) {
     ForgeDiscoveries();
     PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId);
-    PublicKeyCredentialUserEntity user(
-        fido_parsing_utils::Materialize(test_data::kUserId), "nia",
-        std::nullopt);
+    PublicKeyCredentialUserEntity user(base::ToVector(test_data::kUserId),
+                                       "nia", std::nullopt);
     PublicKeyCredentialParams credential_params(
         std::vector<PublicKeyCredentialParams::CredentialInfo>(1));
 
diff --git a/device/fido/make_credential_task_unittest.cc b/device/fido/make_credential_task_unittest.cc
index d4e1d2e5..0144cb4 100644
--- a/device/fido/make_credential_task_unittest.cc
+++ b/device/fido/make_credential_task_unittest.cc
@@ -14,6 +14,7 @@
 #include <vector>
 
 #include "base/base64.h"
+#include "base/containers/to_vector.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "components/device_event_log/device_event_log.h"
@@ -55,8 +56,7 @@
   std::unique_ptr<MakeCredentialTask> CreateMakeCredentialTask(
       FidoDevice* device) {
     PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId);
-    PublicKeyCredentialUserEntity user(
-        fido_parsing_utils::Materialize(test_data::kUserId));
+    PublicKeyCredentialUserEntity user(base::ToVector(test_data::kUserId));
     return std::make_unique<MakeCredentialTask>(
         device,
         CtapMakeCredentialRequest(
@@ -202,8 +202,7 @@
       CtapRequestCommand::kAuthenticatorMakeCredential, std::nullopt);
 
   PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId);
-  PublicKeyCredentialUserEntity user(
-      fido_parsing_utils::Materialize(test_data::kUserId));
+  PublicKeyCredentialUserEntity user(base::ToVector(test_data::kUserId));
   auto request = CtapMakeCredentialRequest(
       test_data::kClientDataJson, std::move(rp), std::move(user),
       PublicKeyCredentialParams(
diff --git a/device/fido/mock_fido_device.cc b/device/fido/mock_fido_device.cc
index 1cc24b96..901a4b22 100644
--- a/device/fido/mock_fido_device.cc
+++ b/device/fido/mock_fido_device.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/containers/to_vector.h"
 #include "base/functional/bind.h"
 #include "base/location.h"
 #include "base/strings/strcat.h"
@@ -14,7 +15,6 @@
 #include "components/apdu/apdu_response.h"
 #include "components/cbor/writer.h"
 #include "device/fido/device_response_converter.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/fido_test_data.h"
 #include "device/fido/public/fido_constants.h"
 
@@ -141,7 +141,8 @@
     std::optional<base::span<const uint8_t>> response,
     base::TimeDelta delay,
     testing::Matcher<base::span<const uint8_t>> request_matcher) {
-  auto data = fido_parsing_utils::MaterializeOrNull(response);
+  auto data =
+      response ? std::make_optional(base::ToVector(*response)) : std::nullopt;
   auto send_response = [data(std::move(data)), delay](DeviceCallback& cb) {
     base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
         FROM_HERE, base::BindOnce(std::move(cb), std::move(data)), delay);
@@ -166,13 +167,14 @@
     base::span<const uint8_t> request,
     std::optional<base::span<const uint8_t>> response,
     base::TimeDelta delay) {
-  auto data = fido_parsing_utils::MaterializeOrNull(response);
+  auto data =
+      response ? std::make_optional(base::ToVector(*response)) : std::nullopt;
   auto send_response = [data(std::move(data)), delay](DeviceCallback& cb) {
     base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
         FROM_HERE, base::BindOnce(std::move(cb), std::move(data)), delay);
   };
 
-  auto request_as_vector = fido_parsing_utils::Materialize(request);
+  auto request_as_vector = base::ToVector(request);
   EXPECT_CALL(*this,
               DeviceTransactPtr(std::move(request_as_vector), ::testing::_))
       .WillOnce(::testing::DoAll(::testing::WithArg<1>(send_response),
@@ -187,7 +189,7 @@
 
 void MockFidoDevice::ExpectRequestAndDoNotRespond(
     base::span<const uint8_t> request) {
-  auto request_as_vector = fido_parsing_utils::Materialize(request);
+  auto request_as_vector = base::ToVector(request);
   EXPECT_CALL(*this,
               DeviceTransactPtr(std::move(request_as_vector), ::testing::_))
       .WillOnce(::testing::Return(0));
diff --git a/device/fido/public_key.cc b/device/fido/public_key.cc
index e4bb76e9..b759b86 100644
--- a/device/fido/public_key.cc
+++ b/device/fido/public_key.cc
@@ -8,7 +8,7 @@
 #include <vector>
 
 #include "base/containers/span.h"
-#include "device/fido/fido_parsing_utils.h"
+#include "base/containers/to_vector.h"
 
 namespace device {
 
@@ -16,7 +16,7 @@
                      base::span<const uint8_t> in_cose_key_bytes,
                      std::optional<std::vector<uint8_t>> in_der_bytes)
     : algorithm(in_algorithm),
-      cose_key_bytes(fido_parsing_utils::Materialize(in_cose_key_bytes)),
+      cose_key_bytes(base::ToVector(in_cose_key_bytes)),
       der_bytes(std::move(in_der_bytes)) {}
 
 PublicKey::~PublicKey() = default;
diff --git a/device/fido/u2f_command_constructor_unittest.cc b/device/fido/u2f_command_constructor_unittest.cc
index baa5c8b..2ce456f7 100644
--- a/device/fido/u2f_command_constructor_unittest.cc
+++ b/device/fido/u2f_command_constructor_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/containers/to_vector.h"
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/fido_parsing_utils.h"
@@ -22,8 +23,7 @@
   PublicKeyCredentialRpEntity rp("acme.com");
   rp.name = "acme.com";
 
-  PublicKeyCredentialUserEntity user(
-      fido_parsing_utils::Materialize(test_data::kUserId));
+  PublicKeyCredentialUserEntity user(base::ToVector(test_data::kUserId));
   user.name = "johnpsmith@example.com";
   user.display_name = "John P. Smith";
 
@@ -74,8 +74,7 @@
   PublicKeyCredentialRpEntity rp("acme.com");
   rp.name = "acme.com";
 
-  PublicKeyCredentialUserEntity user(
-      fido_parsing_utils::Materialize(test_data::kUserId));
+  PublicKeyCredentialUserEntity user(base::ToVector(test_data::kUserId));
   user.name = "johnpsmith@example.com";
   user.display_name = "John P. Smith";
 
@@ -115,12 +114,12 @@
   std::vector<PublicKeyCredentialDescriptor> allowed_list;
   allowed_list.push_back(PublicKeyCredentialDescriptor(
       CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)));
+      base::ToVector(test_data::kU2fSignKeyHandle)));
   get_assertion_req.allow_list = std::move(allowed_list);
 
   const auto u2f_sign_command = ConvertToU2fSignCommand(
       get_assertion_req, ApplicationParameterType::kPrimary,
-      fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle));
+      base::ToVector(test_data::kU2fSignKeyHandle));
 
   EXPECT_TRUE(IsConvertibleToU2fSignCommand(get_assertion_req));
   ASSERT_TRUE(u2f_sign_command);
@@ -138,7 +137,7 @@
   std::vector<PublicKeyCredentialDescriptor> allowed_list;
   allowed_list.push_back(PublicKeyCredentialDescriptor(
       CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)));
+      base::ToVector(test_data::kU2fSignKeyHandle)));
   get_assertion_req.allow_list = std::move(allowed_list);
   get_assertion_req.user_verification = UserVerificationRequirement::kRequired;
 
diff --git a/device/fido/u2f_register_operation_unittest.cc b/device/fido/u2f_register_operation_unittest.cc
index 5baa13417..f8131bf8 100644
--- a/device/fido/u2f_register_operation_unittest.cc
+++ b/device/fido/u2f_register_operation_unittest.cc
@@ -10,6 +10,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/containers/to_vector.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "device/fido/authenticator_make_credential_response.h"
@@ -38,8 +39,7 @@
     std::vector<PublicKeyCredentialDescriptor> registered_keys,
     bool is_individual_attestation = false) {
   PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId);
-  PublicKeyCredentialUserEntity user(
-      fido_parsing_utils::Materialize(test_data::kUserId));
+  PublicKeyCredentialUserEntity user(base::ToVector(test_data::kUserId));
 
   CtapMakeCredentialRequest request(
       test_data::kClientDataJson, std::move(rp), std::move(user),
@@ -153,10 +153,10 @@
   auto request = CreateRegisterRequestWithRegisteredKeys(
       {PublicKeyCredentialDescriptor(
            CredentialType::kPublicKey,
-           fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha)),
+           base::ToVector(test_data::kKeyHandleAlpha)),
        PublicKeyCredentialDescriptor(
            CredentialType::kPublicKey,
-           fido_parsing_utils::Materialize(test_data::kKeyHandleBeta))});
+           base::ToVector(test_data::kKeyHandleBeta))});
 
   auto device = std::make_unique<MockFidoDevice>();
   EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
@@ -201,13 +201,12 @@
   auto request = CreateRegisterRequestWithRegisteredKeys(
       {PublicKeyCredentialDescriptor(
            CredentialType::kPublicKey,
-           fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha)),
+           base::ToVector(test_data::kKeyHandleAlpha)),
+       PublicKeyCredentialDescriptor(CredentialType::kPublicKey,
+                                     base::ToVector(test_data::kKeyHandleBeta)),
        PublicKeyCredentialDescriptor(
            CredentialType::kPublicKey,
-           fido_parsing_utils::Materialize(test_data::kKeyHandleBeta)),
-       PublicKeyCredentialDescriptor(
-           CredentialType::kPublicKey,
-           fido_parsing_utils::Materialize(test_data::kKeyHandleGamma))});
+           base::ToVector(test_data::kKeyHandleGamma))});
 
   auto device = std::make_unique<MockFidoDevice>();
   EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
diff --git a/device/fido/u2f_sign_operation_unittest.cc b/device/fido/u2f_sign_operation_unittest.cc
index bca0a66..2be0d276 100644
--- a/device/fido/u2f_sign_operation_unittest.cc
+++ b/device/fido/u2f_sign_operation_unittest.cc
@@ -12,6 +12,8 @@
 #include <vector>
 
 #include "base/containers/span.h"
+#include "base/containers/to_array.h"
+#include "base/containers/to_vector.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
@@ -66,8 +68,8 @@
 };
 
 TEST_F(U2fSignOperationTest, SignSuccess) {
-  auto request = CreateSignRequest(
-      {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+  auto request =
+      CreateSignRequest({base::ToVector(test_data::kU2fSignKeyHandle)});
 
   auto device = std::make_unique<MockFidoDevice>();
   EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device"));
@@ -95,8 +97,7 @@
 
 TEST_F(U2fSignOperationTest, SignSuccessWithFakeDevice) {
   static const uint8_t kCredentialId[] = {1, 2, 3, 4};
-  auto request =
-      CreateSignRequest({fido_parsing_utils::Materialize(kCredentialId)});
+  auto request = CreateSignRequest({base::ToVector(kCredentialId)});
 
   auto device = std::make_unique<VirtualU2fDevice>();
   ASSERT_TRUE(device->mutable_state()->InjectRegistration(
@@ -124,8 +125,8 @@
 }
 
 TEST_F(U2fSignOperationTest, DelayedSuccess) {
-  auto request = CreateSignRequest(
-      {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+  auto request =
+      CreateSignRequest({base::ToVector(test_data::kU2fSignKeyHandle)});
 
   // Simulates a device that times out waiting for user touch once before
   // responding successfully.
@@ -157,10 +158,10 @@
 TEST_F(U2fSignOperationTest, MultipleHandles) {
   // Two wrong keys followed by a correct key ensuring the wrong keys will be
   // tested first.
-  auto request = CreateSignRequest(
-      {fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha),
-       fido_parsing_utils::Materialize(test_data::kKeyHandleBeta),
-       fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+  auto request =
+      CreateSignRequest({base::ToVector(test_data::kKeyHandleAlpha),
+                         base::ToVector(test_data::kKeyHandleBeta),
+                         base::ToVector(test_data::kU2fSignKeyHandle)});
 
   auto device = std::make_unique<MockFidoDevice>();
   EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
@@ -192,9 +193,9 @@
 TEST_F(U2fSignOperationTest, MultipleHandlesLengthError) {
   // One wrong key that responds with key handle length followed by a correct
   // key.
-  auto request = CreateSignRequest(
-      {fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha),
-       fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+  auto request =
+      CreateSignRequest({base::ToVector(test_data::kKeyHandleAlpha),
+                         base::ToVector(test_data::kU2fSignKeyHandle)});
 
   auto device = std::make_unique<MockFidoDevice>();
   EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
@@ -225,9 +226,8 @@
 // Test that Fake U2F registration is invoked when no credentials in the allowed
 // list are recognized by the device.
 TEST_F(U2fSignOperationTest, FakeEnroll) {
-  auto request = CreateSignRequest(
-      {fido_parsing_utils::Materialize(test_data::kKeyHandleAlpha),
-       fido_parsing_utils::Materialize(test_data::kKeyHandleBeta)});
+  auto request = CreateSignRequest({base::ToVector(test_data::kKeyHandleAlpha),
+                                    base::ToVector(test_data::kKeyHandleBeta)});
 
   auto device = std::make_unique<MockFidoDevice>();
   device->ExpectWinkedAtLeastOnce();
@@ -256,8 +256,8 @@
 // credentials are valid for the authenticator and user presence is not
 // obtained.
 TEST_F(U2fSignOperationTest, DelayedFakeEnrollment) {
-  auto request = CreateSignRequest(
-      {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+  auto request =
+      CreateSignRequest({base::ToVector(test_data::kU2fSignKeyHandle)});
 
   // Simulates a device that times out waiting for user presence during fake
   // enrollment.
@@ -288,8 +288,8 @@
 // Tests that request is dropped gracefully if device returns error on all
 // requests (including fake enrollment).
 TEST_F(U2fSignOperationTest, FakeEnrollErroringOut) {
-  auto request = CreateSignRequest(
-      {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+  auto request =
+      CreateSignRequest({base::ToVector(test_data::kU2fSignKeyHandle)});
 
   // Simulates a device that errors out on all requests (including the sign
   // request and fake registration attempt). The device should then be abandoned
@@ -317,8 +317,8 @@
 // Tests the scenario where device returns success response, but the response is
 // unparse-able.
 TEST_F(U2fSignOperationTest, SignWithCorruptedResponse) {
-  auto request = CreateSignRequest(
-      {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+  auto request =
+      CreateSignRequest({base::ToVector(test_data::kU2fSignKeyHandle)});
 
   auto device = std::make_unique<MockFidoDevice>();
   EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
@@ -339,12 +339,11 @@
 }
 
 TEST_F(U2fSignOperationTest, AlternativeApplicationParameter) {
-  auto request = CreateSignRequest(
-      {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+  auto request =
+      CreateSignRequest({base::ToVector(test_data::kU2fSignKeyHandle)});
   request.app_id = test_data::kAppId;
   request.alternative_application_parameter =
-      fido_parsing_utils::Materialize(base::span<const uint8_t, 32>(
-          test_data::kAlternativeApplicationParameter));
+      base::ToArray(test_data::kAlternativeApplicationParameter);
 
   auto device = std::make_unique<MockFidoDevice>();
   EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
@@ -374,12 +373,11 @@
 
 // This is a regression test in response to https://crbug.com/833398.
 TEST_F(U2fSignOperationTest, AlternativeApplicationParameterRejection) {
-  auto request = CreateSignRequest(
-      {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+  auto request =
+      CreateSignRequest({base::ToVector(test_data::kU2fSignKeyHandle)});
   request.app_id = test_data::kAppId;
   request.alternative_application_parameter =
-      fido_parsing_utils::Materialize(base::span<const uint8_t, 32>(
-          test_data::kAlternativeApplicationParameter));
+      base::ToArray(test_data::kAlternativeApplicationParameter);
 
   auto device = std::make_unique<MockFidoDevice>();
   EXPECT_CALL(*device, GetId()).WillRepeatedly(::testing::Return("device"));
@@ -412,8 +410,8 @@
 
 // Tests that Chrome will retry if a low level error happens.
 TEST_F(U2fSignOperationTest, LowLevelErrorRetries) {
-  auto request = CreateSignRequest(
-      {fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)});
+  auto request =
+      CreateSignRequest({base::ToVector(test_data::kU2fSignKeyHandle)});
 
   // Simulates a device that throws a low level error before responding
   // successfully.
diff --git a/device/fido/virtual_ctap2_device.cc b/device/fido/virtual_ctap2_device.cc
index 36736e3..d566c4d 100644
--- a/device/fido/virtual_ctap2_device.cc
+++ b/device/fido/virtual_ctap2_device.cc
@@ -12,6 +12,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
+#include "base/containers/to_vector.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/numerics/byte_conversions.h"
@@ -201,7 +202,7 @@
   if (!signature.empty()) {
     cbor::Value::MapValue attestation_map;
     attestation_map.emplace("alg", -7);
-    attestation_map.emplace("sig", fido_parsing_utils::Materialize(signature));
+    attestation_map.emplace("sig", base::ToVector(signature));
 
     if (attestation_certificate) {
       cbor::Value::ArrayValue certificate_chain;
@@ -1809,8 +1810,7 @@
 
     if (include_credential) {
       assertion.credential = PublicKeyCredentialDescriptor(
-          CredentialType::kPublicKey,
-          fido_parsing_utils::Materialize(registration.first));
+          CredentialType::kPublicKey, base::ToVector(registration.first));
     }
 
     if (registration.second->is_resident &&
@@ -2930,9 +2930,8 @@
        !mutable_state()->non_zero_aaguid_with_self_attestation)) {
     aaguid = kZeroAaguid;
   }
-  return AttestedCredentialData(aaguid, sha256_length,
-                                fido_parsing_utils::Materialize(key_handle),
-                                std::move(public_key));
+  return AttestedCredentialData(
+      aaguid, sha256_length, base::ToVector(key_handle), std::move(public_key));
 }
 
 size_t VirtualCtap2Device::remaining_resident_credentials() const {
diff --git a/device/fido/virtual_ctap2_device_unittest.cc b/device/fido/virtual_ctap2_device_unittest.cc
index 6fa32da..1a44212b 100644
--- a/device/fido/virtual_ctap2_device_unittest.cc
+++ b/device/fido/virtual_ctap2_device_unittest.cc
@@ -13,6 +13,7 @@
 
 #include "base/check.h"
 #include "base/containers/span.h"
+#include "base/containers/to_vector.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/memory/scoped_refptr.h"
@@ -55,8 +56,7 @@
 void SendCommand(VirtualCtap2Device* device,
                  base::span<const uint8_t> command,
                  FidoDevice::DeviceCallback callback = base::DoNothing()) {
-  device->DeviceTransact(fido_parsing_utils::Materialize(command),
-                         std::move(callback));
+  device->DeviceTransact(base::ToVector(command), std::move(callback));
 }
 
 // DecodeCBOR parses a CBOR structure, ignoring the first byte of |in|, which is
@@ -295,8 +295,7 @@
   TestFuture future;
   device::CtapGetAssertionRequest request = CtapGetAssertionRequest(
       test_data::kRelyingPartyId, test_data::kClientDataJson);
-  std::vector<uint8_t> credential_id =
-      fido_parsing_utils::Materialize(kCredentialId);
+  std::vector<uint8_t> credential_id = base::ToVector(kCredentialId);
   PublicKeyCredentialDescriptor descriptor(
       CredentialType::kPublicKey, std::move(credential_id),
       {FidoTransportProtocol::kUsbHumanInterfaceDevice});
@@ -351,8 +350,7 @@
   TestFuture future;
   device::CtapGetAssertionRequest request = CtapGetAssertionRequest(
       test_data::kRelyingPartyId, test_data::kClientDataJson);
-  std::vector<uint8_t> credential_id =
-      fido_parsing_utils::Materialize(kCredentialId);
+  std::vector<uint8_t> credential_id = base::ToVector(kCredentialId);
   PublicKeyCredentialDescriptor descriptor(
       CredentialType::kPublicKey, std::move(credential_id),
       {FidoTransportProtocol::kUsbHumanInterfaceDevice});
@@ -388,8 +386,7 @@
   TestFuture future;
   device::CtapGetAssertionRequest request = CtapGetAssertionRequest(
       test_data::kRelyingPartyId, test_data::kClientDataJson);
-  std::vector<uint8_t> credential_id =
-      fido_parsing_utils::Materialize(kCredentialId);
+  std::vector<uint8_t> credential_id = base::ToVector(kCredentialId);
   PublicKeyCredentialDescriptor descriptor(
       CredentialType::kPublicKey, std::move(credential_id),
       {FidoTransportProtocol::kUsbHumanInterfaceDevice});
@@ -498,8 +495,7 @@
   MakeDevice(state, config);
 
   PublicKeyCredentialRpEntity rp("acme.com");
-  PublicKeyCredentialUserEntity user(
-      fido_parsing_utils::Materialize(test_data::kUserId));
+  PublicKeyCredentialUserEntity user(base::ToVector(test_data::kUserId));
   CtapMakeCredentialRequest request(
       test_data::kClientDataJson, std::move(rp), std::move(user),
       PublicKeyCredentialParams({{CredentialType::kPublicKey, -7}}));
@@ -552,8 +548,7 @@
   state->simulate_cmtg_key_failure = true;
 
   PublicKeyCredentialRpEntity rp("acme.com");
-  PublicKeyCredentialUserEntity user(
-      fido_parsing_utils::Materialize(test_data::kUserId));
+  PublicKeyCredentialUserEntity user(base::ToVector(test_data::kUserId));
   CtapMakeCredentialRequest request(
       test_data::kClientDataJson, std::move(rp), std::move(user),
       PublicKeyCredentialParams({{CredentialType::kPublicKey, -7}}));
@@ -581,8 +576,7 @@
   MakeDevice(state, config);
 
   PublicKeyCredentialRpEntity rp("acme.com");
-  PublicKeyCredentialUserEntity user(
-      fido_parsing_utils::Materialize(test_data::kUserId));
+  PublicKeyCredentialUserEntity user(base::ToVector(test_data::kUserId));
   CtapMakeCredentialRequest request(
       test_data::kClientDataJson, std::move(rp), std::move(user),
       PublicKeyCredentialParams({{CredentialType::kPublicKey, -7}}));
@@ -641,8 +635,7 @@
   CtapGetAssertionRequest get_request(test_data::kRelyingPartyId,
                                       test_data::kClientDataJson);
   get_request.allow_list = {PublicKeyCredentialDescriptor(
-      CredentialType::kPublicKey,
-      fido_parsing_utils::Materialize(kCredentialId))};
+      CredentialType::kPublicKey, base::ToVector(kCredentialId))};
 
   TestFuture future;
   SendCommand(device_.get(),
diff --git a/device/fido/virtual_fido_device.cc b/device/fido/virtual_fido_device.cc
index bbb5149..1592562 100644
--- a/device/fido/virtual_fido_device.cc
+++ b/device/fido/virtual_fido_device.cc
@@ -9,6 +9,8 @@
 #include <utility>
 #include <vector>
 
+#include "base/containers/to_array.h"
+#include "base/containers/to_vector.h"
 #include "base/logging.h"
 #include "base/notreached.h"
 #include "base/rand_util.h"
@@ -18,7 +20,6 @@
 #include "crypto/hash.h"
 #include "crypto/keypair.h"
 #include "crypto/sign.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/large_blob.h"
 #include "device/fido/p256_public_key.h"
 #include "device/fido/public_key.h"
@@ -256,8 +257,7 @@
     base::span<const uint8_t, kRpIdHashLength> application_parameter,
     std::optional<uint32_t> counter)
     : private_key(std::move(private_key)),
-      application_parameter(
-          fido_parsing_utils::Materialize(application_parameter)),
+      application_parameter(base::ToArray(application_parameter)),
       counter(counter) {}
 VirtualFidoDevice::RegistrationData::RegistrationData(RegistrationData&& data) =
     default;
@@ -317,7 +317,7 @@
     RegistrationData registration) {
   bool was_inserted;
   std::tie(std::ignore, was_inserted) = registrations.emplace(
-      fido_parsing_utils::Materialize(credential_id), std::move(registration));
+      base::ToVector(credential_id), std::move(registration));
   return was_inserted;
 }
 
@@ -358,7 +358,7 @@
 
   bool was_inserted;
   std::tie(std::ignore, was_inserted) = registrations.emplace(
-      fido_parsing_utils::Materialize(credential_id), std::move(registration));
+      base::ToVector(credential_id), std::move(registration));
   return was_inserted;
 }
 
@@ -378,7 +378,7 @@
     std::optional<std::string> user_display_name) {
   return InjectResidentKey(
       credential_id, PublicKeyCredentialRpEntity(std::move(relying_party_id)),
-      PublicKeyCredentialUserEntity(fido_parsing_utils::Materialize(user_id),
+      PublicKeyCredentialUserEntity(base::ToVector(user_id),
                                     std::move(user_name),
                                     std::move(user_display_name)));
 }
@@ -466,7 +466,7 @@
 
 // static
 std::vector<uint8_t> VirtualFidoDevice::GetAttestationKey() {
-  return fido_parsing_utils::Materialize(kAttestationKey);
+  return base::ToVector(kAttestationKey);
 }
 
 std::optional<std::vector<uint8_t>>
@@ -559,8 +559,7 @@
   // Store the registration. Because the key handle is the hashed public key we
   // just generated, no way this should already be registered.
   auto result = mutable_state()->registrations.emplace(
-      fido_parsing_utils::Materialize(key_handle),
-      std::move(registration_data));
+      base::ToVector(key_handle), std::move(registration_data));
   DCHECK(result.second);
   mutable_state()->NotifyCredentialCreated(
       std::make_pair(key_handle, &result.first->second));
diff --git a/device/fido/virtual_fido_device_authenticator.cc b/device/fido/virtual_fido_device_authenticator.cc
index c932ef5..7e26296 100644
--- a/device/fido/virtual_fido_device_authenticator.cc
+++ b/device/fido/virtual_fido_device_authenticator.cc
@@ -7,7 +7,6 @@
 #include "crypto/hash.h"
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/fido_device_authenticator.h"
-#include "device/fido/fido_parsing_utils.h"
 #include "device/fido/fido_request_handler_base.h"
 #include "device/fido/public/fido_constants.h"
 #include "device/fido/public/fido_types.h"
diff --git a/device/fido/virtual_u2f_device_unittest.cc b/device/fido/virtual_u2f_device_unittest.cc
index f93bec1..0759348 100644
--- a/device/fido/virtual_u2f_device_unittest.cc
+++ b/device/fido/virtual_u2f_device_unittest.cc
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/containers/span.h"
+#include "base/containers/to_vector.h"
 #include "base/functional/callback_helpers.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/test/bind.h"
@@ -22,8 +23,7 @@
 namespace {
 
 void SendCommand(VirtualU2fDevice* device, base::span<const uint8_t> command) {
-  device->DeviceTransact(fido_parsing_utils::Materialize(command),
-                         base::DoNothing());
+  device->DeviceTransact(base::ToVector(command), base::DoNothing());
 }
 
 }  // namespace
diff --git a/device/fido/win/fake_webauthn_api.cc b/device/fido/win/fake_webauthn_api.cc
index df9d477..ab4c4939 100644
--- a/device/fido/win/fake_webauthn_api.cc
+++ b/device/fido/win/fake_webauthn_api.cc
@@ -14,6 +14,7 @@
 #include "base/check.h"
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
+#include "base/containers/to_vector.h"
 #include "base/memory/raw_ptr_exclusion.h"
 #include "base/notreached.h"
 #include "base/strings/string_util_win.h"
@@ -190,7 +191,7 @@
     const std::string& rp_id) {
   bool was_inserted;
   std::tie(std::ignore, was_inserted) = registrations_.insert(
-      {fido_parsing_utils::Materialize(credential_id),
+      {base::ToVector(credential_id),
        RegistrationData(VirtualFidoDevice::PrivateKey::FreshP256Key(),
                         crypto::hash::Sha256(rp_id),
                         /*counter=*/0)});
@@ -211,9 +212,8 @@
   registration.provider_name = std::move(provider_name);
 
   bool was_inserted;
-  std::tie(std::ignore, was_inserted) =
-      registrations_.insert({fido_parsing_utils::Materialize(credential_id),
-                             std::move(registration)});
+  std::tie(std::ignore, was_inserted) = registrations_.insert(
+      {base::ToVector(credential_id), std::move(registration)});
   return was_inserted;
 }
 
@@ -281,22 +281,21 @@
   for (size_t i = 0; i < options->pExcludeCredentialList->cCredentials; ++i) {
     PWEBAUTHN_CREDENTIAL_EX exclude_credential =
         UNSAFE_TODO(options->pExcludeCredentialList->ppCredentials[i]);
-    std::vector<uint8_t> credential_id =
-        fido_parsing_utils::Materialize(UNSAFE_TODO(
-            base::span(exclude_credential->pbId, exclude_credential->cbId)));
+    std::vector<uint8_t> credential_id = base::ToVector(UNSAFE_TODO(
+        base::span(exclude_credential->pbId, exclude_credential->cbId)));
     if (registrations_.contains(credential_id)) {
       return NTE_EXISTS;
     }
   }
 
   std::unique_ptr<PublicKey> public_key = private_key->GetPublicKey();
-  std::vector<uint8_t> credential_id = fido_parsing_utils::Materialize(
-      crypto::SHA256Hash(public_key->cose_key_bytes));
+  std::vector<uint8_t> credential_id =
+      base::ToVector(crypto::SHA256Hash(public_key->cose_key_bytes));
   std::string rp_id = base::WideToUTF8(rp->pwszId);
   std::array<uint8_t, crypto::kSHA256Length> rp_id_hash =
       crypto::hash::Sha256(rp_id);
-  std::vector<uint8_t> user_id = fido_parsing_utils::Materialize(
-      UNSAFE_TODO(base::span(user->pbId, user->cbId)));
+  std::vector<uint8_t> user_id =
+      base::ToVector(UNSAFE_TODO(base::span(user->pbId, user->cbId)));
 
   RegistrationData registration(std::move(private_key), std::move(rp_id_hash),
                                 /*counter=*/1);
@@ -462,7 +461,7 @@
   DCHECK(!credential_id.empty());
 
   auto result = std::make_unique<WebAuthnAssertionEx>();
-  result->credential_id = fido_parsing_utils::Materialize(credential_id);
+  result->credential_id = base::ToVector(credential_id);
   bool performed_uv =
       /*user_verified=*/options->dwUserVerificationRequirement !=
       WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED;
diff --git a/device/fido/win/type_conversions_unittest.cc b/device/fido/win/type_conversions_unittest.cc
index ffe4056..2a5b746 100644
--- a/device/fido/win/type_conversions_unittest.cc
+++ b/device/fido/win/type_conversions_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "device/fido/win/type_conversions.h"
 
+#include "base/containers/to_vector.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/cbor/values.h"
@@ -29,68 +30,56 @@
     bool success;
     std::optional<FidoTransportProtocol> expected_transport;
   } test_cases[] = {
-      {L"packed",
-       fido_parsing_utils::Materialize(test_data::kTestSignAuthenticatorData),
-       fido_parsing_utils::Materialize(
-           test_data::kPackedAttestationStatementCBOR),
+      {L"packed", base::ToVector(test_data::kTestSignAuthenticatorData),
+       base::ToVector(test_data::kPackedAttestationStatementCBOR),
        WEBAUTHN_CTAP_TRANSPORT_USB, true,
        FidoTransportProtocol::kUsbHumanInterfaceDevice},
-      {L"packed",
-       fido_parsing_utils::Materialize(test_data::kTestSignAuthenticatorData),
-       fido_parsing_utils::Materialize(
-           test_data::kPackedAttestationStatementCBOR),
+      {L"packed", base::ToVector(test_data::kTestSignAuthenticatorData),
+       base::ToVector(test_data::kPackedAttestationStatementCBOR),
        WEBAUTHN_CTAP_TRANSPORT_NFC, true,
        FidoTransportProtocol::kNearFieldCommunication},
-      {L"packed",
-       fido_parsing_utils::Materialize(test_data::kTestSignAuthenticatorData),
-       fido_parsing_utils::Materialize(
-           test_data::kPackedAttestationStatementCBOR),
+      {L"packed", base::ToVector(test_data::kTestSignAuthenticatorData),
+       base::ToVector(test_data::kPackedAttestationStatementCBOR),
        WEBAUTHN_CTAP_TRANSPORT_INTERNAL, true,
        FidoTransportProtocol::kInternal},
-      {L"packed",
-       fido_parsing_utils::Materialize(test_data::kTestSignAuthenticatorData),
-       fido_parsing_utils::Materialize(
-           test_data::kPackedAttestationStatementCBOR),
+      {L"packed", base::ToVector(test_data::kTestSignAuthenticatorData),
+       base::ToVector(test_data::kPackedAttestationStatementCBOR),
        WEBAUTHN_CTAP_TRANSPORT_TEST, true, std::nullopt},
-      {L"packed",
-       fido_parsing_utils::Materialize(test_data::kTestSignAuthenticatorData),
-       fido_parsing_utils::Materialize(
-           test_data::kPackedAttestationStatementCBOR),
+      {L"packed", base::ToVector(test_data::kTestSignAuthenticatorData),
+       base::ToVector(test_data::kPackedAttestationStatementCBOR),
        WEBAUTHN_CTAP_TRANSPORT_SMART_CARD, true,
        FidoTransportProtocol::kSmartCard},
       // Unknown attestation formats
       {L"weird-unknown-format",
-       fido_parsing_utils::Materialize(test_data::kTestSignAuthenticatorData),
+       base::ToVector(test_data::kTestSignAuthenticatorData),
        {0xa0},  // Empty CBOR map.
        WEBAUTHN_CTAP_TRANSPORT_USB,
        true,
        FidoTransportProtocol::kUsbHumanInterfaceDevice},
       {L"weird-unknown-format",
-       fido_parsing_utils::Materialize(test_data::kTestSignAuthenticatorData),
+       base::ToVector(test_data::kTestSignAuthenticatorData),
        {0x60},  // Empty string. Not a valid attStmt.
        WEBAUTHN_CTAP_TRANSPORT_USB,
        false},
       // Invalid authenticator data
       {L"packed",
        {},
-       fido_parsing_utils::Materialize(
-           test_data::kPackedAttestationStatementCBOR),
+       base::ToVector(test_data::kPackedAttestationStatementCBOR),
        WEBAUTHN_CTAP_TRANSPORT_USB,
        false},
       {L"packed",
        {1, 2, 3},
-       fido_parsing_utils::Materialize(
-           test_data::kPackedAttestationStatementCBOR),
+       base::ToVector(test_data::kPackedAttestationStatementCBOR),
        WEBAUTHN_CTAP_TRANSPORT_USB,
        false},
       // Invalid attestation statement
       {L"packed",
-       fido_parsing_utils::Materialize(test_data::kTestSignAuthenticatorData),
+       base::ToVector(test_data::kTestSignAuthenticatorData),
        {},
        WEBAUTHN_CTAP_TRANSPORT_USB,
        false},
       {L"packed",
-       fido_parsing_utils::Materialize(test_data::kTestSignAuthenticatorData),
+       base::ToVector(test_data::kTestSignAuthenticatorData),
        {1, 2, 3},
        WEBAUTHN_CTAP_TRANSPORT_USB,
        false},
@@ -144,10 +133,8 @@
 TEST(TypeConversionsTest, ToAuthenticatorMakeCredentialResponseVersion8) {
   // With VERSION_8, dwTransports (multi-transport bitmask) should be used
   // instead of inferring from dwUsedTransport alone.
-  auto auth_data =
-      fido_parsing_utils::Materialize(test_data::kTestSignAuthenticatorData);
-  auto att_stmt = fido_parsing_utils::Materialize(
-      test_data::kPackedAttestationStatementCBOR);
+  auto auth_data = base::ToVector(test_data::kTestSignAuthenticatorData);
+  auto att_stmt = base::ToVector(test_data::kPackedAttestationStatementCBOR);
   WEBAUTHN_CREDENTIAL_ATTESTATION attestation = {};
   attestation.dwVersion = WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_8;
   attestation.pwszFormatType = L"packed";
diff --git a/infra/config/generated/builders/ci/Dawn Android arm64 Builder/targets/chromium.dawn.json b/infra/config/generated/builders/ci/Dawn Android arm64 Builder/targets/chromium.dawn.json
index 7421f8d1..dadc615 100644
--- a/infra/config/generated/builders/ci/Dawn Android arm64 Builder/targets/chromium.dawn.json
+++ b/infra/config/generated/builders/ci/Dawn Android arm64 Builder/targets/chromium.dawn.json
@@ -901,35 +901,75 @@
     ]
   },
   "Dawn Android arm64 Release (Samsung S24)": {
-    "isolated_scripts": [
+    "gtest_tests": [
       {
         "args": [
-          "noop_sleep",
-          "--show-stdout",
-          "--browser=android-chromium",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=1",
-          "--compatibility-mode=dont-require-rooted-device",
-          "--initial-find-device-attempts=3"
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512",
+          "--do-not-store-tombstones",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
         ],
-        "experiment_percentage": 100,
         "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test_android_chrome",
-        "module_scheme": "flat",
-        "name": "noop_sleep_tests",
+        "module_name": "//third_party/dawn/src/dawn/tests:dawn_end2end_tests",
+        "module_scheme": "gtest",
+        "name": "dawn_end2end_tests",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
         },
         "swarming": {
           "dimensions": {
-            "device_os": "UP1A.231005.007",
+            "device_os": "AP3A.240905.015.A2",
+            "device_os_type": "user",
+            "device_type": "s5e9945",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=android-chromium",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--jobs=1",
+          "--compatibility-mode=dont-require-rooted-device",
+          "--initial-find-device-attempts=3",
+          "--use-webgpu-power-preference=default-high-performance",
+          "--do-not-store-tombstones"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "module_name": "//chrome/test:telemetry_gpu_integration_test_android_chrome",
+        "module_scheme": "webgpucts",
+        "name": "webgpu_cts_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "device_os": "AP3A.240905.015.A2",
             "device_os_type": "user",
             "device_type": "s5e9945",
             "os": "Android",
@@ -939,7 +979,8 @@
           "hard_timeout": 1800,
           "idempotent": false,
           "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 36
         },
         "test": "telemetry_gpu_integration_test_android_chrome",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_android_chrome/"
diff --git "a/infra/config/generated/builders/ci/Dawn Android arm64 Release \050Samsung S24\051/targets/chromium.dawn.json" "b/infra/config/generated/builders/ci/Dawn Android arm64 Release \050Samsung S24\051/targets/chromium.dawn.json"
index 478dc4cd..7abb480 100644
--- "a/infra/config/generated/builders/ci/Dawn Android arm64 Release \050Samsung S24\051/targets/chromium.dawn.json"
+++ "b/infra/config/generated/builders/ci/Dawn Android arm64 Release \050Samsung S24\051/targets/chromium.dawn.json"
@@ -1,34 +1,74 @@
 {
   "Dawn Android arm64 Release (Samsung S24)": {
-    "isolated_scripts": [
+    "gtest_tests": [
       {
         "args": [
-          "noop_sleep",
-          "--show-stdout",
-          "--browser=android-chromium",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=1",
-          "--compatibility-mode=dont-require-rooted-device",
-          "--initial-find-device-attempts=3"
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512",
+          "--do-not-store-tombstones",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
         ],
-        "experiment_percentage": 100,
         "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test_android_chrome",
-        "module_scheme": "flat",
-        "name": "noop_sleep_tests",
+        "module_name": "//third_party/dawn/src/dawn/tests:dawn_end2end_tests",
+        "module_scheme": "gtest",
+        "name": "dawn_end2end_tests",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
         },
         "swarming": {
           "dimensions": {
-            "device_os": "UP1A.231005.007",
+            "device_os": "AP3A.240905.015.A2",
+            "device_os_type": "user",
+            "device_type": "s5e9945",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=android-chromium",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--jobs=1",
+          "--compatibility-mode=dont-require-rooted-device",
+          "--initial-find-device-attempts=3",
+          "--use-webgpu-power-preference=default-high-performance",
+          "--do-not-store-tombstones"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "module_name": "//chrome/test:telemetry_gpu_integration_test_android_chrome",
+        "module_scheme": "webgpucts",
+        "name": "webgpu_cts_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "device_os": "AP3A.240905.015.A2",
             "device_os_type": "user",
             "device_type": "s5e9945",
             "os": "Android",
@@ -38,7 +78,8 @@
           "hard_timeout": 1800,
           "idempotent": false,
           "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 36
         },
         "test": "telemetry_gpu_integration_test_android_chrome",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_android_chrome/"
diff --git a/infra/config/generated/builders/ci/GPU FYI Mac arm64 Builder/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/ci/GPU FYI Mac arm64 Builder/targets/chromium.gpu.fyi.json
index 583c755..2410050 100644
--- a/infra/config/generated/builders/ci/GPU FYI Mac arm64 Builder/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/ci/GPU FYI Mac arm64 Builder/targets/chromium.gpu.fyi.json
@@ -927,357 +927,6 @@
     "isolated_scripts": [
       {
         "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "context_lost_metal_passthrough_graphite_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "expected_color",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "expected_color_pixel_metal_passthrough_graphite_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "gpu_process_launch_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "hardware_accelerated_feature_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--expected-vendor-id",
-          "106b",
-          "--expected-device-id",
-          "0",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "info_collection_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "pixel_skia_gold_metal_passthrough_graphite_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--dont-restore-color-profile-after-test",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "screenshot_sync_metal_passthrough_graphite_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "trace_test",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "webcodecs",
           "--show-stdout",
           "--browser=release",
@@ -1370,49 +1019,6 @@
           "--passthrough",
           "-v",
           "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite --enable-features=EGLDualGPURendering,ForceHighPerformanceGPUForWebGL",
-          "--enforce-browser-version",
-          "--enable-metal-debug-layers",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_mac_runtimes.json",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "webgl_conformance_metal_passthrough_graphite_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "webgl1_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=swiftshader --force_high_performance_gpu",
           "--enforce-browser-version",
           "--jobs=4",
diff --git "a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050dbg\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050dbg\051/targets/chromium.gpu.fyi.json"
index 12d0a76..7f34ac4 100644
--- "a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050dbg\051/targets/chromium.gpu.fyi.json"
+++ "b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder \050dbg\051/targets/chromium.gpu.fyi.json"
@@ -157,376 +157,6 @@
         "test": "xr_browser_tests",
         "test_id_prefix": "ninja://chrome/test:xr_browser_tests/"
       }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "context_lost_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "expected_color",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "expected_color_pixel_passthrough_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "gpu_process_launch_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "hardware_accelerated_feature_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--expected-vendor-id",
-          "10de",
-          "--expected-device-id",
-          "2184",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "info_collection_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "pixel_skia_gold_passthrough_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--dont-restore-color-profile-after-test",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "screenshot_sync_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "trace_test",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "webgl1_conformance",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d11 --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "webgl_conformance_d3d11_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      }
     ]
   }
 }
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json
index b7ebf0e7..4ee48b1 100644
--- a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json
@@ -3249,44 +3249,6 @@
       },
       {
         "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "context_lost_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "expected_color",
           "--show-stdout",
           "--browser=release_x64",
@@ -3334,171 +3296,6 @@
       },
       {
         "args": [
-          "expected_color",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "expected_color_pixel_passthrough_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "gpu_process_launch_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "hardware_accelerated_feature_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--expected-vendor-id",
-          "10de",
-          "--expected-device-id",
-          "2184",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "info_collection_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "--gtest-benchmark-name=passthrough_command_buffer_perftests",
           "-v",
           "--use-cmd-decoder=passthrough",
@@ -3578,53 +3375,6 @@
       },
       {
         "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "pixel_skia_gold_passthrough_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "screenshot_sync",
           "--show-stdout",
           "--browser=release_x64",
@@ -3664,83 +3414,6 @@
       },
       {
         "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--dont-restore-color-profile-after-test",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "screenshot_sync_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "trace_test",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "webcodecs",
           "--show-stdout",
           "--browser=release_x64",
@@ -3820,46 +3493,6 @@
       },
       {
         "args": [
-          "webgl1_conformance",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d11 --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "webgl_conformance_d3d11_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "webrtc",
           "--show-stdout",
           "--browser=release_x64",
diff --git "a/infra/config/generated/builders/ci/Mac FYI Retina Release \050Apple M2\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/Mac FYI Retina Release \050Apple M2\051/targets/chromium.gpu.fyi.json"
index 0fe3cba..0df4a606 100644
--- "a/infra/config/generated/builders/ci/Mac FYI Retina Release \050Apple M2\051/targets/chromium.gpu.fyi.json"
+++ "b/infra/config/generated/builders/ci/Mac FYI Retina Release \050Apple M2\051/targets/chromium.gpu.fyi.json"
@@ -150,357 +150,6 @@
     "isolated_scripts": [
       {
         "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "context_lost_metal_passthrough_graphite_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "expected_color",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "expected_color_pixel_metal_passthrough_graphite_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "gpu_process_launch_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "hardware_accelerated_feature_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--expected-vendor-id",
-          "106b",
-          "--expected-device-id",
-          "0",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "info_collection_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "pixel_skia_gold_metal_passthrough_graphite_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--dont-restore-color-profile-after-test",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "screenshot_sync_metal_passthrough_graphite_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "trace_test",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "webcodecs",
           "--show-stdout",
           "--browser=release",
@@ -593,49 +242,6 @@
           "--passthrough",
           "-v",
           "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite --enable-features=EGLDualGPURendering,ForceHighPerformanceGPUForWebGL",
-          "--enforce-browser-version",
-          "--enable-metal-debug-layers",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_mac_runtimes.json",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "webgl_conformance_metal_passthrough_graphite_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "webgl1_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=swiftshader --force_high_performance_gpu",
           "--enforce-browser-version",
           "--jobs=4",
diff --git "a/infra/config/generated/builders/ci/Win10 FYI x64 Debug \050NVIDIA\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/Win10 FYI x64 Debug \050NVIDIA\051/targets/chromium.gpu.fyi.json"
index dfa157b..f50119f8 100644
--- "a/infra/config/generated/builders/ci/Win10 FYI x64 Debug \050NVIDIA\051/targets/chromium.gpu.fyi.json"
+++ "b/infra/config/generated/builders/ci/Win10 FYI x64 Debug \050NVIDIA\051/targets/chromium.gpu.fyi.json"
@@ -156,376 +156,6 @@
         "test": "xr_browser_tests",
         "test_id_prefix": "ninja://chrome/test:xr_browser_tests/"
       }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "context_lost_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "expected_color",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "expected_color_pixel_passthrough_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "gpu_process_launch_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "hardware_accelerated_feature_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--expected-vendor-id",
-          "10de",
-          "--expected-device-id",
-          "2184",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "info_collection_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "pixel_skia_gold_passthrough_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--dont-restore-color-profile-after-test",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "screenshot_sync_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "trace_test",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "webgl1_conformance",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d11 --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "webgl_conformance_d3d11_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      }
     ]
   }
 }
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/Win10 FYI x64 Release \050NVIDIA\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/Win10 FYI x64 Release \050NVIDIA\051/targets/chromium.gpu.fyi.json"
index 9d3d6aa..a06d932 100644
--- "a/infra/config/generated/builders/ci/Win10 FYI x64 Release \050NVIDIA\051/targets/chromium.gpu.fyi.json"
+++ "b/infra/config/generated/builders/ci/Win10 FYI x64 Release \050NVIDIA\051/targets/chromium.gpu.fyi.json"
@@ -198,44 +198,6 @@
       },
       {
         "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "context_lost_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "expected_color",
           "--show-stdout",
           "--browser=release_x64",
@@ -283,171 +245,6 @@
       },
       {
         "args": [
-          "expected_color",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "expected_color_pixel_passthrough_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "gpu_process_launch_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "hardware_accelerated_feature_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--expected-vendor-id",
-          "10de",
-          "--expected-device-id",
-          "2184",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "info_collection_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "--gtest-benchmark-name=passthrough_command_buffer_perftests",
           "-v",
           "--use-cmd-decoder=passthrough",
@@ -527,53 +324,6 @@
       },
       {
         "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "pixel_skia_gold_passthrough_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "screenshot_sync",
           "--show-stdout",
           "--browser=release_x64",
@@ -613,83 +363,6 @@
       },
       {
         "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--dont-restore-color-profile-after-test",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "screenshot_sync_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "trace_test",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "webcodecs",
           "--show-stdout",
           "--browser=release_x64",
@@ -769,46 +442,6 @@
       },
       {
         "args": [
-          "webgl1_conformance",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d11 --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "webgl_conformance_d3d11_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "webrtc",
           "--show-stdout",
           "--browser=release_x64",
diff --git a/infra/config/generated/builders/try/android-dawn-arm64-s24-rel/targets/chromium.dawn.json b/infra/config/generated/builders/try/android-dawn-arm64-s24-rel/targets/chromium.dawn.json
index 478dc4cd..7abb480 100644
--- a/infra/config/generated/builders/try/android-dawn-arm64-s24-rel/targets/chromium.dawn.json
+++ b/infra/config/generated/builders/try/android-dawn-arm64-s24-rel/targets/chromium.dawn.json
@@ -1,34 +1,74 @@
 {
   "Dawn Android arm64 Release (Samsung S24)": {
-    "isolated_scripts": [
+    "gtest_tests": [
       {
         "args": [
-          "noop_sleep",
-          "--show-stdout",
-          "--browser=android-chromium",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=1",
-          "--compatibility-mode=dont-require-rooted-device",
-          "--initial-find-device-attempts=3"
+          "--use-gpu-in-tests",
+          "--exclusive-device-type-preference=discrete,integrated",
+          "--test-launcher-retry-limit=0",
+          "--test-launcher-batch-limit=512",
+          "--do-not-store-tombstones",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
         ],
-        "experiment_percentage": 100,
         "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test_android_chrome",
-        "module_scheme": "flat",
-        "name": "noop_sleep_tests",
+        "module_name": "//third_party/dawn/src/dawn/tests:dawn_end2end_tests",
+        "module_scheme": "gtest",
+        "name": "dawn_end2end_tests",
         "resultdb": {
           "enable": true,
           "has_native_resultdb_integration": true
         },
         "swarming": {
           "dimensions": {
-            "device_os": "UP1A.231005.007",
+            "device_os": "AP3A.240905.015.A2",
+            "device_os_type": "user",
+            "device_type": "s5e9945",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://third_party/dawn/src/dawn/tests:dawn_end2end_tests/"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "webgpu_cts",
+          "--show-stdout",
+          "--browser=android-chromium",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--jobs=1",
+          "--compatibility-mode=dont-require-rooted-device",
+          "--initial-find-device-attempts=3",
+          "--use-webgpu-power-preference=default-high-performance",
+          "--do-not-store-tombstones"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "module_name": "//chrome/test:telemetry_gpu_integration_test_android_chrome",
+        "module_scheme": "webgpucts",
+        "name": "webgpu_cts_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "dimensions": {
+            "device_os": "AP3A.240905.015.A2",
             "device_os_type": "user",
             "device_type": "s5e9945",
             "os": "Android",
@@ -38,7 +78,8 @@
           "hard_timeout": 1800,
           "idempotent": false,
           "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 36
         },
         "test": "telemetry_gpu_integration_test_android_chrome",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_android_chrome/"
diff --git a/infra/config/generated/builders/try/gpu-fyi-cq-mac-arm64/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-cq-mac-arm64/targets/chromium.gpu.fyi.json
index 0fe3cba..0df4a606 100644
--- a/infra/config/generated/builders/try/gpu-fyi-cq-mac-arm64/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/try/gpu-fyi-cq-mac-arm64/targets/chromium.gpu.fyi.json
@@ -150,357 +150,6 @@
     "isolated_scripts": [
       {
         "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "context_lost_metal_passthrough_graphite_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "expected_color",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "expected_color_pixel_metal_passthrough_graphite_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "gpu_process_launch_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "hardware_accelerated_feature_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--expected-vendor-id",
-          "106b",
-          "--expected-device-id",
-          "0",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "info_collection_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "pixel_skia_gold_metal_passthrough_graphite_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--dont-restore-color-profile-after-test",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "screenshot_sync_metal_passthrough_graphite_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "trace_test",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "webcodecs",
           "--show-stdout",
           "--browser=release",
@@ -593,49 +242,6 @@
           "--passthrough",
           "-v",
           "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite --enable-features=EGLDualGPURendering,ForceHighPerformanceGPUForWebGL",
-          "--enforce-browser-version",
-          "--enable-metal-debug-layers",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_mac_runtimes.json",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "webgl_conformance_metal_passthrough_graphite_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "webgl1_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=swiftshader --force_high_performance_gpu",
           "--enforce-browser-version",
           "--jobs=4",
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-mac-arm64-apple-m2-retina-rel/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-try-mac-arm64-apple-m2-retina-rel/targets/chromium.gpu.fyi.json
index 0fe3cba..0df4a606 100644
--- a/infra/config/generated/builders/try/gpu-fyi-try-mac-arm64-apple-m2-retina-rel/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/try/gpu-fyi-try-mac-arm64-apple-m2-retina-rel/targets/chromium.gpu.fyi.json
@@ -150,357 +150,6 @@
     "isolated_scripts": [
       {
         "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "context_lost_metal_passthrough_graphite_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "expected_color",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "expected_color_pixel_metal_passthrough_graphite_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "gpu_process_launch_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "hardware_accelerated_feature_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--expected-vendor-id",
-          "106b",
-          "--expected-device-id",
-          "0",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "info_collection_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "pixel_skia_gold_metal_passthrough_graphite_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite",
-          "--enforce-browser-version",
-          "--dont-restore-color-profile-after-test",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "screenshot_sync_metal_passthrough_graphite_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "trace_test",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "webcodecs",
           "--show-stdout",
           "--browser=release",
@@ -593,49 +242,6 @@
           "--passthrough",
           "-v",
           "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=metal --enable-skia-graphite --enable-features=EGLDualGPURendering,ForceHighPerformanceGPUForWebGL",
-          "--enforce-browser-version",
-          "--enable-metal-debug-layers",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_mac_runtimes.json",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "webgl_conformance_metal_passthrough_graphite_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "arm64",
-            "display_attached": "1",
-            "gpu": "apple:m2",
-            "hidpi": "1",
-            "mac_model": "Mac14,7",
-            "os": "Mac-26.5.1",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "webgl1_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=swiftshader --force_high_performance_gpu",
           "--enforce-browser-version",
           "--jobs=4",
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-dbg-64/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-dbg-64/targets/chromium.gpu.fyi.json
index 12d0a76..7f34ac4 100644
--- a/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-dbg-64/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-dbg-64/targets/chromium.gpu.fyi.json
@@ -157,376 +157,6 @@
         "test": "xr_browser_tests",
         "test_id_prefix": "ninja://chrome/test:xr_browser_tests/"
       }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "context_lost_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "expected_color",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "expected_color_pixel_passthrough_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "gpu_process_launch_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "hardware_accelerated_feature_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--expected-vendor-id",
-          "10de",
-          "--expected-device-id",
-          "2184",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "info_collection_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "pixel_skia_gold_passthrough_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--dont-restore-color-profile-after-test",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "screenshot_sync_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "trace_test",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "webgl1_conformance",
-          "--show-stdout",
-          "--browser=debug_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d11 --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "webgl_conformance_d3d11_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      }
     ]
   }
 }
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-rel-64/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-rel-64/targets/chromium.gpu.fyi.json
index 24f645f7..7586e45 100644
--- a/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-rel-64/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-rel-64/targets/chromium.gpu.fyi.json
@@ -199,44 +199,6 @@
       },
       {
         "args": [
-          "context_lost",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "context_lost_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "expected_color",
           "--show-stdout",
           "--browser=release_x64",
@@ -284,171 +246,6 @@
       },
       {
         "args": [
-          "expected_color",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "expected_color_pixel_passthrough_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "gpu_process",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "gpu_process_launch_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "hardware_accelerated_feature",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "hardware_accelerated_feature_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "info_collection",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--expected-vendor-id",
-          "10de",
-          "--expected-device-id",
-          "2184",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "info_collection_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "--gtest-benchmark-name=passthrough_command_buffer_perftests",
           "-v",
           "--use-cmd-decoder=passthrough",
@@ -528,53 +325,6 @@
       },
       {
         "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "pixel_skia_gold_passthrough_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "screenshot_sync",
           "--show-stdout",
           "--browser=release_x64",
@@ -614,83 +364,6 @@
       },
       {
         "args": [
-          "screenshot_sync",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
-          "--enforce-browser-version",
-          "--dont-restore-color-profile-after-test",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "screenshot_sync_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "trace_test",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--enforce-browser-version",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "trace_test",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "webcodecs",
           "--show-stdout",
           "--browser=release_x64",
@@ -770,46 +443,6 @@
       },
       {
         "args": [
-          "webgl1_conformance",
-          "--show-stdout",
-          "--browser=release_x64",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d11 --force_high_performance_gpu",
-          "--enforce-browser-version",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
-          "--jobs=4"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "module_name": "//chrome/test:telemetry_gpu_integration_test",
-        "module_scheme": "flat",
-        "name": "webgl_conformance_d3d11_passthrough_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "webrtc",
           "--show-stdout",
           "--browser=release_x64",
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 57b3731..f48df46 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -3623,7 +3623,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -3707,7 +3706,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -5034,7 +5032,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -5118,7 +5115,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -5509,7 +5505,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -5593,7 +5588,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -5627,7 +5621,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -5711,7 +5704,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -5981,7 +5973,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -6065,7 +6056,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -6099,7 +6089,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -6183,7 +6172,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -6824,7 +6812,7 @@
         '    "dawn"'
         '  ]'
         '}'
-      execution_timeout_secs: 10800
+      execution_timeout_secs: 21600
       build_numbers: YES
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -6901,7 +6889,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -6985,7 +6972,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -7019,7 +7005,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -7103,7 +7088,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -7137,7 +7121,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -7221,7 +7204,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -8080,7 +8062,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -8164,7 +8145,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -8197,7 +8177,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -8281,7 +8260,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -8668,7 +8646,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -8752,7 +8729,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -8785,7 +8761,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -8869,7 +8844,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -9731,6 +9705,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -10059,6 +10034,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -10175,6 +10151,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -11117,6 +11094,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -11233,6 +11211,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -12057,6 +12036,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -12173,6 +12153,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -13189,7 +13170,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -13273,7 +13253,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -13307,7 +13286,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -13391,7 +13369,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -13425,7 +13402,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -13509,7 +13485,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -13543,7 +13518,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -13627,7 +13601,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -13661,7 +13634,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -13745,7 +13717,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -13778,7 +13749,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -13862,7 +13832,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -13895,7 +13864,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -13979,7 +13947,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -14012,7 +13979,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -14096,7 +14062,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -14129,7 +14094,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -14213,7 +14177,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -14246,7 +14209,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -14330,7 +14292,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -14366,6 +14327,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -14482,6 +14444,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -14598,6 +14561,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -14714,6 +14678,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -14830,6 +14795,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -14946,6 +14912,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -15062,6 +15029,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -15176,7 +15144,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -15260,7 +15227,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -15294,7 +15260,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -15371,7 +15336,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -15399,7 +15363,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -15483,7 +15446,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -15516,7 +15478,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -15600,7 +15561,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -15633,7 +15593,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -15717,7 +15676,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -15753,6 +15711,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -15869,6 +15828,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -20263,7 +20223,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -20347,7 +20306,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -39258,7 +39216,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -39454,7 +39411,6 @@
           '    "remote_jobs": 250'
           '  }'
           '}'
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "angle-team@google.com"
@@ -49661,7 +49617,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -49809,7 +49764,6 @@
           '    "remote_jobs": 250'
           '  }'
           '}'
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "angle-team@google.com"
@@ -51007,7 +50961,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac-15"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -51200,7 +51153,6 @@
           '    "remote_jobs": 250'
           '  }'
           '}'
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "angle-team@google.com"
@@ -53247,7 +53199,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -53470,7 +53421,6 @@
           '    "remote_jobs": 250'
           '  }'
           '}'
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "angle-team@google.com"
@@ -60383,7 +60333,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -60467,7 +60416,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -60501,7 +60449,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -60585,7 +60532,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -60619,7 +60565,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -60766,7 +60711,6 @@
           '    "remote_jobs": 500'
           '  }'
           '}'
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -60800,7 +60744,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -60884,7 +60827,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -60918,7 +60860,6 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -61002,7 +60943,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -63600,7 +63540,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -63826,7 +63765,6 @@
           '    "remote_jobs": 250'
           '  }'
           '}'
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "angle-team@google.com"
@@ -66470,7 +66408,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "free_space:standard"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.ci"
       exe {
@@ -66554,7 +66491,6 @@
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
-        dimensions: "free_space:"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-gpu-infra@google.com"
@@ -70320,6 +70256,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/main"
@@ -70538,6 +70475,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/main"
@@ -70793,6 +70731,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/main"
@@ -73184,6 +73123,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -73300,6 +73240,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/main"
@@ -73479,6 +73420,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/main"
@@ -73658,6 +73600,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -73774,6 +73717,7 @@
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.ci"
+      dimensions: "300:ssd:1"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -88268,6 +88212,7 @@
       name: "android-dawn-arm-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -88379,6 +88324,7 @@
       name: "android-dawn-arm64-exp-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -88490,6 +88436,7 @@
       name: "android-dawn-arm64-p10-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -88601,6 +88548,7 @@
       name: "android-dawn-arm64-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -88712,6 +88660,7 @@
       name: "android-dawn-arm64-s24-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -88745,7 +88694,7 @@
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium_trybot"'
         '}'
-      execution_timeout_secs: 14400
+      execution_timeout_secs: 21600
       expiration_secs: 7200
       grace_period {
         seconds: 120
@@ -94891,6 +94840,10 @@
         key: "luci.buildbucket.canary_software"
         value: 5
       }
+      experiments {
+        key: "luci.buildbucket.run_in_turboci"
+        value: 100
+      }
       resultdb {
         enable: true
         bq_exports {
@@ -95136,6 +95089,10 @@
         key: "luci.buildbucket.canary_software"
         value: 5
       }
+      experiments {
+        key: "luci.buildbucket.run_in_turboci"
+        value: 2
+      }
       resultdb {
         enable: true
         bq_exports {
@@ -95198,6 +95155,7 @@
       name: "dawn-android-arm-deps-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -95232,7 +95190,7 @@
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium_trybot"'
         '}'
-      execution_timeout_secs: 14400
+      execution_timeout_secs: 21600
       expiration_secs: 7200
       grace_period {
         seconds: 120
@@ -95310,6 +95268,7 @@
       name: "dawn-android-arm64-deps-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -95344,7 +95303,7 @@
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium_trybot"'
         '}'
-      execution_timeout_secs: 14400
+      execution_timeout_secs: 21600
       expiration_secs: 7200
       grace_period {
         seconds: 120
@@ -95422,6 +95381,7 @@
       name: "dawn-chromium-presubmit"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -95533,6 +95493,7 @@
       name: "dawn-linux-x64-deps-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -95567,7 +95528,7 @@
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium_trybot"'
         '}'
-      execution_timeout_secs: 14400
+      execution_timeout_secs: 21600
       expiration_secs: 7200
       grace_period {
         seconds: 120
@@ -95679,7 +95640,7 @@
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium_trybot"'
         '}'
-      execution_timeout_secs: 14400
+      execution_timeout_secs: 21600
       expiration_secs: 7200
       grace_period {
         seconds: 120
@@ -95757,6 +95718,7 @@
       name: "dawn-mac-x64-deps-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cpu:arm64"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.try"
       exe {
@@ -95790,7 +95752,7 @@
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium_trybot"'
         '}'
-      execution_timeout_secs: 14400
+      execution_timeout_secs: 21600
       expiration_secs: 7200
       grace_period {
         seconds: 120
@@ -95868,6 +95830,7 @@
       name: "dawn-try-linux-x64-intel-uhd770-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -95979,6 +95942,7 @@
       name: "dawn-try-mac-amd-555x-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cpu:arm64"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.try"
       exe {
@@ -96089,6 +96053,7 @@
       name: "dawn-try-mac-amd-exp"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cpu:arm64"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.try"
       exe {
@@ -96199,6 +96164,7 @@
       name: "dawn-try-mac-arm64-deps-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cpu:arm64"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.try"
       exe {
@@ -96309,6 +96275,7 @@
       name: "dawn-try-mac-arm64-m2-exp"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cpu:arm64"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.try"
       exe {
@@ -96419,6 +96386,7 @@
       name: "dawn-try-mac-arm64-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cpu:arm64"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.try"
       exe {
@@ -96529,6 +96497,7 @@
       name: "dawn-try-mac-intel-exp"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cpu:arm64"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.try"
       exe {
@@ -96639,6 +96608,7 @@
       name: "dawn-try-win-x64-intel-exp"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -96751,6 +96721,7 @@
       name: "dawn-try-win-x64-intel-uhd770-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -96863,6 +96834,7 @@
       name: "dawn-try-win-x64-nvidia-exp"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -96975,6 +96947,7 @@
       name: "dawn-try-win-x86-intel-exp"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -97087,6 +97060,7 @@
       name: "dawn-try-win-x86-nvidia-exp"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -97199,6 +97173,7 @@
       name: "dawn-try-win10-x64-intel-asan"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -97311,6 +97286,7 @@
       name: "dawn-try-win10-x64-nvidia-asan"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -97423,6 +97399,7 @@
       name: "dawn-try-win10-x86-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -97535,6 +97512,7 @@
       name: "dawn-try-win11-arm64-snapdragon-x-elite-deps-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -97647,6 +97625,7 @@
       name: "dawn-try-win11-arm64-snapdragon-x-elite-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -97759,6 +97738,7 @@
       name: "dawn-win10-x64-deps-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -97794,7 +97774,7 @@
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium_trybot"'
         '}'
-      execution_timeout_secs: 14400
+      execution_timeout_secs: 21600
       expiration_secs: 7200
       grace_period {
         seconds: 120
@@ -97872,6 +97852,7 @@
       name: "dawn-win10-x86-deps-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -97907,7 +97888,7 @@
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium_trybot"'
         '}'
-      execution_timeout_secs: 14400
+      execution_timeout_secs: 21600
       expiration_secs: 7200
       grace_period {
         seconds: 120
@@ -97985,6 +97966,7 @@
       name: "dawn-win11-arm64-deps-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -98020,7 +98002,7 @@
         '  "led_builder_is_bootstrapped": true,'
         '  "recipe": "chromium_trybot"'
         '}'
-      execution_timeout_secs: 14400
+      execution_timeout_secs: 21600
       expiration_secs: 7200
       grace_period {
         seconds: 120
@@ -98767,6 +98749,10 @@
         key: "luci.buildbucket.canary_software"
         value: 5
       }
+      experiments {
+        key: "luci.buildbucket.run_in_turboci"
+        value: 100
+      }
       resultdb {
         enable: true
         bq_exports {
@@ -100206,7 +100192,7 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "os:Mac-26"
+      dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.try"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
@@ -100323,7 +100309,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Windows-10"
+      dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
       exe {
@@ -104931,7 +104917,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -105042,7 +105027,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -105153,7 +105137,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -105264,7 +105247,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -105375,7 +105357,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -105486,7 +105467,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -105597,7 +105577,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -105708,7 +105687,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -105819,7 +105797,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -105930,7 +105907,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -106041,7 +106017,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -106152,7 +106127,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -106263,7 +106237,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -106374,7 +106347,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -106485,7 +106457,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -106596,7 +106567,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -106707,7 +106677,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -106818,7 +106787,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -106929,7 +106897,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -107587,7 +107554,6 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
@@ -115093,6 +115059,7 @@
       name: "linux-dawn-intel-exp-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -115204,6 +115171,7 @@
       name: "linux-dawn-nvidia-1660-exp-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -115315,6 +115283,7 @@
       name: "linux-dawn-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -124078,6 +124047,7 @@
       name: "mac-angle-chromium-try"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cpu:arm64"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.try"
       exe {
@@ -124536,6 +124506,7 @@
       name: "mac-arm64-dawn-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cpu:arm64"
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.try"
       exe {
@@ -130869,7 +130840,7 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
       dimensions: "cpu:arm64"
-      dimensions: "os:Mac-26"
+      dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.gpu.try"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
@@ -134344,6 +134315,7 @@
       name: "win-dawn-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -137982,6 +137954,7 @@
       name: "win11-arm64-dawn-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
+      dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
@@ -138890,7 +138863,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Windows-10"
+      dimensions: "os:Windows"
       dimensions: "pool:luci.chromium.gpu.try"
       dimensions: "300:ssd:1"
       exe {
diff --git a/infra/config/generated/testing/mixins.pyl b/infra/config/generated/testing/mixins.pyl
index 56e5bd65..1c23738e 100644
--- a/infra/config/generated/testing/mixins.pyl
+++ b/infra/config/generated/testing/mixins.pyl
@@ -412,7 +412,7 @@
     'swarming': {
       'dimensions': {
         'device_type': 's5e9945',
-        'device_os': 'UP1A.231005.007',
+        'device_os': 'AP3A.240905.015.A2',
         'device_os_type': 'user',
         'os': 'Android',
         'pool': 'chromium.tests.gpu',
diff --git a/infra/config/generated/testing/test_suites.pyl b/infra/config/generated/testing/test_suites.pyl
index ba567f9..1719950 100644
--- a/infra/config/generated/testing/test_suites.pyl
+++ b/infra/config/generated/testing/test_suites.pyl
@@ -2774,7 +2774,14 @@
       'gpu_webrtc_metal_passthrough_graphite_telemetry_test',
     ],
 
-    'gpu_fyi_win_release_telemetry_tests': [
+    'gpu_telemetry_tests_v8': [
+      'gpu_common_and_optional_telemetry_tests',
+      'gpu_validating_telemetry_tests',
+      'gpu_webgl_conformance_gles_passthrough_telemetry_tests',
+      'gpu_webgl_conformance_validating_telemetry_tests',
+    ],
+
+    'gpu_v8_win_release_telemetry_tests': [
       'gpu_common_and_optional_telemetry_tests',
       'gpu_passthrough_graphite_telemetry_tests',
       'gpu_passthrough_telemetry_tests',
@@ -2785,13 +2792,6 @@
       'gpu_webrtc_telemetry_test',
     ],
 
-    'gpu_telemetry_tests_v8': [
-      'gpu_common_and_optional_telemetry_tests',
-      'gpu_validating_telemetry_tests',
-      'gpu_webgl_conformance_gles_passthrough_telemetry_tests',
-      'gpu_webgl_conformance_validating_telemetry_tests',
-    ],
-
     'linux_chromeos_gtests': [
       'aura_gtests',
       'chromium_gtests',
diff --git a/infra/config/lib/gpu.star b/infra/config/lib/gpu.star
index 56f520b41..1c3c3d5 100644
--- a/infra/config/lib/gpu.star
+++ b/infra/config/lib/gpu.star
@@ -2,52 +2,150 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-load("@chromium-luci//builders.star", "cpu", "os")
+load("@chromium-luci//builders.star", "builders", "cpu", "os")
 load("@chromium-luci//ci.star", "ci")
 load("@chromium-luci//try.star", "try_")
 load("./ci_constants.star", "ci_constants")
 
-def _gpu_ci_linux_builder(*, name, **kwargs):
-    """Defines a GPU-related linux builder.
-
-    This sets linux-specific defaults that are common to GPU-related builder
-    groups.
-    """
+def _apply_gpu_linux_builder_defaults(kwargs):
+    """Applies defaults applicable to all GPU Linux builders % thin testers."""
     kwargs.setdefault("cores", 8)
+    kwargs.setdefault("cpu", "x86-64")
+    kwargs.setdefault("free_space", None)
     kwargs.setdefault("os", os.LINUX_DEFAULT)
     kwargs.setdefault("ssd", None)
-    return ci.builder(name = name, **kwargs)
+    return kwargs
 
-def _gpu_ci_mac_builder(*, name, **kwargs):
-    """Defines a GPU-related mac builder.
-
-    This sets mac-specific defaults that are common to GPU-related builder
-    groups.
-    """
-    kwargs.setdefault("builderless", True)
+def _apply_gpu_mac_builder_defaults(kwargs):
+    """Applies defaults applicable to all GPU Mac builders."""
     kwargs.setdefault("cores", None)
     kwargs.setdefault("cpu", cpu.ARM64)
-    kwargs.setdefault("os", os.MAC_ANY)
-    return ci.builder(name = name, **kwargs)
-
-def _gpu_ci_windows_builder(*, name, **kwargs):
-    """Defines a GPU-related windows builder.
-
-    This sets windows-specific defaults that are common to GPU-related builder
-    groups.
-    """
-    kwargs.setdefault("builderless", True)
-    kwargs.setdefault("cores", 8)
-    kwargs.setdefault("os", os.WINDOWS_ANY)
-    kwargs.setdefault("ssd", None)
     kwargs.setdefault("free_space", None)
-    return ci.builder(name = name, **kwargs)
+    kwargs.setdefault("os", os.MAC_ANY)
+    kwargs.setdefault("ssd", None)
+    return kwargs
 
-def _gpu_try_optional_tests_builder(*, name, **kwargs):
-    kwargs.setdefault("builderless", False)
-    kwargs.setdefault("execution_timeout", 6 * time.hour)
+def _apply_gpu_win_builder_defaults(kwargs):
+    """Applies defaults applicable to all GPU Windows builders."""
+    kwargs.setdefault("cores", 8)
+    kwargs.setdefault("free_space", None)
+    kwargs.setdefault("os", os.WINDOWS_ANY)
+    kwargs.setdefault("ssd", builders.with_expiration(True, expiration = 5 * time.minute))
+    return kwargs
+
+def _apply_gpu_ci_builder_defaults(kwargs):
+    """Applies defaults applicable to all GPU CI builders % thin testers."""
+    kwargs.setdefault("builderless", True)
+    kwargs.setdefault("pool", gpu.ci.POOL)
+
+    # TODO(crbug.com/543082386): Add the service account here once we confirm
+    # that it is okay for the non-FYI builders to use chromium-ci-gpu-builder@.
+    return kwargs
+
+def _gpu_ci_linux_builder(*args, **kwargs):
+    """Defines a GPU-related Linux CI builder."""
+    kwargs = _apply_gpu_linux_builder_defaults(kwargs)
+    kwargs = _apply_gpu_ci_builder_defaults(kwargs)
+    return ci.builder(*args, **kwargs)
+
+def _gpu_ci_mac_builder(*args, **kwargs):
+    """Defines a GPU-related Mac CI builder."""
+    kwargs = _apply_gpu_mac_builder_defaults(kwargs)
+    kwargs = _apply_gpu_ci_builder_defaults(kwargs)
+    return ci.builder(*args, **kwargs)
+
+def _gpu_ci_windows_builder(*args, **kwargs):
+    """Defines a GPU-related Windows CI builder."""
+    kwargs = _apply_gpu_win_builder_defaults(kwargs)
+    kwargs = _apply_gpu_ci_builder_defaults(kwargs)
+    return ci.builder(*args, **kwargs)
+
+def _apply_gpu_try_builder_defaults(kwargs):
+    """Applies defaults applicable to all GPU try builders."""
+    kwargs.setdefault("builderless", True)
+    kwargs.setdefault("pool", gpu.try_.POOL)
     kwargs.setdefault("service_account", gpu.try_.SERVICE_ACCOUNT)
-    return try_.builder(name = name, **kwargs)
+    return kwargs
+
+def _apply_gpu_manual_try_builder_defaults(kwargs):
+    """Applies defaults applicable to all GPU manual-only try builders."""
+    kwargs = _apply_gpu_try_builder_defaults(kwargs)
+    kwargs.setdefault("max_concurrent_builds", 1)
+    return kwargs
+
+def _gpu_try_manual_linux_builder(*args, **kwargs):
+    """Defines a GPU-related Linux manual-only trybot."""
+    kwargs = _apply_gpu_manual_try_builder_defaults(kwargs)
+    kwargs = _apply_gpu_linux_builder_defaults(kwargs)
+    return try_.builder(*args, **kwargs)
+
+def _gpu_try_manual_mac_builder(*args, **kwargs):
+    """Defines a GPU-related Mac manual-only trybot."""
+    kwargs = _apply_gpu_manual_try_builder_defaults(kwargs)
+    kwargs = _apply_gpu_mac_builder_defaults(kwargs)
+    return try_.builder(*args, **kwargs)
+
+def _gpu_try_manual_win_builder(*args, **kwargs):
+    """Defines a GPU-related Windows manual-only trybot."""
+    kwargs = _apply_gpu_manual_try_builder_defaults(kwargs)
+    kwargs = _apply_gpu_win_builder_defaults(kwargs)
+    return try_.builder(*args, **kwargs)
+
+def _apply_gpu_rate_limited_try_builder_defaults(kwargs):
+    """Applies defaults applicable to all GPU rate limited trybots.
+
+    Rate limited builders are those that are used more frequently than normal
+    manual-only trybots, but are still not automatically added to CLs like
+    optional trybots are.
+    """
+    if not try_.defaults.get_value_from_kwargs("max_concurrent_builds", kwargs):
+        fail("max_concurrent_builds not set on a builder that expects it.")
+    kwargs = _apply_gpu_try_builder_defaults(kwargs)
+    return kwargs
+
+def _gpu_try_rate_limited_linux_builder(*args, **kwargs):
+    """Defines a GPU-related Linux rate limited trybot."""
+    kwargs = _apply_gpu_rate_limited_try_builder_defaults(kwargs)
+    kwargs = _apply_gpu_linux_builder_defaults(kwargs)
+    return try_.builder(*args, **kwargs)
+
+def _gpu_try_rate_limited_mac_builder(*args, **kwargs):
+    """Defines a GPU-related Mac rate limited trybot."""
+    kwargs = _apply_gpu_rate_limited_try_builder_defaults(kwargs)
+    kwargs = _apply_gpu_mac_builder_defaults(kwargs)
+    return try_.builder(*args, **kwargs)
+
+def _gpu_try_rate_limited_win_builder(*args, **kwargs):
+    """Defines a GPU-related Windows rate limited trybot."""
+    kwargs = _apply_gpu_rate_limited_try_builder_defaults(kwargs)
+    kwargs = _apply_gpu_win_builder_defaults(kwargs)
+    return try_.builder(*args, **kwargs)
+
+def _apply_gpu_optional_try_builder_defaults(kwargs):
+    """Applies defaults applicable to all GPU optional trybots."""
+    if not kwargs.get("cq_settings"):
+        fail("cq_settings must be set for an optional builder to set location filters.")
+    kwargs = _apply_gpu_rate_limited_try_builder_defaults(kwargs)
+    kwargs.setdefault("execution_timeout", 6 * time.hour)
+    return kwargs
+
+def _gpu_try_optional_linux_builder(*args, **kwargs):
+    """Defines a GPU-related Linux optional trybot."""
+    kwargs = _apply_gpu_optional_try_builder_defaults(kwargs)
+    kwargs = _apply_gpu_linux_builder_defaults(kwargs)
+    return try_.builder(*args, **kwargs)
+
+def _gpu_try_optional_mac_builder(*args, **kwargs):
+    """Defines a GPU-related mac optional trybot."""
+    kwargs = _apply_gpu_optional_try_builder_defaults(kwargs)
+    kwargs = _apply_gpu_mac_builder_defaults(kwargs)
+    return try_.builder(*args, **kwargs)
+
+def _gpu_try_optional_win_builder(*args, **kwargs):
+    """Defines a GPU-related Windows optional trybot."""
+    kwargs = _apply_gpu_optional_try_builder_defaults(kwargs)
+    kwargs = _apply_gpu_win_builder_defaults(kwargs)
+    return try_.builder(*args, **kwargs)
 
 _common_location_filters = [
     # Inclusion filters.
@@ -131,8 +229,17 @@
         windows_builder = _gpu_ci_windows_builder,
     ),
     try_ = struct(
+        POOL = "luci.chromium.gpu.try",
         SERVICE_ACCOUNT = "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
+        linux_manual_builder = _gpu_try_manual_linux_builder,
+        linux_optional_builder = _gpu_try_optional_linux_builder,
+        linux_rate_limited_builder = _gpu_try_rate_limited_linux_builder,
+        mac_manual_builder = _gpu_try_manual_mac_builder,
+        mac_optional_builder = _gpu_try_optional_mac_builder,
+        mac_rate_limited_builder = _gpu_try_rate_limited_mac_builder,
         optional_trybot_location_filters = _optional_trybot_location_filters,
-        optional_tests_builder = _gpu_try_optional_tests_builder,
+        win_manual_builder = _gpu_try_manual_win_builder,
+        win_optional_builder = _gpu_try_optional_win_builder,
+        win_rate_limited_builder = _gpu_try_rate_limited_win_builder,
     ),
 )
diff --git a/infra/config/subprojects/chromium/ci/chromium.dawn.star b/infra/config/subprojects/chromium/ci/chromium.dawn.star
index 1d84798..2baa12a 100644
--- a/infra/config/subprojects/chromium/ci/chromium.dawn.star
+++ b/infra/config/subprojects/chromium/ci/chromium.dawn.star
@@ -1099,21 +1099,16 @@
         run_tests_serially = True,
     ),
     targets = targets.bundle(
-        # TODO(crbug.com/333424893): Enable tests.
         targets = [
-            "gpu_noop_sleep_telemetry_test",
+            "gpu_dawn_galaxy_s24_gtests",
+            "gpu_dawn_galaxy_s24_telemetry_tests",
         ],
         mixins = [
             "has_native_resultdb_integration",
             "gpu_samsung_s24_stable",
             "limited_capacity_bot",
+            "no_tombstones",
         ],
-        per_test_modifications = {
-            "noop_sleep_tests": targets.mixin(
-                # TODO(crbug.com/333424893): Enable tests.
-                experiment_percentage = 100,
-            ),
-        },
     ),
     targets_settings = targets.settings(
         browser_config = targets.browser_config.ANDROID_CHROMIUM,
@@ -1124,6 +1119,7 @@
         category = "ToT|Android",
         short_name = "s24",
     ),
+    execution_timeout = 6 * time.hour,
 )
 
 ci.thin_tester(
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
index 28c79db..e26ab5b 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -89,6 +89,8 @@
     kwargs.setdefault("execution_timeout", ci_constants.DEFAULT_EXECUTION_TIMEOUT)
     return gpu.ci.windows_builder(name = name, **kwargs)
 
+# TODO(crbug.com/536066698): Move this to gpu.star once multiple files are
+# using use_test_trigger_cas.
 def gpu_fyi_thin_tester_builder_spec(*args, **kwargs):
     return builder_config.builder_spec(
         execution_mode = builder_config.execution_mode.TEST,
@@ -2047,8 +2049,8 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_mac_release_gtests",
-            "gpu_fyi_only_mac_release_telemetry_tests",
+            "gpu_all_mac_release_gtests",
+            "gpu_all_mac_release_telemetry_tests",
         ],
         mixins = [
             "mac_arm64_apple_m1_gpu_stable",
@@ -2120,8 +2122,8 @@
         # Different targets than 'Mac FYI Retina Release (AMD)' since there is
         # no tester on chromium.gpu running a subset of tests.
         targets = [
-            "gpu_fyi_mac_release_gtests",
-            "gpu_fyi_only_mac_release_telemetry_tests",
+            "gpu_all_mac_release_gtests",
+            "gpu_all_mac_release_telemetry_tests",
         ],
         mixins = [
             "limited_capacity_bot",
@@ -2161,7 +2163,7 @@
     targets = targets.bundle(
         targets = [
             "gpu_fyi_mac_release_gtests",
-            "gpu_fyi_only_mac_release_telemetry_tests",
+            "gpu_fyi_mac_release_telemetry_tests",
         ],
         mixins = [
             "mac_arm64_apple_m2_retina_gpu_stable",
@@ -2198,8 +2200,8 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_mac_release_gtests",
-            "gpu_fyi_only_mac_release_telemetry_tests",
+            "gpu_all_mac_release_gtests",
+            "gpu_all_mac_release_telemetry_tests",
         ],
         mixins = [
             "mac_arm64_apple_m3_retina_gpu_stable",
@@ -2236,8 +2238,8 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_mac_release_gtests",
-            "gpu_fyi_only_mac_release_telemetry_tests",
+            "gpu_all_mac_release_gtests",
+            "gpu_all_mac_release_telemetry_tests",
         ],
         mixins = [
             "mac_arm64_apple_m2_retina_gpu_stable",
@@ -2304,8 +2306,8 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_mac_release_gtests",
-            "gpu_fyi_only_mac_release_telemetry_tests",
+            "gpu_all_mac_release_gtests",
+            "gpu_all_mac_release_telemetry_tests",
         ],
         mixins = [
             "mac_mini_intel_gpu_stable",
@@ -2422,8 +2424,8 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_mac_release_gtests",
-            "gpu_fyi_only_mac_release_telemetry_tests",
+            "gpu_all_mac_release_gtests",
+            "gpu_all_mac_release_telemetry_tests",
         ],
         mixins = [
             "mac_retina_amd_gpu_stable",
@@ -2505,6 +2507,7 @@
     targets = targets.bundle(
         targets = [
             "gpu_fyi_mac_debug_gtests",
+            "gpu_fyi_mac_debug_telemetry_tests",
         ],
         mixins = [
             "mac_retina_amd_gpu_stable",
@@ -2577,7 +2580,7 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_win_gtests",
+            "gpu_fyi_win_debug_gtests",
             "gpu_fyi_win_debug_telemetry_tests",
         ],
         mixins = [
@@ -2597,28 +2600,6 @@
                     "crbug.com/380431384 flaky crashes in random tests",
                 ],
             ),
-            "pixel_skia_gold_passthrough_test": targets.per_test_modification(
-                mixins = targets.mixin(
-                    args = [
-                        # TODO(crbug.com/382422293): Remove when fixed
-                        "--jobs=1",
-                    ],
-                ),
-                replacements = targets.replacements(
-                    args = {
-                        # Magic substitution happens after regular replacement, so remove it
-                        # now since we are manually applying the number of jobs above.
-                        targets.magic_args.GPU_PARALLEL_JOBS: None,
-                    },
-                ),
-            ),
-            "trace_test": targets.per_test_modification(
-                mixins = targets.mixin(
-                    swarming = targets.swarming(
-                        shards = 2,
-                    ),
-                ),
-            ),
         },
     ),
     targets_settings = targets.settings(
@@ -2846,11 +2827,11 @@
         # When the experimental driver is identical to the stable driver, this
         # should be running the gpu_noop_sleep_telemetry_test. Otherwise, it
         # should be running the same test_suites as
-        # 'Win10 FYI x64 Release (NVIDIA)'
+        # 'Win10 FYI x64 Release (NVIDIA)' and "Win10 x64 Release (NVIDIA)".
         targets = [
-            "gpu_fyi_win_gtests",
-            "gpu_fyi_win_release_telemetry_tests",
-            "gpu_fyi_win_optional_isolated_scripts",
+            "gpu_all_win_release_gtests",
+            "gpu_all_win_release_telemetry_tests",
+            "gpu_all_win_release_isolated_scripts",
         ],
         mixins = [
             "limited_capacity_bot",
@@ -2898,13 +2879,19 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_win_gtests",
-            "gpu_fyi_win_amd_release_telemetry_tests",
+            "gpu_all_win_release_gtests",
+            "gpu_all_win_release_telemetry_tests",
         ],
         mixins = [
             "win11_amd_rx_5500_xt_stable",
         ],
         per_test_modifications = {
+            "context_lost_passthrough_graphite_tests": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
+            "expected_color_pixel_passthrough_graphite_test": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
             "gl_unittests": targets.mixin(
                 args = [
                     "--test-launcher-filter-file=../../testing/buildbot/filters/win.amd.5500xt.gl_unittests.filter",
@@ -2916,6 +2903,15 @@
                     "gpu bots once the Windows OS supports HW secure decryption.",
                 ],
             ),
+            "pixel_skia_gold_passthrough_graphite_test": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
+            "screenshot_sync_passthrough_graphite_tests": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
+            "webgl_conformance_vulkan_passthrough_tests": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
         },
     ),
     targets_settings = targets.settings(
@@ -2948,13 +2944,25 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_win_gtests",
-            "gpu_fyi_win_intel_release_telemetry_tests",
+            "gpu_all_win_release_gtests",
+            "gpu_all_win_release_telemetry_tests",
         ],
         mixins = [
             "win10_intel_uhd_630_stable",
         ],
         per_test_modifications = {
+            "context_lost_passthrough_graphite_tests": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands if capacity permits and mark as CI-only due to future optional trybot mirroring.",
+            ),
+            "expected_color_pixel_passthrough_graphite_test": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands if capacity permits and mark as CI-only due to future optional trybot mirroring.",
+            ),
+            "pixel_skia_gold_passthrough_graphite_test": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands if capacity permits and mark as CI-only due to future optional trybot mirroring.",
+            ),
+            "screenshot_sync_passthrough_graphite_tests": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands if capacity permits and mark as CI-only due to future optional trybot mirroring.",
+            ),
             "xr_browser_tests": targets.mixin(
                 args = [
                     # TODO(crbug.com/40937024): Remove this once the flakes on Intel are
@@ -2994,18 +3002,30 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_win_gtests",
-            "gpu_fyi_win_intel_release_telemetry_tests",
+            "gpu_all_win_release_gtests",
+            "gpu_all_win_release_telemetry_tests",
         ],
         mixins = [
             "win10_intel_uhd_770_stable",
         ],
         per_test_modifications = {
+            "context_lost_passthrough_graphite_tests": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
+            "expected_color_pixel_passthrough_graphite_test": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
             "gl_tests_passthrough": targets.mixin(
                 args = [
                     "--test-launcher-filter-file=../../testing/buildbot/filters/win.uhd_770.gl_tests_passthrough.filter",
                 ],
             ),
+            "pixel_skia_gold_passthrough_graphite_test": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
+            "screenshot_sync_passthrough_graphite_tests": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
             "xr_browser_tests": targets.mixin(
                 args = [
                     # TODO(crbug.com/40937024): Remove this once the flakes on Intel are
@@ -3045,9 +3065,9 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_win_gtests",
+            "gpu_fyi_win_release_gtests",
             "gpu_fyi_win_release_telemetry_tests",
-            "gpu_fyi_win_optional_isolated_scripts",
+            "gpu_fyi_win_release_isolated_scripts",
         ],
         mixins = [
             "win10_nvidia_gtx_1660_stable",
@@ -3080,21 +3100,6 @@
                     },
                 ),
             ),
-            "pixel_skia_gold_passthrough_test": targets.per_test_modification(
-                mixins = targets.mixin(
-                    args = [
-                        # TODO(crbug.com/382422293): Remove when fixed
-                        "--jobs=1",
-                    ],
-                ),
-                replacements = targets.replacements(
-                    args = {
-                        # Magic substitution happens after regular replacement, so remove it
-                        # now since we are manually applying the number of jobs above.
-                        targets.magic_args.GPU_PARALLEL_JOBS: None,
-                    },
-                ),
-            ),
         },
     ),
     targets_settings = targets.settings(
@@ -3177,13 +3182,19 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_win_gtests",
-            "gpu_fyi_win_amd_release_telemetry_tests",
+            "gpu_all_win_release_gtests",
+            "gpu_all_win_release_telemetry_tests",
         ],
         mixins = [
             "win11_amd_rx_7600_stable",
         ],
         per_test_modifications = {
+            "context_lost_passthrough_graphite_tests": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
+            "expected_color_pixel_passthrough_graphite_test": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
             "gl_tests_passthrough": targets.mixin(
                 args = [
                     "--test-launcher-filter-file=../../testing/buildbot/filters/win.amd.7600.gl_tests_passthrough.filter",
@@ -3194,6 +3205,15 @@
                     "--test-launcher-filter-file=../../testing/buildbot/filters/win.amd.7600.gl_unittests.filter",
                 ],
             ),
+            "pixel_skia_gold_passthrough_graphite_test": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
+            "screenshot_sync_passthrough_graphite_tests": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
+            "webgl_conformance_vulkan_passthrough_tests": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
         },
     ),
     targets_settings = targets.settings(
@@ -3326,14 +3346,20 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_win_gtests",
-            "gpu_fyi_win_amd_release_telemetry_tests",
+            "gpu_all_win_release_gtests",
+            "gpu_all_win_release_telemetry_tests",
         ],
         mixins = [
             "very_limited_capacity_bot",
             "gpu_amd_rx_9070_xt_win_experimental",
         ],
         per_test_modifications = {
+            "context_lost_passthrough_graphite_tests": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
+            "expected_color_pixel_passthrough_graphite_test": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
             "gl_unittests": targets.mixin(
                 args = [
                     # Skip failing tests inline instead of using filter files
@@ -3342,6 +3368,15 @@
                     "--gtest_filter=-DCompPresenterPixelTest.YUY2SwapChain*",
                 ],
             ),
+            "pixel_skia_gold_passthrough_graphite_test": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
+            "screenshot_sync_passthrough_graphite_tests": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
+            "webgl_conformance_vulkan_passthrough_tests": targets.remove(
+                reason = "crbug.com/541312843 enable these tests after the test bundle standardization lands",
+            ),
         },
     ),
     targets_settings = targets.settings(
@@ -3417,9 +3452,9 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_win_gtests",
-            "gpu_fyi_win_release_telemetry_tests",
-            "gpu_fyi_win_optional_isolated_scripts",
+            "gpu_all_win_release_gtests",
+            "gpu_all_win_release_telemetry_tests",
+            "gpu_all_win_release_isolated_scripts",
         ],
         mixins = [
             "very_limited_capacity_bot",
@@ -3456,9 +3491,9 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_win_gtests",
-            "gpu_fyi_win_release_telemetry_tests",
-            "gpu_fyi_win_optional_isolated_scripts",
+            "gpu_all_win_release_gtests",
+            "gpu_all_win_release_telemetry_tests",
+            "gpu_all_win_release_isolated_scripts",
         ],
         mixins = [
             "win11_nvidia_rtx_4070_super_stable",
@@ -3531,9 +3566,9 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_win_gtests",
-            "gpu_fyi_win_optional_isolated_scripts",
-            "gpu_fyi_win_release_telemetry_tests",
+            "gpu_all_win_release_gtests",
+            "gpu_all_win_release_isolated_scripts",
+            "gpu_all_win_release_telemetry_tests",
         ],
         mixins = [
             "win10_nvidia_gtx_1660_stable",
@@ -3614,8 +3649,8 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_fyi_win_gtests",
-            "gpu_fyi_win_release_telemetry_tests",
+            "gpu_all_win_release_gtests",
+            "gpu_all_win_release_telemetry_tests",
         ],
         mixins = [
             "win11_qualcomm_snapdragon_x_elite_stable",
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.star b/infra/config/subprojects/chromium/ci/chromium.gpu.star
index f6899a5..ee8b137 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.star
@@ -510,8 +510,8 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_desktop_passthrough_gtests",
-            "gpu_common_metal_passthrough_graphite_telemetry_tests",
+            "gpu_mac_debug_gtests",
+            "gpu_mac_debug_telemetry_tests",
         ],
         mixins = [
             "mac_mini_intel_gpu_stable",
@@ -565,8 +565,8 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_desktop_passthrough_gtests",
-            "gpu_common_metal_passthrough_graphite_telemetry_tests",
+            "gpu_mac_release_gtests",
+            "gpu_mac_release_telemetry_tests",
         ],
         mixins = [
             "mac_mini_intel_gpu_stable",
@@ -605,8 +605,8 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_desktop_passthrough_gtests",
-            "gpu_common_metal_passthrough_graphite_telemetry_tests",
+            "gpu_mac_debug_gtests",
+            "gpu_mac_debug_telemetry_tests",
         ],
         mixins = [
             "mac_retina_amd_gpu_stable",
@@ -655,8 +655,8 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_desktop_passthrough_gtests",
-            "gpu_common_metal_passthrough_graphite_telemetry_tests",
+            "gpu_mac_release_gtests",
+            "gpu_mac_release_telemetry_tests",
         ],
         mixins = [
             "mac_retina_amd_gpu_stable",
@@ -697,8 +697,8 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_desktop_passthrough_gtests",
-            "gpu_common_metal_passthrough_graphite_telemetry_tests",
+            "gpu_mac_release_gtests",
+            "gpu_mac_release_telemetry_tests",
         ],
         mixins = [
             "mac_arm64_apple_m2_retina_gpu_stable",
@@ -737,8 +737,8 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_win_gtests",
-            "gpu_common_win_telemetry_tests",
+            "gpu_win_debug_gtests",
+            "gpu_win_debug_telemetry_tests",
         ],
         mixins = [
             "win10_nvidia_gtx_1660_stable",
@@ -804,8 +804,8 @@
     ),
     targets = targets.bundle(
         targets = [
-            "gpu_win_gtests",
-            "gpu_common_win_telemetry_tests",
+            "gpu_win_release_gtests",
+            "gpu_win_release_telemetry_tests",
         ],
         mixins = [
             "win10_nvidia_gtx_1660_stable",
diff --git a/infra/config/subprojects/chromium/gpu.try.star b/infra/config/subprojects/chromium/gpu.try.star
index a91462f..b7de958 100644
--- a/infra/config/subprojects/chromium/gpu.try.star
+++ b/infra/config/subprojects/chromium/gpu.try.star
@@ -2,18 +2,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-load("@chromium-luci//builders.star", "builders", "cpu", "os")
+load("@chromium-luci//builders.star", "cpu")
 load("@chromium-luci//gn_args.star", "gn_args")
 load("@chromium-luci//try.star", "try_")
+load("//lib/gpu.star", "gpu")
 load("//lib/siso.star", "siso")
 
 try_.defaults.set(
     bucket = "try",
     executable = "recipe:chromium_trybot",
-    pool = "luci.chromium.try",
-    cores = 8,
-    os = os.LINUX_DEFAULT,
-    cpu = cpu.X86_64,
     build_numbers = True,
     contact_team_email = "chrome-gpu-infra@google.com",
     cq_group = "cq",
@@ -22,7 +19,6 @@
         "chromium_tests.resultdb_module": 100,
     },
     expiration_timeout = 2 * time.hour,
-    service_account = "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
     siso_project = siso.project.DEFAULT_UNTRUSTED,
     subproject_list_view = "luci.chromium.try",
     task_template_canary_percentage = 5,
@@ -40,18 +36,10 @@
 # functions for specializing on OS: XXX_builder and XXX_YYY_builder where XXX is
 # the part after the last dot in the builder group and YYY is the OS
 
-def gpu_android_builder(*, name, **kwargs):
-    return try_.builder(
-        name = name,
-        pool = "luci.chromium.gpu.try",
-        builder_group = "tryserver.chromium.android",
-        builderless = True,
-        max_concurrent_builds = 1,
-        os = os.LINUX_DEFAULT,
-        siso_remote_jobs = siso.remote_jobs.LOW_JOBS_FOR_CQ,
-        ssd = None,
-        **kwargs
-    )
+def gpu_android_builder(*args, **kwargs):
+    kwargs.setdefault("builder_group", "tryserver.chromium.android")
+    kwargs.setdefault("siso_remote_jobs", siso.remote_jobs.LOW_JOBS_FOR_CQ)
+    return gpu.try_.linux_manual_builder(*args, **kwargs)
 
 gpu_android_builder(
     name = "gpu-fyi-try-android-q-pixel-2-32",
@@ -137,18 +125,10 @@
     gn_args = "ci/Android Release (Pixel 2)",
 )
 
-def gpu_chromeos_builder(*, name, **kwargs):
-    return try_.builder(
-        name = name,
-        pool = "luci.chromium.gpu.try",
-        builder_group = "tryserver.chromium.chromiumos",
-        builderless = True,
-        max_concurrent_builds = 1,
-        os = os.LINUX_DEFAULT,
-        siso_remote_jobs = siso.remote_jobs.LOW_JOBS_FOR_CQ,
-        ssd = None,
-        **kwargs
-    )
+def gpu_chromeos_builder(*args, **kwargs):
+    kwargs.setdefault("builder_group", "tryserver.chromium.chromiumos")
+    kwargs.setdefault("siso_remote_jobs", siso.remote_jobs.LOW_JOBS_FOR_CQ)
+    return gpu.try_.linux_manual_builder(*args, **kwargs)
 
 gpu_chromeos_builder(
     name = "gpu-fyi-try-chromeos-amd64-generic",
@@ -158,18 +138,10 @@
     gn_args = "ci/ChromeOS FYI Release (amd64-generic)",
 )
 
-def gpu_linux_builder(*, name, **kwargs):
-    return try_.builder(
-        name = name,
-        pool = "luci.chromium.gpu.try",
-        builder_group = "tryserver.chromium.linux",
-        builderless = True,
-        max_concurrent_builds = 1,
-        os = os.LINUX_DEFAULT,
-        siso_remote_jobs = siso.remote_jobs.LOW_JOBS_FOR_CQ,
-        ssd = None,
-        **kwargs
-    )
+def gpu_linux_builder(*args, **kwargs):
+    kwargs.setdefault("builder_group", "tryserver.chromium.linux")
+    kwargs.setdefault("siso_remote_jobs", siso.remote_jobs.LOW_JOBS_FOR_CQ)
+    return gpu.try_.linux_manual_builder(*args, **kwargs)
 
 gpu_linux_builder(
     name = "gpu-fyi-try-linux-wayland-amd-rel",
@@ -352,20 +324,10 @@
     ),
 )
 
-def gpu_mac_builder(*, name, **kwargs):
-    kwargs.setdefault("cpu", "arm64")
-    return try_.builder(
-        name = name,
-        builder_group = "tryserver.chromium.mac",
-        builderless = True,
-        cores = None,
-        os = os.MAC_ANY,
-        pool = "luci.chromium.gpu.try",
-        max_concurrent_builds = 1,
-        ssd = None,
-        siso_remote_jobs = siso.remote_jobs.LOW_JOBS_FOR_CQ,
-        **kwargs
-    )
+def gpu_mac_builder(*args, **kwargs):
+    kwargs.setdefault("builder_group", "tryserver.chromium.mac")
+    kwargs.setdefault("siso_remote_jobs", siso.remote_jobs.LOW_JOBS_FOR_CQ)
+    return gpu.try_.mac_manual_builder(*args, **kwargs)
 
 gpu_mac_builder(
     name = "gpu-fyi-try-mac-amd-retina-asan",
@@ -530,19 +492,10 @@
     gn_args = "ci/GPU Mac Builder (dbg)",
 )
 
-def gpu_win_builder(*, name, **kwargs):
-    return try_.builder(
-        name = name,
-        pool = "luci.chromium.gpu.try",
-        builder_group = "tryserver.chromium.win",
-        builderless = True,
-        max_concurrent_builds = 1,
-        os = os.WINDOWS_ANY,
-        siso_remote_jobs = siso.remote_jobs.LOW_JOBS_FOR_CQ,
-        ssd = builders.with_expiration(True, expiration = 5 * time.minute),
-        free_space = None,
-        **kwargs
-    )
+def gpu_win_builder(*args, **kwargs):
+    kwargs.setdefault("builder_group", "tryserver.chromium.win")
+    kwargs.setdefault("siso_remote_jobs", siso.remote_jobs.LOW_JOBS_FOR_CQ)
+    return gpu.try_.win_manual_builder(*args, **kwargs)
 
 gpu_win_builder(
     name = "gpu-fyi-try-win10-amd-rel-64",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
index 07ba3ca..651973d 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -1493,7 +1493,7 @@
     contact_team_email = "chrome-gpu-team@google.com",
 )
 
-gpu.try_.optional_tests_builder(
+gpu.try_.linux_optional_builder(
     name = "android_optional_gpu_tests_rel",
     branch_selector = branches.selector.ANDROID_BRANCHES,
     description_html = "Runs GPU tests on Pixel 4 devices. Only automatically added to CLs that touch GPU-related files.",
@@ -1584,10 +1584,6 @@
         os_type = targets.os_type.ANDROID,
         use_android_merge_script_by_default = False,
     ),
-    pool = "luci.chromium.gpu.try",
-    builderless = True,
-    ssd = None,
-    free_space = None,
     alerts_enabled = False,
     contact_team_email = "chrome-gpu-infra@google.com",
     cq_settings = try_.cq_settings(
@@ -1600,7 +1596,7 @@
     max_concurrent_builds = 10,
 )
 
-gpu.try_.optional_tests_builder(
+gpu.try_.linux_optional_builder(
     name = "gpu-fyi-cq-android-arm64",
     branch_selector = branches.selector.ANDROID_BRANCHES,
     description_html = "Runs GPU tests on Pixel 6/10 devices. Only automatically added to CLs that touch GPU-related files.",
@@ -1613,10 +1609,6 @@
         retry_failed_shards = False,
     ),
     gn_args = "ci/GPU FYI Android arm64 Builder",
-    pool = "luci.chromium.gpu.try",
-    builderless = True,
-    ssd = None,
-    free_space = None,
     # Exclude gpu fyi builders.
     alerts_enabled = False,
     contact_team_email = "chrome-gpu-infra@google.com",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.angle.star b/infra/config/subprojects/chromium/try/tryserver.chromium.angle.star
index 6175a3b..d57a6dd7 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.angle.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.angle.star
@@ -4,7 +4,6 @@
 """Definitions of builders in the tryserver.chromium.angle builder group."""
 
 load("@chromium-luci//builder_config.star", "builder_config")
-load("@chromium-luci//builders.star", "builders", "os")
 load("@chromium-luci//consoles.star", "consoles")
 load("@chromium-luci//gn_args.star", "gn_args")
 load("@chromium-luci//try.star", "try_")
@@ -15,11 +14,6 @@
 try_.defaults.set(
     executable = "recipe:angle_chromium_trybot",
     builder_group = "tryserver.chromium.angle",
-    pool = "luci.chromium.gpu.try",
-    builderless = True,
-    cores = 8,
-    os = os.LINUX_DEFAULT,
-    ssd = None,
     contact_team_email = "angle-team@google.com",
     execution_timeout = try_constants.DEFAULT_EXECUTION_TIMEOUT,
     experiments = {
@@ -36,16 +30,7 @@
     name = "tryserver.chromium.angle",
 )
 
-def base_angle_builder(*, name, **kwargs):
-    return try_.builder(
-        name = name,
-        # For some reason, this cannot be set via try_.defaults.set(), as it
-        # complains about it being set twice.
-        free_space = None,
-        **kwargs
-    )
-
-base_angle_builder(
+gpu.try_.linux_rate_limited_builder(
     name = "android-angle-chromium-try",
     description_html = "Builds and tests ANGLE on arm64 Android using ToT ANGLE and a known good Chromium revision.",
     mirrors = [
@@ -64,7 +49,7 @@
     contact_team_email = "angle-team@google.com",
 )
 
-base_angle_builder(
+gpu.try_.linux_rate_limited_builder(
     name = "fuchsia-angle-try",
     description_html = "Builds ANGLE on x64 Fuchsia using ToT ANGLE and a known good Chromium revision. Compile-only.",
     mirrors = [
@@ -82,7 +67,7 @@
     ),
 )
 
-base_angle_builder(
+gpu.try_.linux_rate_limited_builder(
     name = "linux-angle-chromium-try",
     description_html = "Builds and tests ANGLE on x64 Linux using ToT ANGLE and a known good Chromium revision.",
     mirrors = [
@@ -101,15 +86,7 @@
     ),
 )
 
-def angle_mac_builder(**kwargs):
-    return base_angle_builder(
-        cores = None,
-        os = os.MAC_ANY,
-        cpu = None,
-        **kwargs
-    )
-
-angle_mac_builder(
+gpu.try_.mac_rate_limited_builder(
     name = "mac-angle-chromium-try",
     description_html = "Builds and tests ANGLE on x64 Mac using ToT ANGLE and a known good Chromium revision.",
     mirrors = [
@@ -128,14 +105,7 @@
     ),
 )
 
-def angle_win_builder(**kwargs):
-    return base_angle_builder(
-        os = os.WINDOWS_ANY,
-        ssd = builders.with_expiration(True, expiration = 5 * time.minute),
-        **kwargs
-    )
-
-angle_win_builder(
+gpu.try_.win_rate_limited_builder(
     name = "win-angle-chromium-arm64-try",
     description_html = "Builds and tests ANGLE on arm64 Windows using ToT ANGLE and a known good Chromium revision.",
     mirrors = [
@@ -153,7 +123,7 @@
     ),
 )
 
-angle_win_builder(
+gpu.try_.win_rate_limited_builder(
     name = "win-angle-chromium-x64-try",
     description_html = "Builds and tests ANGLE on x64 Windows using ToT ANGLE and a known good Chromium revision.",
     mirrors = [
@@ -172,7 +142,7 @@
     ),
 )
 
-angle_win_builder(
+gpu.try_.win_rate_limited_builder(
     name = "win-angle-chromium-x86-try",
     description_html = "Builds and tests ANGLE on x86 Windows using ToT ANGLE and a known good Chromium revision.",
     mirrors = [
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
index dabb30b..8e53693 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
@@ -162,6 +162,9 @@
         ],
     ),
     contact_team_email = "chrome-fuzzing-core@google.com",
+    experiments = {
+        "luci.buildbucket.run_in_turboci": 100,
+    },
 )
 
 try_.builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star b/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
index 4912c8f..2833f4b 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
@@ -5,7 +5,6 @@
 
 load("@chromium-luci//branches.star", "branches")
 load("@chromium-luci//builder_config.star", "builder_config")
-load("@chromium-luci//builders.star", "builders", "cpu", "os")
 load("@chromium-luci//consoles.star", "consoles")
 load("@chromium-luci//try.star", "try_")
 load("//lib/gpu.star", "gpu")
@@ -15,9 +14,6 @@
 try_.defaults.set(
     executable = try_constants.DEFAULT_EXECUTABLE,
     builder_group = "tryserver.chromium.dawn",
-    pool = try_constants.DEFAULT_POOL,
-    builderless = False,
-    os = os.LINUX_DEFAULT,
     # These builders test GPU configurations. These configurations have very
     # limited hardware, due to the hardware needing specific GPUs. The pool of
     # machines to run builds for these builders is intentionally limited to
@@ -33,7 +29,6 @@
     experiments = {
         "chromium_tests.resultdb_module": 100,
     },
-    service_account = gpu.try_.SERVICE_ACCOUNT,
     siso_keep_going = siso.KEEP_GOING,
     siso_project = siso.project.DEFAULT_UNTRUSTED,
     siso_remote_jobs = siso.remote_jobs.LOW_JOBS_FOR_CQ,
@@ -47,49 +42,22 @@
     ],
 )
 
-def dawn_android_builder(*args, **kwargs):
-    # TODO(crbug.com/543082386): Update this to use shared code from gpu.star.
-    return dawn_linux_builder(*args, **kwargs)
+_dawn_location_filters = [
+    cq.location_filter(path_regexp = "content/test/gpu/.+"),
+    cq.location_filter(path_regexp = "gpu/.+"),
+    cq.location_filter(path_regexp = "testing/buildbot/chromium.dawn.json"),
+    cq.location_filter(path_regexp = "third_party/blink/renderer/modules/webgpu/.+"),
+    cq.location_filter(path_regexp = "third_party/blink/renderer/platform/graphics/gpu/.+"),
+    cq.location_filter(path_regexp = "third_party/blink/web_tests/external/wpt/webgpu/.+"),
+    cq.location_filter(path_regexp = "third_party/blink/web_tests/wpt_internal/webgpu/.+"),
+    cq.location_filter(path_regexp = "third_party/blink/web_tests/WebGPUExpectations"),
+    cq.location_filter(path_regexp = "third_party/dawn/.+"),
+    cq.location_filter(path_regexp = "third_party/webgpu-cts/.+"),
+    cq.location_filter(path_regexp = "tools/clang/scripts/update.py"),
+    cq.location_filter(path_regexp = "ui/gl/features.gni"),
+]
 
-def dawn_linux_builder(*args, **kwargs):
-    # TODO(crbug.com/543082386): Update this to use shared code from gpu.star.
-    kwargs.setdefault("builderless", True)
-    kwargs.setdefault("free_space", None)
-    kwargs.setdefault("os", os.LINUX_DEFAULT)
-    kwargs.setdefault("pool", "luci.chromium.gpu.try")
-    kwargs.setdefault("ssd", None)
-    return try_.builder(*args, **kwargs)
-
-def dawn_mac_builder(*, name, **kwargs):
-    kwargs.setdefault("cpu", None)
-    return try_.builder(
-        name = name,
-        builderless = True,
-        cores = None,
-        free_space = None,
-        os = os.MAC_ANY,
-        ssd = None,
-        pool = "luci.chromium.gpu.try",
-        max_concurrent_builds = 1,
-        **kwargs
-    )
-
-def dawn_win_builderless_builder(*, name, **kwargs):
-    kwargs.setdefault(
-        "ssd",
-        builders.with_expiration(True, expiration = 5 * time.minute),
-    )
-    kwargs.setdefault("max_concurrent_builds", 1)
-    kwargs.setdefault("free_space", None)
-    return try_.builder(
-        name = name,
-        builderless = True,
-        os = os.WINDOWS_ANY,
-        pool = "luci.chromium.gpu.try",
-        **kwargs
-    )
-
-dawn_linux_builder(
+gpu.try_.linux_rate_limited_builder(
     name = "dawn-chromium-presubmit",
     branch_selector = [
         branches.selector.ANDROID_BRANCHES,
@@ -112,7 +80,7 @@
     max_concurrent_builds = 3,
 )
 
-dawn_android_builder(
+gpu.try_.linux_optional_builder(
     name = "dawn-android-arm-deps-rel",
     mirrors = [
         "ci/Dawn Android arm DEPS Builder",
@@ -120,20 +88,7 @@
     ],
     gn_args = "ci/Dawn Android arm DEPS Builder",
     cq_settings = try_.cq_settings(
-        location_filters = [
-            cq.location_filter(path_regexp = "content/test/gpu/.+"),
-            cq.location_filter(path_regexp = "gpu/.+"),
-            cq.location_filter(path_regexp = "testing/buildbot/chromium.dawn.json"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/modules/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/platform/graphics/gpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/external/wpt/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/wpt_internal/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/WebGPUExpectations"),
-            cq.location_filter(path_regexp = "third_party/dawn/.+"),
-            cq.location_filter(path_regexp = "third_party/webgpu-cts/.+"),
-            cq.location_filter(path_regexp = "tools/clang/scripts/update.py"),
-            cq.location_filter(path_regexp = "ui/gl/features.gni"),
-        ],
+        location_filters = _dawn_location_filters,
     ),
     main_list_view = "try",
     max_concurrent_builds = 5,
@@ -142,7 +97,7 @@
     ),
 )
 
-dawn_android_builder(
+gpu.try_.linux_optional_builder(
     name = "dawn-android-arm64-deps-rel",
     mirrors = [
         "ci/Dawn Android arm64 DEPS Builder",
@@ -151,20 +106,7 @@
     ],
     gn_args = "ci/Dawn Android arm64 DEPS Builder",
     cq_settings = try_.cq_settings(
-        location_filters = [
-            cq.location_filter(path_regexp = "content/test/gpu/.+"),
-            cq.location_filter(path_regexp = "gpu/.+"),
-            cq.location_filter(path_regexp = "testing/buildbot/chromium.dawn.json"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/modules/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/platform/graphics/gpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/external/wpt/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/wpt_internal/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/WebGPUExpectations"),
-            cq.location_filter(path_regexp = "third_party/dawn/.+"),
-            cq.location_filter(path_regexp = "third_party/webgpu-cts/.+"),
-            cq.location_filter(path_regexp = "tools/clang/scripts/update.py"),
-            cq.location_filter(path_regexp = "ui/gl/features.gni"),
-        ],
+        location_filters = _dawn_location_filters,
     ),
     main_list_view = "try",
     max_concurrent_builds = 5,
@@ -173,7 +115,7 @@
     ),
 )
 
-dawn_linux_builder(
+gpu.try_.linux_optional_builder(
     name = "dawn-linux-x64-deps-rel",
     branch_selector = branches.selector.LINUX_BRANCHES,
     mirrors = [
@@ -183,20 +125,7 @@
     ],
     gn_args = "ci/Dawn Linux x64 DEPS Builder",
     cq_settings = try_.cq_settings(
-        location_filters = [
-            cq.location_filter(path_regexp = "content/test/gpu/.+"),
-            cq.location_filter(path_regexp = "gpu/.+"),
-            cq.location_filter(path_regexp = "testing/buildbot/chromium.dawn.json"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/modules/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/platform/graphics/gpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/external/wpt/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/wpt_internal/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/WebGPUExpectations"),
-            cq.location_filter(path_regexp = "third_party/dawn/.+"),
-            cq.location_filter(path_regexp = "third_party/webgpu-cts/.+"),
-            cq.location_filter(path_regexp = "tools/clang/scripts/update.py"),
-            cq.location_filter(path_regexp = "ui/gl/features.gni"),
-        ],
+        location_filters = _dawn_location_filters,
     ),
     main_list_view = "try",
     max_concurrent_builds = 5,
@@ -205,7 +134,7 @@
     ),
 )
 
-try_.builder(
+gpu.try_.mac_optional_builder(
     name = "dawn-mac-arm64-deps-rel",
     branch_selector = branches.selector.MAC_BRANCHES,
     description_html = "Runs Dawn tests on Apple silicon at Chromium's pinned Dawn revision",
@@ -214,26 +143,8 @@
         "ci/Dawn Mac arm64 DEPS Release (Apple M2)",
     ],
     gn_args = "ci/Dawn Mac arm64 DEPS Builder",
-    pool = "luci.chromium.gpu.try",
-    builderless = True,
-    os = os.MAC_ANY,
-    cpu = cpu.ARM64,
-    free_space = None,
     cq_settings = try_.cq_settings(
-        location_filters = [
-            cq.location_filter(path_regexp = "content/test/gpu/.+"),
-            cq.location_filter(path_regexp = "gpu/.+"),
-            cq.location_filter(path_regexp = "testing/buildbot/chromium.dawn.json"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/modules/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/platform/graphics/gpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/external/wpt/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/wpt_internal/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/WebGPUExpectations"),
-            cq.location_filter(path_regexp = "third_party/dawn/.+"),
-            cq.location_filter(path_regexp = "third_party/webgpu-cts/.+"),
-            cq.location_filter(path_regexp = "tools/clang/scripts/update.py"),
-            cq.location_filter(path_regexp = "ui/gl/features.gni"),
-        ],
+        location_filters = _dawn_location_filters,
     ),
     main_list_view = "try",
     max_concurrent_builds = 3,
@@ -242,7 +153,7 @@
     ),
 )
 
-try_.builder(
+gpu.try_.mac_optional_builder(
     name = "dawn-mac-x64-deps-rel",
     branch_selector = branches.selector.MAC_BRANCHES,
     mirrors = [
@@ -251,26 +162,8 @@
         "ci/Dawn Mac x64 DEPS Release (Intel)",
     ],
     gn_args = "ci/Dawn Mac x64 DEPS Builder",
-    pool = "luci.chromium.gpu.try",
-    builderless = True,
-    os = os.MAC_ANY,
-    cpu = None,
-    free_space = None,
     cq_settings = try_.cq_settings(
-        location_filters = [
-            cq.location_filter(path_regexp = "content/test/gpu/.+"),
-            cq.location_filter(path_regexp = "gpu/.+"),
-            cq.location_filter(path_regexp = "testing/buildbot/chromium.dawn.json"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/modules/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/platform/graphics/gpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/external/wpt/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/wpt_internal/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/WebGPUExpectations"),
-            cq.location_filter(path_regexp = "third_party/dawn/.+"),
-            cq.location_filter(path_regexp = "third_party/webgpu-cts/.+"),
-            cq.location_filter(path_regexp = "tools/clang/scripts/update.py"),
-            cq.location_filter(path_regexp = "ui/gl/features.gni"),
-        ],
+        location_filters = _dawn_location_filters,
     ),
     main_list_view = "try",
     max_concurrent_builds = 5,
@@ -279,7 +172,7 @@
     ),
 )
 
-dawn_win_builderless_builder(
+gpu.try_.win_optional_builder(
     name = "dawn-win10-x64-deps-rel",
     branch_selector = branches.selector.WINDOWS_BRANCHES,
     mirrors = [
@@ -289,20 +182,7 @@
     ],
     gn_args = "ci/Dawn Win10 x64 DEPS Builder",
     cq_settings = try_.cq_settings(
-        location_filters = [
-            cq.location_filter(path_regexp = "content/test/gpu/.+"),
-            cq.location_filter(path_regexp = "gpu/.+"),
-            cq.location_filter(path_regexp = "testing/buildbot/chromium.dawn.json"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/modules/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/platform/graphics/gpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/external/wpt/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/wpt_internal/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/WebGPUExpectations"),
-            cq.location_filter(path_regexp = "third_party/dawn/.+"),
-            cq.location_filter(path_regexp = "third_party/webgpu-cts/.+"),
-            cq.location_filter(path_regexp = "tools/clang/scripts/update.py"),
-            cq.location_filter(path_regexp = "ui/gl/features.gni"),
-        ],
+        location_filters = _dawn_location_filters,
     ),
     main_list_view = "try",
     max_concurrent_builds = 5,
@@ -311,7 +191,7 @@
     ),
 )
 
-dawn_win_builderless_builder(
+gpu.try_.win_optional_builder(
     name = "dawn-win10-x86-deps-rel",
     branch_selector = branches.selector.WINDOWS_BRANCHES,
     mirrors = [
@@ -323,20 +203,7 @@
     check_for_flakiness = False,
     check_for_flakiness_with_resultdb = False,
     cq_settings = try_.cq_settings(
-        location_filters = [
-            cq.location_filter(path_regexp = "content/test/gpu/.+"),
-            cq.location_filter(path_regexp = "gpu/.+"),
-            cq.location_filter(path_regexp = "testing/buildbot/chromium.dawn.json"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/modules/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/platform/graphics/gpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/external/wpt/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/wpt_internal/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/WebGPUExpectations"),
-            cq.location_filter(path_regexp = "third_party/dawn/.+"),
-            cq.location_filter(path_regexp = "third_party/webgpu-cts/.+"),
-            cq.location_filter(path_regexp = "tools/clang/scripts/update.py"),
-            cq.location_filter(path_regexp = "ui/gl/features.gni"),
-        ],
+        location_filters = _dawn_location_filters,
     ),
     main_list_view = "try",
     max_concurrent_builds = 5,
@@ -345,7 +212,7 @@
     ),
 )
 
-dawn_win_builderless_builder(
+gpu.try_.win_optional_builder(
     name = "dawn-win11-arm64-deps-rel",
     branch_selector = branches.selector.WINDOWS_BRANCHES,
     mirrors = [
@@ -354,20 +221,7 @@
     ],
     gn_args = "ci/Dawn Win11 arm64 DEPS Builder",
     cq_settings = try_.cq_settings(
-        location_filters = [
-            cq.location_filter(path_regexp = "content/test/gpu/.+"),
-            cq.location_filter(path_regexp = "gpu/.+"),
-            cq.location_filter(path_regexp = "testing/buildbot/chromium.dawn.json"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/modules/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/renderer/platform/graphics/gpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/external/wpt/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/wpt_internal/webgpu/.+"),
-            cq.location_filter(path_regexp = "third_party/blink/web_tests/WebGPUExpectations"),
-            cq.location_filter(path_regexp = "third_party/dawn/.+"),
-            cq.location_filter(path_regexp = "third_party/webgpu-cts/.+"),
-            cq.location_filter(path_regexp = "tools/clang/scripts/update.py"),
-            cq.location_filter(path_regexp = "ui/gl/features.gni"),
-        ],
+        location_filters = _dawn_location_filters,
     ),
     main_list_view = "try",
     max_concurrent_builds = 5,
@@ -376,7 +230,7 @@
     ),
 )
 
-dawn_android_builder(
+gpu.try_.linux_rate_limited_builder(
     name = "android-dawn-arm-rel",
     mirrors = [
         "ci/Dawn Android arm Builder",
@@ -392,7 +246,7 @@
     ),
 )
 
-dawn_android_builder(
+gpu.try_.linux_rate_limited_builder(
     name = "android-dawn-arm64-rel",
     mirrors = [
         "ci/Dawn Android arm64 Builder",
@@ -409,7 +263,7 @@
     ),
 )
 
-dawn_android_builder(
+gpu.try_.linux_manual_builder(
     name = "android-dawn-arm64-exp-rel",
     description_html = "Runs ToT Dawn tests on experimental Pixel 6 configs",
     mirrors = [
@@ -417,13 +271,12 @@
         "ci/Dawn Android arm64 Experimental Release (Pixel 6)",
     ],
     gn_args = "ci/Dawn Android arm64 Builder",
-    max_concurrent_builds = 1,
     test_presentation = resultdb.test_presentation(
         grouping_keys = ["status", "v.test_suite", "v.gpu"],
     ),
 )
 
-dawn_android_builder(
+gpu.try_.linux_manual_builder(
     name = "android-dawn-arm64-p10-rel",
     description_html = "Runs ToT Dawn tests on Pixel 10 devices",
     mirrors = [
@@ -431,15 +284,12 @@
         "ci/Dawn Android arm64 Release (Pixel 10)",
     ],
     gn_args = "ci/Dawn Android arm64 Builder",
-    max_concurrent_builds = 1,
     test_presentation = resultdb.test_presentation(
         grouping_keys = ["status", "v.test_suite", "v.gpu"],
     ),
 )
 
-dawn_android_builder(
-    # This is not part of "android-dawn-arm64-rel" at the moment since there is
-    # not sufficient S24 capacity for that.
+gpu.try_.linux_manual_builder(
     name = "android-dawn-arm64-s24-rel",
     description_html = "Runs ToT Dawn tests on Samsung S24 devices",
     mirrors = [
@@ -447,13 +297,13 @@
         "ci/Dawn Android arm64 Release (Samsung S24)",
     ],
     gn_args = "ci/Dawn Android arm64 Builder",
-    max_concurrent_builds = 1,
+    execution_timeout = 6 * time.hour,
     test_presentation = resultdb.test_presentation(
         grouping_keys = ["status", "v.test_suite", "v.gpu"],
     ),
 )
 
-dawn_linux_builder(
+gpu.try_.linux_manual_builder(
     name = "linux-dawn-intel-exp-rel",
     description_html = "Runs ToT Dawn tests on experimental Linux/Intel configs",
     mirrors = [
@@ -461,13 +311,12 @@
         "ci/Dawn Linux x64 Experimental Release (Intel UHD 630)",
     ],
     gn_args = "ci/Dawn Linux x64 Builder",
-    max_concurrent_builds = 1,
     test_presentation = resultdb.test_presentation(
         grouping_keys = ["status", "v.test_suite", "v.gpu"],
     ),
 )
 
-dawn_linux_builder(
+gpu.try_.linux_manual_builder(
     name = "linux-dawn-nvidia-1660-exp-rel",
     description_html = "Runs ToT Dawn tests on experimental Linux/GTX 1660 configs",
     mirrors = [
@@ -475,13 +324,12 @@
         "ci/Dawn Linux x64 Experimental Release (NVIDIA GTX 1660)",
     ],
     gn_args = "ci/Dawn Linux x64 Builder",
-    max_concurrent_builds = 1,
     test_presentation = resultdb.test_presentation(
         grouping_keys = ["status", "v.test_suite", "v.gpu"],
     ),
 )
 
-dawn_linux_builder(
+gpu.try_.linux_rate_limited_builder(
     name = "linux-dawn-rel",
     mirrors = [
         "ci/Dawn Linux x64 Builder",
@@ -498,7 +346,7 @@
     ),
 )
 
-try_.builder(
+gpu.try_.mac_rate_limited_builder(
     name = "mac-arm64-dawn-rel",
     description_html = "Runs Dawn tests on Apple silicon on Dawn ToT",
     mirrors = [
@@ -506,18 +354,13 @@
         "ci/Dawn Mac arm64 Release (Apple M2)",
     ],
     gn_args = "ci/Dawn Mac arm64 Builder",
-    pool = "luci.chromium.gpu.try",
-    builderless = True,
-    os = os.MAC_ANY,
-    cpu = None,
-    free_space = None,
     max_concurrent_builds = 5,
     test_presentation = resultdb.test_presentation(
         grouping_keys = ["status", "v.test_suite", "v.gpu"],
     ),
 )
 
-try_.builder(
+gpu.try_.mac_rate_limited_builder(
     name = "mac-dawn-rel",
     mirrors = [
         "ci/Dawn Mac x64 Builder",
@@ -525,11 +368,6 @@
         "ci/Dawn Mac x64 Release (Intel)",
     ],
     gn_args = "ci/Dawn Mac x64 Builder",
-    pool = "luci.chromium.gpu.try",
-    builderless = True,
-    os = os.MAC_ANY,
-    cpu = cpu.ARM64,
-    free_space = None,
     # Occasionally receives bursty CQ traffic from the Dawn repo, so increase
     # the expiration/pending timeout to more gracefully handle that.
     expiration_timeout = 4 * time.hour,
@@ -539,7 +377,7 @@
     ),
 )
 
-dawn_linux_builder(
+gpu.try_.linux_manual_builder(
     name = "dawn-try-linux-x64-intel-uhd770-rel",
     description_html = "Runs ToT Dawn tests on 12th gen Intel CPUs with UHD 770 GPUs",
     mirrors = [
@@ -547,13 +385,12 @@
         "ci/Dawn Linux x64 Release (Intel UHD 770)",
     ],
     gn_args = "ci/Dawn Linux x64 Builder",
-    max_concurrent_builds = 1,
     test_presentation = resultdb.test_presentation(
         grouping_keys = ["status", "v.test_suite", "v.gpu"],
     ),
 )
 
-dawn_mac_builder(
+gpu.try_.mac_manual_builder(
     name = "dawn-try-mac-amd-555x-rel",
     mirrors = [
         "ci/Dawn Mac x64 Builder",
@@ -565,7 +402,7 @@
     ),
 )
 
-dawn_mac_builder(
+gpu.try_.mac_manual_builder(
     name = "dawn-try-mac-amd-exp",
     mirrors = [
         "ci/Dawn Mac x64 Builder",
@@ -577,7 +414,7 @@
     ),
 )
 
-dawn_mac_builder(
+gpu.try_.mac_manual_builder(
     name = "dawn-try-mac-arm64-deps-rel",
     mirrors = [
         "ci/Dawn Mac arm64 DEPS Builder",
@@ -589,7 +426,7 @@
     ),
 )
 
-dawn_mac_builder(
+gpu.try_.mac_manual_builder(
     name = "dawn-try-mac-arm64-m2-exp",
     description_html = "Manual-only trybot for running ToT Dawn tests on experimental M2 machines",
     mirrors = [
@@ -602,7 +439,7 @@
     ),
 )
 
-dawn_mac_builder(
+gpu.try_.mac_manual_builder(
     name = "dawn-try-mac-arm64-rel",
     mirrors = [
         "ci/Dawn Mac arm64 Builder",
@@ -614,7 +451,7 @@
     ),
 )
 
-dawn_mac_builder(
+gpu.try_.mac_manual_builder(
     name = "dawn-try-mac-intel-exp",
     mirrors = [
         "ci/Dawn Mac x64 Builder",
@@ -628,7 +465,7 @@
 
 # This will be moved into dawn-win11-arm64-deps-rel once the tests have been
 # confirmed to be stable enough.
-dawn_win_builderless_builder(
+gpu.try_.win_manual_builder(
     name = "dawn-try-win11-arm64-snapdragon-x-elite-deps-rel",
     mirrors = [
         "ci/Dawn Win11 arm64 DEPS Builder",
@@ -642,7 +479,7 @@
 
 # This will be moved into win11-arm64-dawn-rel once the tests have been
 # confirmed to be stable enough.
-dawn_win_builderless_builder(
+gpu.try_.win_manual_builder(
     name = "dawn-try-win11-arm64-snapdragon-x-elite-rel",
     mirrors = [
         "ci/Dawn Win11 arm64 Builder",
@@ -654,7 +491,7 @@
     ),
 )
 
-dawn_win_builderless_builder(
+gpu.try_.win_manual_builder(
     name = "dawn-try-win-x64-intel-exp",
     mirrors = [
         "ci/Dawn Win10 x64 Builder",
@@ -666,7 +503,7 @@
     ),
 )
 
-dawn_win_builderless_builder(
+gpu.try_.win_manual_builder(
     name = "dawn-try-win-x64-intel-uhd770-rel",
     description_html = "Runs ToT Dawn tests on 12th gen Intel CPUs with UHD 770 GPUs",
     mirrors = [
@@ -683,7 +520,7 @@
     ),
 )
 
-dawn_win_builderless_builder(
+gpu.try_.win_manual_builder(
     name = "dawn-try-win-x64-nvidia-exp",
     description_html = "Runs ToT Dawn tests on experimental NVIDIA configs",
     mirrors = [
@@ -696,7 +533,7 @@
     ),
 )
 
-dawn_win_builderless_builder(
+gpu.try_.win_manual_builder(
     name = "dawn-try-win-x86-intel-exp",
     mirrors = [
         "ci/Dawn Win10 x86 Builder",
@@ -708,7 +545,7 @@
     ),
 )
 
-dawn_win_builderless_builder(
+gpu.try_.win_manual_builder(
     name = "dawn-try-win-x86-nvidia-exp",
     description_html = "Runs ToT Dawn tests on experimental Win/NVIDIA/x86 configs",
     mirrors = [
@@ -721,7 +558,7 @@
     ),
 )
 
-dawn_win_builderless_builder(
+gpu.try_.win_rate_limited_builder(
     name = "win-dawn-rel",
     mirrors = [
         "ci/Dawn Win10 x64 Builder",
@@ -738,7 +575,7 @@
     ),
 )
 
-dawn_win_builderless_builder(
+gpu.try_.win_rate_limited_builder(
     name = "win11-arm64-dawn-rel",
     mirrors = [
         "ci/Dawn Win11 arm64 Builder",
@@ -754,7 +591,7 @@
     ),
 )
 
-dawn_win_builderless_builder(
+gpu.try_.win_manual_builder(
     name = "dawn-try-win10-x86-rel",
     mirrors = [
         "ci/Dawn Win10 x86 Builder",
@@ -767,7 +604,7 @@
     ),
 )
 
-dawn_win_builderless_builder(
+gpu.try_.win_manual_builder(
     name = "dawn-try-win10-x64-intel-asan",
     mirrors = [
         "ci/Dawn Win10 x64 ASAN Builder",
@@ -779,7 +616,7 @@
     ),
 )
 
-dawn_win_builderless_builder(
+gpu.try_.win_manual_builder(
     name = "dawn-try-win10-x64-nvidia-asan",
     mirrors = [
         "ci/Dawn Win10 x64 ASAN Builder",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star b/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star
index 1b00a6d..3e34685 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star
@@ -142,6 +142,9 @@
         ],
     ),
     free_space = builders.free_space.high,
+    experiments = {
+        "luci.buildbucket.run_in_turboci": 100,
+    },
 )
 
 try_.builder(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
index 8d52f2e..99e902e7 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
@@ -74,6 +74,9 @@
     cq_settings = try_.cq_settings(
         on_default_cq = True,
     ),
+    experiments = {
+        "luci.buildbucket.run_in_turboci": 2,
+    },
     properties = {
         "$build/binary_size": {
             "analyze_targets": [
@@ -1120,7 +1123,7 @@
     contact_team_email = "chrome-gpu-team@google.com",
 )
 
-gpu.try_.optional_tests_builder(
+gpu.try_.linux_optional_builder(
     name = "linux_optional_gpu_tests_rel",
     branch_selector = branches.selector.LINUX_BRANCHES,
     description_html = ("Runs GPU tests on Linux machines with NVIDIA GTX 1660 and Intel UHD 630 GPUs. " +
@@ -1138,10 +1141,6 @@
         browser_config = targets.browser_config.RELEASE,
         os_type = targets.os_type.LINUX,
     ),
-    pool = "luci.chromium.gpu.try",
-    builderless = True,
-    ssd = None,
-    free_space = None,
     alerts_enabled = False,
     contact_team_email = "chrome-gpu-infra@google.com",
     cq_settings = try_.cq_settings(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
index 40b490f5..cb63048b 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -931,7 +931,7 @@
     execution_timeout = 10 * time.hour,
 )
 
-gpu.try_.optional_tests_builder(
+gpu.try_.mac_optional_builder(
     name = "mac_optional_gpu_tests_rel",
     branch_selector = branches.selector.MAC_BRANCHES,
     description_html = ("Runs GPU tests on Mac Minis with Intel UHD 630 GPUs and Macbook Pros with AMD GPUs. " +
@@ -972,11 +972,6 @@
         browser_config = targets.browser_config.RELEASE,
         os_type = targets.os_type.MAC,
     ),
-    pool = "luci.chromium.gpu.try",
-    builderless = True,
-    cpu = "arm64",
-    ssd = None,
-    free_space = None,
     alerts_enabled = False,
     contact_team_email = "chrome-gpu-infra@google.com",
     cq_settings = try_.cq_settings(
@@ -989,7 +984,7 @@
     max_concurrent_builds = 7,
 )
 
-gpu.try_.optional_tests_builder(
+gpu.try_.mac_optional_builder(
     name = "gpu-fyi-cq-mac-arm64",
     branch_selector = branches.selector.MAC_BRANCHES,
     description_html = ("Runs GPU tests on M2 Macbook Pros. Only automatically added to CLs that " +
@@ -1002,11 +997,6 @@
         retry_failed_shards = False,
     ),
     gn_args = "ci/GPU FYI Mac arm64 Builder",
-    pool = "luci.chromium.gpu.try",
-    builderless = True,
-    cpu = "arm64",
-    ssd = None,
-    free_space = None,
     alerts_enabled = False,
     contact_team_email = "chrome-gpu-infra@google.com",
     cq_settings = try_.cq_settings(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.win.star b/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
index 3de9fd5..39d2272 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
@@ -5,7 +5,7 @@
 
 load("@chromium-luci//branches.star", "branches")
 load("@chromium-luci//builder_config.star", "builder_config")
-load("@chromium-luci//builders.star", "builders", "os")
+load("@chromium-luci//builders.star", "os")
 load("@chromium-luci//consoles.star", "consoles")
 load("@chromium-luci//gn_args.star", "gn_args")
 load("@chromium-luci//targets.star", "targets")
@@ -534,7 +534,7 @@
     contact_team_email = "chrome-webium-product-eng@google.com",
 )
 
-gpu.try_.optional_tests_builder(
+gpu.try_.win_optional_builder(
     name = "win_optional_gpu_tests_rel",
     branch_selector = branches.selector.WINDOWS_BRANCHES,
     description_html = ("Runs GPU tests on Windows 10 machines with NVIDIA GTX 1660 and Intel UHD 630 GPUs. " +
@@ -611,17 +611,12 @@
         browser_config = targets.browser_config.RELEASE_X64,
         os_type = targets.os_type.WINDOWS,
     ),
-    pool = "luci.chromium.gpu.try",
-    builderless = True,
-    os = os.WINDOWS_DEFAULT,
-    ssd = builders.with_expiration(True, expiration = 5 * time.minute),
-    free_space = None,
     alerts_enabled = False,
     contact_team_email = "chrome-gpu-infra@google.com",
     cq_settings = try_.cq_settings(
         location_filters = gpu.try_.optional_trybot_location_filters.WINDOWS,
     ),
-    # default is 6 in _gpu_optional_tests_builder()
+    # default is 6 for GPU optional builders.
     execution_timeout = 5 * time.hour,
     main_list_view = "try",
     # This is higher than the default of 7 for optional GPU builders
@@ -633,7 +628,7 @@
     siso_remote_jobs = siso.remote_jobs.HIGH_JOBS_FOR_CQ,
 )
 
-gpu.try_.optional_tests_builder(
+gpu.try_.win_optional_builder(
     name = "gpu-fyi-cq-win-arm64",
     branch_selector = branches.selector.WINDOWS_BRANCHES,
     description_html = "Runs GPU tests on Windows/ARM64 configs. Only automatically added to CLs that touch GPU-related files.",
@@ -645,17 +640,12 @@
         retry_failed_shards = False,
     ),
     gn_args = "ci/GPU FYI Win arm64 Builder",
-    pool = "luci.chromium.gpu.try",
-    builderless = True,
-    os = os.WINDOWS_DEFAULT,
-    ssd = builders.with_expiration(True, expiration = 5 * time.minute),
-    free_space = None,
     alerts_enabled = False,
     contact_team_email = "chrome-gpu-infra@google.com",
     cq_settings = try_.cq_settings(
         location_filters = gpu.try_.optional_trybot_location_filters.WINDOWS,
     ),
-    # default is 6 in _gpu_optional_tests_builder()
+    # default is 6 for GPU optional builders.
     execution_timeout = 5 * time.hour,
     main_list_view = "try",
     # This is higher than the default of 7 for optional GPU builders
diff --git a/infra/config/targets/bundles.star b/infra/config/targets/bundles.star
index cdb8757..94a6a6b 100644
--- a/infra/config/targets/bundles.star
+++ b/infra/config/targets/bundles.star
@@ -3133,6 +3133,78 @@
 )
 
 targets.bundle(
+    name = "gpu_all_mac_debug_gtests",
+    targets = [
+        "gpu_mac_debug_gtests",
+        "gpu_fyi_mac_debug_gtests",
+    ],
+)
+
+targets.bundle(
+    name = "gpu_all_mac_debug_telemetry_tests",
+    targets = [
+        "gpu_mac_debug_telemetry_tests",
+        "gpu_fyi_mac_debug_telemetry_tests",
+    ],
+)
+
+targets.bundle(
+    name = "gpu_all_mac_release_gtests",
+    targets = [
+        "gpu_mac_release_gtests",
+        "gpu_fyi_mac_release_gtests",
+    ],
+)
+
+targets.bundle(
+    name = "gpu_all_mac_release_telemetry_tests",
+    targets = [
+        "gpu_mac_release_telemetry_tests",
+        "gpu_fyi_mac_release_telemetry_tests",
+    ],
+)
+
+targets.bundle(
+    name = "gpu_all_win_debug_gtests",
+    targets = [
+        "gpu_win_debug_gtests",
+        "gpu_fyi_win_debug_gtests",
+    ],
+)
+
+targets.bundle(
+    name = "gpu_all_win_debug_telemetry_tests",
+    targets = [
+        "gpu_win_debug_telemetry_tests",
+        "gpu_fyi_win_debug_telemetry_tests",
+    ],
+)
+
+targets.bundle(
+    name = "gpu_all_win_release_gtests",
+    targets = [
+        "gpu_win_release_gtests",
+        "gpu_fyi_win_release_gtests",
+    ],
+)
+
+targets.bundle(
+    name = "gpu_all_win_release_isolated_scripts",
+    targets = [
+        "gpu_win_release_isolated_scripts",
+        "gpu_fyi_win_release_isolated_scripts",
+    ],
+)
+
+targets.bundle(
+    name = "gpu_all_win_release_telemetry_tests",
+    targets = [
+        "gpu_win_release_telemetry_tests",
+        "gpu_fyi_win_release_telemetry_tests",
+    ],
+)
+
+targets.bundle(
     name = "gpu_angle_fuchsia_unittests_isolated_scripts",
     targets = [
         "angle_unittests",
@@ -3395,15 +3467,6 @@
 )
 
 targets.bundle(
-    name = "gpu_common_win_telemetry_tests",
-    targets = [
-        "gpu_common_and_optional_telemetry_tests",
-        "gpu_passthrough_telemetry_tests",
-        "gpu_webgl_conformance_d3d11_passthrough_telemetry_tests",
-    ],
-)
-
-targets.bundle(
     name = "gpu_dawn_android_compat_telemetry_tests",
     targets = [
         "gpu_dawn_webgpu_compat_cts",
@@ -3468,6 +3531,39 @@
     ],
 )
 
+targets.bundle(
+    name = "gpu_dawn_galaxy_s24_gtests",
+    targets = [
+        "dawn_end2end_tests",
+    ],
+    per_test_modifications = {
+        "dawn_end2end_tests": targets.mixin(
+            android_swarming = targets.swarming(
+                shards = 2,
+            ),
+        ),
+    },
+)
+
+targets.bundle(
+    name = "gpu_dawn_galaxy_s24_telemetry_tests",
+    targets = [
+        "webgpu_cts_tests",
+    ],
+    per_test_modifications = {
+        "webgpu_cts_tests": [
+            targets.mixin(
+                android_swarming = targets.swarming(
+                    shards = 36,
+                ),
+            ),
+            "gpu_integration_test_common_args",
+            "webgpu_telemetry_cts",
+            "linux_vulkan",
+        ],
+    },
+)
+
 # GPU gtests that test only Dawn
 targets.bundle(
     name = "gpu_dawn_gtests",
@@ -4192,6 +4288,13 @@
 )
 
 targets.bundle(
+    name = "gpu_fyi_mac_debug_telemetry_tests",
+    targets = [
+        # No FYI-only Mac/Debug Telemetry tests at this time.
+    ],
+)
+
+targets.bundle(
     name = "gpu_fyi_mac_release_gtests",
     targets = [
         "gpu_angle_unit_gtests",
@@ -4255,29 +4358,21 @@
 )
 
 targets.bundle(
-    name = "gpu_fyi_win_amd_release_telemetry_tests",
+    name = "gpu_fyi_win_debug_gtests",
     targets = [
-        "gpu_common_and_optional_telemetry_tests",
-        "gpu_passthrough_telemetry_tests",
-        "gpu_webcodecs_telemetry_test",
-        "gpu_webrtc_telemetry_test",
-        "gpu_webgl2_conformance_d3d11_passthrough_telemetry_tests",
-        "gpu_webgl_conformance_d3d11_passthrough_telemetry_tests",
+        "gpu_fyi_win_release_gtests",
     ],
 )
 
 targets.bundle(
     name = "gpu_fyi_win_debug_telemetry_tests",
     targets = [
-        "gpu_common_and_optional_telemetry_tests",
-        "gpu_passthrough_telemetry_tests",
-        "gpu_webgl_conformance_d3d11_passthrough_telemetry_tests",
         "gpu_webgl_conformance_vulkan_passthrough_telemetry_tests",
     ],
 )
 
 targets.bundle(
-    name = "gpu_fyi_win_gtests",
+    name = "gpu_fyi_win_release_gtests",
     targets = [
         "gpu_angle_unit_gtests",
         "gpu_common_gtests_passthrough",
@@ -4289,22 +4384,20 @@
 )
 
 targets.bundle(
-    name = "gpu_fyi_win_intel_release_telemetry_tests",
+    name = "gpu_fyi_win_release_isolated_scripts",
     targets = [
-        "gpu_common_and_optional_telemetry_tests",
-        "gpu_passthrough_telemetry_tests",
-        "gpu_webcodecs_telemetry_test",
-        "gpu_webrtc_telemetry_test",
-        "gpu_webgl2_conformance_d3d11_passthrough_telemetry_tests",
-        "gpu_webgl_conformance_d3d11_passthrough_telemetry_tests",
-        "gpu_webgl_conformance_vulkan_passthrough_telemetry_tests",
+        "gpu_command_buffer_perf_passthrough_isolated_scripts",
     ],
 )
 
 targets.bundle(
-    name = "gpu_fyi_win_optional_isolated_scripts",
+    name = "gpu_fyi_win_release_telemetry_tests",
     targets = [
-        "gpu_command_buffer_perf_passthrough_isolated_scripts",
+        "gpu_passthrough_graphite_telemetry_tests",
+        "gpu_webcodecs_telemetry_test",
+        "gpu_webrtc_telemetry_test",
+        "gpu_webgl2_conformance_d3d11_passthrough_telemetry_tests",
+        "gpu_webgl_conformance_vulkan_passthrough_telemetry_tests",
     ],
 )
 
@@ -4330,8 +4423,7 @@
 targets.bundle(
     name = "gpu_linux_release_gtests",
     targets = [
-        "gpu_angle_unit_gtests",
-        "gpu_common_gtests_passthrough",
+        "gpu_desktop_passthrough_gtests",
     ],
 )
 
@@ -4344,6 +4436,36 @@
     ],
 )
 
+targets.bundle(
+    name = "gpu_mac_debug_gtests",
+    targets = [
+        "gpu_mac_release_gtests",
+    ],
+)
+
+targets.bundle(
+    name = "gpu_mac_debug_telemetry_tests",
+    targets = [
+        "gpu_mac_release_telemetry_tests",
+    ],
+)
+
+targets.bundle(
+    name = "gpu_mac_release_gtests",
+    targets = [
+        "gpu_desktop_passthrough_gtests",
+    ],
+)
+
+targets.bundle(
+    name = "gpu_mac_release_telemetry_tests",
+    targets = [
+        "gpu_common_and_optional_telemetry_tests",
+        "gpu_metal_passthrough_graphite_telemetry_tests",
+        "gpu_webgl_conformance_metal_passthrough_graphite_telemetry_tests",
+    ],
+)
+
 # Copy of gpu_passthrough_telemetry_tests that forces running with Skia/Ganesh.
 targets.bundle(
     name = "gpu_passthrough_ganesh_telemetry_tests",
@@ -4622,7 +4744,21 @@
 )
 
 targets.bundle(
-    name = "gpu_win_gtests",
+    name = "gpu_win_debug_gtests",
+    targets = [
+        "gpu_win_release_gtests",
+    ],
+)
+
+targets.bundle(
+    name = "gpu_win_debug_telemetry_tests",
+    targets = [
+        "gpu_win_release_telemetry_tests",
+    ],
+)
+
+targets.bundle(
+    name = "gpu_win_release_gtests",
     targets = [
         "gpu_angle_unit_gtests",
         "gpu_common_gtests_passthrough",
@@ -4630,6 +4766,22 @@
     ],
 )
 
+targets.bundle(
+    name = "gpu_win_release_isolated_scripts",
+    targets = [
+        # No non-FYI isolated script tests at the moment.
+    ],
+)
+
+targets.bundle(
+    name = "gpu_win_release_telemetry_tests",
+    targets = [
+        "gpu_common_and_optional_telemetry_tests",
+        "gpu_passthrough_telemetry_tests",
+        "gpu_webgl_conformance_d3d11_passthrough_telemetry_tests",
+    ],
+)
+
 # Use this for targets for which we only need the bare minimum coverage,
 # roughly one build config per platform.
 targets.bundle(
diff --git a/infra/config/targets/compound_suites.star b/infra/config/targets/compound_suites.star
index 5483319..b228b2e 100644
--- a/infra/config/targets/compound_suites.star
+++ b/infra/config/targets/compound_suites.star
@@ -74,8 +74,10 @@
     ],
 )
 
+# This is functionally equivalent to the gpu_all_win_release_telemetry_tests
+# bundle, but is defined separately since V8 needs to use legacy suites for now.
 targets.legacy_compound_suite(
-    name = "gpu_fyi_win_release_telemetry_tests",
+    name = "gpu_v8_win_release_telemetry_tests",
     basic_suites = [
         "gpu_common_and_optional_telemetry_tests",
         "gpu_passthrough_graphite_telemetry_tests",
diff --git a/infra/config/targets/mixins.star b/infra/config/targets/mixins.star
index 7e8b7b6..a396d37 100644
--- a/infra/config/targets/mixins.star
+++ b/infra/config/targets/mixins.star
@@ -781,7 +781,7 @@
     swarming = targets.swarming(
         dimensions = {
             "device_type": "s5e9945",
-            "device_os": "UP1A.231005.007",
+            "device_os": "AP3A.240905.015.A2",
             "device_os_type": "user",
             "os": "Android",
             "pool": "chromium.tests.gpu",
diff --git a/ios/chrome/app/strings/ios_chromium_strings.grd b/ios/chrome/app/strings/ios_chromium_strings.grd
index d8d24cd2..51ecab33 100644
--- a/ios/chrome/app/strings/ios_chromium_strings.grd
+++ b/ios/chrome/app/strings/ios_chromium_strings.grd
@@ -815,7 +815,7 @@
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_CPE_TITLE" desc="Text for the title of a Tips notification that encourages the user to enable the CPE.">
         Chromium Tip: Get your passwords in any app
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_ALT1_TITLE" desc="Text for the title of a Tips notification that encourages the user to set the app as their default browser">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_ALT_TITLE" desc="Text for the title of a Tips notification that encourages the user to set the app as their default browser">
          Use Chromium by default
       </message>
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_BODY" desc="Text for the body of a Tips notification that encourages the user to set the app as their default browser">
@@ -824,13 +824,13 @@
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_TITLE" desc="Text for the title of a Tips notification that encourages the user to set the app as their default browser">
         Chromium Tip: use Chromium by default
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_BODY_IPAD" desc="Text for the body of a Tips notification that encourages the user to put the app into the iOS dock">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_BODY_IPAD" desc="Text for the body of a Tips notification that encourages the user to put the app into the iOS dock">
          Access Chromium faster from your iPad's Home Screen.
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_BODY_IPHONE" desc="Text for the body of a Tips notification that encourages the user to put the app into the iOS dock">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_BODY_IPHONE" desc="Text for the body of a Tips notification that encourages the user to put the app into the iOS dock">
          Access Chromium faster from your iPhone's Home Screen.
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_TITLE" desc="Text for the title of a Tips notification that encourages the user to put the app into the iOS dock">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_TITLE" desc="Text for the title of a Tips notification that encourages the user to put the app into the iOS dock">
          Chromium Tip: Move Chromium to your home screen
       </message>
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_BODY_IPAD" desc="Text for the body of a Tips notification that encourages the user to put the app into the iOS dock">
@@ -848,7 +848,7 @@
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_ENHANCED_SAFE_BROWSING_TITLE_FROM_DESKTOP" desc="Text for the title of a Tips notification that encourages the user to enable Enhanced Safe Browsing.">
         Chromium Tip: Enhanced Safe Browsing
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT1_TITLE" desc="Text for the title of a Tips notification that encourages the user to search with Lens">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT_TITLE" desc="Text for the title of a Tips notification that encourages the user to search with Lens">
          Chromium Tip: Search what you see with Google Lens
       </message>
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_LENS_OVERLAY_TITLE" desc="Text for the title of a Tips notification that encourages the user to use the Lens Overlay feature.">
@@ -860,10 +860,10 @@
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_OMNIBOX_POSITION_TITLE" desc="Text for the title of a Tips notification that reminds the user to set their preferred omnibox position">
         Chromium Tip: Choose your address bar position
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT1_BODY_IPAD" desc="Text for the body of a Tips notification that encourages the user to complete the Set Up List">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT_BODY_IPAD" desc="Text for the body of a Tips notification that encourages the user to complete the Set Up List">
          Tap here to get the most out of Chromium on your iPad.
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT1_BODY_IPHONE" desc="Text for the body of a Tips notification that encourages the user to complete the Set Up List">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT_BODY_IPHONE" desc="Text for the body of a Tips notification that encourages the user to complete the Set Up List">
          Tap here to get the most out of Chromium on your iPhone.
       </message>
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_BODY" desc="Text for the body of a Tips notification that encourages the user to complete the Set Up List">
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_ALT1_TITLE.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_ALT_TITLE.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_ALT1_TITLE.png.sha1
rename to ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_ALT_TITLE.png.sha1
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_BODY_IPAD.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_BODY_IPAD.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_BODY_IPAD.png.sha1
rename to ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_BODY_IPAD.png.sha1
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_BODY_IPHONE.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_BODY_IPHONE.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_BODY_IPHONE.png.sha1
rename to ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_BODY_IPHONE.png.sha1
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_TITLE.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_TITLE.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_TITLE.png.sha1
rename to ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_TITLE.png.sha1
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT1_TITLE.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT_TITLE.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT1_TITLE.png.sha1
rename to ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT_TITLE.png.sha1
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT1_BODY_IPAD.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT_BODY_IPAD.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT1_BODY_IPAD.png.sha1
rename to ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT_BODY_IPAD.png.sha1
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT1_BODY_IPHONE.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT_BODY_IPHONE.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT1_BODY_IPHONE.png.sha1
rename to ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT_BODY_IPHONE.png.sha1
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings.grd b/ios/chrome/app/strings/ios_google_chrome_strings.grd
index 4cf34e4..d986bfc 100644
--- a/ios/chrome/app/strings/ios_google_chrome_strings.grd
+++ b/ios/chrome/app/strings/ios_google_chrome_strings.grd
@@ -815,7 +815,7 @@
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_CPE_TITLE" desc="Text for the title of a Tips notification that encourages the user to enable the CPE.">
         Chrome Tip: Get your passwords in any app
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_ALT1_TITLE" desc="Text for the title of a Tips notification that encourages the user to set the app as their default browser">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_ALT_TITLE" desc="Text for the title of a Tips notification that encourages the user to set the app as their default browser">
          Use Chrome by default
       </message>
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_BODY" desc="Text for the body of a Tips notification that encourages the user to set the app as their default browser">
@@ -824,13 +824,13 @@
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_TITLE" desc="Text for the title of a Tips notification that encourages the user to set the app as their default browser">
         Chrome Tip: use Chrome by default
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_BODY_IPAD" desc="Text for the body of a Tips notification that encourages the user to put the app into the iOS dock">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_BODY_IPAD" desc="Text for the body of a Tips notification that encourages the user to put the app into the iOS dock">
          Access Chrome faster from your iPad's Home Screen.
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_BODY_IPHONE" desc="Text for the body of a Tips notification that encourages the user to put the app into the iOS dock">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_BODY_IPHONE" desc="Text for the body of a Tips notification that encourages the user to put the app into the iOS dock">
          Access Chrome faster from your iPhone's Home Screen.
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_TITLE" desc="Text for the title of a Tips notification that encourages the user to put the app into the iOS dock">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_TITLE" desc="Text for the title of a Tips notification that encourages the user to put the app into the iOS dock">
          Chrome Tip: Move Chrome to your home screen
       </message>
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_BODY_IPAD" desc="Text for the body of a Tips notification that encourages the user to put the app into the iOS dock">
@@ -848,7 +848,7 @@
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_ENHANCED_SAFE_BROWSING_TITLE_FROM_DESKTOP" desc="Text for the title of a Tips notification that encourages the user to enable Enhanced Safe Browsing.">
         Chrome Tip: Enhanced Safe Browsing
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT1_TITLE" desc="Text for the title of a Tips notification that encourages the user to search with Lens">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT_TITLE" desc="Text for the title of a Tips notification that encourages the user to search with Lens">
          Chrome Tip: Search what you see with Google Lens
       </message>
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_LENS_OVERLAY_TITLE" desc="Text for the title of a Tips notification that encourages the user to use the Lens Overlay feature.">
@@ -860,10 +860,10 @@
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_OMNIBOX_POSITION_TITLE" desc="Text for the title of a Tips notification that reminds the user to set their preferred omnibox position">
         Chrome Tip: Choose your address bar position
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT1_BODY_IPAD" desc="Text for the body of a Tips notification that encourages the user to complete the Set Up List">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT_BODY_IPAD" desc="Text for the body of a Tips notification that encourages the user to complete the Set Up List">
          Tap here to get the most out of Chrome on your iPad.
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT1_BODY_IPHONE" desc="Text for the body of a Tips notification that encourages the user to complete the Set Up List">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT_BODY_IPHONE" desc="Text for the body of a Tips notification that encourages the user to complete the Set Up List">
          Tap here to get the most out of Chrome on your iPhone.
       </message>
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_BODY" desc="Text for the body of a Tips notification that encourages the user to complete the Set Up List">
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_ALT1_TITLE.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_ALT_TITLE.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_ALT1_TITLE.png.sha1
rename to ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_ALT_TITLE.png.sha1
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_BODY_IPAD.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_BODY_IPAD.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_BODY_IPAD.png.sha1
rename to ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_BODY_IPAD.png.sha1
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_BODY_IPHONE.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_BODY_IPHONE.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_BODY_IPHONE.png.sha1
rename to ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_BODY_IPHONE.png.sha1
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_TITLE.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_TITLE.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_TITLE.png.sha1
rename to ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_TITLE.png.sha1
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT1_TITLE.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT_TITLE.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT1_TITLE.png.sha1
rename to ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT_TITLE.png.sha1
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT1_BODY_IPAD.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT_BODY_IPAD.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT1_BODY_IPAD.png.sha1
rename to ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT_BODY_IPAD.png.sha1
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT1_BODY_IPHONE.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT_BODY_IPHONE.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT1_BODY_IPHONE.png.sha1
rename to ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT_BODY_IPHONE.png.sha1
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 745fe50..09c232b 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -5081,13 +5081,13 @@
         Choose the notifications you want to receive
       </message>
 
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_ENHANCED_SAFE_BROWSING_ALT2_TITLE" desc="Text for the title of a Tips notification that encourages the user to enable Enhanced Safe Browsing">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_ENHANCED_SAFE_BROWSING_ALT_TITLE" desc="Text for the title of a Tips notification that encourages the user to enable Enhanced Safe Browsing">
          Turn On Enhanced Safe Browsing
       </message>
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_ENHANCED_SAFE_BROWSING_BODY" desc="Text for the body of a Tips notification that encourages the user to enable Enhanced Safe Browsing">
         Add an extra layer of protection against online threats.
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT1_BODY" desc="Text for the body of a Tips notification that encourages the user to search with Lens">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT_BODY" desc="Text for the body of a Tips notification that encourages the user to search with Lens">
          Now you can shop and translate with your camera. Tap to try it out.
       </message>
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_LENS_BODY" desc="Text for the body of a Tips notification that encourages the user to search with Lens">
@@ -5096,19 +5096,19 @@
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_LENS_OVERLAY_BODY" desc="Text for the body of a Tips notification that encourages the user to use the Lens Overlay feature.">
         Learn more as you browse and search what you see on any page to explore products, places and text.
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_OMNIBOX_POSITION_ALT2_TITLE" desc="Text for the title of a Tips notification that reminds the user to set their preferred omnibox position">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_OMNIBOX_POSITION_ALT_TITLE" desc="Text for the title of a Tips notification that reminds the user to set their preferred omnibox position">
          Choose your address bar position
       </message>
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_OMNIBOX_POSITION_BODY" desc="Text for the body of a Tips notification that reminds the user to set their preferred omnibox position">
         Switch the position of your address bar for a customized experience.
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_SIGNIN_ALT1_BODY" desc="Text for the body of a Tips notification that encourages the user to sign in to their account">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_SIGNIN_ALT_BODY" desc="Text for the body of a Tips notification that encourages the user to sign in to their account">
          Sign in to get all your stuff like passwords, bookmarks, and more.
       </message>
       <message name="IDS_IOS_NOTIFICATIONS_TIPS_TRUSTED_VAULT_KEY_RETRIVAL_BODY" desc="TODO">
         Verify it's you to access all your passwords on this device.
       </message>
-      <message name="IDS_IOS_NOTIFICATIONS_TIPS_WHATS_NEW_ALT1_BODY" desc="Text for the body of a Tips notification that takes the user to the Whats New page">
+      <message name="IDS_IOS_NOTIFICATIONS_TIPS_WHATS_NEW_ALT_BODY" desc="Text for the body of a Tips notification that takes the user to the Whats New page">
          Family passwords, new customizations and more. Tap to learn more.
       </message>
       <message name="IDS_IOS_NOTIFICATIONS_TITLE" desc="Title for Notifications settings menu.">
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_ENHANCED_SAFE_BROWSING_ALT2_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_ENHANCED_SAFE_BROWSING_ALT_TITLE.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_ENHANCED_SAFE_BROWSING_ALT2_TITLE.png.sha1
rename to ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_ENHANCED_SAFE_BROWSING_ALT_TITLE.png.sha1
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT1_BODY.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT_BODY.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT1_BODY.png.sha1
rename to ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT_BODY.png.sha1
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_OMNIBOX_POSITION_ALT2_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_OMNIBOX_POSITION_ALT_TITLE.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_OMNIBOX_POSITION_ALT2_TITLE.png.sha1
rename to ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_OMNIBOX_POSITION_ALT_TITLE.png.sha1
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SIGNIN_ALT1_BODY.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SIGNIN_ALT_BODY.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SIGNIN_ALT1_BODY.png.sha1
rename to ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_SIGNIN_ALT_BODY.png.sha1
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_WHATS_NEW_ALT1_BODY.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_WHATS_NEW_ALT_BODY.png.sha1
similarity index 100%
rename from ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_WHATS_NEW_ALT1_BODY.png.sha1
rename to ios/chrome/app/strings/ios_strings_grd/IDS_IOS_NOTIFICATIONS_TIPS_WHATS_NEW_ALT_BODY.png.sha1
diff --git a/ios/chrome/browser/app_bar/coordinator/app_bar_mediator.mm b/ios/chrome/browser/app_bar/coordinator/app_bar_mediator.mm
index 3405c53..be1d60a 100644
--- a/ios/chrome/browser/app_bar/coordinator/app_bar_mediator.mm
+++ b/ios/chrome/browser/app_bar/coordinator/app_bar_mediator.mm
@@ -892,6 +892,22 @@
   return NO;
 }
 
+// Returns YES if the primary identity is in an unverified state requiring
+// re-authentication / managing account approval.
+- (BOOL)isPrimaryIdentityUnverified {
+  if (!_authenticationService ||
+      !_authenticationService->HasPrimaryIdentity() || !_identityManager) {
+    return NO;
+  }
+  CoreAccountId accountId =
+      _identityManager->GetPrimaryAccountId(signin::ConsentLevel::kSignin);
+  if (accountId.empty()) {
+    return NO;
+  }
+  return _identityManager->HasAccountWithRefreshTokenInPersistentErrorState(
+      accountId);
+}
+
 // Returns YES if Gemini is eligible to be shown in the App Bar.
 - (BOOL)isGeminiEligible {
   if (!IsPageActionMenuEnabled() || [self isEEAOrJapan] || !_geminiService) {
@@ -901,11 +917,14 @@
   bool geminiAllowed = _geminiService->IsProfileEligibleForGemini();
   if (!geminiAllowed && _authenticationService &&
       gemini::GeminiAllowedByPolicy(_prefService)) {
-    if (!_authenticationService->HasPrimaryIdentity()) {
-      // If the profile is ineligible, it might be just because the user is
-      // signed out. We still want to show the Gemini button (disabled) for
-      // signed-out users to encourage sign-in, unless a local enterprise
-      // policy explicitly disables it or sign-in is disabled.
+    BOOL isSignedOut = !_authenticationService->HasPrimaryIdentity();
+    BOOL isUnverified = [self isPrimaryIdentityUnverified];
+    if (isSignedOut || isUnverified) {
+      // If the profile is ineligible, it might be because the user is
+      // signed out or in an unverified sign-in state. We still want to show
+      // the Gemini button (disabled) for these users to encourage
+      // sign-in/verification, unless a local enterprise policy explicitly
+      // disables it or sign-in is disabled.
       geminiAllowed = _authenticationService->SigninEnabled();
     } else if (net::NetworkChangeNotifier::IsOffline() && _identityManager) {
       // Optimistically allow signed-in users when offline, unless their
@@ -986,8 +1005,9 @@
   UIImage* avatar = nil;
   switch (state) {
     case AppBarAssistantButtonState::kAsk:
-      enabled = _geminiBrowserAgent &&
-                _geminiBrowserAgent->IsGeminiAvailableForActiveWebState();
+      enabled = (_geminiBrowserAgent &&
+                 _geminiBrowserAgent->IsGeminiAvailableForActiveWebState()) ||
+                [self isPrimaryIdentityUnverified];
       highlighted = enabled && _geminiBrowserAgent &&
                     _geminiBrowserAgent->is_floaty_invoked();
       break;
diff --git a/ios/chrome/browser/app_bar/coordinator/app_bar_mediator_unittest.mm b/ios/chrome/browser/app_bar/coordinator/app_bar_mediator_unittest.mm
index 3bcd080d..f5fc16a7 100644
--- a/ios/chrome/browser/app_bar/coordinator/app_bar_mediator_unittest.mm
+++ b/ios/chrome/browser/app_bar/coordinator/app_bar_mediator_unittest.mm
@@ -19,6 +19,7 @@
 #import "components/signin/public/base/consent_level.h"
 #import "components/signin/public/base/signin_pref_names.h"
 #import "components/signin/public/identity_manager/account_capabilities_test_mutator.h"
+#import "components/signin/public/identity_manager/identity_test_utils.h"
 #import "components/signin/public/identity_manager/tribool.h"
 #import "components/sync_preferences/testing_pref_service_syncable.h"
 #import "components/tab_groups/tab_group_id.h"
@@ -1052,6 +1053,31 @@
   EXPECT_OCMOCK_VERIFY(consumer_);
 }
 
+// Tests that the assistant button is in the ask state and enabled when signed
+// in with an unverified primary identity.
+TEST_F(AppBarMediatorTest, TestAssistantButtonStateAsk_UnverifiedIdentity) {
+  SetLocationEligible(true);
+  SignInAndSetCapability(false);
+
+  signin::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(regular_profile_.get());
+  CoreAccountId account_id =
+      identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSignin);
+  signin::UpdatePersistentErrorOfRefreshTokenForAccount(
+      identity_manager, account_id,
+      GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
+          GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+              CREDENTIALS_REJECTED_BY_SERVER));
+
+  OCMExpect([consumer_ setAssistantButtonState:AppBarAssistantButtonState::kAsk
+                                   highlighted:NO
+                                       enabled:YES
+                                        avatar:nil
+                                      signedIn:YES]);
+  [mediator_ updateAssistantButton];
+  EXPECT_OCMOCK_VERIFY(consumer_);
+}
+
 // Tests that tapping the assistant button in the ask state dispatches the
 // Gemini entry flow command when ChromeNextIa is enabled.
 // Tests that the assistant button state is updated when the network connection
diff --git a/ios/chrome/browser/autofill/atmemory/coordinator/at_memory_granular_fill_coordinator.mm b/ios/chrome/browser/autofill/atmemory/coordinator/at_memory_granular_fill_coordinator.mm
index e418a769..ee0697b 100644
--- a/ios/chrome/browser/autofill/atmemory/coordinator/at_memory_granular_fill_coordinator.mm
+++ b/ios/chrome/browser/autofill/atmemory/coordinator/at_memory_granular_fill_coordinator.mm
@@ -43,6 +43,7 @@
 
   _mediator = [[AtMemoryGranularFillMediator alloc] init];
   _mediator.fillHandler = self.fillHandler;
+  _mediator.consumer = _atMemoryGranularFillViewController;
 
   [self.baseNavigationController
       pushViewController:_atMemoryGranularFillViewController
diff --git a/ios/chrome/browser/autofill/atmemory/coordinator/at_memory_granular_fill_mediator.h b/ios/chrome/browser/autofill/atmemory/coordinator/at_memory_granular_fill_mediator.h
index b4ba7e569..347bb9b 100644
--- a/ios/chrome/browser/autofill/atmemory/coordinator/at_memory_granular_fill_mediator.h
+++ b/ios/chrome/browser/autofill/atmemory/coordinator/at_memory_granular_fill_mediator.h
@@ -8,10 +8,14 @@
 #import <Foundation/Foundation.h>
 
 @protocol AtMemoryFillCommands;
+@protocol AtMemoryGranularFillConsumer;
 
 // Mediator for AtMemory granular fill.
 @interface AtMemoryGranularFillMediator : NSObject
 
+// The consumer for this mediator.
+@property(nonatomic, weak) id<AtMemoryGranularFillConsumer> consumer;
+
 // Handler for filling commands.
 @property(nonatomic, weak) id<AtMemoryFillCommands> fillHandler;
 
diff --git a/ios/chrome/browser/autofill/atmemory/coordinator/at_memory_granular_fill_mediator.mm b/ios/chrome/browser/autofill/atmemory/coordinator/at_memory_granular_fill_mediator.mm
index 9c8d5a5..fe241105 100644
--- a/ios/chrome/browser/autofill/atmemory/coordinator/at_memory_granular_fill_mediator.mm
+++ b/ios/chrome/browser/autofill/atmemory/coordinator/at_memory_granular_fill_mediator.mm
@@ -4,6 +4,18 @@
 
 #import "ios/chrome/browser/autofill/atmemory/coordinator/at_memory_granular_fill_mediator.h"
 
+#import "ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_consumer.h"
+
 @implementation AtMemoryGranularFillMediator
 
+- (void)setConsumer:(id<AtMemoryGranularFillConsumer>)consumer {
+  if (_consumer == consumer) {
+    return;
+  }
+  _consumer = consumer;
+
+  // TODO(crbug.com/522340351): Set the title and granular fill items on the
+  // consumer.
+}
+
 @end
diff --git a/ios/chrome/browser/autofill/atmemory/ui/BUILD.gn b/ios/chrome/browser/autofill/atmemory/ui/BUILD.gn
index 183625df..0564978 100644
--- a/ios/chrome/browser/autofill/atmemory/ui/BUILD.gn
+++ b/ios/chrome/browser/autofill/atmemory/ui/BUILD.gn
@@ -4,6 +4,9 @@
 
 source_set("ui") {
   sources = [
+    "at_memory_granular_fill_consumer.h",
+    "at_memory_granular_fill_item.h",
+    "at_memory_granular_fill_item.mm",
     "at_memory_granular_fill_view_controller.h",
     "at_memory_granular_fill_view_controller.mm",
     "at_memory_inline_notice_view.h",
diff --git a/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_consumer.h b/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_consumer.h
new file mode 100644
index 0000000..ccbb6f18
--- /dev/null
+++ b/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_consumer.h
@@ -0,0 +1,23 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_AUTOFILL_ATMEMORY_UI_AT_MEMORY_GRANULAR_FILL_CONSUMER_H_
+#define IOS_CHROME_BROWSER_AUTOFILL_ATMEMORY_UI_AT_MEMORY_GRANULAR_FILL_CONSUMER_H_
+
+#import <Foundation/Foundation.h>
+
+@class AtMemoryGranularFillItem;
+
+// Consumer protocol for AtMemory granular fill UI and mediator.
+@protocol AtMemoryGranularFillConsumer <NSObject>
+
+// Sets the title for the granular fill screen.
+- (void)setTitle:(NSString*)title;
+
+// Sets the list of granular fill items to be displayed.
+- (void)setGranularFillItems:(NSArray<AtMemoryGranularFillItem*>*)items;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_AUTOFILL_ATMEMORY_UI_AT_MEMORY_GRANULAR_FILL_CONSUMER_H_
diff --git a/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_item.h b/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_item.h
new file mode 100644
index 0000000..ded67140
--- /dev/null
+++ b/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_item.h
@@ -0,0 +1,16 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_AUTOFILL_ATMEMORY_UI_AT_MEMORY_GRANULAR_FILL_ITEM_H_
+#define IOS_CHROME_BROWSER_AUTOFILL_ATMEMORY_UI_AT_MEMORY_GRANULAR_FILL_ITEM_H_
+
+#import <Foundation/Foundation.h>
+
+// Item representing an attribute field for AtMemory granular fill.
+// TODO(crbug.com/522340351): Implement attributes and values for this item.
+@interface AtMemoryGranularFillItem : NSObject
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_AUTOFILL_ATMEMORY_UI_AT_MEMORY_GRANULAR_FILL_ITEM_H_
diff --git a/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_item.mm b/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_item.mm
new file mode 100644
index 0000000..7e40c981
--- /dev/null
+++ b/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_item.mm
@@ -0,0 +1,11 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_item.h"
+
+// TODO(crbug.com/522340351): Implement item equality and properties for
+// AtMemoryGranularFillItem.
+@implementation AtMemoryGranularFillItem
+
+@end
diff --git a/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_view_controller.h b/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_view_controller.h
index bc2cab7..fa8c3d0 100644
--- a/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_view_controller.h
+++ b/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_view_controller.h
@@ -5,11 +5,13 @@
 #ifndef IOS_CHROME_BROWSER_AUTOFILL_ATMEMORY_UI_AT_MEMORY_GRANULAR_FILL_VIEW_CONTROLLER_H_
 #define IOS_CHROME_BROWSER_AUTOFILL_ATMEMORY_UI_AT_MEMORY_GRANULAR_FILL_VIEW_CONTROLLER_H_
 
+#import "ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_consumer.h"
 #import "ios/chrome/browser/shared/ui/table_view/chrome_table_view_controller.h"
 
 // View controller that displays the details page with tap-to-fill chips for
 // AtMemory granular fill.
-@interface AtMemoryGranularFillViewController : ChromeTableViewController
+@interface AtMemoryGranularFillViewController
+    : ChromeTableViewController <AtMemoryGranularFillConsumer>
 
 @end
 
diff --git a/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_view_controller.mm b/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_view_controller.mm
index 52b9ccf..98cc4cd 100644
--- a/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_view_controller.mm
+++ b/ios/chrome/browser/autofill/atmemory/ui/at_memory_granular_fill_view_controller.mm
@@ -6,4 +6,14 @@
 
 @implementation AtMemoryGranularFillViewController
 
+#pragma mark - AtMemoryGranularFillConsumer
+
+- (void)setTitle:(NSString*)title {
+  [super setTitle:title];
+}
+
+- (void)setGranularFillItems:(NSArray<AtMemoryGranularFillItem*>*)items {
+  // TODO(crbug.com/522340351): Set the granular fill items in the table view.
+}
+
 @end
diff --git a/ios/chrome/browser/autofill/atmemory/ui/at_memory_inline_notice_view.h b/ios/chrome/browser/autofill/atmemory/ui/at_memory_inline_notice_view.h
index 885827f..f0c1baa 100644
--- a/ios/chrome/browser/autofill/atmemory/ui/at_memory_inline_notice_view.h
+++ b/ios/chrome/browser/autofill/atmemory/ui/at_memory_inline_notice_view.h
@@ -46,6 +46,12 @@
 // Delegate to handle actions from the notice view.
 @property(nonatomic, weak) id<AtMemoryInlineNoticeViewDelegate> delegate;
 
+// Registers the cell class with the table view.
++ (void)registerCellForTableView:(UITableView*)tableView;
+
+// Dequeues a cell from the table view.
++ (UITableViewCell*)dequeueTableViewCell:(UITableView*)tableView;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_AUTOFILL_ATMEMORY_UI_AT_MEMORY_INLINE_NOTICE_VIEW_H_
diff --git a/ios/chrome/browser/autofill/atmemory/ui/at_memory_inline_notice_view.mm b/ios/chrome/browser/autofill/atmemory/ui/at_memory_inline_notice_view.mm
index 5c7d3dd..36606be5 100644
--- a/ios/chrome/browser/autofill/atmemory/ui/at_memory_inline_notice_view.mm
+++ b/ios/chrome/browser/autofill/atmemory/ui/at_memory_inline_notice_view.mm
@@ -58,6 +58,17 @@
   return copy;
 }
 
++ (void)registerCellForTableView:(UITableView*)tableView {
+  [tableView registerClass:UITableViewCell.class
+      forCellReuseIdentifier:NSStringFromClass(self.class)];
+}
+
++ (UITableViewCell*)dequeueTableViewCell:(UITableView*)tableView {
+  UITableViewCell* cell = [tableView
+      dequeueReusableCellWithIdentifier:NSStringFromClass(self.class)];
+  return cell;
+}
+
 @end
 
 #pragma mark - AtMemoryInlineNoticeView
diff --git a/ios/chrome/browser/autofill/atmemory/ui/at_memory_search_view_controller.mm b/ios/chrome/browser/autofill/atmemory/ui/at_memory_search_view_controller.mm
index 687a274..e509527 100644
--- a/ios/chrome/browser/autofill/atmemory/ui/at_memory_search_view_controller.mm
+++ b/ios/chrome/browser/autofill/atmemory/ui/at_memory_search_view_controller.mm
@@ -8,6 +8,7 @@
 #import "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/autofill/atmemory/public/at_memory_commands.h"
 #import "ios/chrome/browser/autofill/atmemory/public/at_memory_constants.h"
+#import "ios/chrome/browser/autofill/atmemory/ui/at_memory_inline_notice_view.h"
 #import "ios/chrome/browser/autofill/atmemory/ui/at_memory_search_mutator.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ui/base/l10n/l10n_util.h"
@@ -48,7 +49,8 @@
 
 }  // namespace
 
-@interface AtMemorySearchViewController () <UISearchResultsUpdating>
+@interface AtMemorySearchViewController () <UISearchResultsUpdating,
+                                            AtMemoryInlineNoticeViewDelegate>
 @end
 
 @implementation AtMemorySearchViewController {
@@ -97,6 +99,8 @@
 }
 
 - (void)loadModel {
+  [AtMemoryInlineNoticeConfiguration registerCellForTableView:self.tableView];
+
   __weak __typeof(self) weakSelf = self;
   _dataSource = [[UITableViewDiffableDataSource alloc]
       initWithTableView:self.tableView
@@ -223,6 +227,7 @@
                       itemIdentifier:(ItemIdentifier)itemIdentifier {
   switch (itemIdentifier) {
     case ItemIdentifier::kNoticeItem:
+      return [self noticeCellForTableView:tableView];
     case ItemIdentifier::kSearchItem:
     case ItemIdentifier::kFetchingItem:
     case ItemIdentifier::kNoDataItem:
@@ -234,4 +239,29 @@
   NOTREACHED();
 }
 
+// Returns the table view cell for the notice state.
+- (UITableViewCell*)noticeCellForTableView:(UITableView*)tableView {
+  UITableViewCell* cell =
+      [AtMemoryInlineNoticeConfiguration dequeueTableViewCell:tableView];
+  cell.selectionStyle = UITableViewCellSelectionStyleNone;
+  cell.backgroundColor = [UIColor clearColor];
+  cell.contentView.backgroundColor = [UIColor clearColor];
+
+  AtMemoryInlineNoticeConfiguration* config =
+      [[AtMemoryInlineNoticeConfiguration alloc] init];
+  config.delegate = self;
+  cell.contentConfiguration = config;
+  return cell;
+}
+
+#pragma mark - AtMemoryInlineNoticeViewDelegate
+
+- (void)inlineNoticeViewDidTapOK:(AtMemoryInlineNoticeView*)view {
+  // TODO(crbug.com/540433768): Forward to mutator to handle notice dismissal.
+}
+
+- (void)inlineNoticeViewDidTapSettings:(AtMemoryInlineNoticeView*)view {
+  // TODO(crbug.com/540433768): Forward to mutator to handle settings redirect.
+}
+
 @end
diff --git a/ios/chrome/browser/content_suggestions/coordinator/content_suggestions_coordinator.mm b/ios/chrome/browser/content_suggestions/coordinator/content_suggestions_coordinator.mm
index dc55442b..c9874fbb 100644
--- a/ios/chrome/browser/content_suggestions/coordinator/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/content_suggestions/coordinator/content_suggestions_coordinator.mm
@@ -770,6 +770,7 @@
             [UIImage imageWithData:_tipsMediator.config.productImageData];
 
         if (productImage) {
+          // C2PA: Product image would not have C2PA metadata. b/541315801
           SearchImageWithLensCommand* command =
               [[SearchImageWithLensCommand alloc] initWithImage:productImage
                                                      entryPoint:entryPoint];
diff --git a/ios/chrome/browser/content_suggestions/most_visited_tiles/coordinator/BUILD.gn b/ios/chrome/browser/content_suggestions/most_visited_tiles/coordinator/BUILD.gn
index 4635a693..1f2f7fa7 100644
--- a/ios/chrome/browser/content_suggestions/most_visited_tiles/coordinator/BUILD.gn
+++ b/ios/chrome/browser/content_suggestions/most_visited_tiles/coordinator/BUILD.gn
@@ -54,6 +54,7 @@
   deps = [
     ":coordinator",
     "//base",
+    "//base/test:test_support",
     "//components/feature_engagement/test:test_support",
     "//components/history/core/browser",
     "//components/history/core/test",
@@ -63,6 +64,8 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/content_suggestions/model:metrics_recorder",
     "//ios/chrome/browser/content_suggestions/most_visited_tiles/ui",
+    "//ios/chrome/browser/content_suggestions/ui",
+    "//ios/chrome/browser/content_suggestions/ui:commands",
     "//ios/chrome/browser/content_suggestions/ui:public",
     "//ios/chrome/browser/favicon/model",
     "//ios/chrome/browser/history/model",
diff --git a/ios/chrome/browser/content_suggestions/most_visited_tiles/coordinator/most_visited_tiles_mediator.mm b/ios/chrome/browser/content_suggestions/most_visited_tiles/coordinator/most_visited_tiles_mediator.mm
index 59d2d19..f9c39cdf 100644
--- a/ios/chrome/browser/content_suggestions/most_visited_tiles/coordinator/most_visited_tiles_mediator.mm
+++ b/ios/chrome/browser/content_suggestions/most_visited_tiles/coordinator/most_visited_tiles_mediator.mm
@@ -43,6 +43,7 @@
 #import "ios/chrome/browser/menu/ui_bundled/browser_action_factory.h"
 #import "ios/chrome/browser/net/model/crurl.h"
 #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_actions_delegate.h"
+#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h"
 #import "ios/chrome/browser/ntp_tiles/model/most_visited_sites_observer_bridge.h"
 #import "ios/chrome/browser/policy/model/policy_util.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
@@ -204,6 +205,13 @@
   return _mostVisitedConfig;
 }
 
+- (void)setConsumer:(id<ContentSuggestionsConsumer>)consumer {
+  _consumer = consumer;
+  if (IsNTPRedesignEnabled() && _consumer && _mostVisitedConfig) {
+    [_consumer setMostVisitedTilesConfig:_mostVisitedConfig];
+  }
+}
+
 #pragma mark - MostVisitedSitesObserving
 
 - (void)mostVisitedSites:(ntp_tiles::MostVisitedSites*)mostVisitedSites
diff --git a/ios/chrome/browser/content_suggestions/most_visited_tiles/coordinator/most_visited_tiles_mediator_unittest.mm b/ios/chrome/browser/content_suggestions/most_visited_tiles/coordinator/most_visited_tiles_mediator_unittest.mm
index feba7336..39be4ae 100644
--- a/ios/chrome/browser/content_suggestions/most_visited_tiles/coordinator/most_visited_tiles_mediator_unittest.mm
+++ b/ios/chrome/browser/content_suggestions/most_visited_tiles/coordinator/most_visited_tiles_mediator_unittest.mm
@@ -6,6 +6,7 @@
 
 #import "base/functional/bind.h"
 #import "base/memory/raw_ptr.h"
+#import "base/test/scoped_feature_list.h"
 #import "base/time/time.h"
 #import "components/feature_engagement/test/mock_tracker.h"
 #import "components/history/core/browser/history_service.h"
@@ -21,6 +22,7 @@
 #import "ios/chrome/browser/content_suggestions/most_visited_tiles/ui/most_visited_tile_view.h"
 #import "ios/chrome/browser/content_suggestions/most_visited_tiles/ui/most_visited_tiles_config.h"
 #import "ios/chrome/browser/content_suggestions/ui/content_suggestions_actions_provider.h"
+#import "ios/chrome/browser/content_suggestions/ui/content_suggestions_consumer.h"
 #import "ios/chrome/browser/favicon/model/ios_chrome_large_icon_cache_factory.h"
 #import "ios/chrome/browser/favicon/model/ios_chrome_large_icon_service_factory.h"
 #import "ios/chrome/browser/history/model/history_service_factory.h"
@@ -43,6 +45,7 @@
 #import "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 #import "third_party/ocmock/gtest_support.h"
+#import "ui/base/device_form_factor.h"
 #import "ui/base/l10n/l10n_util_mac.h"
 #import "url/gurl.h"
 
@@ -408,3 +411,30 @@
   EXPECT_TRUE(foundMoveLeft);
   EXPECT_FALSE(foundMoveRight);
 }
+
+// Tests that setConsumer delivers cached config when NTP Redesign is enabled.
+TEST_F(MostVisitedTilesMediatorTest,
+       TestSetConsumerDeliversConfigWhenNTPRedesignEnabled) {
+  if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) {
+    return;
+  }
+
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(kNewTabPageRedesign);
+
+  ntp_tiles::NTPTilesVector tiles_vector;
+  ntp_tiles::NTPTile tile;
+  tile.url = GURL("http://chromium.org");
+  tile.title = u"Chromium";
+  tiles_vector.push_back(tile);
+  std::map<ntp_tiles::SectionType, ntp_tiles::NTPTilesVector> sections;
+  sections[ntp_tiles::SectionType::PERSONALIZED] = tiles_vector;
+  captured_observer_->OnURLsAvailable(/*is_user_triggered=*/true, sections);
+
+  id mock_consumer = OCMProtocolMock(@protocol(ContentSuggestionsConsumer));
+  OCMExpect([mock_consumer setMostVisitedTilesConfig:[OCMArg any]]);
+
+  mediator_.consumer = mock_consumer;
+
+  EXPECT_OCMOCK_VERIFY(mock_consumer);
+}
diff --git a/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.mm b/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.mm
index 8b3a11bf..e2e3e25 100644
--- a/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.mm
+++ b/ios/chrome/browser/context_menu/ui_bundled/context_menu_configuration_provider.mm
@@ -647,6 +647,8 @@
       imageURL, referrer, params.frame_id, params.frame_security_origin,
       ^(NSData* rawData) {
         // Arbitrary web image data requires sanitization before use.
+        // TODO(b/541315801): C2PA: Web images could have C2PA metadata;
+        // candidate to pass raw bytes.
         [weakSelf
             sanitizeImageData:rawData
                      mimeType:kJPEGImageMimeType
diff --git a/ios/chrome/browser/enterprise/connectors/reporting/ios_realtime_reporting_client.h b/ios/chrome/browser/enterprise/connectors/reporting/ios_realtime_reporting_client.h
index 1abf1dfc..4c772e4 100644
--- a/ios/chrome/browser/enterprise/connectors/reporting/ios_realtime_reporting_client.h
+++ b/ios/chrome/browser/enterprise/connectors/reporting/ios_realtime_reporting_client.h
@@ -48,28 +48,17 @@
   // policy::CloudPolicyClient::Observer overrides:
   void OnClientError(policy::CloudPolicyClient* client) override;
 
-  // Report safe browsing event through real-time reporting channel, if enabled.
-  // Declared as virtual for tests.
-  virtual void ReportRealtimeEvent(const std::string& name,
-                                   const ReportingSettings& settings,
-                                   base::DictValue event);
 
  private:
   // RealtimeReportingClientBase overrides (all overrides below):
   std::string GetProfileIdentifier() override;
   std::string GetBrowserClientId() override;
   std::string GetContentAreaAccountEmail(const GURL& url) override;
-  base::DictValue GetContext() override;
+
   ::chrome::cros::reporting::proto::UploadEventsRequest
   CreateUploadEventsRequest() override;
   bool ShouldIncludeDeviceInfo(bool per_profile) override;
-  void UploadCallbackDeprecated(
-      base::DictValue event_wrapper,
-      bool per_profile,
-      policy::CloudPolicyClient* client,
-      EnterpriseReportingEventType event_type,
-      base::TimeTicks upload_started_at,
-      policy::CloudPolicyClient::Result upload_result) override;
+
   void UploadCallback(
       bool per_profile,
       policy::CloudPolicyClient* client,
diff --git a/ios/chrome/browser/enterprise/connectors/reporting/ios_realtime_reporting_client.mm b/ios/chrome/browser/enterprise/connectors/reporting/ios_realtime_reporting_client.mm
index f8e4b32..11649cbb 100644
--- a/ios/chrome/browser/enterprise/connectors/reporting/ios_realtime_reporting_client.mm
+++ b/ios/chrome/browser/enterprise/connectors/reporting/ios_realtime_reporting_client.mm
@@ -116,15 +116,6 @@
   return service->GetReportingSettings();
 }
 
-void IOSRealtimeReportingClient::ReportRealtimeEvent(
-    const std::string& name,
-    const ReportingSettings& settings,
-    base::DictValue event) {
-  ReportEventWithTimestampDeprecated(name, settings, std::move(event),
-                                     base::Time::Now(),
-                                     /*include_profile_user_name=*/true);
-}
-
 std::string IOSRealtimeReportingClient::GetProfileUserName() {
   if (!username_.empty()) {
     return username_;
@@ -158,33 +149,6 @@
   return IncludeDeviceInfo(per_profile, IsProfileAffilicated(profile_));
 }
 
-void IOSRealtimeReportingClient::UploadCallbackDeprecated(
-    base::DictValue event_wrapper,
-    bool per_profile,
-    policy::CloudPolicyClient* client,
-    EnterpriseReportingEventType event_type,
-    base::TimeTicks upload_started_at,
-    policy::CloudPolicyClient::Result upload_result) {
-  // TODO(crbug.com/256553070): Do not crash if the client is unregistered.
-  CHECK(!upload_result.IsClientNotRegisteredError());
-
-  if (upload_result.IsSuccess()) {
-    base::UmaHistogramEnumeration("Enterprise.ReportingEventUploadSuccess",
-                                  event_type);
-    base::UmaHistogramCustomTimes(
-        GetSuccessfulUploadDurationUmaMetricName(event_type),
-        base::TimeTicks::Now() - upload_started_at, base::Milliseconds(1),
-        base::Minutes(5), 50);
-  } else {
-    base::UmaHistogramEnumeration("Enterprise.ReportingEventUploadFailure",
-                                  event_type);
-    base::UmaHistogramCustomTimes(
-        GetFailedUploadDurationUmaMetricName(event_type),
-        base::TimeTicks::Now() - upload_started_at, base::Milliseconds(1),
-        base::Minutes(5), 50);
-  }
-}
-
 void IOSRealtimeReportingClient::UploadCallback(
     bool per_profile,
     policy::CloudPolicyClient* client,
@@ -208,10 +172,6 @@
   }
 }
 
-base::DictValue IOSRealtimeReportingClient::GetContext() {
-  return ::enterprise_connectors::GetContext(profile_);
-}
-
 ::chrome::cros::reporting::proto::UploadEventsRequest
 IOSRealtimeReportingClient::CreateUploadEventsRequest() {
   return ::enterprise_connectors::CreateUploadEventsRequest(profile_);
diff --git a/ios/chrome/browser/first_run/interactive_lens/coordinator/interactive_lens_promo_coordinator.mm b/ios/chrome/browser/first_run/interactive_lens/coordinator/interactive_lens_promo_coordinator.mm
index 7caf2c4e..d675f96 100644
--- a/ios/chrome/browser/first_run/interactive_lens/coordinator/interactive_lens_promo_coordinator.mm
+++ b/ios/chrome/browser/first_run/interactive_lens/coordinator/interactive_lens_promo_coordinator.mm
@@ -69,6 +69,7 @@
         return presenter;
       };
 
+  // C2PA: Promo image should not have C2PA metadata. b/541315801
   [_lensOverlayHandler
           searchImageWithLens:_promoViewController.lensSearchImage
                    entrypoint:LensOverlayEntrypoint::kFREPromo
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index d70dec2..0cde134 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -1464,6 +1464,9 @@
     {"new-tab-page-redesign", flag_descriptions::kNewTabPageRedesignName,
      flag_descriptions::kNewTabPageRedesignDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kNewTabPageRedesign)},
+    {"ntp-mvt-in-bottom-sheet", flag_descriptions::kMVTInBottomSheetName,
+     flag_descriptions::kMVTInBottomSheetDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(kMVTInBottomSheet)},
     {"plus-button-in-fakebox", flag_descriptions::kPlusButtonInFakeboxName,
      flag_descriptions::kPlusButtonInFakeboxDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kPlusButtonInFakebox)},
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index aeeee5e..6d9aadc 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -1372,6 +1372,11 @@
     "When enabled, the bottom toolbar will not get collapsed when scrolling "
     "into fullscreen mode.";
 
+inline constexpr char kMVTInBottomSheetName[] = "NTP MVT in Bottom Sheet";
+inline constexpr char kMVTInBottomSheetDescription[] =
+    "Places the Most Visited Tiles inside the bottom sheet card for the NTP "
+    "Redesign.";
+
 inline constexpr char kManualLogUploadsInFREName[] =
     "Manual log uploads in the FRE";
 inline constexpr char kManualLogUploadsInFREDescription[] =
diff --git a/ios/chrome/browser/intelligence/bwg/coordinator/gemini_entry_flow_coordinator.mm b/ios/chrome/browser/intelligence/bwg/coordinator/gemini_entry_flow_coordinator.mm
index 9302ade..19ca4f3 100644
--- a/ios/chrome/browser/intelligence/bwg/coordinator/gemini_entry_flow_coordinator.mm
+++ b/ios/chrome/browser/intelligence/bwg/coordinator/gemini_entry_flow_coordinator.mm
@@ -6,6 +6,7 @@
 
 #import "base/notreached.h"
 #import "components/signin/public/base/signin_metrics.h"
+#import "components/signin/public/identity_manager/identity_manager.h"
 #import "ios/chrome/browser/authentication/account_menu/coordinator/account_menu_coordinator.h"
 #import "ios/chrome/browser/authentication/account_menu/public/account_menu_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/continuation.h"
@@ -24,6 +25,7 @@
 #import "ios/chrome/browser/shared/public/snackbar/snackbar_message.h"
 #import "ios/chrome/browser/signin/model/authentication_service.h"
 #import "ios/chrome/browser/signin/model/authentication_service_factory.h"
+#import "ios/chrome/browser/signin/model/identity_manager_factory.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ios/web/public/web_state.h"
 #import "ui/base/l10n/l10n_util.h"
@@ -95,8 +97,27 @@
   AuthenticationService* authService =
       AuthenticationServiceFactory::GetForProfile(self.browser->GetProfile());
 
-  // If the user is already signed in, proceed to the next step.
-  if (authService->HasPrimaryIdentity()) {
+  if (!authService) {
+    [self finishWithResult:kGeminiEntryFlowResultUnknown];
+    return;
+  }
+
+  signin::IdentityManager* identityManager =
+      IdentityManagerFactory::GetForProfile(self.browser->GetProfile());
+
+  BOOL isUnverified = NO;
+  if (authService && authService->HasPrimaryIdentity() && identityManager) {
+    CoreAccountId accountId =
+        identityManager->GetPrimaryAccountId(signin::ConsentLevel::kSignin);
+    if (!accountId.empty() &&
+        identityManager->HasAccountWithRefreshTokenInPersistentErrorState(
+            accountId)) {
+      isUnverified = YES;
+    }
+  }
+
+  // If the user is signed in and verified, proceed to the next step.
+  if (authService->HasPrimaryIdentity() && !isUnverified) {
     [self evaluateEligibilityAndRoute];
     return;
   }
@@ -114,7 +135,7 @@
     return;
   }
 
-  // User is signed out, present sign-in.
+  // User is signed out or unverified, present sign-in.
   [self presentSignIn];
 }
 
diff --git a/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent.h b/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent.h
index 66a482c0..bbfe9a5 100644
--- a/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent.h
+++ b/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent.h
@@ -7,9 +7,10 @@
 
 #import <UIKit/UIKit.h>
 
-#import <map>
 #import <memory>
 #import <set>
+#import <utility>
+#import <vector>
 
 #import "base/memory/raw_ptr.h"
 #import "base/observer_list.h"
@@ -75,6 +76,9 @@
                            public TabGridStateObserver,
                            public GeminiContainerMediatorEventHandler {
  public:
+  using AttachedTabsList =
+      std::vector<std::pair<web::WebStateID, __strong GeminiPageContext*>>;
+
   // Observer interface for GeminiBrowserAgent.
   class Observer : public base::CheckedObserver {
    public:
@@ -217,6 +221,9 @@
   // shared tabs.
   NSArray<GeminiPageContext*>* GetSharedTabs() const;
 
+  // Returns whether there is at least one shared (non-active) tab attached.
+  bool HasSharedTabs() const;
+
   // Generates partial page contexts for `tabs_to_fetch` and triggers async
   // full page context retrieval for them. Page contexts are inserted directly
   // into `attached_tabs_`.
@@ -238,6 +245,9 @@
   // the provided web state.
   GeminiTabHelper* GetActiveTabHelper(web::WebState* web_state) const;
 
+  // Returns the ID of the active web state, or an invalid ID if none exists.
+  web::WebStateID GetActiveWebStateID() const;
+
   // Callback for scroll events.
   void OnScrollEvent();
 
@@ -398,6 +408,17 @@
       NSString* tab_id,
       ios::provider::GeminiPageContextAttachmentState new_state);
 
+  // Returns the attached page context for `tab_id`, or nil if not found.
+  GeminiPageContext* GetAttachedPageContext(web::WebStateID tab_id) const;
+
+  // Adds or updates `page_context` for `tab_id` in `attached_tabs_`, preserving
+  // the insertion order if `tab_id` already exists.
+  void SetAttachedPageContext(web::WebStateID tab_id,
+                              GeminiPageContext* page_context);
+
+  // Removes the entry for `tab_id` from `attached_tabs_`.
+  void RemoveAttachedPageContext(web::WebStateID tab_id);
+
   // Mediator for the Gemini container. Remove after bottom sheet migrations.
   __strong GeminiContainerMediator* gemini_container_mediator_ = nil;
 
@@ -426,8 +447,8 @@
   bool is_keyboard_visible_ = false;
 
   // The active and shared tabs currently attached to the floaty, represented by
-  // a mapping of the tab's WebStateID to its page context.
-  std::map<web::WebStateID, __strong GeminiPageContext*> attached_tabs_;
+  // a list of WebStateID and page context tuples in insertion order.
+  AttachedTabsList attached_tabs_;
 
   // Used to track the last shown view state of an invoked floaty. Used to show
   // a hidden floaty with the previous view state.
diff --git a/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent.mm b/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent.mm
index 3a86c422..45aba9e4 100644
--- a/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent.mm
+++ b/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent.mm
@@ -800,33 +800,33 @@
   switch (status) {
     case AVAuthorizationStatusNotDetermined: {
       RecordLiveOSMicPromptShown();
-      [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
-                               completionHandler:^(BOOL granted) {
-                                 dispatch_async(dispatch_get_main_queue(), ^{
-                                   if (granted) {
-                                     RecordLiveOSMicPromptAllowed();
-                                     if (!browser_->GetProfile()->GetPrefs()->GetBoolean(
-                                             prefs::kIOSGeminiLiveMicrophoneSetting)) {
-                                       ShowGeminiMicrophonePermissionAlert(
-                                           base_view_controller,
-                                           browser_->GetProfile()->AsWeakPtr(),
-                                           completion);
-                                     } else {
-                                       if (completion) {
-                                         completion(YES);
-                                       }
-                                     }
-                                   } else {
-                                     RecordLiveOSMicPromptDenied();
-                                     // If user reject mic permission on the
-                                     // native iOS alert, we call completion to
-                                     // reset state.
-                                     if (completion) {
-                                       completion(NO);
-                                     }
-                                   }
-                                 });
-                               }];
+      [AVCaptureDevice
+          requestAccessForMediaType:AVMediaTypeAudio
+                  completionHandler:^(BOOL granted) {
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                      if (granted) {
+                        RecordLiveOSMicPromptAllowed();
+                        if (!browser_->GetProfile()->GetPrefs()->GetBoolean(
+                                prefs::kIOSGeminiLiveMicrophoneSetting)) {
+                          ShowGeminiMicrophonePermissionAlert(
+                              base_view_controller,
+                              browser_->GetProfile()->AsWeakPtr(), completion);
+                        } else {
+                          if (completion) {
+                            completion(YES);
+                          }
+                        }
+                      } else {
+                        RecordLiveOSMicPromptDenied();
+                        // If user reject mic permission on the
+                        // native iOS alert, we call completion to
+                        // reset state.
+                        if (completion) {
+                          completion(NO);
+                        }
+                      }
+                    });
+                  }];
       break;
     }
     case AVAuthorizationStatusAuthorized:
@@ -1449,52 +1449,52 @@
 
 void GeminiBrowserAgent::OnTabPickerSelectionChanged(
     std::set<web::WebStateID> selected_tabs) {
-  web::WebState* active_web_state =
-      browser_->GetWebStateList()->GetActiveWebState();
-  if (!active_web_state) {
-    return;
-  }
-  web::WebStateID active_web_state_id = active_web_state->GetUniqueIdentifier();
+  web::WebStateID active_web_state_id = GetActiveWebStateID();
 
-  // `new_attached_tabs` will replace `attached_tabs_`.
-  std::map<web::WebStateID, __strong GeminiPageContext*> new_attached_tabs;
+  // Create `new_attached_tabs` which will replace `attached_tabs_`.
+  AttachedTabsList new_attached_tabs;
   std::vector<web::WebStateID> tabs_to_fetch;
 
-  // Process each selected tab.
+  // Add existing attached tabs to `new_attached_tabs` in insertion order,
+  // ensuring their attachment state reflects whether they were selected.
+  for (const auto& [tab_id, context] : attached_tabs_) {
+    if (tab_id == active_web_state_id) {
+      if (IsPageContextEligibleForTabPicker(context)) {
+        // Update the active tab's selection state if it was shown in the Tab
+        // Picker.
+        ios::provider::GeminiPageContextAttachmentState new_state =
+            selected_tabs.contains(active_web_state_id)
+                ? ios::provider::GeminiPageContextAttachmentState::kAttached
+                : ios::provider::GeminiPageContextAttachmentState::kDetached;
+        context.geminiPageContextAttachmentState = new_state;
+      }
+      new_attached_tabs.emplace_back(tab_id, context);
+    } else if (selected_tabs.contains(tab_id)) {
+      // Keep existing page context for a tab that is still selected.
+      new_attached_tabs.emplace_back(tab_id, context);
+    }
+  }
+
+  // Identify newly selected tabs and queue them for page context fetch.
   for (web::WebStateID selected_tab : selected_tabs) {
     if (selected_tab == active_web_state_id) {
+      // We already processed the active tab above, so we can ignore it here.
       continue;
     }
-
-    if (GeminiPageContext* existing_context =
-            base::FindPtrOrNull(attached_tabs_, selected_tab)) {
-      // The tab was already selected. Reuse its existing page context.
-      new_attached_tabs[selected_tab] = existing_context;
-    } else {
-      // The tab is newly selected. Mark it as needing to fetch page context.
+    if (!GetAttachedPageContext(selected_tab)) {
+      // Tab is newly selected and we will need to fetch its page context.
       tabs_to_fetch.push_back(selected_tab);
     }
   }
 
-  // Ensure the active tab's state reflects whether it was selected.
-  GeminiPageContext* active_page_context =
-      base::FindPtrOrNull(attached_tabs_, active_web_state_id);
-  CHECK(active_page_context);
-
-  if (IsPageContextEligibleForTabPicker(active_page_context)) {
-    ios::provider::GeminiPageContextAttachmentState new_state =
-        selected_tabs.count(active_web_state_id)
-            ? ios::provider::GeminiPageContextAttachmentState::kAttached
-            : ios::provider::GeminiPageContextAttachmentState::kDetached;
-
-    active_page_context.geminiPageContextAttachmentState = new_state;
-  }
-  new_attached_tabs[active_web_state_id] = active_page_context;
-
   attached_tabs_ = std::move(new_attached_tabs);
 
   UpdateAttachedTabContexts(tabs_to_fetch);
 
+  GeminiPageContext* active_page_context =
+      GetAttachedPageContext(active_web_state_id);
+  CHECK(active_page_context);
+
   ios::provider::UpdateActivePageContext(active_page_context, GetSharedTabs());
 }
 
@@ -1520,7 +1520,7 @@
 
     partial_context.geminiPageContextAttachmentState =
         ios::provider::GeminiPageContextAttachmentState::kAttached;
-    attached_tabs_[tab_to_fetch] = partial_context;
+    SetAttachedPageContext(tab_to_fetch, partial_context);
 
     tabs_to_fetch_str.push_back(
         base::NumberToString(tab_to_fetch.identifier()));
@@ -1676,7 +1676,7 @@
     return;
   }
   web::WebStateID removed_web_state_id = web_state->GetUniqueIdentifier();
-  attached_tabs_.erase(removed_web_state_id);
+  RemoveAttachedPageContext(removed_web_state_id);
 }
 
 void GeminiBrowserAgent::OnWebStateDeleted(web::WebState* web_state) {
@@ -1684,7 +1684,7 @@
     return;
   }
   web::WebStateID deleted_web_state_id = web_state->GetUniqueIdentifier();
-  attached_tabs_.erase(deleted_web_state_id);
+  RemoveAttachedPageContext(deleted_web_state_id);
 }
 
 void GeminiBrowserAgent::OnActiveWebStateChanged(web::WebState* old_active,
@@ -1705,6 +1705,15 @@
     }
     [old_active->GetWebViewProxy().scrollViewProxy
         removeObserver:scroll_observer_];
+
+    web::WebStateID old_active_id = old_active->GetUniqueIdentifier();
+    if (HasSharedTabs() && GetAttachedPageContext(old_active_id)) {
+      // We are switching tabs and there is more than one tab attached to the
+      // conversation. Refetch the old active tab's page context to ensure it
+      // reflects its most recent state (instead of the state when the Floaty
+      // was last invoked).
+      UpdateAttachedTabContexts({old_active_id});
+    }
   }
 
   if (new_active) {
@@ -1966,8 +1975,9 @@
   }
 
   web::WebStateID new_active_id = active_web_state->GetUniqueIdentifier();
-  if (attached_tabs_.count(new_active_id) == 0 ||
-      attached_tabs_[new_active_id].geminiPageContextAttachmentState !=
+  GeminiPageContext* active_context = GetAttachedPageContext(new_active_id);
+  if (!active_context ||
+      active_context.geminiPageContextAttachmentState !=
           ios::provider::GeminiPageContextAttachmentState::kAttached) {
     attached_tabs_.clear();
   }
@@ -2011,8 +2021,8 @@
   // Save the new active web state to attached tabs.
   if (active_web_state && !IsTabGridVisible() &&
       IsGeminiMultiTabContextEnabled()) {
-    attached_tabs_[active_web_state->GetUniqueIdentifier()] =
-        active_page_context;
+    SetAttachedPageContext(active_web_state->GetUniqueIdentifier(),
+                           active_page_context);
   }
 
   ios::provider::UpdateActivePageContext(active_page_context, GetSharedTabs());
@@ -2020,24 +2030,25 @@
 
 NSArray<GeminiPageContext*>* GeminiBrowserAgent::GetSharedTabs() const {
   NSMutableArray<GeminiPageContext*>* shared_tabs = [NSMutableArray array];
-  web::WebState* active_web_state =
-      browser_->GetWebStateList()->GetActiveWebState();
-  web::WebStateID active_web_state_id =
-      active_web_state ? active_web_state->GetUniqueIdentifier()
-                       : web::WebStateID();
+  web::WebStateID active_web_state_id = GetActiveWebStateID();
 
   for (const auto& [tab_id, context] : attached_tabs_) {
-    if (tab_id != active_web_state_id &&
-        context.geminiPageContextAttachmentState ==
-            ios::provider::GeminiPageContextAttachmentState::kAttached) {
+    if (tab_id != active_web_state_id) {
       [shared_tabs addObject:context];
     }
   }
   return shared_tabs;
 }
 
-void GeminiBrowserAgent::UpdateFloatyWithPartialPageContext() {
+bool GeminiBrowserAgent::HasSharedTabs() const {
+  if (attached_tabs_.size() > 1) {
+    return true;
+  }
+  return !attached_tabs_.empty() &&
+         attached_tabs_.begin()->first != GetActiveWebStateID();
+}
 
+void GeminiBrowserAgent::UpdateFloatyWithPartialPageContext() {
   web::WebState* active_web_state =
       browser_->GetWebStateList()->GetActiveWebState();
   GeminiTabHelper* tab_helper = GetActiveTabHelper(active_web_state);
@@ -2172,6 +2183,13 @@
   PropagatePageContextToProvider(gemini_page_context);
 }
 
+web::WebStateID GeminiBrowserAgent::GetActiveWebStateID() const {
+  web::WebState* active_web_state =
+      browser_->GetWebStateList()->GetActiveWebState();
+  return active_web_state ? active_web_state->GetUniqueIdentifier()
+                          : web::WebStateID();
+}
+
 GeminiTabHelper* GeminiBrowserAgent::GetActiveTabHelper(
     web::WebState* web_state) const {
   web::WebState* active_web_state =
@@ -2218,8 +2236,7 @@
 void GeminiBrowserAgent::RetrieveCachedPageContextForTab(
     web::WebStateID selected_tab,
     std::unique_ptr<optimization_guide::proto::PageContext> proto_context) {
-  GeminiPageContext* partial_context =
-      base::FindPtrOrNull(attached_tabs_, selected_tab);
+  GeminiPageContext* partial_context = GetAttachedPageContext(selected_tab);
   if (!partial_context) {
     return;
   }
@@ -2318,22 +2335,23 @@
 void GeminiBrowserAgent::OnFullPageContextAvailableForSharedTab(
     web::WebStateID web_state_id,
     GeminiPageContext* full_page_context) {
+  GeminiPageContext* existing_context = GetAttachedPageContext(web_state_id);
   // The tab was un-shared or closed before full page context became available.
-  if (!attached_tabs_.contains(web_state_id)) {
+  if (!existing_context) {
     return;
   }
 
   full_page_context.geminiPageContextAttachmentState =
-      attached_tabs_[web_state_id].geminiPageContextAttachmentState;
+      existing_context.geminiPageContextAttachmentState;
 
-  attached_tabs_[web_state_id] = full_page_context;
+  SetAttachedPageContext(web_state_id, full_page_context);
 
   // Re-evaluate and push the updated state to the provider.
   web::WebState* active_web_state =
       browser_->GetWebStateList()->GetActiveWebState();
   if (active_web_state) {
-    GeminiPageContext* active_page_context = base::FindPtrOrNull(
-        attached_tabs_, active_web_state->GetUniqueIdentifier());
+    GeminiPageContext* active_page_context =
+        GetAttachedPageContext(active_web_state->GetUniqueIdentifier());
     ios::provider::UpdateActivePageContext(active_page_context,
                                            GetSharedTabs());
   }
@@ -2352,15 +2370,13 @@
   web::WebStateID detached_tab_id =
       web::WebStateID::FromSerializedValue(identifier_value);
 
-  web::WebState* active_web_state =
-      browser_->GetWebStateList()->GetActiveWebState();
-  CHECK(active_web_state);
-  CHECK(detached_tab_id != active_web_state->GetUniqueIdentifier());
+  web::WebStateID active_web_state_id = GetActiveWebStateID();
+  CHECK(detached_tab_id != active_web_state_id);
 
-  attached_tabs_.erase(detached_tab_id);
+  RemoveAttachedPageContext(detached_tab_id);
 
-  GeminiPageContext* active_page_context = base::FindPtrOrNull(
-      attached_tabs_, active_web_state->GetUniqueIdentifier());
+  GeminiPageContext* active_page_context =
+      GetAttachedPageContext(active_web_state_id);
   ios::provider::UpdateActivePageContext(active_page_context, GetSharedTabs());
 }
 
@@ -2376,7 +2392,34 @@
       web::WebStateID::FromSerializedValue(identifier_value);
 
   if (GeminiPageContext* page_context =
-          base::FindPtrOrNull(attached_tabs_, attached_tab_id)) {
+          GetAttachedPageContext(attached_tab_id)) {
     page_context.geminiPageContextAttachmentState = new_state;
   }
 }
+
+GeminiPageContext* GeminiBrowserAgent::GetAttachedPageContext(
+    web::WebStateID tab_id) const {
+  for (const auto& [id, context] : attached_tabs_) {
+    if (id == tab_id) {
+      return context;
+    }
+  }
+  return nil;
+}
+
+void GeminiBrowserAgent::SetAttachedPageContext(
+    web::WebStateID tab_id,
+    GeminiPageContext* page_context) {
+  for (auto& [id, existing_context] : attached_tabs_) {
+    if (id == tab_id) {
+      existing_context = page_context;
+      return;
+    }
+  }
+  attached_tabs_.emplace_back(tab_id, page_context);
+}
+
+void GeminiBrowserAgent::RemoveAttachedPageContext(web::WebStateID tab_id) {
+  std::erase_if(attached_tabs_,
+                [tab_id](const auto& pair) { return pair.first == tab_id; });
+}
diff --git a/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent_unittest.mm b/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent_unittest.mm
index 345a800..35008d6 100644
--- a/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent_unittest.mm
+++ b/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent_unittest.mm
@@ -260,13 +260,18 @@
   }
 
   // Getter for raw `attached_tabs_` member.
-  std::map<web::WebStateID, GeminiPageContext*> GetRawAttachedTabs() {
+  GeminiBrowserAgent::AttachedTabsList GetRawAttachedTabs() {
     return gemini_browser_agent_->attached_tabs_;
   }
 
+  // Getter for an attached tab context by ID.
+  GeminiPageContext* GetRawAttachedTabContext(web::WebStateID id) {
+    return gemini_browser_agent_->GetAttachedPageContext(id);
+  }
+
   // Setter for raw `attached_tabs_` member.
-  void SetRawAttachedTab(web::WebStateID id, GeminiPageContext* context) {
-    gemini_browser_agent_->attached_tabs_[id] = context;
+  void SetRawAttachedTab(web::WebStateID id, GeminiPageContext* page_context) {
+    gemini_browser_agent_->SetAttachedPageContext(id, page_context);
   }
 
   // Wrapper for `AttachedTabsCount`.
@@ -1236,7 +1241,7 @@
   // Verify that attached tabs now contains only the new active tab.
   auto raw_tabs = GetRawAttachedTabs();
   EXPECT_EQ(raw_tabs.size(), 1u);
-  EXPECT_TRUE(raw_tabs.count(new_active_id));
+  EXPECT_NE(nil, GetRawAttachedTabContext(new_active_id));
 
   // Simulate un-minimizing the floaty.
   gemini_browser_agent_->OnViewStateChanged(
@@ -1246,7 +1251,7 @@
 
   // Verify that attached tabs now remains only the new active tab.
   EXPECT_EQ(raw_tabs.size(), 1u);
-  EXPECT_TRUE(raw_tabs.count(new_active_id));
+  EXPECT_NE(nil, GetRawAttachedTabContext(new_active_id));
 }
 
 // Tests that switching from live to floaty mode on an eligible page keeps the
@@ -1370,8 +1375,9 @@
   // Verify it starts as attached.
   auto tabs = GetRawAttachedTabs();
   ASSERT_EQ(1u, tabs.size());
-  ASSERT_EQ(ios::provider::GeminiPageContextAttachmentState::kAttached,
-            tabs[active_id].geminiPageContextAttachmentState);
+  ASSERT_EQ(
+      ios::provider::GeminiPageContextAttachmentState::kAttached,
+      GetRawAttachedTabContext(active_id).geminiPageContextAttachmentState);
 
   NSString* tab_id_str =
       [NSString stringWithFormat:@"%d", active_id.identifier()];
@@ -1381,8 +1387,9 @@
   // Verify it is still in the map but detached.
   tabs = GetRawAttachedTabs();
   EXPECT_EQ(1u, tabs.size());
-  EXPECT_EQ(ios::provider::GeminiPageContextAttachmentState::kDetached,
-            tabs[active_id].geminiPageContextAttachmentState);
+  EXPECT_EQ(
+      ios::provider::GeminiPageContextAttachmentState::kDetached,
+      GetRawAttachedTabContext(active_id).geminiPageContextAttachmentState);
 }
 
 // Tests that DetachTabWithID completely removes a shared tab from the cache.
@@ -1419,7 +1426,7 @@
   // Verify the shared tab is completely removed.
   tabs = GetRawAttachedTabs();
   EXPECT_EQ(1u, tabs.size());
-  EXPECT_EQ(0u, tabs.count(other_id));
+  EXPECT_EQ(nil, GetRawAttachedTabContext(other_id));
 }
 
 // Tests that disabling the page content sharing pref clears attached tabs.
@@ -1481,7 +1488,7 @@
   auto tabs = GetRawAttachedTabs();
   ASSERT_EQ(2u, tabs.size());
 
-  GeminiPageContext* other_context = tabs[other_id];
+  GeminiPageContext* other_context = GetRawAttachedTabContext(other_id);
   ASSERT_TRUE(other_context);
   EXPECT_EQ(ios::provider::GeminiPageContextAttachmentState::kAttached,
             other_context.geminiPageContextAttachmentState);
@@ -1526,8 +1533,7 @@
   // because FakeWebState is naturally ineligible (no URL or MIME type), it
   // falls back to kBlocked without attempting to take a native view snapshot
   // (which would crash the test runner).
-  auto tabs = GetRawAttachedTabs();
-  GeminiPageContext* other_context = tabs[other_id];
+  GeminiPageContext* other_context = GetRawAttachedTabContext(other_id);
   EXPECT_EQ(ios::provider::GeminiPageContextComputationState::kBlocked,
             other_context.geminiPageContextComputationState);
 }
@@ -1564,13 +1570,53 @@
   TriggerPersistTabContextLookupComplete(std::move(cache_map));
 
   // Since cached APC exists, the page context should be updated to kSuccess.
-  auto tabs = GetRawAttachedTabs();
-  GeminiPageContext* other_context = tabs[other_id];
+  GeminiPageContext* other_context = GetRawAttachedTabContext(other_id);
   EXPECT_EQ(ios::provider::GeminiPageContextComputationState::kSuccess,
             other_context.geminiPageContextComputationState);
   EXPECT_TRUE(other_context.uniquePageContext != nullptr);
 }
 
+// Tests that switching away from a shared tab immediately triggers a page
+// context refetch for that tab.
+TEST_F(GeminiBrowserAgentTest, TestSwitchingTabRefetchesSharedTab) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures({kGeminiMultiTabContext, kPageActionMenu}, {});
+
+  web::WebStateID active_id = web_state_->GetUniqueIdentifier();
+  GeminiPageContext* active_context = [[GeminiPageContext alloc] init];
+  active_context.geminiPageContextAttachmentState =
+      ios::provider::GeminiPageContextAttachmentState::kAttached;
+  SetRawAttachedTab(active_id, active_context);
+
+  std::unique_ptr<web::FakeWebState> other_web_state =
+      std::make_unique<web::FakeWebState>();
+  other_web_state->SetBrowserState(profile_);
+  GeminiTabHelper::CreateForWebState(other_web_state.get());
+  WebViewProxyTabHelper::CreateForWebState(other_web_state.get());
+  web::WebStateID other_id = other_web_state->GetUniqueIdentifier();
+  browser_->GetWebStateList()->InsertWebState(
+      std::move(other_web_state),
+      WebStateList::InsertionParams::Automatic().Activate(false));
+
+  gemini_browser_agent_->OnTabPickerSelectionChanged({active_id, other_id});
+
+  // Switch to `other_id` and manually set its computation state to kSuccess.
+  browser_->GetWebStateList()->ActivateWebStateAt(1);
+  GetRawAttachedTabContext(other_id).geminiPageContextComputationState =
+      ios::provider::GeminiPageContextComputationState::kSuccess;
+
+  // Switch back to `active_id` so that we switch away from `other_id`, which
+  // should immediately trigger a refetch for `other_id`.
+  browser_->GetWebStateList()->ActivateWebStateAt(0);
+
+  // Verify that a fresh partial context was generated for `other_id`. Since
+  // `FakeWebState` lacks a URL, it is ineligible and the refetch immediately
+  // sets its partial context state to `kBlocked`.
+  EXPECT_EQ(
+      ios::provider::GeminiPageContextComputationState::kBlocked,
+      GetRawAttachedTabContext(other_id).geminiPageContextComputationState);
+}
+
 // Tests the logic backing the metrics block providers.
 TEST_F(GeminiBrowserAgentTest, TestMetricsBlockProviders) {
   base::test::ScopedFeatureList feature_list;
@@ -1603,6 +1649,53 @@
   EXPECT_TRUE(GetSharedTabsCount() > 0);
 }
 
+// Test that attached shared tabs preserve their insertion order regardless of
+// WebStateID values or subsequent context updates.
+TEST_F(GeminiBrowserAgentTest, TestSharedTabsPreserveInsertionOrder) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures({kGeminiMultiTabContext, kPageActionMenu}, {});
+
+  // Create WebStateIDs out of numerical order to verify that sorting is by
+  // insertion order rather than by WebStateID value.
+  web::WebStateID id_first = web::WebStateID::FromSerializedValue(100);
+  web::WebStateID id_second = web::WebStateID::FromSerializedValue(50);
+  web::WebStateID id_third = web::WebStateID::FromSerializedValue(200);
+
+  GeminiPageContext* context_first = [[GeminiPageContext alloc] init];
+  context_first.geminiPageContextAttachmentState =
+      ios::provider::GeminiPageContextAttachmentState::kAttached;
+  SetRawAttachedTab(id_first, context_first);
+
+  GeminiPageContext* context_second = [[GeminiPageContext alloc] init];
+  context_second.geminiPageContextAttachmentState =
+      ios::provider::GeminiPageContextAttachmentState::kAttached;
+  SetRawAttachedTab(id_second, context_second);
+
+  GeminiPageContext* context_third = [[GeminiPageContext alloc] init];
+  context_third.geminiPageContextAttachmentState =
+      ios::provider::GeminiPageContextAttachmentState::kAttached;
+  SetRawAttachedTab(id_third, context_third);
+
+  auto raw_tabs = GetRawAttachedTabs();
+  ASSERT_EQ(3u, raw_tabs.size());
+  EXPECT_EQ(id_first, raw_tabs[0].first);
+  EXPECT_EQ(id_second, raw_tabs[1].first);
+  EXPECT_EQ(id_third, raw_tabs[2].first);
+
+  // Updating an existing tab's context should not alter insertion order.
+  GeminiPageContext* updated_context_first = [[GeminiPageContext alloc] init];
+  updated_context_first.geminiPageContextAttachmentState =
+      ios::provider::GeminiPageContextAttachmentState::kAttached;
+  SetRawAttachedTab(id_first, updated_context_first);
+
+  raw_tabs = GetRawAttachedTabs();
+  ASSERT_EQ(3u, raw_tabs.size());
+  EXPECT_EQ(id_first, raw_tabs[0].first);
+  EXPECT_EQ(id_second, raw_tabs[1].first);
+  EXPECT_EQ(id_third, raw_tabs[2].first);
+  EXPECT_EQ(updated_context_first, raw_tabs[0].second);
+}
+
 // Tests that ShowGeminiLiveMicrophoneAlert presents the OS settings alert when
 // OS-level microphone permission is denied.
 TEST_F(GeminiBrowserAgentTest, TestShowGeminiLiveMicrophoneAlertWhenOSDenied) {
diff --git a/ios/chrome/browser/intelligence/proto_wrappers/annotated_page_content_extraction_utils.mm b/ios/chrome/browser/intelligence/proto_wrappers/annotated_page_content_extraction_utils.mm
index 1fff6fd..1e02146f 100644
--- a/ios/chrome/browser/intelligence/proto_wrappers/annotated_page_content_extraction_utils.mm
+++ b/ios/chrome/browser/intelligence/proto_wrappers/annotated_page_content_extraction_utils.mm
@@ -886,7 +886,9 @@
             PopulateIframeData(*iframe_data, destination_node, origin,
                                on_frame_extracted);
             grafter.RegisterPlaceholder(*remote, destination_node);
-            return;
+            // Break rather than return so that the placeholder node's geometry
+            // is populated below
+            break;
           }
         }
 
@@ -944,6 +946,13 @@
       if (form_control_data) {
         PopulateFormControlData(*form_control_data, autofill_context,
                                 destination_node);
+        if (destination_node->content_attributes()
+                .form_control_data()
+                .redaction_decision() !=
+            optimization_guide::proto::
+                REDACTION_DECISION_NO_REDACTION_NECESSARY) {
+          grafter.set_has_sensitive_fields_to_redact(true);
+        }
       }
       break;
     }
diff --git a/ios/chrome/browser/intelligence/proto_wrappers/annotated_page_content_extraction_utils_unittest.mm b/ios/chrome/browser/intelligence/proto_wrappers/annotated_page_content_extraction_utils_unittest.mm
index c0196a9..07752b1 100644
--- a/ios/chrome/browser/intelligence/proto_wrappers/annotated_page_content_extraction_utils_unittest.mm
+++ b/ios/chrome/browser/intelligence/proto_wrappers/annotated_page_content_extraction_utils_unittest.mm
@@ -308,3 +308,62 @@
                    .iframe_data()
                    .has_redacted_frame_metadata());
 }
+
+TEST_F(AnnotatedPageContentExtractionUtilsTest,
+       IframePlaceholderPreservesGeometry) {
+  base::Value node_content = base::test::ParseJson(R"(
+    {
+      "contentAttributes": {
+        "attributeType": 5,
+        "iframeData": {
+          "remoteFrameToken": {
+            "value": "1234567890ABCDEF1234567890ABCDEF"
+          }
+        },
+        "geometry": {
+          "visibleBoundingBox": {
+            "x": 20,
+            "y": 350,
+            "width": 375,
+            "height": 500
+          }
+        }
+      }
+    }
+  )");
+
+  ASSERT_TRUE(node_content.is_dict());
+  FrameGrafter grafter;
+  optimization_guide::proto::ContentNode destination_node;
+  url::Origin origin = url::Origin::Create(GURL("https://example.com"));
+
+  PopulateAPCNodeFromContentTree(
+      node_content.GetDict(), origin, grafter,
+      /*autofill_context=*/nullptr, &destination_node,
+      base::RepeatingCallback<void(bool, const std::string&)>());
+
+  EXPECT_TRUE(destination_node.content_attributes().has_geometry());
+  EXPECT_TRUE(destination_node.content_attributes()
+                  .geometry()
+                  .has_visible_bounding_box());
+  EXPECT_EQ(destination_node.content_attributes()
+                .geometry()
+                .visible_bounding_box()
+                .x(),
+            20);
+  EXPECT_EQ(destination_node.content_attributes()
+                .geometry()
+                .visible_bounding_box()
+                .y(),
+            350);
+  EXPECT_EQ(destination_node.content_attributes()
+                .geometry()
+                .visible_bounding_box()
+                .width(),
+            375);
+  EXPECT_EQ(destination_node.content_attributes()
+                .geometry()
+                .visible_bounding_box()
+                .height(),
+            500);
+}
diff --git a/ios/chrome/browser/intelligence/proto_wrappers/frame_grafter.h b/ios/chrome/browser/intelligence/proto_wrappers/frame_grafter.h
index 8517919..52aef512 100644
--- a/ios/chrome/browser/intelligence/proto_wrappers/frame_grafter.h
+++ b/ios/chrome/browser/intelligence/proto_wrappers/frame_grafter.h
@@ -5,6 +5,8 @@
 #ifndef IOS_CHROME_BROWSER_INTELLIGENCE_PROTO_WRAPPERS_FRAME_GRAFTER_H_
 #define IOS_CHROME_BROWSER_INTELLIGENCE_PROTO_WRAPPERS_FRAME_GRAFTER_H_
 
+#import <CoreGraphics/CoreGraphics.h>
+
 #import <map>
 #import <vector>
 
@@ -13,6 +15,12 @@
 #import "components/autofill/core/common/unique_ids.h"
 #import "components/optimization_guide/proto/features/common_quality_data.pb.h"
 
+// Entry for a form control bounding box and its redaction decision.
+struct RedactionBoxEntry {
+  CGRect visible_box;
+  optimization_guide::proto::RedactionDecision decision;
+};
+
 // TODO(crbug.com/458081684): Move away from all autofill dependencies once
 // the migration in ios/web is done for frame registration.
 
@@ -59,6 +67,35 @@
   // Returns the remote frame tokens for all registered placeholders.
   std::vector<autofill::RemoteFrameToken> GetRemoteFrames() const;
 
+  // Traverses the fully assembled ContentNode tree and populates
+  // universal_bounding_boxes_for_redaction_ with top-level absolute
+  // coordinates for form controls, recursively accumulating ancestor iframe
+  // offsets.
+  void CollectFormControlRedactionBoxesFromTree(
+      const optimization_guide::proto::ContentNode& root_node);
+
+  // Returns the flat registry of redaction boxes across the page, translated to
+  // top-level document coordinates.
+  const std::vector<RedactionBoxEntry>& universal_bounding_boxes_for_redaction()
+      const {
+    return universal_bounding_boxes_for_redaction_;
+  }
+
+  // Returns a mutable reference to the universal list of redaction boxes across
+  // the page in top-level document coordinates.
+  std::vector<RedactionBoxEntry>&
+  mutable_universal_bounding_boxes_for_redaction() {
+    return universal_bounding_boxes_for_redaction_;
+  }
+
+  // Whether any form control across the page requires redaction.
+  bool has_sensitive_fields_to_redact() const {
+    return has_sensitive_fields_to_redact_;
+  }
+  void set_has_sensitive_fields_to_redact(bool val) {
+    has_sensitive_fields_to_redact_ = val;
+  }
+
   // Resolves all unregistered content by using `mapping_lookup` to map
   // placeholders to their content (RemoteFrameToken => LocalFrameToken
   // mapping). The `placer` is used to complete grafting of content that doesn't
@@ -81,6 +118,10 @@
   std::map<autofill::RemoteFrameToken,
            raw_ptr<optimization_guide::proto::ContentNode>>
       placeholders_;
+  // Flat registry of all redaction boxes translated to top-level coordinates.
+  std::vector<RedactionBoxEntry> universal_bounding_boxes_for_redaction_;
+  // True if any form control on the page requires redaction.
+  bool has_sensitive_fields_to_redact_ = false;
 };
 
 #endif  // IOS_CHROME_BROWSER_INTELLIGENCE_PROTO_WRAPPERS_FRAME_GRAFTER_H_
diff --git a/ios/chrome/browser/intelligence/proto_wrappers/frame_grafter.mm b/ios/chrome/browser/intelligence/proto_wrappers/frame_grafter.mm
index 0ce2a87..c234eda 100644
--- a/ios/chrome/browser/intelligence/proto_wrappers/frame_grafter.mm
+++ b/ios/chrome/browser/intelligence/proto_wrappers/frame_grafter.mm
@@ -137,3 +137,55 @@
   unregistered_content_.clear();
   placeholders_.clear();
 }
+
+namespace {
+
+void TraverseAndCollectFormControlRedactionBoxes(
+    const optimization_guide::proto::ContentNode& node,
+    CGPoint accumulated_offset,
+    std::vector<RedactionBoxEntry>& redaction_boxes) {
+  const auto& attributes = node.content_attributes();
+  const auto& geometry = attributes.geometry();
+
+  CGPoint next_offset = accumulated_offset;
+  if (attributes.has_form_control_data()) {
+    const auto& form_control = attributes.form_control_data();
+    if (form_control.redaction_decision() !=
+            optimization_guide::proto::
+                REDACTION_DECISION_NO_REDACTION_NECESSARY &&
+        geometry.has_visible_bounding_box()) {
+      const auto& box = geometry.visible_bounding_box();
+      redaction_boxes.push_back({
+          .visible_box = CGRectMake(box.x() + accumulated_offset.x,
+                                    box.y() + accumulated_offset.y, box.width(),
+                                    box.height()),
+          .decision = form_control.redaction_decision(),
+      });
+    }
+  } else if (attributes.attribute_type() ==
+             optimization_guide::proto::CONTENT_ATTRIBUTE_IFRAME) {
+    if (!geometry.has_visible_bounding_box()) {
+      return;
+    }
+    const auto& iframe_box = geometry.visible_bounding_box();
+    next_offset.x += iframe_box.x();
+    next_offset.y += iframe_box.y();
+  }
+
+  for (const auto& child_node : node.children_nodes()) {
+    TraverseAndCollectFormControlRedactionBoxes(child_node, next_offset,
+                                                redaction_boxes);
+  }
+}
+
+}  // namespace
+
+void FrameGrafter::CollectFormControlRedactionBoxesFromTree(
+    const optimization_guide::proto::ContentNode& root_node) {
+  universal_bounding_boxes_for_redaction_.clear();
+  if (!has_sensitive_fields_to_redact_) {
+    return;
+  }
+  TraverseAndCollectFormControlRedactionBoxes(
+      root_node, CGPointZero, universal_bounding_boxes_for_redaction_);
+}
diff --git a/ios/chrome/browser/intelligence/proto_wrappers/frame_grafter_unittest.mm b/ios/chrome/browser/intelligence/proto_wrappers/frame_grafter_unittest.mm
index 5364243..67579ce4 100644
--- a/ios/chrome/browser/intelligence/proto_wrappers/frame_grafter_unittest.mm
+++ b/ios/chrome/browser/intelligence/proto_wrappers/frame_grafter_unittest.mm
@@ -338,3 +338,262 @@
   ASSERT_EQ(unresolved.size(), 1u);
   EXPECT_EQ(unresolved[0], &placeholder);
 }
+
+// Test that redaction bounding boxes from root and child frames are translated
+// by the placeholder origin and collected via post-assembly tree traversal.
+TEST_F(FrameGrafterTest, TranslatesFormControlBoundingBoxesForRedaction) {
+  FrameGrafter grafter;
+  autofill::LocalFrameToken local_token = CreateLocalToken();
+  autofill::RemoteFrameToken remote_token = CreateRemoteToken();
+
+  optimization_guide::proto::ContentNode root_node;
+
+  // Root frame child node with a redaction box at (10, 20, 100, 30).
+  auto* root_control = root_node.add_children_nodes();
+  root_control->mutable_content_attributes()
+      ->mutable_form_control_data()
+      ->set_redaction_decision(
+          optimization_guide::proto::
+              REDACTION_DECISION_REDACTED_HAS_BEEN_PASSWORD);
+  auto* root_box = root_control->mutable_content_attributes()
+                       ->mutable_geometry()
+                       ->mutable_visible_bounding_box();
+  root_box->set_x(10);
+  root_box->set_y(20);
+  root_box->set_width(100);
+  root_box->set_height(30);
+
+  // Placeholder positioned at (50, 100, 400, 300).
+  auto* placeholder = root_node.add_children_nodes();
+  placeholder->mutable_content_attributes()->set_attribute_type(
+      optimization_guide::proto::CONTENT_ATTRIBUTE_IFRAME);
+  auto* iframe_box = placeholder->mutable_content_attributes()
+                         ->mutable_geometry()
+                         ->mutable_visible_bounding_box();
+  iframe_box->set_x(50);
+  iframe_box->set_y(100);
+  iframe_box->set_width(400);
+  iframe_box->set_height(300);
+  grafter.RegisterPlaceholder(remote_token, placeholder);
+
+  // Subframe has a child node with redaction decision at local coordinates (15,
+  // 25, 80, 20).
+  FrameGrafter::FrameContent* content = grafter.DeclareContent(local_token);
+  auto* sub_control = content->content.add_children_nodes();
+  sub_control->mutable_content_attributes()
+      ->mutable_form_control_data()
+      ->set_redaction_decision(
+          optimization_guide::proto::
+              REDACTION_DECISION_REDACTED_IS_SENSITIVE_PAYMENT_FIELD);
+  auto* sub_box = sub_control->mutable_content_attributes()
+                      ->mutable_geometry()
+                      ->mutable_visible_bounding_box();
+  sub_box->set_x(15);
+  sub_box->set_y(25);
+  sub_box->set_width(80);
+  sub_box->set_height(20);
+
+  auto mapping_lookup = base::BindRepeating(
+      [](autofill::RemoteFrameToken remote, autofill::LocalFrameToken local,
+         autofill::RemoteFrameToken requested_remote)
+          -> std::optional<autofill::LocalFrameToken> {
+        if (requested_remote == remote) {
+          return local;
+        }
+        return std::nullopt;
+      },
+      remote_token, local_token);
+
+  auto placer = base::BindRepeating(
+      [](FrameGrafter::FrameContent unregistered) { FAIL(); });
+
+  grafter.ResolveUnregisteredContent(mapping_lookup, placer, base::DoNothing());
+  grafter.set_has_sensitive_fields_to_redact(true);
+  grafter.CollectFormControlRedactionBoxesFromTree(root_node);
+
+  const auto& boxes = grafter.universal_bounding_boxes_for_redaction();
+  ASSERT_EQ(boxes.size(), 2u);
+
+  // Main frame box is untouched: (10, 20, 100, 30).
+  EXPECT_TRUE(
+      CGRectEqualToRect(boxes[0].visible_box, CGRectMake(10, 20, 100, 30)));
+  EXPECT_EQ(
+      boxes[0].decision,
+      optimization_guide::proto::REDACTION_DECISION_REDACTED_HAS_BEEN_PASSWORD);
+
+  // Child frame box is shifted by placeholder (50, 100): (65, 125, 80, 20).
+  EXPECT_TRUE(
+      CGRectEqualToRect(boxes[1].visible_box, CGRectMake(65, 125, 80, 20)));
+  EXPECT_EQ(boxes[1].decision,
+            optimization_guide::proto::
+                REDACTION_DECISION_REDACTED_IS_SENSITIVE_PAYMENT_FIELD);
+}
+
+// Test that redaction bounding boxes inside multi-level nested iframes (Main ->
+// Iframe A -> Iframe B) accumulate ancestor offsets correctly.
+TEST_F(FrameGrafterTest, MultiLevelNestedIframeRedactionBoxes) {
+  FrameGrafter grafter;
+  autofill::LocalFrameToken local_token_a = CreateLocalToken();
+  autofill::RemoteFrameToken remote_token_a = CreateRemoteToken();
+  autofill::LocalFrameToken local_token_b = CreateLocalToken();
+  autofill::RemoteFrameToken remote_token_b = CreateRemoteToken();
+
+  optimization_guide::proto::ContentNode root_node;
+
+  // Iframe A placeholder at (100, 200, 500, 400).
+  auto* placeholder_a = root_node.add_children_nodes();
+  placeholder_a->mutable_content_attributes()->set_attribute_type(
+      optimization_guide::proto::CONTENT_ATTRIBUTE_IFRAME);
+  auto* box_a = placeholder_a->mutable_content_attributes()
+                    ->mutable_geometry()
+                    ->mutable_visible_bounding_box();
+  box_a->set_x(100);
+  box_a->set_y(200);
+  box_a->set_width(500);
+  box_a->set_height(400);
+  grafter.RegisterPlaceholder(remote_token_a, placeholder_a);
+
+  // Content A contains Iframe B placeholder at local coordinates (30, 40, 200,
+  // 150).
+  FrameGrafter::FrameContent* content_a = grafter.DeclareContent(local_token_a);
+  auto* placeholder_b = content_a->content.add_children_nodes();
+  placeholder_b->mutable_content_attributes()->set_attribute_type(
+      optimization_guide::proto::CONTENT_ATTRIBUTE_IFRAME);
+  auto* box_b = placeholder_b->mutable_content_attributes()
+                    ->mutable_geometry()
+                    ->mutable_visible_bounding_box();
+  box_b->set_x(30);
+  box_b->set_y(40);
+  box_b->set_width(200);
+  box_b->set_height(150);
+  grafter.RegisterPlaceholder(remote_token_b, placeholder_b);
+
+  // Content B contains a sensitive payment form control at local coordinates
+  // (10, 15, 60, 20).
+  FrameGrafter::FrameContent* content_b = grafter.DeclareContent(local_token_b);
+  auto* sub_control = content_b->content.add_children_nodes();
+  sub_control->mutable_content_attributes()
+      ->mutable_form_control_data()
+      ->set_redaction_decision(
+          optimization_guide::proto::
+              REDACTION_DECISION_REDACTED_IS_SENSITIVE_PAYMENT_FIELD);
+  auto* sub_box = sub_control->mutable_content_attributes()
+                      ->mutable_geometry()
+                      ->mutable_visible_bounding_box();
+  sub_box->set_x(10);
+  sub_box->set_y(15);
+  sub_box->set_width(60);
+  sub_box->set_height(20);
+
+  auto mapping_lookup = base::BindRepeating(
+      [](autofill::RemoteFrameToken rem_a, autofill::LocalFrameToken loc_a,
+         autofill::RemoteFrameToken rem_b, autofill::LocalFrameToken loc_b,
+         autofill::RemoteFrameToken requested_remote)
+          -> std::optional<autofill::LocalFrameToken> {
+        if (requested_remote == rem_a) {
+          return loc_a;
+        }
+        if (requested_remote == rem_b) {
+          return loc_b;
+        }
+        return std::nullopt;
+      },
+      remote_token_a, local_token_a, remote_token_b, local_token_b);
+
+  auto placer = base::BindRepeating(
+      [](FrameGrafter::FrameContent unregistered) { FAIL(); });
+
+  grafter.ResolveUnregisteredContent(mapping_lookup, placer, base::DoNothing());
+  grafter.set_has_sensitive_fields_to_redact(true);
+  grafter.CollectFormControlRedactionBoxesFromTree(root_node);
+
+  const auto& boxes = grafter.universal_bounding_boxes_for_redaction();
+  ASSERT_EQ(boxes.size(), 1u);
+
+  // Expected offset = (100 + 30 + 10, 200 + 40 + 15) = (140, 255).
+  EXPECT_TRUE(
+      CGRectEqualToRect(boxes[0].visible_box, CGRectMake(140, 255, 60, 20)));
+  EXPECT_EQ(boxes[0].decision,
+            optimization_guide::proto::
+                REDACTION_DECISION_REDACTED_IS_SENSITIVE_PAYMENT_FIELD);
+}
+
+// Test that if an intermediate parent iframe has no visible bounding box (e.g.
+// display: none), its child redaction boxes are skipped.
+TEST_F(FrameGrafterTest, HiddenParentIframeSkipsChildRedactions) {
+  FrameGrafter grafter;
+  autofill::LocalFrameToken local_token = CreateLocalToken();
+  autofill::RemoteFrameToken remote_token = CreateRemoteToken();
+
+  optimization_guide::proto::ContentNode root_node;
+
+  // Iframe placeholder with NO visible bounding box.
+  auto* placeholder = root_node.add_children_nodes();
+  placeholder->mutable_content_attributes()->set_attribute_type(
+      optimization_guide::proto::CONTENT_ATTRIBUTE_IFRAME);
+  grafter.RegisterPlaceholder(remote_token, placeholder);
+
+  // Subframe has a child node with a redaction decision.
+  FrameGrafter::FrameContent* content = grafter.DeclareContent(local_token);
+  auto* sub_control = content->content.add_children_nodes();
+  sub_control->mutable_content_attributes()
+      ->mutable_form_control_data()
+      ->set_redaction_decision(
+          optimization_guide::proto::
+              REDACTION_DECISION_REDACTED_IS_SENSITIVE_PAYMENT_FIELD);
+  auto* sub_box = sub_control->mutable_content_attributes()
+                      ->mutable_geometry()
+                      ->mutable_visible_bounding_box();
+  sub_box->set_x(15);
+  sub_box->set_y(25);
+  sub_box->set_width(80);
+  sub_box->set_height(20);
+
+  auto mapping_lookup = base::BindRepeating(
+      [](autofill::RemoteFrameToken remote, autofill::LocalFrameToken local,
+         autofill::RemoteFrameToken requested_remote)
+          -> std::optional<autofill::LocalFrameToken> {
+        if (requested_remote == remote) {
+          return local;
+        }
+        return std::nullopt;
+      },
+      remote_token, local_token);
+
+  auto placer = base::BindRepeating(
+      [](FrameGrafter::FrameContent unregistered) { FAIL(); });
+
+  grafter.ResolveUnregisteredContent(mapping_lookup, placer, base::DoNothing());
+  grafter.set_has_sensitive_fields_to_redact(true);
+  grafter.CollectFormControlRedactionBoxesFromTree(root_node);
+
+  const auto& boxes = grafter.universal_bounding_boxes_for_redaction();
+  EXPECT_TRUE(boxes.empty());
+}
+
+// Test that CollectFormControlRedactionBoxesFromTree skips tree traversal when
+// has_sensitive_fields_to_redact is false.
+TEST_F(FrameGrafterTest,
+       CollectFormControlRedactionBoxesFromTree_SkipsWhenNoSensitiveFields) {
+  FrameGrafter grafter;
+  optimization_guide::proto::ContentNode root_node;
+
+  auto* root_control = root_node.add_children_nodes();
+  root_control->mutable_content_attributes()
+      ->mutable_form_control_data()
+      ->set_redaction_decision(
+          optimization_guide::proto::
+              REDACTION_DECISION_REDACTED_HAS_BEEN_PASSWORD);
+  auto* root_box = root_control->mutable_content_attributes()
+                       ->mutable_geometry()
+                       ->mutable_visible_bounding_box();
+  root_box->set_x(10);
+  root_box->set_y(20);
+  root_box->set_width(100);
+  root_box->set_height(30);
+
+  EXPECT_FALSE(grafter.has_sensitive_fields_to_redact());
+  grafter.CollectFormControlRedactionBoxesFromTree(root_node);
+
+  EXPECT_TRUE(grafter.universal_bounding_boxes_for_redaction().empty());
+}
diff --git a/ios/chrome/browser/intelligence/proto_wrappers/page_context_extractor_java_script_feature_unittest.mm b/ios/chrome/browser/intelligence/proto_wrappers/page_context_extractor_java_script_feature_unittest.mm
index 1f3bfb6..f249a45 100644
--- a/ios/chrome/browser/intelligence/proto_wrappers/page_context_extractor_java_script_feature_unittest.mm
+++ b/ios/chrome/browser/intelligence/proto_wrappers/page_context_extractor_java_script_feature_unittest.mm
@@ -2254,6 +2254,56 @@
   EXPECT_FALSE(offscreen_interaction_info);
 }
 
+// Test fieldset extraction logic.
+TEST_P(PageContextExtractorJavaScriptFeatureTest,
+       ExtractPageContext_FieldsetControl) {
+  const std::string html = "<html><body>"
+                           "  <fieldset name=\"test_fieldset\">"
+                           "    <input type=\"text\" name=\"test_input\" />"
+                           "  </fieldset>"
+                           "</body></html>";
+  web::test::LoadHtml(base::SysUTF8ToNSString(html),
+                      test_server_.GetURL(kMainPagePath), web_state());
+
+  std::optional<base::Value> result_value = RunExtraction(
+      web_state()->GetPageWorldWebFramesManager()->GetMainWebFrame(),
+      /*include_cross_origin_frame_content=*/false,
+      /*use_rich_extraction=*/true,
+      /*use_rich_extraction_with_actionable=*/false,
+      /*extract_paid_content=*/false,
+      /*attempt_paid_content_json_fixing=*/false, "nonce", base::Seconds(1));
+  ASSERT_TRUE(result_value.has_value());
+  EXPECT_FALSE(result_value->is_none());
+
+  const base::DictValue& dict = result_value->GetDict();
+  const base::DictValue* root_node = dict.FindDict("rootNode");
+  ASSERT_TRUE(root_node);
+  const base::ListValue* root_children = root_node->FindList("childrenNodes");
+  ASSERT_TRUE(root_children);
+  ASSERT_GE(root_children->size(), 1u);
+
+  const base::DictValue& fieldset_node = (*root_children)[0].GetDict();
+  std::optional<double> fieldset_attribute_type =
+      fieldset_node.FindDoubleByDottedPath("contentAttributes.attributeType");
+  ASSERT_TRUE(fieldset_attribute_type.has_value());
+  EXPECT_EQ(*fieldset_attribute_type,
+            static_cast<double>(
+                optimization_guide::proto::CONTENT_ATTRIBUTE_FORM_CONTROL));
+
+  std::optional<double> fieldset_form_control_type =
+      fieldset_node.FindDoubleByDottedPath(
+          "contentAttributes.formControlData.formControlType");
+  ASSERT_TRUE(fieldset_form_control_type.has_value());
+  EXPECT_EQ(*fieldset_form_control_type,
+            static_cast<double>(
+                optimization_guide::proto::FORM_CONTROL_TYPE_FIELDSET));
+
+  const std::string* field_name = fieldset_node.FindStringByDottedPath(
+      "contentAttributes.formControlData.fieldName");
+  ASSERT_TRUE(field_name);
+  EXPECT_EQ(*field_name, "test_fieldset");
+}
+
 INSTANTIATE_TEST_SUITE_P(All,
                          PageContextExtractorJavaScriptFeatureTest,
                          ::testing::Values(IPCExtractionMethod::kNative,
diff --git a/ios/chrome/browser/intelligence/proto_wrappers/page_context_wrapper.mm b/ios/chrome/browser/intelligence/proto_wrappers/page_context_wrapper.mm
index 929a498..dfd082f 100644
--- a/ios/chrome/browser/intelligence/proto_wrappers/page_context_wrapper.mm
+++ b/ios/chrome/browser/intelligence/proto_wrappers/page_context_wrapper.mm
@@ -52,6 +52,8 @@
 #import "ios/web/public/js_messaging/content_world.h"
 #import "ios/web/public/js_messaging/web_frame.h"
 #import "ios/web/public/js_messaging/web_frames_manager.h"
+#import "ios/web/public/ui/crw_web_view_proxy.h"
+#import "ios/web/public/ui/crw_web_view_scroll_view_proxy.h"
 #import "ios/web/public/web_state.h"
 #import "net/base/schemeful_site.h"
 #import "url/gurl.h"
@@ -246,6 +248,11 @@
   // Unique pointer to the PageContext proto.
   std::unique_ptr<optimization_guide::proto::PageContext> _pageContext;
 
+  // The boxes to redact over the screenshot.
+  std::vector<CGRect> _universalBoundingBoxesForRedaction;
+
+  // Raw screenshot image before redaction masking is applied.
+  UIImage* _rawScreenshotImage;
   // The current PageContext instance's metrics logger. Only created when async
   // tasks execution is started.
   PageContextWrapperMetrics* _pageContextMetrics;
@@ -523,7 +530,7 @@
       return;
     }
 
-    [strongSelf encodeImageAndSetTabScreenshot:image];
+    [strongSelf stashRawScreenshotImage:image];
     barrier.Run();
   };
 
@@ -903,6 +910,9 @@
           _grafter, registrar, _config->include_same_site_only(),
           _pageContext->mutable_annotated_page_content());
     }
+    if (_rawScreenshotImage) {
+      [self applyRedactionsAndEncodeScreenshot:_rawScreenshotImage];
+    }
     response = base::ok(std::move(_pageContext));
     completionStatus = PageContextCompletionStatus::kSuccess;
   }
@@ -933,7 +943,7 @@
         if (!strongSelf) {
           return;
         }
-        [strongSelf encodeImageAndSetTabScreenshot:image];
+        [strongSelf stashRawScreenshotImage:image];
         barrier.Run();
       });
 }
@@ -948,17 +958,59 @@
   }
 }
 
-// Convert UIImage snapshot to PNG, and then to base64 encoded string. Set the
-// tab screenshot on the current PageContext.
-- (void)encodeImageAndSetTabScreenshot:(UIImage*)image {
-  [self stopTextHighlighting];
+// Returns YES for any RedactionDecision enums that require screenshot
+// redaction.
+// Note: In this CL, this defaults to NO. Subsequent CLs will override
+// this method to return YES for their respective redaction decision enums
+// when enabled.
+- (BOOL)shouldRedactDecisionForScreenshot:
+    (optimization_guide::proto::RedactionDecision)decision {
+  return NO;
+}
 
-  if (!image) {
-    [_pageContextMetrics
-        executionFinishedForTask:PageContextTask::kScreenshot
-            withCompletionStatus:PageContextCompletionStatus::kFailure];
-    DLOG(WARNING) << "Failed to fetch webpage screenshot.";
-    return;
+- (void)applyRedactionsAndEncodeScreenshot:(UIImage*)image {
+  if (_pageContext && _pageContext->has_annotated_page_content()) {
+    _grafter.CollectFormControlRedactionBoxesFromTree(
+        _pageContext->annotated_page_content().root_node());
+  }
+
+  // If the page has a non-standard zoomScale, we scale the extracted DOM layout
+  // coordinates by zoomScale so that they align 1:1 with the pixels in the
+  // screenshot bitmap captured from WKWebView.
+  CGFloat zoomScale = 1.0;
+  if (_webState && _webState->GetWebViewProxy() &&
+      _webState->GetWebViewProxy().scrollViewProxy) {
+    zoomScale = _webState->GetWebViewProxy().scrollViewProxy.zoomScale;
+  }
+
+  _universalBoundingBoxesForRedaction.clear();
+
+  for (const auto& entry : _grafter.universal_bounding_boxes_for_redaction()) {
+    if ([self shouldRedactDecisionForScreenshot:entry.decision]) {
+      _universalBoundingBoxesForRedaction.push_back(
+          CGRectMake(entry.visible_box.origin.x * zoomScale,
+                     entry.visible_box.origin.y * zoomScale,
+                     entry.visible_box.size.width * zoomScale,
+                     entry.visible_box.size.height * zoomScale));
+    }
+  }
+
+  if (!_universalBoundingBoxesForRedaction.empty()) {
+    UIGraphicsImageRendererFormat* format =
+        [UIGraphicsImageRendererFormat defaultFormat];
+    format.scale = image.scale;
+    format.opaque = NO;
+
+    UIGraphicsImageRenderer* renderer =
+        [[UIGraphicsImageRenderer alloc] initWithSize:image.size format:format];
+    image =
+        [renderer imageWithActions:^(UIGraphicsImageRendererContext* context) {
+          [image drawAtPoint:CGPointZero];
+          [[UIColor blackColor] setFill];
+          for (const CGRect& rect : _universalBoundingBoxesForRedaction) {
+            [[UIBezierPath bezierPathWithRect:rect] fill];
+          }
+        }];
   }
 
   NSData* imageData = UIImagePNGRepresentation(image);
@@ -973,11 +1025,31 @@
   NSString* base64String = [imageData base64EncodedStringWithOptions:0];
   _pageContext->set_tab_screenshot(base::SysNSStringToUTF8(base64String));
 
+  _universalBoundingBoxesForRedaction.clear();
+  _rawScreenshotImage = nil;
+
   [_pageContextMetrics
       executionFinishedForTask:PageContextTask::kScreenshot
           withCompletionStatus:PageContextCompletionStatus::kSuccess];
 }
 
+// Temporarily saves the raw screenshot image captured from the WebState so that
+// redactions can be applied and PNG encoding performed after APC extraction and
+// iframe coordinate grafting complete.
+- (void)stashRawScreenshotImage:(UIImage*)image {
+  [self stopTextHighlighting];
+
+  if (!image) {
+    [_pageContextMetrics
+        executionFinishedForTask:PageContextTask::kScreenshot
+            withCompletionStatus:PageContextCompletionStatus::kFailure];
+    DLOG(WARNING) << "Failed to fetch webpage screenshot.";
+    return;
+  }
+
+  _rawScreenshotImage = image;
+}
+
 // If it exists, convert the PDF data to base64 encoded string and set it in
 // the PageContext proto.
 - (void)setEncodedPDFData:(std::string)base64PDFData {
@@ -1622,4 +1694,28 @@
       .Run(base::unexpected(PageContextWrapperError::kTimeout));
 }
 
+#pragma mark - Testing
+
+// Test-only hooks to allow synchronous unit testing of the screenshot masking
+// and redaction bounding box collection engine without requiring an
+// asynchronous WKWebView E2E snapshot round-trip.
+- (void)encodeImageAndSetTabScreenshotForTesting:(UIImage*)image {
+  [self stashRawScreenshotImage:image];
+  if (_rawScreenshotImage) {
+    [self applyRedactionsAndEncodeScreenshot:_rawScreenshotImage];
+  }
+}
+
+- (void)setBoxesToRedactForTesting:(const std::vector<CGRect>&)boxes {
+  _universalBoundingBoxesForRedaction = boxes;
+}
+
+- (const std::vector<CGRect>&)boxesToRedactForTesting {
+  return _universalBoundingBoxesForRedaction;
+}
+
+- (optimization_guide::proto::PageContext*)pageContextForTesting {
+  return _pageContext.get();
+}
+
 @end
diff --git a/ios/chrome/browser/intelligence/proto_wrappers/page_context_wrapper_unittest.mm b/ios/chrome/browser/intelligence/proto_wrappers/page_context_wrapper_unittest.mm
index cdd6824b..8e7086f0 100644
--- a/ios/chrome/browser/intelligence/proto_wrappers/page_context_wrapper_unittest.mm
+++ b/ios/chrome/browser/intelligence/proto_wrappers/page_context_wrapper_unittest.mm
@@ -99,6 +99,13 @@
 
 // TODO(crbug.com/475577435): Extend test coverage for Rich Extraction.
 
+@interface PageContextWrapper (Testing)
+- (void)encodeImageAndSetTabScreenshotForTesting:(UIImage*)image;
+- (void)setBoxesToRedactForTesting:(const std::vector<CGRect>&)boxes;
+- (const std::vector<CGRect>&)boxesToRedactForTesting;
+- (optimization_guide::proto::PageContext*)pageContextForTesting;
+@end
+
 namespace {
 
 const char kMainPagePath[] = "/main.html";
@@ -7811,6 +7818,67 @@
   }
 }
 
+UIImage* CreateTestImage(CGSize size, CGFloat scale) {
+  UIGraphicsImageRendererFormat* format =
+      [UIGraphicsImageRendererFormat defaultFormat];
+  format.scale = scale;
+  UIGraphicsImageRenderer* renderer =
+      [[UIGraphicsImageRenderer alloc] initWithSize:size format:format];
+  return [renderer imageWithActions:^(UIGraphicsImageRendererContext* context) {
+    [[UIColor whiteColor] setFill];
+    [context fillRect:CGRectMake(0, 0, size.width, size.height)];
+  }];
+}
+
+// Test that screenshot redaction masking handles empty bounding box lists
+// without error. Configures a wrapper with no redaction boxes, encodes a
+// synthetic test image, and verifies that a valid base64-encoded PNG screenshot
+// is produced.
+TEST_P(PageContextWrapperTest, ImageRedaction_EmptyArray) {
+  PageContextWrapper* wrapper = [[PageContextWrapper alloc]
+        initWithWebState:web_state()
+                  config:PageContextWrapperConfigBuilder().Build()
+      completionCallback:base::BindOnce(
+                             [](PageContextWrapperCallbackResponse response) {
+                             })];
+  [wrapper setBoxesToRedactForTesting:{}];
+
+  UIImage* test_image = CreateTestImage(CGSizeMake(100, 100), 2.0);
+
+  [wrapper encodeImageAndSetTabScreenshotForTesting:test_image];
+
+  EXPECT_TRUE([wrapper boxesToRedactForTesting].empty());
+
+  optimization_guide::proto::PageContext* page_context =
+      [wrapper pageContextForTesting];
+  ASSERT_TRUE(page_context->has_tab_screenshot());
+  std::string screenshot_data;
+  ASSERT_TRUE(
+      base::Base64Decode(page_context->tab_screenshot(), &screenshot_data));
+  EXPECT_GT(screenshot_data.length(), 0u);
+}
+
+// Test that redaction bounding boxes extending outside image boundaries do not
+// crash. Configures negative coordinates and boxes larger than the test image,
+// and verifies that the renderer safely clips drawing operations without error.
+TEST_P(PageContextWrapperTest, ImageRedaction_OutOfBoundsClippingSafety) {
+  PageContextWrapper* wrapper = [[PageContextWrapper alloc]
+        initWithWebState:web_state()
+                  config:PageContextWrapperConfigBuilder().Build()
+      completionCallback:base::BindOnce(
+                             [](PageContextWrapperCallbackResponse response) {
+                             })];
+
+  std::vector<CGRect> boxes = {CGRectMake(-10, -10, 50, 50),
+                               CGRectMake(80, 80, 100, 100)};
+  [wrapper setBoxesToRedactForTesting:boxes];
+
+  UIImage* test_image = CreateTestImage(CGSizeMake(50, 50), 1.0);
+
+  [wrapper encodeImageAndSetTabScreenshotForTesting:test_image];
+  EXPECT_TRUE([wrapper boxesToRedactForTesting].empty());
+}
+
 INSTANTIATE_TEST_SUITE_P(,
                          PageContextWrapperTest,
                          testing::Bool(),
diff --git a/ios/chrome/browser/intelligence/proto_wrappers/resources/annotated_page_content_extraction.ts b/ios/chrome/browser/intelligence/proto_wrappers/resources/annotated_page_content_extraction.ts
index 722355a..b216dff 100644
--- a/ios/chrome/browser/intelligence/proto_wrappers/resources/annotated_page_content_extraction.ts
+++ b/ios/chrome/browser/intelligence/proto_wrappers/resources/annotated_page_content_extraction.ts
@@ -340,6 +340,7 @@
 const TAG_TEXTAREA = 'TEXTAREA';
 const TAG_SELECT = 'SELECT';
 const TAG_BUTTON = 'BUTTON';
+const TAG_FIELDSET = 'FIELDSET';
 const TAG_P = 'P';
 const TAG_OL = 'OL';
 const TAG_UL = 'UL';
@@ -868,6 +869,10 @@
     return FormControlType.TEXT_AREA;
   }
 
+  if (tagName === TAG_FIELDSET) {
+    return FormControlType.FIELDSET;
+  }
+
   // Fallback, though we shouldn't reach here for form controls.
   return undefined;
 }
@@ -2694,7 +2699,8 @@
     case TAG_INPUT:
     case TAG_TEXTAREA:
     case TAG_SELECT:
-    case TAG_BUTTON: {
+    case TAG_BUTTON:
+    case TAG_FIELDSET: {
       return {
         childrenNodes: [],
         contentAttributes: {
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
index 31f9bb5..235df33 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
@@ -358,6 +358,7 @@
   // infrastructure still needs to be built to allow the restoration window to
   // be displayed when exiting and re-entering the experience.
   [self prepareSnapshotCapturingInfrastructure];
+  // C2PA: LensImageMetadata provides its own C2PA support. b/541315801
   LensImageSource* imageSource =
       [[LensImageSource alloc] initWithImageMetadata:metadata];
   [self handleOverlayImageSourceFound:imageSource
@@ -405,6 +406,7 @@
   [self captureSnapshotWithCompletion:^(UIImage* snapshot) {
     LensImageSource* imageSource =
         [[LensImageSource alloc] initWithSnapshot:snapshot];
+    // C2PA: Snapshots should not have C2PA metadata. b/541315801
     [weakSelf handleOverlayImageSourceFound:imageSource
                                    animated:animated
                                  completion:completion];
diff --git a/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm b/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm
index c8f94f1..c34fc57 100644
--- a/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm
+++ b/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm
@@ -1024,6 +1024,8 @@
     return;
   }
 
+  // TODO(b/541315801): C2PA: Copied image could have C2PA metadata; candidate
+  // to pass raw bytes.
   UIImage* image = optionalImage->ToUIImage();
   if (usingLens) {
     id<LensCommands> handler =
diff --git a/ios/chrome/browser/menu/ui_bundled/browser_action_factory.mm b/ios/chrome/browser/menu/ui_bundled/browser_action_factory.mm
index 9d6febe..cf302cce 100644
--- a/ios/chrome/browser/menu/ui_bundled/browser_action_factory.mm
+++ b/ios/chrome/browser/menu/ui_bundled/browser_action_factory.mm
@@ -378,6 +378,8 @@
           return;
         }
 
+        // TODO(b/541315801): C2PA: Clipboard images could have C2PA metadata;
+        // candidate to pass raw bytes.
         UIImage* image = [optionalImage.value().ToUIImage() copy];
         SearchImageWithLensCommand* command =
             [[SearchImageWithLensCommand alloc]
diff --git a/ios/chrome/browser/metrics/model/BUILD.gn b/ios/chrome/browser/metrics/model/BUILD.gn
index aa99e809..fff57a7a 100644
--- a/ios/chrome/browser/metrics/model/BUILD.gn
+++ b/ios/chrome/browser/metrics/model/BUILD.gn
@@ -451,6 +451,7 @@
     ":tab_usage_recorder_metrics",
     "//base/test:test_support",
     "//components/country_codes",
+    "//components/metrics",
     "//components/metrics/demographics",
     "//components/metrics/private_metrics",
     "//components/metrics/private_metrics:private_metrics_features",
diff --git a/ios/chrome/browser/metrics/model/metrics_app_interface.h b/ios/chrome/browser/metrics/model/metrics_app_interface.h
index efe8de9..16ee2db 100644
--- a/ios/chrome/browser/metrics/model/metrics_app_interface.h
+++ b/ios/chrome/browser/metrics/model/metrics_app_interface.h
@@ -171,6 +171,15 @@
 + (NSError*)expectCount:(int)expectedCount
           forUserAction:(NSString*)userAction [[nodiscard]];
 
+// Returns YES if advanced reporting migration is completed.
++ (BOOL)isAdvancedReportingProfileMigrationDone;
+
+// Returns YES if advanced reporting is enabled for the profile.
++ (BOOL)isAdvancedReportingEnabled;
+
+// Sets the advanced reporting enabled preference for the profile.
++ (void)setAdvancedReportingEnabledPref:(BOOL)enabled;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_METRICS_MODEL_METRICS_APP_INTERFACE_H_
diff --git a/ios/chrome/browser/metrics/model/metrics_app_interface.mm b/ios/chrome/browser/metrics/model/metrics_app_interface.mm
index b4a1b07..673f8ae 100644
--- a/ios/chrome/browser/metrics/model/metrics_app_interface.mm
+++ b/ios/chrome/browser/metrics/model/metrics_app_interface.mm
@@ -14,17 +14,22 @@
 #import "components/metrics/dwa/dwa_entry_builder.h"
 #import "components/metrics/dwa/dwa_recorder.h"
 #import "components/metrics/dwa/dwa_service.h"
+#import "components/metrics/metrics_profile_pref_names.h"
 #import "components/metrics/metrics_service.h"
 #import "components/metrics/private_metrics/private_metrics_reporting_service.h"
 #import "components/metrics/private_metrics/puma_histogram_functions.h"
 #import "components/metrics/private_metrics/puma_service.h"
 #import "components/metrics_services_manager/metrics_services_manager.h"
 #import "components/network_time/network_time_tracker.h"
+#import "components/prefs/pref_service.h"
 #import "components/regional_capabilities/regional_capabilities_country_id.h"
 #import "components/ukm/ukm_service.h"
 #import "components/ukm/ukm_test_helper.h"
 #import "ios/chrome/browser/metrics/model/ios_chrome_metrics_service_accessor.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
+#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
+#import "ios/chrome/browser/shared/model/profile/profile_manager_ios.h"
+#import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/app/histogram_test_util.h"
 #import "ios/testing/nserror_util.h"
 #import "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
@@ -442,4 +447,29 @@
   return nil;
 }
 
++ (PrefService*)profilePrefs {
+  ProfileIOS* profile = chrome_test_util::GetOriginalProfile();
+  return profile ? profile->GetPrefs() : nullptr;
+}
+
++ (BOOL)isAdvancedReportingProfileMigrationDone {
+  PrefService* prefs = [self profilePrefs];
+  return prefs ? prefs->GetBoolean(
+                     metrics::prefs::kAdvancedReportingProfileMigrationDone)
+               : NO;
+}
+
++ (BOOL)isAdvancedReportingEnabled {
+  PrefService* prefs = [self profilePrefs];
+  return prefs ? prefs->GetBoolean(metrics::prefs::kAdvancedReportingEnabled)
+               : NO;
+}
+
++ (void)setAdvancedReportingEnabledPref:(BOOL)enabled {
+  PrefService* prefs = [self profilePrefs];
+  if (prefs) {
+    prefs->SetBoolean(metrics::prefs::kAdvancedReportingEnabled, enabled);
+  }
+}
+
 @end
diff --git a/ios/chrome/browser/metrics/model/ukm_egtest.mm b/ios/chrome/browser/metrics/model/ukm_egtest.mm
index eac36b7..bd8db44b 100644
--- a/ios/chrome/browser/metrics/model/ukm_egtest.mm
+++ b/ios/chrome/browser/metrics/model/ukm_egtest.mm
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #import "base/ios/ios_util.h"
+#import "base/strings/sys_string_conversions.h"
+#import "components/metrics/metrics_features.h"
 #import "ios/chrome/browser/authentication/test/signin_earl_grey.h"
 #import "ios/chrome/browser/authentication/test/signin_earl_grey_ui_test_util.h"
 #import "ios/chrome/browser/metrics/model/metrics_app_interface.h"
@@ -12,6 +14,7 @@
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#import "ios/testing/earl_grey/app_launch_manager.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
 
 using chrome_test_util::GoogleServicesSettingsButton;
@@ -364,4 +367,124 @@
 }
 // LINT.ThenChange(/chrome/browser/metrics/ukm_browsertest.cc:HistoryDeleteCheck)
 
+- (AppLaunchConfiguration)appConfigurationForTestCase {
+  AppLaunchConfiguration config = [super appConfigurationForTestCase];
+  if ([self isRunningTest:@selector(testConsentMigrationSignedIn)] ||
+      [self isRunningTest:@selector(testConsentMigrationRollback)]) {
+    config.features_disabled.push_back(
+        metrics::features::kRestructureMetricsConsentSettings);
+  }
+  return config;
+}
+
+- (void)testConsentMigrationSignedIn {
+  // Since the app launched with the feature disabled (due to
+  // appConfigurationForTestCase), verify migration is not done yet.
+  GREYAssert(![MetricsAppInterface isAdvancedReportingProfileMigrationDone],
+             @"Migration should not be done when feature is disabled.");
+
+  // Make sure any pending prefs changes are written to disk before relaunch.
+  [ChromeEarlGrey commitPendingUserPrefsWrite];
+
+  // Relaunch the app with the feature enabled and metrics reporting force
+  // enabled.
+  AppLaunchConfiguration config_enabled;
+  config_enabled.features_enabled.push_back(
+      metrics::features::kRestructureMetricsConsentSettings);
+  config_enabled.additional_args.push_back("--force-enable-metrics-reporting");
+
+  // Pass the fake identity to survive relaunch.
+  FakeSystemIdentity* identity = [FakeSystemIdentity fakeIdentity1];
+  std::string add_identities_arg =
+      "-add-fake-identities=" +
+      [FakeSystemIdentity encodeIdentitiesToBase64:@[ identity ]];
+  config_enabled.additional_args.push_back(add_identities_arg);
+
+  config_enabled.relaunch_policy = ForceRelaunchByCleanShutdown;
+  [[AppLaunchManager sharedManager]
+      ensureAppLaunchedWithConfiguration:config_enabled];
+
+  // Re-apply the mock overrides in the newly launched app process.
+  [MetricsAppInterface overrideMetricsAndCrashReportingForTesting];
+  GREYAssert(![MetricsAppInterface setMetricsAndCrashReportingForTesting:YES],
+             @"Unpaired set/reset of user consent.");
+
+  // Verify that migration is now done, and advanced reporting is enabled (since
+  // MSBB was enabled in setUp).
+  GREYAssert([MetricsAppInterface isAdvancedReportingProfileMigrationDone],
+             @"Migration should be done after relaunch.");
+  GREYAssert([MetricsAppInterface isAdvancedReportingEnabled],
+             @"Advanced reporting should be enabled.");
+
+  // Verify that the UKM recorder is indeed enabled.
+  GREYAssert([MetricsAppInterface checkUKMRecordingEnabled:YES],
+             @"Failed to assert that UKM was enabled.");
+}
+
+- (void)testConsentMigrationRollback {
+  // Since the app launched with the feature disabled, verify migration is not
+  // done yet.
+  GREYAssert(![MetricsAppInterface isAdvancedReportingProfileMigrationDone],
+             @"Migration should not be done when feature is disabled.");
+
+  // Make sure any pending prefs changes are written to disk before relaunch.
+  [ChromeEarlGrey commitPendingUserPrefsWrite];
+
+  // Relaunch the app with the feature enabled.
+  AppLaunchConfiguration config_enabled;
+  config_enabled.features_enabled.push_back(
+      metrics::features::kRestructureMetricsConsentSettings);
+
+  FakeSystemIdentity* identity = [FakeSystemIdentity fakeIdentity1];
+  std::string add_identities_arg =
+      "-add-fake-identities=" +
+      [FakeSystemIdentity encodeIdentitiesToBase64:@[ identity ]];
+  config_enabled.additional_args.push_back(add_identities_arg);
+  config_enabled.relaunch_policy = ForceRelaunchByCleanShutdown;
+
+  [[AppLaunchManager sharedManager]
+      ensureAppLaunchedWithConfiguration:config_enabled];
+
+  // Re-apply the mock overrides.
+  [MetricsAppInterface overrideMetricsAndCrashReportingForTesting];
+  GREYAssert(![MetricsAppInterface setMetricsAndCrashReportingForTesting:YES],
+             @"Unpaired set/reset of user consent.");
+
+  // Verify that migration is now done, and advanced reporting is enabled (since
+  // MSBB was enabled in setUp).
+  GREYAssert([MetricsAppInterface isAdvancedReportingProfileMigrationDone],
+             @"Migration should be done after relaunch.");
+  GREYAssert([MetricsAppInterface isAdvancedReportingEnabled],
+             @"Advanced reporting should be enabled.");
+  GREYAssert([MetricsAppInterface checkUKMRecordingEnabled:YES],
+             @"UKM should be enabled.");
+
+  // Manually disable advanced reporting preference.
+  [MetricsAppInterface setAdvancedReportingEnabledPref:NO];
+  GREYAssert([MetricsAppInterface checkUKMRecordingEnabled:NO],
+             @"UKM should be disabled when advanced reporting is disabled.");
+
+  [ChromeEarlGrey commitPendingUserPrefsWrite];
+
+  // Relaunch the app with the feature flag DISABLED (rollback).
+  AppLaunchConfiguration config_disabled;
+  config_disabled.features_disabled.push_back(
+      metrics::features::kRestructureMetricsConsentSettings);
+  config_disabled.additional_args.push_back(add_identities_arg);
+  config_disabled.relaunch_policy = ForceRelaunchByCleanShutdown;
+
+  [[AppLaunchManager sharedManager]
+      ensureAppLaunchedWithConfiguration:config_disabled];
+
+  // Re-apply the mock overrides.
+  [MetricsAppInterface overrideMetricsAndCrashReportingForTesting];
+  GREYAssert(![MetricsAppInterface setMetricsAndCrashReportingForTesting:YES],
+             @"Unpaired set/reset of user consent.");
+
+  // Since the feature flag is DISABLED, the UKM recorder should ignore
+  // the advanced reporting pref and remain ENABLED (since MSBB is enabled).
+  GREYAssert([MetricsAppInterface checkUKMRecordingEnabled:YES],
+             @"UKM should still be enabled under old logic.");
+}
+
 @end
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_bottom_sheet_view_controller.h b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_bottom_sheet_view_controller.h
index 3ccdeda..0d4b72b6 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_bottom_sheet_view_controller.h
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_bottom_sheet_view_controller.h
@@ -41,6 +41,9 @@
 // The magic stack view controller.
 @property(nonatomic, strong) UIViewController* magicStackViewController;
 
+// Embeds the Most Visited view.
+- (void)embedMostVisitedView:(UIView*)mostVisitedView;
+
 // Clears state and delegates.
 - (void)invalidate;
 
@@ -53,6 +56,9 @@
 // Returns the collapsed offset of the bottom sheet.
 - (CGFloat)collapsedOffset;
 
+// Updates the bottom sheet position to match its current snapping state.
+- (void)updateBottomSheetPositionAnimated:(BOOL)animated;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_NTP_UI_BUNDLED_NEW_TAB_PAGE_BOTTOM_SHEET_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_bottom_sheet_view_controller.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_bottom_sheet_view_controller.mm
index d081302..e5e5dce 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_bottom_sheet_view_controller.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_bottom_sheet_view_controller.mm
@@ -8,6 +8,8 @@
 
 #import "ios/chrome/browser/content_suggestions/magic_stack/public/magic_stack_constants.h"
 #import "ios/chrome/browser/content_suggestions/ui/content_suggestions_collection_utils.h"
+#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_constants.h"
+#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h"
 #import "ios/chrome/browser/ntp/ui_bundled/scroll_delegate_proxy.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
 
@@ -21,6 +23,7 @@
 
 // Spacing/margin constants for content container.
 constexpr CGFloat kContentContainerTopMargin = 16.0;
+constexpr CGFloat kSpaceBetweenModules = 14.0;
 constexpr CGFloat kMagicStackToFeedSpacing = 16.0;
 
 // Minimum drag velocity required to trigger a state transition.
@@ -35,9 +38,12 @@
 
 @implementation NewTabPageBottomSheetViewController {
   UIView* _dragHandle;
+  UIView* _headerContainerView;
   UIView* _magicStackContainerView;
+  NSLayoutConstraint* _magicStackTopConstraint;
+  UIView* _mostVisitedContainerView;
+  UIView* _mostVisitedView;
   UIView* _contentContainerView;
-  NSLayoutConstraint* _contentContainerTopConstraint;
   BottomSheetSnappingState _sheetState;
 
   CGSize _lastSize;
@@ -87,21 +93,56 @@
     [_dragHandle.heightAnchor constraintEqualToConstant:5],
   ]];
 
+  // Add header container view that encapsulates MVT and Magic Stack.
+  _headerContainerView = [[UIView alloc] init];
+  _headerContainerView.translatesAutoresizingMaskIntoConstraints = NO;
+  [visualEffectView.contentView addSubview:_headerContainerView];
+
+  [NSLayoutConstraint activateConstraints:@[
+    [_headerContainerView.leadingAnchor
+        constraintEqualToAnchor:visualEffectView.contentView.leadingAnchor],
+    [_headerContainerView.trailingAnchor
+        constraintEqualToAnchor:visualEffectView.contentView.trailingAnchor],
+    [_headerContainerView.topAnchor
+        constraintEqualToAnchor:_dragHandle.bottomAnchor
+                       constant:kContentContainerTopMargin],
+  ]];
+
+  // Add most visited tiles container view if feature is enabled.
+  if (IsMVTInBottomSheetEnabled()) {
+    _mostVisitedContainerView = [[UIView alloc] init];
+    _mostVisitedContainerView.translatesAutoresizingMaskIntoConstraints = NO;
+    [_headerContainerView addSubview:_mostVisitedContainerView];
+
+    [NSLayoutConstraint activateConstraints:@[
+      [_mostVisitedContainerView.leadingAnchor
+          constraintEqualToAnchor:_headerContainerView.leadingAnchor],
+      [_mostVisitedContainerView.trailingAnchor
+          constraintEqualToAnchor:_headerContainerView.trailingAnchor],
+      [_mostVisitedContainerView.topAnchor
+          constraintEqualToAnchor:_headerContainerView.topAnchor],
+    ]];
+  }
+
   // Add magic stack container view.
   _magicStackContainerView = [[UIView alloc] init];
   _magicStackContainerView.translatesAutoresizingMaskIntoConstraints = NO;
-  [visualEffectView.contentView addSubview:_magicStackContainerView];
+  [_headerContainerView addSubview:_magicStackContainerView];
+
+  _magicStackTopConstraint = [_magicStackContainerView.topAnchor
+      constraintEqualToAnchor:_headerContainerView.topAnchor
+                     constant:0.0];
+  _magicStackTopConstraint.active = YES;
 
   [NSLayoutConstraint activateConstraints:@[
     [_magicStackContainerView.leadingAnchor
-        constraintEqualToAnchor:visualEffectView.contentView.leadingAnchor],
+        constraintEqualToAnchor:_headerContainerView.leadingAnchor],
     [_magicStackContainerView.trailingAnchor
-        constraintEqualToAnchor:visualEffectView.contentView.trailingAnchor],
-    [_magicStackContainerView.topAnchor
-        constraintEqualToAnchor:_dragHandle.bottomAnchor
-                       constant:kContentContainerTopMargin],
+        constraintEqualToAnchor:_headerContainerView.trailingAnchor],
     [_magicStackContainerView.heightAnchor
         constraintEqualToConstant:kMagicStackHeight],
+    [_headerContainerView.bottomAnchor
+        constraintEqualToAnchor:_magicStackContainerView.bottomAnchor],
   ]];
 
   // Add content container view.
@@ -109,16 +150,14 @@
   _contentContainerView.translatesAutoresizingMaskIntoConstraints = NO;
   [visualEffectView.contentView addSubview:_contentContainerView];
 
-  _contentContainerTopConstraint = [_contentContainerView.topAnchor
-      constraintEqualToAnchor:_dragHandle.bottomAnchor
-                     constant:kContentContainerTopMargin];
-  _contentContainerTopConstraint.active = YES;
-
   [NSLayoutConstraint activateConstraints:@[
     [_contentContainerView.leadingAnchor
         constraintEqualToAnchor:visualEffectView.contentView.leadingAnchor],
     [_contentContainerView.trailingAnchor
         constraintEqualToAnchor:visualEffectView.contentView.trailingAnchor],
+    [_contentContainerView.topAnchor
+        constraintEqualToAnchor:_headerContainerView.bottomAnchor
+                       constant:kMagicStackToFeedSpacing],
     [_contentContainerView.bottomAnchor
         constraintEqualToAnchor:visualEffectView.contentView.bottomAnchor],
   ]];
@@ -133,6 +172,10 @@
   [self updateContentContainerInsetForOffset:
             [self targetOffsetForState:_sheetState]];
 
+  if (_mostVisitedView) {
+    [self embedMostVisitedView:_mostVisitedView];
+  }
+
   if (_magicStackViewController) {
     [self embedMagicStackViewController];
   }
@@ -166,6 +209,8 @@
   self.delegate = nil;
   self.feedViewController = nil;
   self.magicStackViewController = nil;
+  _mostVisitedView = nil;
+  _headerContainerView = nil;
 }
 
 #pragma mark - Action Targets
@@ -284,6 +329,29 @@
   [_magicStackViewController didMoveToParentViewController:self];
 }
 
+- (void)embedMostVisitedView:(UIView*)mostVisitedView {
+  if (_mostVisitedView != mostVisitedView) {
+    if (_mostVisitedView) {
+      [_mostVisitedView removeFromSuperview];
+    }
+    _mostVisitedView = mostVisitedView;
+  }
+  if (self.isViewLoaded && _mostVisitedView && _mostVisitedContainerView) {
+    if (_mostVisitedView.superview == _mostVisitedContainerView) {
+      return;
+    }
+    _mostVisitedView.translatesAutoresizingMaskIntoConstraints = NO;
+    [_mostVisitedContainerView addSubview:_mostVisitedView];
+    AddSameConstraints(_mostVisitedView, _mostVisitedContainerView);
+    [self.view setNeedsLayout];
+    [self.view layoutIfNeeded];
+    CGFloat offset = _bottomSheetTopConstraint
+                         ? _bottomSheetTopConstraint.constant
+                         : [self restingOffset];
+    [self updateContentContainerInsetForOffset:offset];
+  }
+}
+
 #pragma mark - Snapping Offsets
 
 - (CGFloat)collapsedOffset {
@@ -428,21 +496,47 @@
   CGFloat expanded = [self expandedOffset];
   CGFloat resting = [self restingOffset];
   if (resting <= expanded) {
-    _contentContainerTopConstraint.constant = kContentContainerTopMargin;
-    _magicStackContainerView.alpha = 1.0;
+    if (IsMVTInBottomSheetEnabled()) {
+      _mostVisitedContainerView.alpha = 0.0;
+      _magicStackTopConstraint.constant =
+          content_suggestions::FakeOmniboxHeight();
+    } else {
+      _magicStackContainerView.alpha = 1.0;
+      _magicStackTopConstraint.constant = 0.0;
+    }
     return;
   }
   CGFloat progress = (topOffset - expanded) / (resting - expanded);
   progress = MIN(1.0, MAX(0.0, progress));
 
-  _magicStackContainerView.alpha = progress;
+  if (IsMVTInBottomSheetEnabled()) {
+    _mostVisitedContainerView.alpha = progress;
 
-  CGFloat expandedTopPadding =
-      content_suggestions::FakeOmniboxHeight() + kContentContainerTopMargin;
-  CGFloat restingTopPadding = kMagicStackHeight + kMagicStackToFeedSpacing;
+    CGFloat mvtHeight = CGRectGetHeight(_mostVisitedContainerView.bounds);
+    if (mvtHeight <= 0 && _mostVisitedView) {
+      mvtHeight = [_mostVisitedView
+                      systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]
+                      .height;
+    }
 
-  _contentContainerTopConstraint.constant =
-      progress * restingTopPadding + (1.0 - progress) * expandedTopPadding;
+    CGFloat expandedMagicStackTop = content_suggestions::FakeOmniboxHeight();
+    CGFloat restingMagicStackTop =
+        (mvtHeight > 0) ? (mvtHeight + kSpaceBetweenModules) : 0.0;
+
+    _magicStackTopConstraint.constant =
+        progress * restingMagicStackTop +
+        (1.0 - progress) * expandedMagicStackTop;
+  } else {
+    _magicStackContainerView.alpha = progress;
+
+    CGFloat expandedMagicStackTop =
+        content_suggestions::FakeOmniboxHeight() - kMagicStackHeight;
+    CGFloat restingMagicStackTop = 0.0;
+
+    _magicStackTopConstraint.constant =
+        progress * restingMagicStackTop +
+        (1.0 - progress) * expandedMagicStackTop;
+  }
 }
 
 - (void)handlePan:(UIPanGestureRecognizer*)gesture {
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_bottom_sheet_view_controller_unittest.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_bottom_sheet_view_controller_unittest.mm
index 42a1159..d7a934e1 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_bottom_sheet_view_controller_unittest.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_bottom_sheet_view_controller_unittest.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_bottom_sheet_view_controller.h"
 
+#import "base/test/scoped_feature_list.h"
+#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h"
 #import "testing/gtest/include/gtest/gtest.h"
 #import "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
@@ -92,3 +94,63 @@
       updateContentContainerInsetForOffset:(expanded + resting) / 2.0];
   EXPECT_FLOAT_EQ(0.5, container.alpha);
 }
+
+// Tests that the header container embeds magic stack container view and exists.
+TEST_F(NewTabPageBottomSheetViewControllerTest, TestHeaderContainerHierarchy) {
+  [view_controller_ loadViewIfNeeded];
+  UIView* headerContainer =
+      [view_controller_ valueForKey:@"_headerContainerView"];
+  EXPECT_NE(nil, headerContainer);
+  EXPECT_TRUE([headerContainer isDescendantOfView:view_controller_.view]);
+
+  UIView* magicStackContainer =
+      [view_controller_ valueForKey:@"_magicStackContainerView"];
+  EXPECT_NE(nil, magicStackContainer);
+  EXPECT_TRUE([magicStackContainer isDescendantOfView:headerContainer]);
+
+  UIView* contentContainer =
+      [view_controller_ valueForKey:@"_contentContainerView"];
+  EXPECT_NE(nil, contentContainer);
+  EXPECT_TRUE([contentContainer isDescendantOfView:view_controller_.view]);
+}
+
+// Tests that the MVT container view alpha updates based on top offset when
+// kMVTInBottomSheet is enabled.
+TEST_F(NewTabPageBottomSheetViewControllerTest,
+       TestMVTContainerAlphaWhenEnabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(kMVTInBottomSheet);
+
+  [view_controller_ loadViewIfNeeded];
+  UIView* mvtContainer =
+      [view_controller_ valueForKey:@"_mostVisitedContainerView"];
+  EXPECT_NE(nil, mvtContainer);
+
+  id mock_delegate =
+      OCMProtocolMock(@protocol(NewTabPageBottomSheetViewControllerDelegate));
+  view_controller_.delegate = mock_delegate;
+
+  OCMStub([mock_delegate
+              restingOffsetForBottomSheetViewController:view_controller_])
+      .andReturn(400.0);
+  OCMStub([mock_delegate
+              collapsedOffsetForBottomSheetViewController:view_controller_])
+      .andReturn(600.0);
+
+  CGFloat expanded = [view_controller_ expandedOffset];
+  CGFloat resting = [view_controller_ restingOffset];
+
+  // At resting offset, progress should be 1.0, meaning alpha is 1.0
+  [view_controller_ updateContentContainerInsetForOffset:resting];
+  EXPECT_FLOAT_EQ(1.0, mvtContainer.alpha);
+
+  // At expanded offset, progress should be 0.0, meaning alpha is 0.0
+  [view_controller_ updateContentContainerInsetForOffset:expanded];
+  EXPECT_FLOAT_EQ(0.0, mvtContainer.alpha);
+
+  // At halfway between expanded and resting, progress should be 0.5, alpha
+  // should be 0.5
+  [view_controller_
+      updateContentContainerInsetForOffset:(expanded + resting) / 2.0];
+  EXPECT_FLOAT_EQ(0.5, mvtContainer.alpha);
+}
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h
index 516eb68..ca80f63 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h
@@ -57,6 +57,12 @@
 // Feature flag to enable the New Tab Page padding updates.
 BASE_DECLARE_FEATURE(kNewTabPagePaddingUpdate);
 
+// Feature flag to place the Most Visited Tiles in the bottom sheet.
+BASE_DECLARE_FEATURE(kMVTInBottomSheet);
+
+// Checks if the Most Visited Tiles should be placed in the bottom sheet.
+bool IsMVTInBottomSheetEnabled();
+
 #pragma mark - Feature parameters
 
 // A parameter value for the feed's refresh threshold when the feed has already
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.mm
index 2fe015e2..7c0c2ee 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.mm
@@ -31,6 +31,8 @@
 
 BASE_FEATURE(kNewTabPageRedesign, base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kMVTInBottomSheet, base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kNewTabPagePaddingUpdate, base::FEATURE_DISABLED_BY_DEFAULT);
 
 #pragma mark - Feature parameters
@@ -66,6 +68,10 @@
 
 #pragma mark - Helpers
 
+bool IsMVTInBottomSheetEnabled() {
+  return base::FeatureList::IsEnabled(kMVTInBottomSheet);
+}
+
 bool IsDiscoverFeedTopSyncPromoEnabled() {
   // Promo should not be shown on FRE, or for users in Great Britain for AADC
   // compliance.
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_redesign_view_controller.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_redesign_view_controller.mm
index c986654c..578bffd 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_redesign_view_controller.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_redesign_view_controller.mm
@@ -17,6 +17,7 @@
 #import "ios/chrome/browser/ntp/ui_bundled/fake_location_bar_view.h"
 #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_bottom_sheet_view_controller.h"
 #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_color_palette.h"
+#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_constants.h"
 #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_content_delegate.h"
 #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h"
 #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_commands.h"
@@ -60,9 +61,6 @@
 // resting/collapsed.
 constexpr CGFloat kRestingSheetMVTTopMargin = 12.0;
 
-// Default fallback height for the MVTs container before initial layout.
-constexpr CGFloat kDefaultMVTHeightFallback = 124.0;
-
 // Top margin of the Google logo view.
 constexpr CGFloat kLogoTopMargin = 40.0;
 
@@ -112,9 +110,6 @@
 @property(nonatomic, assign, readwrite) CGFloat collectionShiftingOffset;
 @property(nonatomic, assign, readwrite) BOOL scrolledToMinimumHeight;
 
-// The Most Visited Tiles (MVTs) view.
-@property(nonatomic, strong) UIView* mostVisitedView;
-
 // Private helpers
 - (void)handleTraitChanges;
 
@@ -131,6 +126,7 @@
 
   FakeLocationBarView* _fakeLocationBar;
   UIView* _mostVisitedContainerView;
+  UIView* _mostVisitedView;
   NSLayoutConstraint* _fakeLocationBarTopConstraint;
   NTPIdentityDiscButton* _identityDiscButton;
 
@@ -244,12 +240,14 @@
     _quickActionsViewController.view.hidden = !self.quickActionsVisible;
   }
 
-  // Add Most Visited Tiles (MVTs) container.
-  _mostVisitedContainerView = [[UIView alloc] init];
-  _mostVisitedContainerView.translatesAutoresizingMaskIntoConstraints = NO;
-  // Insert BELOW the sheet.
-  [self.view insertSubview:_mostVisitedContainerView
-              belowSubview:_bottomSheetViewController.view];
+  // Add Most Visited Tiles (MVTs) container if not in bottom sheet.
+  if (!IsMVTInBottomSheetEnabled()) {
+    _mostVisitedContainerView = [[UIView alloc] init];
+    _mostVisitedContainerView.translatesAutoresizingMaskIntoConstraints = NO;
+    // Insert BELOW the sheet.
+    [self.view insertSubview:_mostVisitedContainerView
+                belowSubview:_bottomSheetViewController.view];
+  }
 
   // Configure layout constraints
   _fakeLocationBarTopConstraint = [_fakeLocationBar.topAnchor
@@ -271,13 +269,17 @@
         constraintEqualToAnchor:_fakeLocationBar.trailingAnchor],
     [_buttonStack.centerYAnchor
         constraintEqualToAnchor:_fakeLocationBar.centerYAnchor],
-
-    [_mostVisitedContainerView.widthAnchor
-        constraintEqualToAnchor:_fakeLocationBar.widthAnchor],
-    [_mostVisitedContainerView.centerXAnchor
-        constraintEqualToAnchor:_fakeLocationBar.centerXAnchor],
   ]];
 
+  if (!IsMVTInBottomSheetEnabled()) {
+    [NSLayoutConstraint activateConstraints:@[
+      [_mostVisitedContainerView.widthAnchor
+          constraintEqualToAnchor:_fakeLocationBar.widthAnchor],
+      [_mostVisitedContainerView.centerXAnchor
+          constraintEqualToAnchor:_fakeLocationBar.centerXAnchor],
+    ]];
+  }
+
   if (IsAimEnabledInNtp()) {
     _qaTopConstraint = [_quickActionsViewController.view.topAnchor
         constraintEqualToAnchor:_fakeLocationBar.bottomAnchor
@@ -292,24 +294,30 @@
     ]];
   }
 
-  UIView* anchorView = self.quickActionsVisible
-                           ? _quickActionsViewController.view
-                           : _fakeLocationBar;
-  CGFloat constant = self.quickActionsVisible ? kQuickActionSpacingBottom
-                                              : kOmniboxToMVTSpacing;
+  if (!IsMVTInBottomSheetEnabled()) {
+    UIView* anchorView = self.quickActionsVisible
+                             ? _quickActionsViewController.view
+                             : _fakeLocationBar;
+    CGFloat constant = self.quickActionsVisible ? kQuickActionSpacingBottom
+                                                : kOmniboxToMVTSpacing;
 
-  _mvtTopConstraint = [_mostVisitedContainerView.topAnchor
-      constraintEqualToAnchor:anchorView.bottomAnchor
-                     constant:constant];
-  _mvtTopConstraint.active = YES;
+    _mvtTopConstraint = [_mostVisitedContainerView.topAnchor
+        constraintEqualToAnchor:anchorView.bottomAnchor
+                       constant:constant];
+    _mvtTopConstraint.active = YES;
+  }
 
   _fakeLocationBar.layer.cornerRadius =
       _fakeLocationBarHeightConstraint.constant / 2.0;
 
   [self refreshFakeboxContent];
 
-  if (self.mostVisitedView) {
-    [self embedMostVisitedView];
+  if (_mostVisitedView) {
+    if (IsMVTInBottomSheetEnabled()) {
+      [_bottomSheetViewController embedMostVisitedView:_mostVisitedView];
+    } else {
+      [self embedMostVisitedView];
+    }
   }
 
   if (_searchEngineLogoView) {
@@ -411,7 +419,7 @@
   self.searchEngineLogoView = nil;
   self.NTPContentDelegate = nil;
   self.NTPShortcutsHandler = nil;
-  self.mostVisitedView = nil;
+  _mostVisitedView = nil;
   self.magicStackViewController = nil;
   [self setFeedViewController:nil];
   [_bottomSheetViewController invalidate];
@@ -438,8 +446,11 @@
 
 - (CGFloat)restingOffsetForBottomSheetViewController:
     (NewTabPageBottomSheetViewController*)viewController {
-  return [self centeredFakeOmniboxTop] + [self topContentHeight] +
-         kRestingSheetMVTTopMargin;
+  CGFloat offset = [self centeredFakeOmniboxTop] + [self topContentHeight];
+  if (!IsMVTInBottomSheetEnabled()) {
+    offset += kRestingSheetMVTTopMargin;
+  }
+  return offset;
 }
 
 - (CGFloat)collapsedOffsetForBottomSheetViewController:
@@ -474,7 +485,9 @@
 
   // Interpolate opacity for logo, MVTs row, and identity disc
   _searchEngineLogoView.alpha = progress;
-  _mostVisitedContainerView.alpha = progress;
+  if (!IsMVTInBottomSheetEnabled()) {
+    _mostVisitedContainerView.alpha = progress;
+  }
   _identityDiscButton.alpha = progress;
   if (_quickActionsViewController) {
     _quickActionsViewController.view.alpha = progress;
@@ -483,6 +496,33 @@
   [self.view layoutIfNeeded];
 }
 
+#pragma mark - ContentSuggestionsConsumer
+
+- (void)setMostVisitedTilesConfig:(MostVisitedTilesConfig*)config {
+  if (_mostVisitedView) {
+    [_mostVisitedView removeFromSuperview];
+  }
+  if (!config) {
+    _mostVisitedView = nil;
+    return;
+  }
+  MagicStackModuleContainer* container =
+      [[MagicStackModuleContainer alloc] initWithFrame:CGRectZero noInset:YES];
+  [container configureWithConfig:config];
+
+  _mostVisitedView = container;
+
+  if (IsMVTInBottomSheetEnabled()) {
+    if (_bottomSheetViewController) {
+      [_bottomSheetViewController embedMostVisitedView:_mostVisitedView];
+    }
+  } else {
+    if (self.isViewLoaded) {
+      [self embedMostVisitedView];
+    }
+  }
+}
+
 #pragma mark - NewTabPageConsumer
 
 - (void)omniboxDidBecomeFirstResponder {
@@ -574,29 +614,24 @@
   }
 }
 
-- (void)setMostVisitedView:(UIView*)mostVisitedView {
-  if (_mostVisitedView == mostVisitedView) {
-    return;
-  }
-  if (_mostVisitedView) {
-    [_mostVisitedView removeFromSuperview];
-  }
-  _mostVisitedView = mostVisitedView;
-  if (self.isViewLoaded && _mostVisitedView) {
-    [self embedMostVisitedView];
-  }
-}
-
 #pragma mark - Private
 
 // Add _mostVisitedView to the view hierarchy.
 - (void)embedMostVisitedView {
+  if (IsMVTInBottomSheetEnabled()) {
+    return;
+  }
   if (!_mostVisitedView || !_mostVisitedContainerView) {
     return;
   }
   _mostVisitedView.translatesAutoresizingMaskIntoConstraints = NO;
   [_mostVisitedContainerView addSubview:_mostVisitedView];
   AddSameConstraints(_mostVisitedView, _mostVisitedContainerView);
+  [self.view setNeedsLayout];
+  [self.view layoutIfNeeded];
+  if (_bottomSheetViewController) {
+    [_bottomSheetViewController updateBottomSheetPositionAnimated:NO];
+  }
 }
 
 - (void)addSearchEngineLogoView {
@@ -646,11 +681,15 @@
     height += kOmniboxToMVTSpacing;
   }
 
-  CGFloat mvtHeight = CGRectGetHeight(_mostVisitedContainerView.bounds);
-  if (mvtHeight <= 0) {
-    mvtHeight = kDefaultMVTHeightFallback;
+  if (!IsMVTInBottomSheetEnabled()) {
+    CGFloat mvtHeight = CGRectGetHeight(_mostVisitedContainerView.bounds);
+    if (mvtHeight <= 0 && _mostVisitedView) {
+      mvtHeight = [_mostVisitedView
+                      systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]
+                      .height;
+    }
+    height += mvtHeight;
   }
-  height += mvtHeight;
 
   return height;
 }
@@ -669,15 +708,7 @@
   return screenHeight * 0.35;
 }
 
-#pragma mark - ContentSuggestionsConsumer
 
-- (void)setMostVisitedTilesConfig:(MostVisitedTilesConfig*)config {
-  MagicStackModuleContainer* container =
-      [[MagicStackModuleContainer alloc] initWithFrame:CGRectZero noInset:YES];
-  [container configureWithConfig:config];
-
-  self.mostVisitedView = container;
-}
 
 #pragma mark - SearchEngineLogoConsumer
 
@@ -771,17 +802,19 @@
     BOOL isVisible = self.quickActionsVisible;
     _quickActionsViewController.view.hidden = !isVisible;
 
-    _mvtTopConstraint.active = NO;
+    if (!IsMVTInBottomSheetEnabled()) {
+      _mvtTopConstraint.active = NO;
 
-    UIView* anchorView =
-        isVisible ? _quickActionsViewController.view : _fakeLocationBar;
-    CGFloat constant =
-        isVisible ? kQuickActionSpacingBottom : kOmniboxToMVTSpacing;
+      UIView* anchorView =
+          isVisible ? _quickActionsViewController.view : _fakeLocationBar;
+      CGFloat constant =
+          isVisible ? kQuickActionSpacingBottom : kOmniboxToMVTSpacing;
 
-    _mvtTopConstraint = [_mostVisitedContainerView.topAnchor
-        constraintEqualToAnchor:anchorView.bottomAnchor
-                       constant:constant];
-    _mvtTopConstraint.active = YES;
+      _mvtTopConstraint = [_mostVisitedContainerView.topAnchor
+          constraintEqualToAnchor:anchorView.bottomAnchor
+                         constant:constant];
+      _mvtTopConstraint.active = YES;
+    }
 
     [self.view layoutIfNeeded];
   }
diff --git a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
index f2438b5bb..2f9e093 100644
--- a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
+++ b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
@@ -2211,6 +2211,8 @@
   }
   __weak __typeof(self) weakSelf = self;
 
+  // TODO(b/541315801): C2PA: Shared image could have C2PA metadata; candidate
+  // to pass raw bytes.
   _imageTranscoder->TranscodeImage(
       _imageSearchData, @"image/jpeg", nil, nil, nil,
       base::BindOnce(
@@ -2231,6 +2233,8 @@
 
   id<LensCommands> lensHandler = HandlerForProtocol(
       self.currentInterface.browser->GetCommandDispatcher(), LensCommands);
+  // TODO(b/541315801): C2PA: Shared image could have C2PA metadata; candidate
+  // to pass raw bytes.
   UIImage* image = [UIImage imageWithData:imageData];
   SearchImageWithLensCommand* command = [[SearchImageWithLensCommand alloc]
       initWithImage:image
diff --git a/ios/chrome/browser/tips_notifications/model/utils.mm b/ios/chrome/browser/tips_notifications/model/utils.mm
index d0055cb..7dbf66e 100644
--- a/ios/chrome/browser/tips_notifications/model/utils.mm
+++ b/ios/chrome/browser/tips_notifications/model/utils.mm
@@ -30,13 +30,13 @@
 int DockingBodyID(bool isAlternativeStringEnabled) {
   if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) {
     if (isAlternativeStringEnabled) {
-      return IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_BODY_IPAD;
+      return IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_BODY_IPAD;
     }
     return IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_BODY_IPAD;
   }
 
   if (isAlternativeStringEnabled) {
-    return IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_BODY_IPHONE;
+    return IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_BODY_IPHONE;
   }
   return IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_BODY_IPHONE;
 }
@@ -44,9 +44,9 @@
 // Returns the string id of the body text for the setup list promo notification.
 int SetupListBodyAlternativeID() {
   if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) {
-    return IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT1_BODY_IPAD;
+    return IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT_BODY_IPAD;
   }
-  return IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT1_BODY_IPHONE;
+  return IDS_IOS_NOTIFICATIONS_TIPS_SETUPLIST_CONTINUATION_ALT_BODY_IPHONE;
 }
 
 // Returns the title and the body text ids for the default browser promo
@@ -54,7 +54,7 @@
 ContentIDs DefaultBrowserContentIDsForAlternative(
     bool isAlternativeStringEnabled) {
   if (isAlternativeStringEnabled) {
-    return {IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_ALT1_TITLE,
+    return {IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_ALT_TITLE,
             IDS_IOS_NOTIFICATIONS_TIPS_DEFAULT_BROWSER_BODY};
   }
 
@@ -67,7 +67,7 @@
 ContentIDs WhatsNewContentIDsForAlternative(bool isAlternativeStringEnabled) {
   if (isAlternativeStringEnabled) {
     return {IDS_IOS_NOTIFICATIONS_TIPS_WHATS_NEW_TITLE,
-            IDS_IOS_NOTIFICATIONS_TIPS_WHATS_NEW_ALT1_BODY};
+            IDS_IOS_NOTIFICATIONS_TIPS_WHATS_NEW_ALT_BODY};
   }
   return {IDS_IOS_NOTIFICATIONS_TIPS_WHATS_NEW_TITLE,
           IDS_IOS_NOTIFICATIONS_TIPS_WHATS_NEW_BODY};
@@ -77,7 +77,7 @@
 ContentIDs SignInContentIDsForAlternative(bool isAlternativeStringEnabled) {
   if (isAlternativeStringEnabled) {
     return {IDS_IOS_NOTIFICATIONS_TIPS_SIGNIN_TITLE,
-            IDS_IOS_NOTIFICATIONS_TIPS_SIGNIN_ALT1_BODY};
+            IDS_IOS_NOTIFICATIONS_TIPS_SIGNIN_ALT_BODY};
   }
   return {IDS_IOS_NOTIFICATIONS_TIPS_SIGNIN_TITLE,
           IDS_IOS_NOTIFICATIONS_TIPS_SIGNIN_BODY};
@@ -97,7 +97,7 @@
 // Returns the title and the body text ids for the dockingpromo notification.
 ContentIDs DockingContentIDsForAlternative(bool isAlternativeStringEnabled) {
   if (isAlternativeStringEnabled) {
-    return {IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT1_TITLE,
+    return {IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_ALT_TITLE,
             DockingBodyID(isAlternativeStringEnabled)};
   }
   return {IDS_IOS_NOTIFICATIONS_TIPS_DOCKING_TITLE,
@@ -109,7 +109,7 @@
 ContentIDs OmniboxPositionContentIDsForAlternative(
     bool isAlternativeStringEnabled) {
   if (isAlternativeStringEnabled) {
-    return {IDS_IOS_NOTIFICATIONS_TIPS_OMNIBOX_POSITION_ALT2_TITLE,
+    return {IDS_IOS_NOTIFICATIONS_TIPS_OMNIBOX_POSITION_ALT_TITLE,
             IDS_IOS_NOTIFICATIONS_TIPS_OMNIBOX_POSITION_BODY};
   }
   return {IDS_IOS_NOTIFICATIONS_TIPS_OMNIBOX_POSITION_TITLE,
@@ -119,8 +119,8 @@
 // Returns the title and the body text ids for the lens promo notification.
 ContentIDs LensContentIDsForAlternative(bool isAlternativeStringEnabled) {
   if (isAlternativeStringEnabled) {
-    return {IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT1_TITLE,
-            IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT1_BODY};
+    return {IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT_TITLE,
+            IDS_IOS_NOTIFICATIONS_TIPS_LENS_ALT_BODY};
   }
   return {IDS_IOS_NOTIFICATIONS_TIPS_LENS_TITLE,
           IDS_IOS_NOTIFICATIONS_TIPS_LENS_BODY};
@@ -131,7 +131,7 @@
 ContentIDs SafeBrowsingContentIDsForAlternative(
     bool isAlternativeStringEnabled) {
   if (isAlternativeStringEnabled) {
-    return {IDS_IOS_NOTIFICATIONS_TIPS_ENHANCED_SAFE_BROWSING_ALT2_TITLE,
+    return {IDS_IOS_NOTIFICATIONS_TIPS_ENHANCED_SAFE_BROWSING_ALT_TITLE,
             IDS_IOS_NOTIFICATIONS_TIPS_ENHANCED_SAFE_BROWSING_BODY};
   }
   return {IDS_IOS_NOTIFICATIONS_TIPS_ENHANCED_SAFE_BROWSING_TITLE,
diff --git a/ios/chrome/browser/webui/ui_bundled/private_ai_internals/OWNERS b/ios/chrome/browser/webui/ui_bundled/private_ai_internals/OWNERS
new file mode 100644
index 0000000..b187c8d
--- /dev/null
+++ b/ios/chrome/browser/webui/ui_bundled/private_ai_internals/OWNERS
@@ -0,0 +1 @@
+file://components/private_ai/OWNERS
\ No newline at end of file
diff --git a/ios/chrome/browser/webui/ui_bundled/private_ai_internals/private_ai_internals_ui.mm b/ios/chrome/browser/webui/ui_bundled/private_ai_internals/private_ai_internals_ui.mm
index 67209ae3..f94b3a3 100644
--- a/ios/chrome/browser/webui/ui_bundled/private_ai_internals/private_ai_internals_ui.mm
+++ b/ios/chrome/browser/webui/ui_bundled/private_ai_internals/private_ai_internals_ui.mm
@@ -36,7 +36,9 @@
   source->AddString("default_url", private_ai::kPrivateAiUrl.Get());
   source->AddString(
       "default_api_key",
-      private_ai::PrivateAiInternalsPageHandler::kApiKeyPlaceholder);
+      private_ai::PrivateAiService::GetApiKey(::GetChannel()).empty()
+          ? ""
+          : private_ai::PrivateAiInternalsPageHandler::kApiKeyPlaceholder);
   source->AddString("default_proxy_url",
                     private_ai::kPrivateAiProxyServerUrl.Get());
   source->AddBoolean(
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
index 2dde099..6a9ce87 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-1a914ab3846a5c8652570f5e2889a39be6b19bc8
\ No newline at end of file
+ab9c521afaaed495786f59c640d2eb54485720ab
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
index d3a8f18..c3ff91f 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-4382979bd4cd17a0540c0e6b6a1d4b059d84e0dc
\ No newline at end of file
+df4da0af4ede91d543da3d940807923fe75ffa88
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index fb8e60e1..6dbbeed 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-c65a81af40d3967776f3731d9744dd517de755b7
\ No newline at end of file
+1a044b2ebca07e78c68b51637fc533e07c9be4ec
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
index dfb2b22..732fd99 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-ab1ccb89a42249da64d28a781186f018082994aa
\ No newline at end of file
+0f628e398f92ae6517fbc113eb5768757286f73d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index 3b71edb..f124da10 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-bcbb230c7618c747ee1b81dc25cdd1d0d99d85ed
\ No newline at end of file
+71c09bf4cd32f90b008d0ac2569f60224fbb1d6b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
index 0e8bf8b..5f4a05e 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-5d96b66a143a4855c0601b2c7248f4914d40ee31
\ No newline at end of file
+3c85afd27e0f925a8d6454304385d347a4f79cd4
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
index 525fc34..0e5a3cd 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-d2877760654d2bc377cfd40a921bf19e630e0335
\ No newline at end of file
+92a1ed3da261aab446b7d97a7eb98f815ba0ca46
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
index d9a7fab..70bbd69a 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-2b736b18c7192bc4a203e544c9db806a8ec57ab8
\ No newline at end of file
+971acef8a102a6894ee45445105f192c6df187fa
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
index a251976..6b7698e5 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-f72b8fb23d060502313686ff90a8172104a643ea
\ No newline at end of file
+789a581927b1cc021267fd31b520848d22c639ac
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 903650a..1fde90be 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-90697a7ff80702e39cc357ad6980a91bcf0068a9
\ No newline at end of file
+9212ee45b3368f92af94b3790f56f3e54cb5530d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 9ac6dc9..a573a68 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-4ed879426fb8fe169b033d4aa193427ce1230a76
\ No newline at end of file
+9d02aa4f659a23f07081144996d6c05c7327c857
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
index e8ecc98..14a805a 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-b9f7d8aeeb7f6ddff1470e65db5405ba6ee13076
\ No newline at end of file
+df6095909b39e29bf4cac7b91563c4e07df75164
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 05bb7ed..8c8b3de9 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-f61ec2bb34ef0d4f47e7604481ff4b7edaeec117
\ No newline at end of file
+b07a5502dceea6540a72888001dd261d5ac1a8e3
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
index c437ab3..8758bd59 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-92822b40cf37aadea6760918e400885dde83920b
\ No newline at end of file
+d1ff1f8717f9cd6b89e0359a5f1d53e906b367af
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index fb5f4f3..b0b7044 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-b10f39160bcafad72f707cfe61ad2287280575a5
\ No newline at end of file
+10aec140d08ed2abaa08b66e2adcf55343bde149
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
index add1aef4..95eb086 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-2ae377d8c3835fb2928cd48c5ad569cb7cdc4b97
\ No newline at end of file
+f12b58ee6aca32c1c49bae64c0d91cf2110265d9
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 15a67d0..6ff1ab9 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-95d1ff35bf1739ad6c6baf1e60303f217b92d07c
\ No newline at end of file
+04458ab5aa30d47b47ce1928e36002117efb6384
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
index a0ff6e4..927ac633 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-f3532b60d307da9e0a716345ff304e31e3d2cf05
\ No newline at end of file
+19e46d6be32966c22364c6ebf41a810b29269909
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index 8bf22116..142b1cd1 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-37dd53e9cb1b75d26ca84b00e909dd30fda4777c
\ No newline at end of file
+d4116d7d02d8a3f6390a1101f7732fbadc2502ab
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index 997959c..28546740 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-f8a042ad0cd04ad9410e3ede10b0bd857b5e2b9f
\ No newline at end of file
+540f3c2be8f13781777d369dee6bd876795960ac
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 40c84f33c..44847ba 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-e545a08429e74ac324aa4b4ad04dcbe3dfaab46c
\ No newline at end of file
+34161a407b327ab93c99cc1db5ca446106fdc1b7
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
index 22449d8..d7ee364d 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-24aa80e89c996fdccc912399ffee7b6240170af9
\ No newline at end of file
+39cf1384c77c3d6671380b4cd9734cd49a5afa62
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index e841ac33..2753010 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-9965620a95f8d19e896221c1effd5e16d071face
\ No newline at end of file
+89f0b00caa6a056c9f1aa247b1379a1ed6acbb15
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 245379f..fae77f7 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-5097daa32b50a3fa673f55d81b8f51a3bacca11e
\ No newline at end of file
+bbc701e760376a0a6f2b09cca76b004e34d6785c
\ No newline at end of file
diff --git a/ios_internal b/ios_internal
index 824a640..c39c69a 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 824a640140c461884db8ada2da1bb1a3ba8ef09d
+Subproject commit c39c69a52e62b3a5a47c43b4bd884a7d289f509e
diff --git a/mojo/core/platform_handle_in_transit.cc b/mojo/core/platform_handle_in_transit.cc
index 20d1a5a..95ba20c 100644
--- a/mojo/core/platform_handle_in_transit.cc
+++ b/mojo/core/platform_handle_in_transit.cc
@@ -64,6 +64,13 @@
     return INVALID_HANDLE_VALUE;
   }
 
+  if (error == ERROR_INVALID_HANDLE ||
+      trust == PlatformHandleInTransit::kUntrustedTarget) {
+    DPLOG(ERROR) << "DuplicateHandle failed from " << from_process << " to "
+                 << to_process << " for handle " << handle;
+    return INVALID_HANDLE_VALUE;
+  }
+
   base::debug::Alias(&handle);
   base::debug::Alias(&from_process);
   base::debug::Alias(&to_process);
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index dad7159..a81bf1ab 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -879,9 +879,7 @@
   edited_pages_unload_preventers_.clear();
 #endif
 
-  for (auto& page : pages_) {
-    page->Unload();
-  }
+  ForEachPage([](PDFiumPage* page) { page->Unload(); });
 
   if (doc()) {
     FORM_DoDocumentAAction(form(), FPDFDOC_AACTION_WC);
@@ -1342,20 +1340,21 @@
 
   FX_DOWNLOADHINTS& download_hints = document_->download_hints();
   bool need_update = false;
-  for (size_t i = 0; i < pages_.size(); ++i) {
-    if (pages_[i]->available()) {
-      continue;
+  ForEachPage([&](PDFiumPage* page) {
+    if (page->available()) {
+      return;
     }
 
-    pages_[i]->MarkAvailable();
+    page->MarkAvailable();
+    uint32_t page_index = page->index();
     // We still need to call IsPageAvail() even if the whole document is
     // already downloaded.
-    FPDFAvail_IsPageAvail(fpdf_availability(), i, &download_hints);
+    FPDFAvail_IsPageAvail(fpdf_availability(), page_index, &download_hints);
     need_update = true;
-    if (IsPageVisible(i)) {
-      client_->Invalidate(GetPageScreenRect(i));
+    if (IsPageVisible(page_index)) {
+      client_->Invalidate(GetPageScreenRect(page_index));
     }
-  }
+  });
 
   // Transition `document_loaded_` to true after finishing any calls to
   // FPDFAvail_IsPageAvail(), since we no longer need to defer calls to this
@@ -1701,20 +1700,20 @@
   return FPDFCatalog_IsTagged(doc());
 }
 
-std::unique_ptr<AccessibilityStructureElement> PDFiumEngine::GetStructureTree()
-    const {
+std::unique_ptr<AccessibilityStructureElement>
+PDFiumEngine::GetStructureTree() {
   auto structure_tree_root = std::make_unique<AccessibilityStructureElement>();
   structure_tree_root->type = PdfTagType::kDocument;
   structure_tree_root->children.reserve(pages_.size());
   structure_tree_root->language = GetDocumentLanguage(doc());
 
-  for (const std::unique_ptr<PDFiumPage>& page : pages_) {
+  ForEachPage([&](PDFiumPage* page) {
     auto page_structure = page->GetStructureTree();
     if (page_structure) {
       page_structure->parent = structure_tree_root.get();
     }
     structure_tree_root->children.push_back(std::move(page_structure));
-  }
+  });
   return structure_tree_root;
 }
 
@@ -2927,11 +2926,11 @@
   SelectionChangeInvalidator selection_invalidator(this);
 
   selection_.clear();
-  for (const auto& page : pages_) {
+  ForEachPage([&](PDFiumPage* page) {
     if (page->GetCharCount()) {
-      selection_.push_back(PDFiumRange::AllTextOnPage(page.get()));
+      selection_.push_back(PDFiumRange::AllTextOnPage(page));
     }
-  }
+  });
 
   if (caret_ && IsSelecting()) {
     caret_->SetVisible(false);
@@ -3219,17 +3218,18 @@
     return std::nullopt;
   }
 
-  gfx::Size page_size = GetPageSize(0);
-  for (size_t i = 1; i < pages_.size(); ++i) {
-    if (page_size != GetPageSize(i)) {
-      return std::nullopt;
-    }
+  gfx::Size uniform_page_size = GetPageSize(0);
+  const bool has_mismatch = ForEachPageUntilTrue([&](PDFiumPage* page) {
+    return GetPageSize(page->index()) != uniform_page_size;
+  });
+  if (has_mismatch) {
+    return std::nullopt;
   }
 
-  // Convert `page_size` back to points.
+  // Convert `uniform_page_size` back to points.
   return gfx::Size(
-      ConvertUnit(page_size.width(), kPixelsPerInch, kPointsPerInch),
-      ConvertUnit(page_size.height(), kPixelsPerInch, kPointsPerInch));
+      ConvertUnit(uniform_page_size.width(), kPixelsPerInch, kPointsPerInch),
+      ConvertUnit(uniform_page_size.height(), kPixelsPerInch, kPointsPerInch));
 }
 
 void PDFiumEngine::AppendBlankPages(size_t num_pages) {
@@ -3280,6 +3280,39 @@
   return gfx::Size();
 }
 
+void PDFiumEngine::ForEachPage(base::FunctionRef<void(PDFiumPage*)> callback) {
+  ForEachPageUntilTrue([&](PDFiumPage* page) {
+    callback(page);
+    return false;
+  });
+}
+
+bool PDFiumEngine::ForEachPageUntilTrue(
+    base::FunctionRef<bool(PDFiumPage*)> callback) {
+  if (pages_.empty()) {
+    return false;
+  }
+
+  // Defer page destruction during iteration to protect any active page pointers
+  // held on the stack across re-entrant callbacks.
+  base::ScopedClosureRunner deferred_page_unloader =
+      CreateScopedDeferredPageUnload();
+
+  std::vector<base::WeakPtr<PDFiumPage>> snapshot;
+  snapshot.reserve(pages_.size());
+  for (const auto& page : pages_) {
+    CHECK(page);
+    snapshot.push_back(page->GetWeakPtr());
+  }
+
+  for (const base::WeakPtr<PDFiumPage>& weak_page : snapshot) {
+    if (weak_page && callback(weak_page.get())) {
+      return true;
+    }
+  }
+  return false;
+}
+
 void PDFiumEngine::LoadDocument() {
   // Check if the document is ready for loading. If it isn't just bail for now,
   // we will call LoadDocument() again later.
@@ -3481,6 +3514,7 @@
   if (pages_.size() > new_page_count) {
     const size_t deferred_count_before = deferred_page_deletions_.size();
     for (size_t i = new_page_count; i < pages_.size(); ++i) {
+      pages_[i]->InvalidateWeakPtrs();
       if (defer_page_unload_ || !pages_[i]->Unload()) {
         deferred_page_deletions_.push_back(std::move(pages_[i]));
       }
@@ -3641,7 +3675,8 @@
 
   visible_pages_.clear();
   gfx::Rect visible_rect(plugin_size());
-  for (size_t i = 0; i < pages_.size(); ++i) {
+  ForEachPage([&](PDFiumPage* page) {
+    uint32_t i = page->index();
     // Check an entire PageScreenRect, since we might need to repaint side
     // borders and shadows even if the page itself is not visible.
     // For example, when user use pdf with different page sizes and zoomed in
@@ -3655,10 +3690,10 @@
       if (defer_page_unload_) {
         deferred_page_unloads_.push_back(i);
       } else {
-        pages_[i]->Unload();
+        page->Unload();
       }
     }
-  }
+  });
 
   // Any pending highlighting of form fields will be invalid since these are in
   // screen coordinates.
@@ -5170,14 +5205,13 @@
 }
 #endif  // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
 
-bool PDFiumEngine::HasMeaningfulText() const {
+bool PDFiumEngine::HasMeaningfulText() {
   if (!document_loaded_) {
     return false;
   }
 
   size_t total_char_count = 0;
-
-  for (const auto& page : pages_) {
+  const bool has_meaningful_text = ForEachPageUntilTrue([&](PDFiumPage* page) {
 #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
     // PDFium determines the character count, but pages requiring Searchify
     // are bypassed via `page->IsPageSearchified()`. Since Searchify only
@@ -5192,9 +5226,10 @@
         return true;
       }
     }
-  }
+    return false;
+  });
 
-  return false;
+  return has_meaningful_text;
 }
 
 bool PDFiumEngine::HasJavaScript() const {
@@ -5591,7 +5626,7 @@
 }
 
 PDFiumEngine::InkIdentifiers PDFiumEngine::ScanForInkAnnotations(
-    base::TimeDelta timeout) const {
+    base::TimeDelta timeout) {
   base::TimeTicks start_time = base::TimeTicks::Now();
 
   InkIdentifiers result = {
@@ -5599,9 +5634,9 @@
       .v2_ink_path = PDFLoadedWithV2InkAnnotations::kUnknown,
   };
 
-  for (const auto& page : pages_) {
+  const bool stopped_early = ForEachPageUntilTrue([&](PDFiumPage* page) {
     if (base::TimeTicks::Now() - start_time >= timeout) {
-      return result;
+      return true;
     }
 
     bool page_already_loaded = !!page->page();
@@ -5623,10 +5658,13 @@
       page->Unload();
     }
 
-    if (result.ink_text_annotations == PDFLoadedWithInkTextAnnotations::kTrue &&
-        result.v2_ink_path == PDFLoadedWithV2InkAnnotations::kTrue) {
-      return result;
-    }
+    return result.ink_text_annotations ==
+               PDFLoadedWithInkTextAnnotations::kTrue &&
+           result.v2_ink_path == PDFLoadedWithV2InkAnnotations::kTrue;
+  });
+
+  if (stopped_early) {
+    return result;
   }
 
   if (result.ink_text_annotations ==
@@ -5678,19 +5716,17 @@
 
 DocumentInkTextBoxesMap PDFiumEngine::LoadTextAnnotationsFromPdf() {
   DocumentInkTextBoxesMap document_textboxes;
-  for (size_t i = 0; i < pages_.size(); ++i) {
-    base::ScopedClosureRunner unload_preventer =
-        CreateScopedDeferredPageUnload();
-    PDFiumPage* page = pages_[i].get();
+  ForEachPage([&](PDFiumPage* page) {
     std::vector<ReadInkTextResult> page_results =
         ReadInkTextAnnotationsFromPage(page->GetPage());
     if (page_results.empty()) {
-      continue;
+      return;
     }
 
     std::vector<InkTextBox> page_textboxes;
     page_textboxes.reserve(page_results.size());
 
+    uint32_t i = page->index();
     for (auto& result : page_results) {
       InkLoadedTextId loaded_text_id(next_ink_loaded_text_id_++);
       result.textbox.ink_loaded_text_id = loaded_text_id;
@@ -5711,7 +5747,7 @@
     }
 
     document_textboxes[i] = std::move(page_textboxes);
-  }
+  });
 
   return document_textboxes;
 }
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h
index f485033..e9b0f905 100644
--- a/pdf/pdfium/pdfium_engine.h
+++ b/pdf/pdfium/pdfium_engine.h
@@ -20,6 +20,7 @@
 #include "base/containers/span.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
+#include "base/functional/function_ref.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
@@ -389,7 +390,7 @@
   // exceeds a threshold. This method should be called after making sure the
   // document is loaded to ensure the pages are available for checking;
   // otherwise, it may return false if pages are not yet available.
-  virtual bool HasMeaningfulText() const;
+  virtual bool HasMeaningfulText();
 
   // Returns true if the PDF contains JavaScript actions. This method should be
   // called after the document is loaded; otherwise, it returns false if the
@@ -403,7 +404,7 @@
 
   // Returns a copy of the structure tree which describes the logical
   // organization of the PDF, if present.
-  std::unique_ptr<AccessibilityStructureElement> GetStructureTree() const;
+  std::unique_ptr<AccessibilityStructureElement> GetStructureTree();
 
   virtual uint32_t GetLoadedByteSize();
 
@@ -478,7 +479,7 @@
   // Scans the document to detect the presence of Ink annotations (Ink text
   // annotations and "V2" Ink paths) within `timeout`. Virtual to support
   // testing.
-  virtual InkIdentifiers ScanForInkAnnotations(base::TimeDelta timeout) const;
+  virtual InkIdentifiers ScanForInkAnnotations(base::TimeDelta timeout);
 
   // Loads "V2" Ink paths from a page in the PDF identified by `page_index`. The
   // `page_index` must be in bounds.
@@ -781,6 +782,14 @@
 
   gfx::Size plugin_size() const;
 
+  // Iterates through all pages in the document safely. Takes a snapshot of page
+  // weak pointers to protect against re-entrant container modifications.
+  void ForEachPage(base::FunctionRef<void(PDFiumPage*)> callback);
+
+  // Same as ForEachPage(), but iterates through all pages or until `callback`
+  // returns true. Returns whether early exit occurred.
+  bool ForEachPageUntilTrue(base::FunctionRef<bool(PDFiumPage*)> callback);
+
   // We finished getting the pdf file, so load it. This will complete
   // asynchronously (due to password fetching) and may be run multiple times.
   void LoadDocument();
diff --git a/pdf/pdfium/pdfium_engine_unittest.cc b/pdf/pdfium/pdfium_engine_unittest.cc
index f9b626d..23c1194 100644
--- a/pdf/pdfium/pdfium_engine_unittest.cc
+++ b/pdf/pdfium/pdfium_engine_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/functional/callback.h"
+#include "base/functional/function_ref.h"
 #include "base/i18n/language_tag.h"
 #include "base/i18n/tag_converters.h"
 #include "base/path_service.h"
@@ -1857,6 +1858,16 @@
   void SetLastFocusedPage(PDFiumEngine* engine, int page_index) {
     engine->last_focused_page_ = page_index;
   }
+
+  void ForEachPage(PDFiumEngine* engine,
+                   base::FunctionRef<void(PDFiumPage*)> callback) {
+    engine->ForEachPage(callback);
+  }
+
+  bool ForEachPageUntilTrue(PDFiumEngine* engine,
+                            base::FunctionRef<bool(PDFiumPage*)> callback) {
+    return engine->ForEachPageUntilTrue(callback);
+  }
 };
 
 // Simulates an XFA page deletion when handling a char event.
@@ -1937,6 +1948,71 @@
   }
 }
 
+TEST_P(PDFiumEnginePageMutationTest, ForEachPage) {
+  NiceMock<MockTestClient> client(/*use_skia_renderer=*/GetParam());
+  std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
+      &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
+  ASSERT_TRUE(engine);
+  ASSERT_EQ(2, engine->GetNumberOfPages());
+
+  int visited_count = 0;
+  ForEachPage(engine.get(), [&](PDFiumPage* page) {
+    EXPECT_TRUE(page);
+    ++visited_count;
+  });
+  EXPECT_EQ(2, visited_count);
+}
+
+TEST_P(PDFiumEnginePageMutationTest, ForEachPageUntilTrue) {
+  NiceMock<MockTestClient> client(/*use_skia_renderer=*/GetParam());
+  std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
+      &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
+  ASSERT_TRUE(engine);
+  ASSERT_EQ(2, engine->GetNumberOfPages());
+
+  int visited_count = 0;
+  const bool stopped_early =
+      ForEachPageUntilTrue(engine.get(), [&](PDFiumPage* page) {
+        EXPECT_TRUE(page);
+        ++visited_count;
+        return true;
+      });
+  EXPECT_TRUE(stopped_early);
+  EXPECT_EQ(1, visited_count);
+
+  visited_count = 0;
+  const bool completed_all =
+      ForEachPageUntilTrue(engine.get(), [&](PDFiumPage* page) {
+        EXPECT_TRUE(page);
+        ++visited_count;
+        return false;
+      });
+  EXPECT_FALSE(completed_all);
+  EXPECT_EQ(2, visited_count);
+}
+
+TEST_P(PDFiumEnginePageMutationTest, ForEachPageMidIterationPageDeletion) {
+  NiceMock<MockTestClient> client(/*use_skia_renderer=*/GetParam());
+  std::unique_ptr<PDFiumEngine> engine = InitializeEngine(
+      &client, FILE_PATH_LITERAL("annotation_form_fields.pdf"));
+  ASSERT_TRUE(engine);
+  ASSERT_EQ(2, engine->GetNumberOfPages());
+
+  int visited_count = 0;
+  ForEachPage(engine.get(), [&](PDFiumPage* page) {
+    ++visited_count;
+    if (visited_count == 1) {
+      // Delete page 2 in the document and update layout mid-iteration.
+      FPDFPage_Delete(GetDoc(engine.get()), 1);
+      InvalidateAllPages(engine.get());
+    }
+  });
+
+  // Iteration should complete safely without crashing or accessing deleted
+  // page.
+  EXPECT_EQ(1, visited_count);
+}
+
 INSTANTIATE_TEST_SUITE_P(All, PDFiumEnginePageMutationTest, testing::Bool());
 
 class PDFiumEngineTabbingTest : public PDFiumTestBase {
diff --git a/pdf/pdfium/pdfium_page.cc b/pdf/pdfium/pdfium_page.cc
index 6f7d751..075ae06b 100644
--- a/pdf/pdfium/pdfium_page.cc
+++ b/pdf/pdfium/pdfium_page.cc
@@ -446,8 +446,6 @@
 PDFiumPage::PDFiumPage(PDFiumEngine* engine, uint32_t i)
     : engine_(engine), index_(i) {}
 
-PDFiumPage::PDFiumPage(PDFiumPage&& that) = default;
-
 PDFiumPage::~PDFiumPage() {
   DCHECK_EQ(0, preventing_page_unload_count_);
   DCHECK_EQ(0, preventing_text_page_unload_count_);
diff --git a/pdf/pdfium/pdfium_page.h b/pdf/pdfium/pdfium_page.h
index 9598f23..c5bcc12 100644
--- a/pdf/pdfium/pdfium_page.h
+++ b/pdf/pdfium/pdfium_page.h
@@ -18,6 +18,7 @@
 #include "base/functional/callback_forward.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "pdf/buildflags.h"
 #include "pdf/page_orientation.h"
 #include "pdf/ui/thumbnail.h"
@@ -85,9 +86,13 @@
   PDFiumPage(PDFiumEngine* engine, uint32_t i);
   PDFiumPage(const PDFiumPage&) = delete;
   PDFiumPage& operator=(const PDFiumPage&) = delete;
-  PDFiumPage(PDFiumPage&& that);
+  PDFiumPage(PDFiumPage&& that) = delete;
+  PDFiumPage& operator=(PDFiumPage&& that) = delete;
   ~PDFiumPage();
 
+  base::WeakPtr<PDFiumPage> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
+  void InvalidateWeakPtrs() { weak_factory_.InvalidateWeakPtrs(); }
+
   // Unloads the PDFium data for this page from memory. Returns true if the
   // unload happened, or false if an unload preventer blocked it.
   bool Unload();
@@ -565,6 +570,8 @@
   // this page has never been Searchified, then this is null.
   std::optional<bool> has_searchify_added_text_;
 #endif
+
+  base::WeakPtrFactory<PDFiumPage> weak_factory_{this};
 };
 
 constexpr uint32_t MakeARGB(unsigned int a,
diff --git a/pdf/test/test_pdfium_engine.h b/pdf/test/test_pdfium_engine.h
index 735eab1e..b14a9ecd 100644
--- a/pdf/test/test_pdfium_engine.h
+++ b/pdf/test/test_pdfium_engine.h
@@ -101,7 +101,7 @@
 
   MOCK_METHOD(bool, IsPDFDocTagged, (), (const override));
 
-  MOCK_METHOD(bool, HasMeaningfulText, (), (const override));
+  MOCK_METHOD(bool, HasMeaningfulText, (), (override));
 
   MOCK_METHOD(bool, HasJavaScript, (), (const override));
 
@@ -153,7 +153,7 @@
   MOCK_METHOD(InkIdentifiers,
               ScanForInkAnnotations,
               (base::TimeDelta),
-              (const override));
+              (override));
 
   MOCK_METHOD((std::map<InkModeledShapeId, ink::PartitionedMesh>),
               LoadV2InkPathsForPage,
diff --git a/remoting/host/linux/pipewire_capture_stream.cc b/remoting/host/linux/pipewire_capture_stream.cc
index 2dce510..3a1feed 100644
--- a/remoting/host/linux/pipewire_capture_stream.cc
+++ b/remoting/host/linux/pipewire_capture_stream.cc
@@ -26,6 +26,12 @@
 
 namespace remoting {
 
+namespace {
+
+constexpr base::TimeDelta kIdleFrameInterval = base::Seconds(1);
+
+}  // namespace
+
 // SharedScreenCastStream runs the pipewire loop, and invokes frame callbacks,
 // on a separate thread. This class is responsible for bouncing them back to
 // the corresponding methods of `parent_` on `callback_sequence`.
@@ -187,6 +193,8 @@
                                  resolution_.width(), resolution_.height(),
                                  false, callback_proxy_.get());
   video_capture_started_ = true;
+  idle_frame_timer_.Start(FROM_HERE, kIdleFrameInterval, this,
+                          &PipewireCaptureStream::OnIdleFrameTimer);
 }
 
 void PipewireCaptureStream::StopVideoCapture() {
@@ -194,6 +202,7 @@
   if (!video_capture_started_) {
     return;
   }
+  idle_frame_timer_.Stop();
   stream_->StopScreenCastStream();
   callback_proxy_->Stop();
   is_capturing_frame_ = false;
@@ -205,6 +214,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   callback_ = callback;
   if (!callback_) {
+    idle_frame_timer_.Stop();
     callback_proxy_->Stop();
     is_capturing_frame_ = false;
     return;
@@ -220,6 +230,9 @@
   // current stack frame and could potentially delete `this`, so we should only
   // access class members if the weak pointer remains valid.
   if (self) {
+    if (video_capture_started_) {
+      idle_frame_timer_.Reset();
+    }
     callback_proxy_->Start(capture_session_token_);
   }
 }
@@ -392,11 +405,35 @@
   }
 
   should_mark_current_frame_dirty_ = false;
+  if (video_capture_started_ && callback_) {
+    idle_frame_timer_.Reset();
+  }
   if (callback_) {
     callback_->OnCaptureResult(result, std::move(frame));
   }
 }
 
+void PipewireCaptureStream::OnIdleFrameTimer() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!video_capture_started_ || !callback_ || is_capturing_frame_) {
+    return;
+  }
+  auto frame = stream_->CaptureFrame();
+  if (!frame) {
+    return;
+  }
+  // An idle frame has an empty updated region to indicate that nothing on the
+  // screen has changed.
+  frame->mutable_updated_region()->Clear();
+  auto self = weak_ptr_factory_.GetWeakPtr();
+  OnFrameCaptureStart(capture_session_token_);
+  if (!self) {
+    return;
+  }
+  OnCaptureResult(capture_session_token_,
+                  webrtc::DesktopCapturer::Result::SUCCESS, std::move(frame));
+}
+
 void PipewireCaptureStream::OnCursorPositionChanged(int capture_session_token) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (capture_session_token != capture_session_token_) {
diff --git a/remoting/host/linux/pipewire_capture_stream.h b/remoting/host/linux/pipewire_capture_stream.h
index 8c76e60..238119f 100644
--- a/remoting/host/linux/pipewire_capture_stream.h
+++ b/remoting/host/linux/pipewire_capture_stream.h
@@ -17,6 +17,7 @@
 #include "base/synchronization/lock.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/thread_annotations.h"
+#include "base/timer/timer.h"
 #include "remoting/host/linux/capture_stream.h"
 #include "third_party/webrtc/api/scoped_refptr.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h"
@@ -81,6 +82,7 @@
   // may potentially be deleted at that point.
   void RecaptureLatestFrameAsDirty();
   void RemoveCursorObserver(CursorObserver* observer);
+  void OnIdleFrameTimer();
 
   // Called by the callback proxy.
   void OnFrameCaptureStart(int capture_session_token);
@@ -96,6 +98,26 @@
   webrtc::DesktopSize resolution_ GUARDED_BY_CONTEXT(sequence_checker_);
   bool video_capture_started_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
 
+  // Emits periodic empty frames when no frames are received from PipeWire (e.g.
+  // on a static desktop).
+  //
+  // Why this is done in the capturer instead of the encoder-wrapper:
+  // Synthesizing keep-alive frames downstream in the video encoder wrapper
+  // bypasses WebRTC's input pipeline. When the desktop is static, WebRTC's
+  // pending key-frame requests (PLIs) are never passed to the encoder, causing
+  // the client decoder to fail to recover if a key frame was lost or dropped.
+  // Pushing idle frames through the capturer ensures they flow through WebRTC's
+  // normal pipeline so pending key-frame requests are properly consumed and
+  // encoded as key frames.
+  //
+  // Why we do not use WebRTC's ZeroHertz mode (allow_zero_hertz_video):
+  // WebRTC's ZeroHertzAdapterMode buffers and delays every captured frame by 1
+  // frame interval (1/max_fps, ~16-33 ms), to pace frames evenly at max_fps.
+  // Emitting periodic idle frames directly from the capturer preserves 0 ms
+  // passthrough latency on all active frames.
+  base::RetainingOneShotTimer idle_frame_timer_
+      GUARDED_BY_CONTEXT(sequence_checker_);
+
   // Tracks the current active video capture session. Incremented on every
   // StartVideoCapture() call. This serves as a token to invalidate stale,
   // in-flight frame-capturing tasks that are already posted to the
diff --git a/remoting/protocol/webrtc_video_encoder_wrapper.cc b/remoting/protocol/webrtc_video_encoder_wrapper.cc
index c47585a..cd32b7e 100644
--- a/remoting/protocol/webrtc_video_encoder_wrapper.cc
+++ b/remoting/protocol/webrtc_video_encoder_wrapper.cc
@@ -590,12 +590,6 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!encode_pending_);
 
-  if (keep_alive_timer_.IsRunning()) {
-    keep_alive_timer_.Reset();
-  } else {
-    keep_alive_timer_.Start(FROM_HERE, kKeepAliveInterval, this,
-                            &WebrtcVideoEncoderWrapper::ExtrapolateFrame);
-  }
   if (top_off_active_) {
     top_off_timer_.Reset();
   }
@@ -683,9 +677,4 @@
   EncodeDesktopFrame(last_capturer_fed_frame_->Share());
 }
 
-// static
-base::TimeDelta WebrtcVideoEncoderWrapper::GetKeepAliveIntervalForTesting() {
-  return kKeepAliveInterval;
-}
-
 }  // namespace remoting::protocol
diff --git a/remoting/protocol/webrtc_video_encoder_wrapper.h b/remoting/protocol/webrtc_video_encoder_wrapper.h
index 26fa8b6..515136f 100644
--- a/remoting/protocol/webrtc_video_encoder_wrapper.h
+++ b/remoting/protocol/webrtc_video_encoder_wrapper.h
@@ -61,8 +61,6 @@
   void OnRttUpdate(int64_t rtt_ms) override;
   webrtc::VideoEncoder::EncoderInfo GetEncoderInfo() const override;
 
-  static base::TimeDelta GetKeepAliveIntervalForTesting();
-
  private:
   static constexpr int kStatsWindow = 5;
 
@@ -132,19 +130,12 @@
   bool top_off_active_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
 
   // Timer to extrapolate top-off frames in a reasonable interval, until
-  // `top_off_active_` is false. It will be suppressed if either a capturer-fed
-  // frame or a keep-alive frame is encoded within the top-off interval.
+  // `top_off_active_` is false. It will be suppressed if a capturer-fed
+  // frame is encoded within the top-off interval.
   base::RetainingOneShotTimer top_off_timer_
       GUARDED_BY_CONTEXT(sequence_checker_);
 
-  // Timer to extrapolate keep-alive frames. It will be suppressed if either a
-  // capturer-fed frame or a top-off frame is encoded within the keep-alive
-  // interval.
-  base::RetainingOneShotTimer keep_alive_timer_
-      GUARDED_BY_CONTEXT(sequence_checker_);
-
-  // The last capturer-fed desktop frame, used for top-off and keep-alive
-  // extrapolation.
+  // The last capturer-fed desktop frame, used for top-off extrapolation.
   std::unique_ptr<webrtc::SharedDesktopFrame> last_capturer_fed_frame_
       GUARDED_BY_CONTEXT(sequence_checker_);
 
diff --git a/remoting/protocol/webrtc_video_encoder_wrapper_unittest.cc b/remoting/protocol/webrtc_video_encoder_wrapper_unittest.cc
index 56edd90..8abebde 100644
--- a/remoting/protocol/webrtc_video_encoder_wrapper_unittest.cc
+++ b/remoting/protocol/webrtc_video_encoder_wrapper_unittest.cc
@@ -576,8 +576,6 @@
 
   std::vector<VideoFrameType> frame_types{VideoFrameType::kVideoFrameKey};
   encoder->Encode(MakeVideoFrame(), &frame_types);
-  ASSERT_LT(base::Seconds(1),
-            WebrtcVideoEncoderWrapper::GetKeepAliveIntervalForTesting());
   task_environment_.FastForwardBy(base::Seconds(1));
 
   PostQuitAndRun();
@@ -605,24 +603,54 @@
   PostQuitAndRun();
 }
 
-TEST_F(WebrtcVideoEncoderWrapperTest, ExtrapolateKeepAliveFrames) {
+TEST_F(WebrtcVideoEncoderWrapperTest,
+       EmptyFramesDroppedWithinKeepAliveInterval) {
+  // First frame (non-empty) and fourth frame (after keep-alive interval) are
+  // encoded.
   EXPECT_CALL(callback_, OnEncodedImage(_, _))
-      .Times(6)  // 1 capturer-fed frame plus 5 extrapolated frames.
+      .Times(2)
       .WillRepeatedly(Return(kResultOk));
 
+  // Frame 2 and 3 (empty frames within keep-alive interval) are dropped.
+  EXPECT_CALL(callback_, OnFrameDropped(2, 0, true));
+  EXPECT_CALL(callback_, OnFrameDropped(3, 0, true));
+
   auto encoder = InitEncoder(GetVp9Format(), GetVp9Codec());
   encoder->SetEncoderForTest(std::move(mock_video_encoder_));
 
-  std::vector<VideoFrameType> frame_types{VideoFrameType::kVideoFrameKey};
-  encoder->Encode(MakeVideoFrame(), &frame_types);
-  task_environment_.FastForwardBy(
-      WebrtcVideoEncoderWrapper::GetKeepAliveIntervalForTesting() * 5);
+  auto frame1 = MakeVideoFrame();
+  frame1.set_rtp_timestamp(1);
+  auto frame2 = MakeEmptyVideoFrame();
+  frame2.set_rtp_timestamp(2);
+  auto frame3 = MakeEmptyVideoFrame();
+  frame3.set_rtp_timestamp(3);
+  auto frame4 = MakeEmptyVideoFrame();
+  frame4.set_rtp_timestamp(4);
+
+  std::vector<VideoFrameType> frame_types{VideoFrameType::kVideoFrameDelta};
+  std::vector<VideoFrameType> key_frame_types{VideoFrameType::kVideoFrameKey};
+
+  // 1. Initial key frame.
+  encoder->Encode(frame1, &key_frame_types);
+
+  // 2. Empty frame after 500ms (dropped).
+  task_environment_.FastForwardBy(base::Milliseconds(500));
+  encoder->Encode(frame2, &frame_types);
+
+  // 3. Empty frame after another 500ms (total 1s < 2s keep-alive interval,
+  // dropped).
+  task_environment_.FastForwardBy(base::Milliseconds(500));
+  encoder->Encode(frame3, &frame_types);
+
+  // 4. Empty frame after another 1100ms (total > 2s keep-alive interval,
+  // encoded).
+  task_environment_.FastForwardBy(base::Milliseconds(1100));
+  encoder->Encode(frame4, &frame_types);
 
   PostQuitAndRun();
 }
 
-TEST_F(WebrtcVideoEncoderWrapperTest,
-       KeepAliveExtrapolationSuppressedByCapturerFedFrames) {
+TEST_F(WebrtcVideoEncoderWrapperTest, EmptyFrameNotDroppedIfKeyFrameRequested) {
   EXPECT_CALL(callback_, OnEncodedImage(_, _))
       .Times(2)
       .WillRepeatedly(Return(kResultOk));
@@ -630,16 +658,19 @@
   auto encoder = InitEncoder(GetVp9Format(), GetVp9Codec());
   encoder->SetEncoderForTest(std::move(mock_video_encoder_));
 
-  std::vector<VideoFrameType> frame_types{VideoFrameType::kVideoFrameKey};
-  encoder->Encode(MakeVideoFrame(), &frame_types);
-  task_environment_.FastForwardBy(
-      WebrtcVideoEncoderWrapper::GetKeepAliveIntervalForTesting() -
-      base::Milliseconds(100));
-  encoder->Encode(MakeVideoFrame(), &frame_types);
-  // This would extrapolate a keep-alive frame if it weren't suppressed.
-  task_environment_.FastForwardBy(
-      WebrtcVideoEncoderWrapper::GetKeepAliveIntervalForTesting() -
-      base::Milliseconds(100));
+  auto frame1 = MakeVideoFrame();
+  frame1.set_rtp_timestamp(1);
+  auto frame2 = MakeEmptyVideoFrame();
+  frame2.set_rtp_timestamp(2);
+
+  std::vector<VideoFrameType> key_frame_types{VideoFrameType::kVideoFrameKey};
+
+  // 1. Initial frame.
+  encoder->Encode(frame1, &key_frame_types);
+
+  // 2. Empty frame after 500ms, but with key-frame requested -> NOT dropped.
+  task_environment_.FastForwardBy(base::Milliseconds(500));
+  encoder->Encode(frame2, &key_frame_types);
 
   PostQuitAndRun();
 }
@@ -648,6 +679,12 @@
        ExtrapolatedFramesDoNotInheritInputTimestamps) {
   base::TimeTicks input_timestamp = base::TimeTicks::Now();
 
+  EXPECT_CALL(*mock_video_encoder_, Encode)
+      .WillOnce(RespondWithEncodedFrame(1.f))
+      .WillOnce(RespondWithEncodedFrame(0.50f))
+      .WillOnce(RespondWithEncodedFrame(0.25f))
+      .WillOnce(RespondWithEncodedFrame(0.f));
+
   {
     InSequence s;
 
@@ -661,7 +698,7 @@
 
     // Extrapolated frames should not have an input timestamp.
     EXPECT_CALL(observer_, OnEncodedFrameSent(_, _))
-        .Times(5)
+        .Times(3)
         .WillRepeatedly([](EncodedImageCallback::Result result,
                            const WebrtcVideoEncoder::EncodedFrame& frame) {
           auto* stats = static_cast<TestFrameStats*>(frame.stats.get());
@@ -670,7 +707,7 @@
   }
 
   EXPECT_CALL(callback_, OnEncodedImage(_, _))
-      .Times(6)
+      .Times(4)
       .WillRepeatedly(Return(kResultOk));
 
   auto encoder = InitEncoder(GetVp9Format(), GetVp9Codec());
@@ -678,8 +715,7 @@
 
   std::vector<VideoFrameType> frame_types{VideoFrameType::kVideoFrameKey};
   encoder->Encode(MakeVideoFrameWithTimestamp(input_timestamp), &frame_types);
-  task_environment_.FastForwardBy(
-      WebrtcVideoEncoderWrapper::GetKeepAliveIntervalForTesting() * 5);
+  task_environment_.FastForwardBy(base::Seconds(1));
 
   PostQuitAndRun();
 }
@@ -689,8 +725,9 @@
   constexpr int64_t kInitialCaptureTimeMs = 500;
   constexpr int64_t kInitialNtpTimeMs = 1600000000000;
 
-  base::TimeDelta interval =
-      WebrtcVideoEncoderWrapper::GetKeepAliveIntervalForTesting();
+  EXPECT_CALL(*mock_video_encoder_, Encode)
+      .WillOnce(RespondWithEncodedFrame(1.f))
+      .WillOnce(RespondWithEncodedFrame(0.f));
 
   EXPECT_CALL(callback_, OnEncodedImage(_, _))
       .WillOnce([&](const EncodedImage& encoded_image,
@@ -702,12 +739,9 @@
       })
       .WillOnce([&](const EncodedImage& encoded_image,
                     const CodecSpecificInfo* codec_specific_info) {
-        EXPECT_EQ(encoded_image.RtpTimestamp(),
-                  kInitialRtpTimestamp + interval.InMilliseconds() * 90);
-        EXPECT_EQ(encoded_image.capture_time_ms_,
-                  kInitialCaptureTimeMs + interval.InMilliseconds());
-        EXPECT_EQ(encoded_image.ntp_time_ms_,
-                  kInitialNtpTimeMs + interval.InMilliseconds());
+        EXPECT_GT(encoded_image.RtpTimestamp(), kInitialRtpTimestamp);
+        EXPECT_GT(encoded_image.capture_time_ms_, kInitialCaptureTimeMs);
+        EXPECT_GT(encoded_image.ntp_time_ms_, kInitialNtpTimeMs);
         return kResultOk;
       });
 
@@ -722,7 +756,7 @@
   input_frame.set_ntp_time_ms(kInitialNtpTimeMs);
 
   encoder->Encode(input_frame, &frame_types);
-  task_environment_.FastForwardBy(interval);
+  task_environment_.FastForwardBy(base::Seconds(1));
 
   PostQuitAndRun();
 }
diff --git a/remoting/signaling/jingle_message_proto_converter.cc b/remoting/signaling/jingle_message_proto_converter.cc
index 295bcde..cc54779 100644
--- a/remoting/signaling/jingle_message_proto_converter.cc
+++ b/remoting/signaling/jingle_message_proto_converter.cc
@@ -32,7 +32,7 @@
     case AuthenticationMethod::CORP_SESSION_AUTHZ_SPAKE2_CURVE25519:
       return ftl::AUTHENTICATION_METHOD_CORP_SESSION_AUTHZ_SPAKE2_CURVE25519;
   }
-  NOTREACHED();
+  return ftl::AUTHENTICATION_METHOD_UNSPECIFIED;
 }
 
 AuthenticationMethod ProtoToAuthMethod(ftl::AuthenticationMethod method) {
@@ -47,9 +47,8 @@
       return AuthenticationMethod::CLOUD_SESSION_AUTHZ_SPAKE2_CURVE25519;
     case ftl::AUTHENTICATION_METHOD_CORP_SESSION_AUTHZ_SPAKE2_CURVE25519:
       return AuthenticationMethod::CORP_SESSION_AUTHZ_SPAKE2_CURVE25519;
-    default:
-      return AuthenticationMethod::INVALID;
   }
+  return AuthenticationMethod::INVALID;
 }
 
 void JingleAuthenticationToProto(const JingleAuthentication& auth,
@@ -110,7 +109,7 @@
     case SessionDescription::Type::kAnswer:
       return ftl::SessionDescription::ANSWER;
   }
-  NOTREACHED();
+  return ftl::SessionDescription::SDP_TYPE_UNSPECIFIED;
 }
 
 SessionDescription::Type ProtoToSdpType(ftl::SessionDescription::SdpType type) {
@@ -121,9 +120,8 @@
       return SessionDescription::Type::kOffer;
     case ftl::SessionDescription::ANSWER:
       return SessionDescription::Type::kAnswer;
-    default:
-      return SessionDescription::Type::kUnspecified;
   }
+  return SessionDescription::Type::kUnspecified;
 }
 
 void JingleTransportInfoToProto(const JingleTransportInfo& transport,
@@ -257,6 +255,7 @@
     case SessionTerminate::Reason::kUnknownReason:
       return ftl::SessionTerminate::UNKNOWN_REASON;
   }
+  return ftl::SessionTerminate::UNKNOWN_REASON;
 }
 
 SessionTerminate::Reason ProtoTerminateReasonToJingle(
@@ -281,6 +280,7 @@
     case ftl::SessionTerminate::UNKNOWN_REASON:
       return SessionTerminate::Reason::kUnknownReason;
   }
+  return SessionTerminate::Reason::kUnknownReason;
 }
 
 }  // namespace
@@ -462,6 +462,7 @@
   } else {
     // Ensure we set the error case even if error_type is missing.
     ftl::ErrorStanza* error = stanza.mutable_error();
+    error->set_condition(ftl::ErrorStanza::CONDITION_UNSPECIFIED);
     if (!reply.text.empty()) {
       error->set_text(reply.text);
     }
@@ -503,6 +504,7 @@
   } else if (stanza.has_error()) {
     reply->reply_type = JingleMessageReply::REPLY_ERROR;
     const ftl::ErrorStanza& error = stanza.error();
+    reply->error_type = JingleMessageReply::UNSPECIFIED;
     switch (error.condition()) {
       case ftl::ErrorStanza::BAD_REQUEST:
         reply->error_type = JingleMessageReply::BAD_REQUEST;
@@ -522,9 +524,6 @@
       case ftl::ErrorStanza::CONDITION_UNSPECIFIED:
         reply->error_type = JingleMessageReply::UNSPECIFIED;
         break;
-      default:
-        reply->error_type = JingleMessageReply::UNSPECIFIED;
-        break;
     }
     reply->text = error.text();
     return true;
diff --git a/remoting/signaling/jingle_message_proto_converter_unittest.cc b/remoting/signaling/jingle_message_proto_converter_unittest.cc
index 8099d409..889b3944 100644
--- a/remoting/signaling/jingle_message_proto_converter_unittest.cc
+++ b/remoting/signaling/jingle_message_proto_converter_unittest.cc
@@ -506,4 +506,63 @@
             "11");
 }
 
+TEST_F(JingleMessageProtoConverterTest, ConvertUnknownTerminateReason) {
+  JingleMessage message;
+  message.from = from_address_;
+  message.to = to_address_;
+  message.message_id = kMessageId;
+  message.sid = kSid;
+  SessionTerminate terminate;
+  terminate.reason = static_cast<SessionTerminate::Reason>(999);
+  message.SetPayload(std::move(terminate));
+
+  ftl::IqStanza stanza = message.ToFtlIqStanza();
+  EXPECT_EQ(stanza.jingle().session_terminate().reason(),
+            ftl::SessionTerminate::UNKNOWN_REASON);
+}
+
+TEST_F(JingleMessageProtoConverterTest, ConvertUnknownAuthMethod) {
+  JingleMessage message;
+  message.from = from_address_;
+  message.to = to_address_;
+  message.message_id = kMessageId;
+  message.sid = kSid;
+  SessionInitiate initiate;
+  JingleAuthentication auth;
+  auth.supported_methods = {static_cast<AuthenticationMethod>(999)};
+  auth.method = static_cast<AuthenticationMethod>(999);
+  initiate.authentication = auth;
+  message.SetPayload(std::move(initiate));
+
+  ftl::IqStanza stanza = message.ToFtlIqStanza();
+  EXPECT_TRUE(stanza.jingle().has_session_initiate());
+  EXPECT_TRUE(stanza.jingle().session_initiate().has_authentication());
+  EXPECT_EQ(
+      stanza.jingle().session_initiate().authentication().supported_methods(0),
+      ftl::AUTHENTICATION_METHOD_UNSPECIFIED);
+  EXPECT_EQ(stanza.jingle().session_initiate().authentication().method(),
+            ftl::AUTHENTICATION_METHOD_UNSPECIFIED);
+}
+
+TEST_F(JingleMessageProtoConverterTest, ConvertUnknownSdpType) {
+  JingleMessage message;
+  message.from = from_address_;
+  message.to = to_address_;
+  message.message_id = kMessageId;
+  message.sid = kSid;
+
+  JingleTransportInfo transport;
+  SessionDescription sdp;
+  sdp.type = static_cast<SessionDescription::Type>(999);
+  sdp.sdp = "test_sdp";
+  transport.session_description = sdp;
+  message.SetPayload(std::move(transport));
+
+  ftl::IqStanza stanza = message.ToFtlIqStanza();
+  EXPECT_TRUE(stanza.jingle().has_transport_info());
+  EXPECT_TRUE(stanza.jingle().transport_info().has_session_description());
+  EXPECT_EQ(stanza.jingle().transport_info().session_description().type(),
+            ftl::SessionDescription::SDP_TYPE_UNSPECIFIED);
+}
+
 }  // namespace remoting
diff --git a/remoting/signaling/jingle_message_struct_converter.cc b/remoting/signaling/jingle_message_struct_converter.cc
index 971c467..f69eaef 100644
--- a/remoting/signaling/jingle_message_struct_converter.cc
+++ b/remoting/signaling/jingle_message_struct_converter.cc
@@ -49,9 +49,8 @@
       return SessionDescriptionStruct::SdpType::kOffer;
     case SessionDescription::Type::kAnswer:
       return SessionDescriptionStruct::SdpType::kAnswer;
-    default:
-      NOTREACHED();
   }
+  return SessionDescriptionStruct::SdpType::kUnspecified;
 }
 
 SessionDescription::Type FromSdpTypeStruct(
@@ -63,9 +62,8 @@
       return SessionDescription::Type::kOffer;
     case SessionDescriptionStruct::SdpType::kAnswer:
       return SessionDescription::Type::kAnswer;
-    default:
-      NOTREACHED();
   }
+  return SessionDescription::Type::kUnspecified;
 }
 
 SessionTerminateStruct::Reason ToTerminateReasonStruct(
@@ -89,9 +87,8 @@
       return SessionTerminateStruct::Reason::kIncompatibleParameters;
     case SessionTerminate::Reason::kUnknownReason:
       return SessionTerminateStruct::Reason::kUnknownReason;
-    default:
-      NOTREACHED();
   }
+  return SessionTerminateStruct::Reason::kUnknownReason;
 }
 
 SessionTerminate::Reason FromTerminateReasonStruct(
@@ -115,9 +112,8 @@
       return SessionTerminate::Reason::kIncompatibleParameters;
     case SessionTerminateStruct::Reason::kUnknownReason:
       return SessionTerminate::Reason::kUnknownReason;
-    default:
-      NOTREACHED();
   }
+  return SessionTerminate::Reason::kUnknownReason;
 }
 
 ErrorStanzaStruct::Condition ToErrorConditionStruct(
@@ -134,9 +130,9 @@
     case JingleMessageReply::UNSUPPORTED_INFO:
       return ErrorStanzaStruct::Condition::kUnsupportedInfo;
     case JingleMessageReply::UNSPECIFIED:
-    default:
       return ErrorStanzaStruct::Condition::kUnspecified;
   }
+  return ErrorStanzaStruct::Condition::kUnspecified;
 }
 
 JingleMessageReply::ErrorType FromErrorConditionStruct(
@@ -153,9 +149,9 @@
     case ErrorStanzaStruct::Condition::kUnsupportedInfo:
       return JingleMessageReply::UNSUPPORTED_INFO;
     case ErrorStanzaStruct::Condition::kUnspecified:
-    default:
       return JingleMessageReply::UNSPECIFIED;
   }
+  return JingleMessageReply::UNSPECIFIED;
 }
 
 }  // namespace
diff --git a/remoting/signaling/jingle_message_struct_converter_unittest.cc b/remoting/signaling/jingle_message_struct_converter_unittest.cc
index 06df133f..9a7f5f4 100644
--- a/remoting/signaling/jingle_message_struct_converter_unittest.cc
+++ b/remoting/signaling/jingle_message_struct_converter_unittest.cc
@@ -192,6 +192,50 @@
   EXPECT_EQ(reply2.text, "Bad Request Details");
 }
 
+TEST(JingleMessageStructConverterTest,
+     JingleMessageReplyConversion_UnknownError) {
+  JingleMessageReply reply;
+  reply.from = SignalingAddress(kFromJid);
+  reply.to = SignalingAddress(kToJid);
+  reply.message_id = "reply_id";
+  reply.reply_type = JingleMessageReply::REPLY_ERROR;
+  reply.error_type = static_cast<JingleMessageReply::ErrorType>(999);
+  reply.text = "Unknown Error Details";
+
+  auto stanza = JingleMessageReplyToStruct(reply);
+  const auto* error_struct =
+      std::get_if<internal::ErrorStanzaStruct>(&stanza.payload);
+  ASSERT_TRUE(error_struct);
+  EXPECT_EQ(error_struct->condition,
+            internal::ErrorStanzaStruct::Condition::kUnspecified);
+  EXPECT_EQ(error_struct->text, "Unknown Error Details");
+
+  JingleMessageReply reply2;
+  ASSERT_TRUE(JingleMessageReplyFromStruct(stanza, &reply2));
+  EXPECT_EQ(reply2.reply_type, JingleMessageReply::REPLY_ERROR);
+  EXPECT_EQ(reply2.error_type, JingleMessageReply::UNSPECIFIED);
+  EXPECT_EQ(reply2.text, "Unknown Error Details");
+}
+
+TEST(JingleMessageStructConverterTest,
+     JingleMessageReplyConversion_InvalidErrorConditionStruct) {
+  internal::IqStanzaStruct stanza;
+  stanza.id = "reply_id";
+  stanza.sender.local_part = "from";
+  stanza.receiver.local_part = "to";
+  internal::ErrorStanzaStruct error_struct;
+  error_struct.condition =
+      static_cast<internal::ErrorStanzaStruct::Condition>(999);
+  error_struct.text = "Invalid Error Condition Struct";
+  stanza.payload = error_struct;
+
+  JingleMessageReply reply;
+  ASSERT_TRUE(JingleMessageReplyFromStruct(stanza, &reply));
+  EXPECT_EQ(reply.reply_type, JingleMessageReply::REPLY_ERROR);
+  EXPECT_EQ(reply.error_type, JingleMessageReply::UNSPECIFIED);
+  EXPECT_EQ(reply.text, "Invalid Error Condition Struct");
+}
+
 TEST(JingleMessageStructConverterTest, SessionInitiateConversion) {
   JingleMessage message;
   message.from = SignalingAddress(kFromJid);
@@ -360,6 +404,26 @@
               testing::ElementsAre("attr1", "attr2"));
 }
 
+TEST(JingleMessageStructConverterTest, ConvertUnknownTerminateReason) {
+  internal::IqStanzaStruct stanza;
+  stanza.id = "test_id";
+  stanza.sender.local_part = "from";
+  stanza.receiver.local_part = "to";
+  internal::JingleMessageStruct jingle_struct;
+  jingle_struct.session_id = "test_sid";
+  internal::SessionTerminateStruct terminate_struct;
+  terminate_struct.reason =
+      static_cast<internal::SessionTerminateStruct::Reason>(999);
+  jingle_struct.action = terminate_struct;
+  stanza.payload = jingle_struct;
+
+  JingleMessage message;
+  std::string error;
+  ASSERT_TRUE(JingleMessageFromStruct(stanza, &message, &error));
+  ASSERT_TRUE(std::holds_alternative<SessionTerminate>(message.payload()));
+  EXPECT_EQ(std::get<SessionTerminate>(message.payload()).reason,
+            SessionTerminate::Reason::kUnknownReason);
+}
 
 }  // namespace
 }  // namespace remoting
diff --git a/services/webnn/public/mojom/webnn_tensor.mojom b/services/webnn/public/mojom/webnn_tensor.mojom
index f0e6998e..8a7eaeac 100644
--- a/services/webnn/public/mojom/webnn_tensor.mojom
+++ b/services/webnn/public/mojom/webnn_tensor.mojom
@@ -4,7 +4,6 @@
 
 module webnn.mojom;
 
-import "gpu/ipc/common/sync_token.mojom";
 import "mojo/public/mojom/base/big_buffer.mojom";
 import "services/webnn/public/mojom/webnn_graph.mojom";
 import "services/webnn/public/mojom/webnn_error.mojom";
@@ -79,8 +78,10 @@
   // Returns access of an exportable `WebNNTensor` back to WebNN after the
   // renderer is finished with it.
   //
-  // WebNN execution will be deferred asynchronously until the provided fence
-  // has been released, ensuring that any renderer-side work is complete.
+  // WebNN execution will be deferred asynchronously until the fence with
+  // `release_count` has been released, ensuring that any renderer-side work is
+  // complete. A value of 0 means ImportTensor does not wait for a SyncToken
+  // for this import.
   //
   // This call is the counterpart to `ExportTensor()`. Ownership of an
   // exportable tensor may be passed back and forth between WebNN and the
@@ -89,5 +90,5 @@
   // `flow_id` is the same ID used in `ExportTensor()`. This allows the GPU
   // process to terminate the cross-process trace flow, linking this import
   // back to the renderer-side export.
-  ImportTensor(uint64 flow_id, gpu.mojom.SyncToken fence);
+  ImportTensor(uint64 flow_id, uint64 release_count);
 };
diff --git a/services/webnn/webnn_tensor_impl.cc b/services/webnn/webnn_tensor_impl.cc
index b388bca..0da8a15a8 100644
--- a/services/webnn/webnn_tensor_impl.cc
+++ b/services/webnn/webnn_tensor_impl.cc
@@ -119,8 +119,7 @@
       GetMojoReceiver().GetBadMessageCallback()));
 }
 
-void WebNNTensorImpl::ImportTensor(uint64_t flow_id,
-                                   const gpu::SyncToken& fence) {
+void WebNNTensorImpl::ImportTensor(uint64_t flow_id, uint64_t release_count) {
   ScopedTrace scoped_trace("WebNNTensorImpl::ImportTensor");
 
   if (!usage().Has(MLTensorUsageFlags::kWebGpuInterop)) {
@@ -133,6 +132,13 @@
     return;
   }
 
+  gpu::SyncToken fence;
+  if (release_count != 0) {
+    fence = gpu::SyncToken(context_->gpu_task_scheduler()->namespace_id(),
+                           context_->gpu_task_scheduler()->command_buffer_id(),
+                           release_count);
+  }
+
   // Defer the next task until the fence is released, after prior scheduled
   // tasks run.
   context_->RunOrScheduleTask(
@@ -159,7 +165,7 @@
           },
           base::RetainedRef(this), std::move(scoped_trace), flow_id,
           GetMojoReceiver().GetBadMessageCallback()),
-      {fence});
+      fence);
 }
 
 void WebNNTensorImpl::ExportTensor(uint64_t flow_id, uint64_t release_count) {
diff --git a/services/webnn/webnn_tensor_impl.h b/services/webnn/webnn_tensor_impl.h
index c5d18a6c..9dca496 100644
--- a/services/webnn/webnn_tensor_impl.h
+++ b/services/webnn/webnn_tensor_impl.h
@@ -136,7 +136,7 @@
   // mojom::WebNNTensor
   void ReadTensor(ReadTensorCallback callback) override;
   void WriteTensor(mojo_base::BigBuffer src_buffer) override;
-  void ImportTensor(uint64_t flow_id, const gpu::SyncToken& fence) override;
+  void ImportTensor(uint64_t flow_id, uint64_t release_count) override;
   void ExportTensor(uint64_t flow_id, uint64_t release_count) override;
   void ExportTensorSync(uint64_t flow_id,
                         uint64_t release_count,
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 56e5bd65..1c23738e 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -412,7 +412,7 @@
     'swarming': {
       'dimensions': {
         'device_type': 's5e9945',
-        'device_os': 'UP1A.231005.007',
+        'device_os': 'AP3A.240905.015.A2',
         'device_os_type': 'user',
         'os': 'Android',
         'pool': 'chromium.tests.gpu',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index cdc28217..3f301b1 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -880,7 +880,7 @@
           'win10_nvidia_gtx_1660_stable',
         ],
         'test_suites': {
-          'gpu_telemetry_tests': 'gpu_fyi_win_release_telemetry_tests',
+          'gpu_telemetry_tests': 'gpu_v8_win_release_telemetry_tests',
         },
       },
     },
diff --git a/testing/perf/cbb_ref_info/chrome/dev/android.json b/testing/perf/cbb_ref_info/chrome/dev/android.json
index cedfc9c9..066bb3867 100644
--- a/testing/perf/cbb_ref_info/chrome/dev/android.json
+++ b/testing/perf/cbb_ref_info/chrome/dev/android.json
@@ -2,5 +2,5 @@
   "browser": "chrome",
   "channel": "dev",
   "platform": "android",
-  "version": "153.0.7978.2"
+  "version": "153.0.7993.0"
 }
\ No newline at end of file
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 2650ecc0..ae8e3f0 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -6216,6 +6216,42 @@
             ]
         }
     ],
+    "ClankBgRefreshConfigLaunch": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_15m_prestable_clank_20260803",
+                    "params": {
+                        "feed_close_refresh_delay": "15m"
+                    },
+                    "enable_features": [
+                        "InterestFeedV2"
+                    ]
+                },
+                {
+                    "name": "Enabled_10m_prestable_clank_20260803",
+                    "params": {
+                        "feed_close_refresh_delay": "10m"
+                    },
+                    "enable_features": [
+                        "InterestFeedV2"
+                    ]
+                },
+                {
+                    "name": "Enabled_20m_prestable_clank_20260803",
+                    "params": {
+                        "feed_close_refresh_delay": "20m"
+                    },
+                    "enable_features": [
+                        "InterestFeedV2"
+                    ]
+                }
+            ]
+        }
+    ],
     "ClankDefaultSearchApi": [
         {
             "platforms": [
diff --git a/third_party/.gitignore b/third_party/.gitignore
index 540c9f72..4b694c6e 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -71,6 +71,7 @@
 /js_code_coverage/node_modules/
 /jsr-305/src
 /kotlinc/current
+/ktfmt/cipd/
 /leveldb
 /libassistant
 /libc++-static/libc++.a
diff --git a/third_party/blink/common/dom/dom_node_id_mojom_traits.cc b/third_party/blink/common/dom/dom_node_id_mojom_traits.cc
deleted file mode 100644
index bfafdd09..0000000
--- a/third_party/blink/common/dom/dom_node_id_mojom_traits.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2026 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/public/common/dom/dom_node_id_mojom_traits.h"
-
-namespace mojo {
-
-// static
-bool StructTraits<blink::mojom::DOMNodeIdDataView, blink::DOMNodeIdType>::Read(
-    blink::mojom::DOMNodeIdDataView data,
-    blink::DOMNodeIdType* out) {
-  *out = blink::DOMNodeIdType(data.value());
-  return true;
-}
-
-}  // namespace mojo
diff --git a/third_party/blink/public/common/dom/dom_node_id_mojom_traits.h b/third_party/blink/public/common/dom/dom_node_id_mojom_traits.h
index ffdf17bd..50071587 100644
--- a/third_party/blink/public/common/dom/dom_node_id_mojom_traits.h
+++ b/third_party/blink/public/common/dom/dom_node_id_mojom_traits.h
@@ -6,19 +6,20 @@
 #define THIRD_PARTY_BLINK_PUBLIC_COMMON_DOM_DOM_NODE_ID_MOJOM_TRAITS_H_
 
 #include "mojo/public/cpp/bindings/struct_traits.h"
-#include "third_party/blink/public/common/common_export.h"
 #include "third_party/blink/public/common/dom/dom_node_id.h"
 #include "third_party/blink/public/mojom/dom/dom_node_id.mojom-data-view.h"
 
 namespace mojo {
 
 template <>
-struct BLINK_COMMON_EXPORT
-    StructTraits<blink::mojom::DOMNodeIdDataView, blink::DOMNodeIdType> {
+struct StructTraits<blink::mojom::DOMNodeIdDataView, blink::DOMNodeIdType> {
   static int32_t value(const blink::DOMNodeIdType& id) { return id.value(); }
 
   static bool Read(blink::mojom::DOMNodeIdDataView data,
-                   blink::DOMNodeIdType* out);
+                   blink::DOMNodeIdType* out) {
+    *out = blink::DOMNodeIdType(data.value());
+    return true;
+  }
 };
 
 }  // namespace mojo
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 4f68095..4ad9547f 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -13,6 +13,35 @@
   import("//build/config/android/config.gni")
 }
 
+mojom("dom_node_id") {
+  generate_java = true
+  generate_legacy_js_bindings = true
+  sources = [ "dom/dom_node_id.mojom" ]
+  webui_module_path = "/"
+
+  cpp_typemaps = [
+    {
+      types = [
+        {
+          mojom = "blink.mojom.DOMNodeId"
+          cpp = "::blink::DOMNodeIdType"
+        },
+      ]
+      traits_headers =
+          [ "//third_party/blink/public/common/dom/dom_node_id_mojom_traits.h" ]
+      traits_public_deps = [ "//third_party/blink/public/common:headers" ]
+    },
+  ]
+
+  export_class_attribute = "BLINK_COMMON_EXPORT"
+  export_define = "BLINK_COMMON_IMPLEMENTATION=1"
+  export_header = "third_party/blink/public/common/common_export.h"
+
+  export_class_attribute_blink = "PLATFORM_EXPORT"
+  export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
+  export_header_blink = "third_party/blink/renderer/platform/platform_export.h"
+}
+
 # This target includes all mojom interfaces which can be used from
 # renderer/platform. In particular these mojom interfaces can't use types that
 # are typemapped to a type in renderer/core.
@@ -78,7 +107,6 @@
     "devtools/devtools_frontend.mojom",
     "devtools/inspector_issue.mojom",
     "disk_allocator.mojom",
-    "dom/dom_node_id.mojom",
     "dom_storage/dom_storage.mojom",
     "dom_storage/session_storage_namespace.mojom",
     "dom_storage/storage_area.mojom",
@@ -320,6 +348,7 @@
     "//services/service_manager/public/mojom",
     "//services/viz/public/mojom",
     "//skia/public/mojom",
+    ":dom_node_id",
     "//third_party/blink/public/mojom/content_extraction",
     "//third_party/blink/public/mojom/fingerprinting_protection",
     "//third_party/blink/public/mojom/gpu",
@@ -883,18 +912,6 @@
         "//third_party/blink/public/common/webrtc/rtc_logging_mojom_traits.h",
       ]
     },
-    {
-      types = [
-        {
-          mojom = "blink.mojom.DOMNodeId"
-          cpp = "::blink::DOMNodeIdType"
-        },
-      ]
-      traits_headers =
-          [ "//third_party/blink/public/common/dom/dom_node_id_mojom_traits.h" ]
-      traits_sources =
-          [ "//third_party/blink/common/dom/dom_node_id_mojom_traits.cc" ]
-    },
   ]
   if (is_chromeos && use_cups) {
     cpp_typemaps += [
@@ -1220,6 +1237,7 @@
     "//services/service_manager/public/mojom",
     "//services/viz/public/mojom",
     "//skia/public/mojom",
+    ":dom_node_id",
     "//third_party/blink/public/mojom/fingerprinting_protection",
     "//third_party/blink/public/mojom/runtime_feature_state",
     "//third_party/blink/public/mojom/service_worker:storage",
@@ -1443,6 +1461,7 @@
 
   overridden_deps_blink = [
     ":android_mojo_bindings",
+    ":dom_node_id",
     ":mojom_platform",
     "//cc/mojom",
     "//services/network/public/mojom",
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
index 1a0207441..ac69d0b 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -264,8 +264,8 @@
   }
 
   // If there are any pending updates, we cancel them, as the fast updates
-  // can't detect a locked display.
-  // See: ../paint/README.md#Transform-update-optimization for more information
+  // can't detect a locked display. See:
+  // ../paint/README.md#Property-tree-update-optimization
   document_->View()->RemoveAllPendingUpdates();
 
   // There are two ways we can get locked:
@@ -546,8 +546,8 @@
   // Since we do perform updates in a locked display if we're in a forced
   // update scope, when ending a forced update scope in a locked display, we
   // remove all pending updates, to prevent them from being executed in a
-  // locked display.
-  // See: ../paint/README.md#Transform-update-optimization for more information
+  // locked display. See:
+  // ../paint/README.md#Property-tree-update-optimization
   if (is_locked_) {
     document_->View()->RemoveAllPendingUpdates();
   }
diff --git a/third_party/blink/renderer/core/editing/commands/apply_style_command.cc b/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
index f49f9ae..936bb25 100644
--- a/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
@@ -458,11 +458,8 @@
       auto* span = MakeGarbageCollected<HTMLSpanElement>(GetDocument());
       // Prevent merging the span with adjacent siblings, to ensure the DOM
       // structure and traversal order do not change.
-      SurroundNodeRangeWithElement(
-          node, node, span, editing_state,
-          RuntimeEnabledFeatures::AvoidMergingStyledSpanWithSiblingsEnabled()
-              ? kDoNotMergeSiblings
-              : kMergeSiblings);
+      SurroundNodeRangeWithElement(node, node, span, editing_state,
+                                   kDoNotMergeSiblings);
       if (editing_state->IsAborted())
         return;
       element = span;
@@ -1588,21 +1585,17 @@
       }
 
       if (style_to_push_down) {
-        EditingStyle* filtered_style_to_push_down = style_to_push_down;
-
-        if (RuntimeEnabledFeatures::
-                RemoveFormatFilterBackgroundColorEnabled()) {
-          // Filter out styles that should be removed - don't push down styles
-          // that conflict with the styles we're trying to remove
-          filtered_style_to_push_down = style_to_push_down->Copy();
-          if (style && style->Style() && filtered_style_to_push_down->Style()) {
-            // Remove any properties from style_to_push_down that are present in
-            // the style being removed
-            for (const CSSPropertyValue& property :
-                 style->Style()->Properties()) {
-              filtered_style_to_push_down->Style()->RemoveProperty(
-                  property.PropertyID());
-            }
+        EditingStyle* const filtered_style_to_push_down =
+            style_to_push_down->Copy();
+        // Filter out styles that should be removed - don't push down styles
+        // that conflict with the styles we're trying to remove
+        if (style && style->Style() && filtered_style_to_push_down->Style()) {
+          // Remove any properties from style_to_push_down that are present in
+          // the style being removed
+          for (const CSSPropertyValue& property :
+               style->Style()->Properties()) {
+            filtered_style_to_push_down->Style()->RemoveProperty(
+                property.PropertyID());
           }
         }
 
diff --git a/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc b/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc
index 04d66c39..5276383 100644
--- a/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc
+++ b/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc
@@ -626,9 +626,7 @@
   }
 
   if (paste_mode == PasteMode::kAllMimeTypes) {
-    RuntimeEnabledFeatures::InputEventDataTransferForInsertCmdEnabled()
-        ? PasteFromClipboard(frame, source, data_transfer)
-        : PasteFromClipboard(frame, source);
+    PasteFromClipboard(frame, source, data_transfer);
     return;
   }
   PasteAsPlainTextFromClipboard(frame, source);
diff --git a/third_party/blink/renderer/core/editing/commands/clipboard_commands.h b/third_party/blink/renderer/core/editing/commands/clipboard_commands.h
index 5afc4497..cedf743 100644
--- a/third_party/blink/renderer/core/editing/commands/clipboard_commands.h
+++ b/third_party/blink/renderer/core/editing/commands/clipboard_commands.h
@@ -141,7 +141,7 @@
   static void PasteAsPlainTextFromClipboard(LocalFrame&, EditorCommandSource);
   static void PasteFromClipboard(LocalFrame&,
                                  EditorCommandSource,
-                                 DataTransfer* = nullptr);
+                                 DataTransfer*);
   static void PasteFromImageUrl(LocalFrame&, EditorCommandSource, String);
 
   using FragmentAndPlainText = std::pair<DocumentFragment*, const bool>;
diff --git a/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc b/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc
index 4199186..0fde934 100644
--- a/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/composite_edit_command.cc
@@ -1767,9 +1767,7 @@
 
   const VisiblePosition visible_start = EndingVisibleSelection().VisibleStart();
   const VisiblePosition visible_end = EndingVisibleSelection().VisibleEnd();
-  if (RuntimeEnabledFeatures::
-          HandleDisconnectedSelectionDuringDOMChangesEnabled() &&
-      (visible_start.IsNull() || visible_end.IsNull())) {
+  if (visible_start.IsNull() || visible_end.IsNull()) {
     // A synchronous DOM mutation may invalidate VP endpoints.
     return std::nullopt;
   }
@@ -1787,10 +1785,8 @@
 
   const Position selection_start = EndingDomSelection().Start();
   const Position selection_end = EndingDomSelection().End();
-  if (RuntimeEnabledFeatures::
-          HandleDisconnectedSelectionDuringDOMChangesEnabled() &&
-      (!IsPreservedSelectionEndpointUsable(selection_start) ||
-       !IsPreservedSelectionEndpointUsable(selection_end))) {
+  if (!IsPreservedSelectionEndpointUsable(selection_start) ||
+      !IsPreservedSelectionEndpointUsable(selection_end)) {
     // A synchronous DOM mutation may stale raw-DOM endpoints.
     return std::nullopt;
   }
diff --git a/third_party/blink/renderer/core/editing/commands/editor_command.cc b/third_party/blink/renderer/core/editing/commands/editor_command.cc
index 06e630a1..9ce6440 100644
--- a/third_party/blink/renderer/core/editing/commands/editor_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/editor_command.cc
@@ -139,13 +139,9 @@
   // granularity (deleteWord*, deleteSoftLine*).
   auto get_deletion_input_type = [&frame](InputType selection_type,
                                           InputType caret_type) -> InputType {
-    if (RuntimeEnabledFeatures::
-            InputEventsDeleteNonCollapsedSelectionEnabled()) {
-      return frame.Selection().ComputeVisibleSelectionInDomTree().IsRange()
-                 ? selection_type
-                 : caret_type;
-    }
-    return caret_type;
+    return frame.Selection().ComputeVisibleSelectionInDomTree().IsRange()
+               ? selection_type
+               : caret_type;
   };
 
   switch (command_type) {
@@ -486,19 +482,13 @@
     if (kill_ring) {
       editor.AddToKillRing(editor.SelectedRange());
     }
-    InputEvent::InputType input_type;
-    if (RuntimeEnabledFeatures::
-            InputEventsDeleteNonCollapsedSelectionEnabled()) {
-      // When deleting a non-collapsed selection, the granularity shouldn't
-      // matter - we're just deleting the selected content. Use the appropriate
-      // "content" input type based on direction only.
-      input_type = (direction == DeleteDirection::kBackward)
-                       ? InputEvent::InputType::kDeleteContentBackward
-                       : InputEvent::InputType::kDeleteContentForward;
-    } else {
-      // use granularity-based input type.
-      input_type = DeletionInputTypeFromTextGranularity(direction, granularity);
-    }
+    // When deleting a non-collapsed selection, the granularity shouldn't
+    // matter - we're just deleting the selected content. Use the appropriate
+    // "content" input type based on direction only.
+    const InputEvent::InputType input_type =
+        (direction == DeleteDirection::kBackward)
+            ? InputEvent::InputType::kDeleteContentBackward
+            : InputEvent::InputType::kDeleteContentForward;
     editor.DeleteSelectionWithSmartDelete(
         CanSmartCopyOrDelete(frame) ? DeleteMode::kSmart : DeleteMode::kSimple,
         input_type);
@@ -1267,24 +1257,6 @@
          selection.RootEditableElement();
 }
 
-static bool EnabledRangeInEditableText(LocalFrame& frame,
-                                       Event*,
-                                       EditorCommandSource source) {
-  if (source == EditorCommandSource::kDom &&
-      frame.GetInputMethodController().GetActiveEditContext()) {
-    return false;
-  }
-
-  frame.GetDocument()->UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
-  if (source == EditorCommandSource::kMenuOrKeyBinding &&
-      !frame.Selection().SelectionHasFocus()) {
-    return false;
-  }
-  auto visible_selection =
-      frame.Selection().ComputeVisibleSelectionInDomTreeDeprecated();
-  return visible_selection.IsRange() && visible_selection.IsContentEditable();
-}
-
 static bool EnabledRangeInRichlyEditableText(LocalFrame& frame,
                                              Event*,
                                              EditorCommandSource source) {
@@ -1306,11 +1278,7 @@
 static bool IsRemoveFormatAllowed(LocalFrame& frame,
                                   Event* event,
                                   EditorCommandSource source) {
-  if (RuntimeEnabledFeatures::
-          DisableRemoveFormatForPlainTextOnlyEditableDivEnabled()) {
-    return EnabledRangeInRichlyEditableText(frame, event, source);
-  }
-  return EnabledRangeInEditableText(frame, event, source);
+  return EnabledRangeInRichlyEditableText(frame, event, source);
 }
 
 static bool EnabledRedo(LocalFrame& frame, Event*, EditorCommandSource) {
diff --git a/third_party/blink/renderer/core/editing/commands/selection_for_undo_step.cc b/third_party/blink/renderer/core/editing/commands/selection_for_undo_step.cc
index 8bb3eed..058dbd9b 100644
--- a/third_party/blink/renderer/core/editing/commands/selection_for_undo_step.cc
+++ b/third_party/blink/renderer/core/editing/commands/selection_for_undo_step.cc
@@ -8,7 +8,6 @@
 #include "third_party/blink/renderer/core/editing/selection_template.h"
 #include "third_party/blink/renderer/core/editing/text_affinity.h"
 #include "third_party/blink/renderer/core/editing/visible_selection.h"
-#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
@@ -51,9 +50,7 @@
   // Guard against concurrent DOM modifications that may have disconnected
   // positions. This prevents crashes when JavaScript callbacks modify DOM
   // during editing operations.
-  if (RuntimeEnabledFeatures::
-          HandleDisconnectedSelectionDuringDOMChangesEnabled() &&
-      (!anchor_.IsConnected() || !focus_.IsConnected())) {
+  if (!anchor_.IsConnected() || !focus_.IsConnected()) {
     // If positions are disconnected, return empty selection
     return SelectionInDomTree();
   }
diff --git a/third_party/blink/renderer/core/editing/commands/style_commands.cc b/third_party/blink/renderer/core/editing/commands/style_commands.cc
index 36ee2603..2ad37f8 100644
--- a/third_party/blink/renderer/core/editing/commands/style_commands.cc
+++ b/third_party/blink/renderer/core/editing/commands/style_commands.cc
@@ -50,7 +50,6 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/html/html_font_element.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
@@ -326,9 +325,7 @@
   // When toggling off a decoration in an empty element, clear the typing style
   // completely rather than setting it to "none". This prevents stale decoration
   // properties from being applied to subsequently typed text.
-  if (RuntimeEnabledFeatures::
-          FixStrikethroughToggleInEmptyContentEditableEnabled() &&
-      new_style == "none" &&
+  if (new_style == "none" &&
       property_id == CSSPropertyID::kWebkitTextDecorationsInEffect) {
     const VisibleSelection& selection =
         frame.Selection().ComputeVisibleSelectionInDomTree();
diff --git a/third_party/blink/renderer/core/editing/dom_selection.cc b/third_party/blink/renderer/core/editing/dom_selection.cc
index 9ba508d..9f80830 100644
--- a/third_party/blink/renderer/core/editing/dom_selection.cc
+++ b/third_party/blink/renderer/core/editing/dom_selection.cc
@@ -890,10 +890,7 @@
 
   TextIteratorBehavior::Builder behavior_builder;
   behavior_builder.SetForSelectionToString(true);
-
-  if (RuntimeEnabledFeatures::SelectionToStringSkipsUserSelectNoneEnabled()) {
-    behavior_builder.SetSkipsUnselectableContent(true);
-  }
+  behavior_builder.SetSkipsUnselectableContent(true);
 
   return PlainText(range, behavior_builder.Build());
 }
diff --git a/third_party/blink/renderer/core/editing/editing_utilities.cc b/third_party/blink/renderer/core/editing/editing_utilities.cc
index 6eb61ad..5f9d6d9b 100644
--- a/third_party/blink/renderer/core/editing/editing_utilities.cc
+++ b/third_party/blink/renderer/core/editing/editing_utilities.cc
@@ -1858,16 +1858,10 @@
   if (is_canceled) {
     return;
   }
-  if (RuntimeEnabledFeatures::InputEventDataTransferForInsertCmdEnabled()) {
-    frame.GetEditor().InsertTextWithoutSendingTextEvent(
-        replacement, false, nullptr,
-        InputEvent::InputType::kInsertReplacementText,
-        EditCommand::PasswordEchoBehavior::kDoNotEcho, data_transfer);
-  } else {
-    frame.GetEditor().InsertTextWithoutSendingTextEvent(
-        replacement, false, nullptr,
-        InputEvent::InputType::kInsertReplacementText);
-  }
+  frame.GetEditor().InsertTextWithoutSendingTextEvent(
+      replacement, false, nullptr,
+      InputEvent::InputType::kInsertReplacementText,
+      EditCommand::PasswordEchoBehavior::kDoNotEcho, data_transfer);
 }
 
 // |IsEmptyNonEditableNodeInEditable()| is introduced for fixing
diff --git a/third_party/blink/renderer/core/editing/editor.cc b/third_party/blink/renderer/core/editing/editor.cc
index 9c14e5b..b6526cb 100644
--- a/third_party/blink/renderer/core/editing/editor.cc
+++ b/third_party/blink/renderer/core/editing/editor.cc
@@ -405,12 +405,8 @@
     return true;
 
   if (should_insert && drop_target->isConnected()) {
-    if (RuntimeEnabledFeatures::InputEventDataTransferForInsertCmdEnabled()) {
-      ReplaceSelectionAfterDragging(fragment, insert_mode, drag_source_type,
-                                    data_transfer);
-    } else {
-      ReplaceSelectionAfterDragging(fragment, insert_mode, drag_source_type);
-    }
+    ReplaceSelectionAfterDragging(fragment, insert_mode, drag_source_type,
+                                  data_transfer);
   }
   return true;
 }
diff --git a/third_party/blink/renderer/core/editing/editor.h b/third_party/blink/renderer/core/editing/editor.h
index 250594f..1f176e96 100644
--- a/third_party/blink/renderer/core/editing/editor.h
+++ b/third_party/blink/renderer/core/editing/editor.h
@@ -215,7 +215,7 @@
   void ReplaceSelectionAfterDragging(DocumentFragment*,
                                      InsertMode,
                                      DragSourceType,
-                                     DataTransfer* = nullptr);
+                                     DataTransfer*);
 
   // Return false if frame was destroyed by event handler, should stop executing
   // remaining actions.
diff --git a/third_party/blink/renderer/core/editing/ime/edit_context.cc b/third_party/blink/renderer/core/editing/ime/edit_context.cc
index 9b377a18..94d89f8 100644
--- a/third_party/blink/renderer/core/editing/ime/edit_context.cc
+++ b/third_party/blink/renderer/core/editing/ime/edit_context.cc
@@ -616,10 +616,6 @@
 wtf_size_t FindNextBoundaryOffset(const String& str, wtf_size_t current);
 
 void EditContext::EnsureSelectionWithinTextBounds() {
-  if (!RuntimeEnabledFeatures::
-          UseBoundedSelectionOffsetsInEditContextDeleteOperationsEnabled()) {
-    return;
-  }
   SetSelection(std::min(selection_start_, text_.length()),
                std::min(selection_end_, text_.length()));
 }
diff --git a/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc b/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc
index c668d6e..10da495 100644
--- a/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc
+++ b/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc
@@ -274,7 +274,6 @@
       // elements are currently not supported - absent in mathml_names.h
       auto* mathml_element = DynamicTo<MathMLElement>(ancestor);
       if (mathml_element &&
-          RuntimeEnabledFeatures::MathMLSkipMtrTagInAncestorWrappingEnabled() &&
           mathml_element->HasTagName(mathml_names::kMtrTag)) {
         if (ancestor == highest_node_to_be_serialized_) {
           break;
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 5d858716..6f5c989 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -1363,8 +1363,7 @@
   // A set of objects needing a transform property tree update. These updates
   // are deferred until the end prepaint and updating them directly, if
   // possible, avoids needing to walk the tree to update them. See:
-  // https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/core/paint/README.md#Transform-update-optimization
-  // for more on the fast path
+  // ../paint/README.md#Property-tree-update-optimization
   // TODO(yotha): unify these into one HeapHashMap.
   Member<GCedHeapHashSet<Member<LayoutObject>>> pending_transform_updates_;
   Member<GCedHeapHashSet<Member<LayoutObject>>> pending_opacity_updates_;
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
index d2b9cc6..714f5df 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -1424,12 +1424,13 @@
 
   class UnboundedSurfaceState final
       : public GarbageCollected<UnboundedSurfaceState>,
-        public ExecutionContextLifecycleObserver {
+        public ExecutionContextLifecycleObserver,
+        public mojom::blink::UnboundedSurfaceClient {
    public:
     UnboundedSurfaceState(WebFrameWidgetImpl* widget, ExecutionContext* context)
         : ExecutionContextLifecycleObserver(context),
           widget_(widget),
-          client_receiver_(widget, context),
+          client_receiver_(this, context),
           host_(context) {}
 
     void Trace(Visitor* visitor) const override {
@@ -1443,9 +1444,24 @@
 
     void ContextDestroyed() override { widget_->UnboundedContextDestroyed(); }
 
+    // mojom::blink::UnboundedSurfaceClient overrides:
+    void OnSurfaceAllocated(
+        const viz::FrameSinkId& frame_sink_id,
+        const viz::LocalSurfaceId& local_surface_id) override {
+      if (widget_ && widget_->unbounded_surface_state_.Get() == this) {
+        widget_->OnSurfaceAllocated(frame_sink_id, local_surface_id);
+      }
+    }
+
+    void OnDismissed() override {
+      if (widget_ && widget_->unbounded_surface_state_.Get() == this) {
+        widget_->OnDismissed();
+      }
+    }
+
     Member<WebFrameWidgetImpl> widget_;
     HeapMojoAssociatedReceiver<mojom::blink::UnboundedSurfaceClient,
-                               WebFrameWidgetImpl>
+                               UnboundedSurfaceState>
         client_receiver_;
     HeapMojoAssociatedRemote<mojom::blink::UnboundedSurfaceHost> host_;
 
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index b1ad6f6..3614a8cc 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -30,6 +30,8 @@
 #include "base/containers/adapters.h"
 #include "base/containers/enum_set.h"
 #include "base/feature_list.h"
+#include "base/system/sys_info.h"
+#include "build/build_config.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/forms/form_control_type.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame/frame.mojom-blink.h"
@@ -1670,6 +1672,23 @@
     return promise;
   }
 
+#if BUILDFLAG(IS_ANDROID)
+  // Unbounded elements rely on
+  // AttachedSurfaceControl.buildReparentTransaction(), which requires Android U
+  // (API level 34 / Android 14) or higher. See
+  // UnboundedSurfacePopupWindow::create() in
+  // content/public/android/java/src/org/chromium/content/browser/UnboundedSurfacePopupWindow.java
+  int32_t major_version = 0, minor_version = 0, bugfix_version = 0;
+  base::SysInfo::OperatingSystemVersionNumbers(&major_version, &minor_version,
+                                               &bugfix_version);
+  if (major_version < 14) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kNotSupportedError,
+        "Unbounded elements are only supported on Android 14 (API level 34) or "
+        "higher."));
+    return promise;
+  }
+#endif
   SetUnboundedElementActive(true);
 
   mojo::PendingAssociatedRemote<mojom::blink::UnboundedSurfaceHost> host_remote;
diff --git a/third_party/blink/renderer/core/page/autoscroll_controller.cc b/third_party/blink/renderer/core/page/autoscroll_controller.cc
index be77bb1..10fec1ba 100644
--- a/third_party/blink/renderer/core/page/autoscroll_controller.cc
+++ b/third_party/blink/renderer/core/page/autoscroll_controller.cc
@@ -489,9 +489,7 @@
         return;
       }
 
-      if (!RuntimeEnabledFeatures::
-              SelectionUpdateOnlyAfterAutoscrollEnabled() ||
-          scroll_result_) {
+      if (scroll_result_) {
         event_handler.UpdateSelectionForMouseDrag();
       }
 
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 6e52ca2..39e5914 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -3986,9 +3986,8 @@
   // If we reach FragmentPaintPropertyTreeBuilder for an object needing a
   // pending transform update, we need to go ahead and do a regular transform
   // update so that the context (e.g.,
-  // |translation_2d_to_layout_shift_root_delta|) is updated properly.
-  // See: ../paint/README.md#Transform-update-optimization for more on
-  // optimized transform updates
+  // |translation_2d_to_layout_shift_root_delta|) is updated properly. See:
+  // README.md#Property-tree-update-optimization
   if (object_.GetFrameView()->RemovePendingTransformUpdate(object_))
     object_.GetMutableForPainting().SetOnlyThisNeedsPaintPropertyUpdate();
   if (object_.GetFrameView()->RemovePendingOpacityUpdate(object_))
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_test.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_test.cc
index dbd7f9f..0f4d42a 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_test.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_test.cc
@@ -419,8 +419,7 @@
     NOTIMPLEMENTED();
   }
 
-  void ImportTensor(uint64_t flow_id,
-                    const gpu::SyncToken& sync_token_fence) override {
+  void ImportTensor(uint64_t flow_id, uint64_t release_count) override {
     NOTIMPLEMENTED();
   }
 
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_tensor.cc b/third_party/blink/renderer/modules/ml/webnn/ml_tensor.cc
index ae45cfe..a54b1855 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_tensor.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_tensor.cc
@@ -461,7 +461,8 @@
 
         // WaitSyncToken(t3)
         // WebNNTensor::ImportTensor calls WaitSyncToken.
-        tensor->remote_tensor_->ImportTensor(flow_id, webgpu_finished_access_token);
+        tensor->remote_tensor_->ImportTensor(
+            flow_id, webgpu_finished_access_token.release_count());
 
         // Resume use of MLTensor.
         tensor->gpu_buffer_.Clear();
diff --git a/third_party/blink/renderer/modules/xr/xr_system.cc b/third_party/blink/renderer/modules/xr/xr_system.cc
index ff33a01..b85aeece 100644
--- a/third_party/blink/renderer/modules/xr/xr_system.cc
+++ b/third_party/blink/renderer/modules/xr/xr_system.cc
@@ -82,11 +82,8 @@
 const char kNoDevicesMessage[] = "No XR hardware found.";
 
 const char kImmersiveArModeNotValid[] =
-    "Failed to execute '%s' on 'XRSystem': The provided value 'immersive-ar' "
-    "is not a valid enum value of type XRSessionMode.";
-
-const char kTrackedImageWidthInvalid[] =
-    "trackedImages[%d].widthInMeters invalid, must be a positive number.";
+    "Failed to execute 'requestSession' on 'XRSystem': The provided value "
+    "'immersive-ar' is not a valid enum value of type XRSessionMode.";
 
 constexpr device::mojom::XRSessionFeature kDefaultImmersiveVrFeatures[] = {
     device::mojom::XRSessionFeature::REF_SPACE_VIEWER,
@@ -1267,8 +1264,7 @@
   if (session_mode == device::mojom::blink::XRSessionMode::kImmersiveAr &&
       !IsImmersiveArAllowed()) {
     DVLOG(1) << __func__ << ": Immersive AR not allowed";
-    exception_state.ThrowTypeError(UNSAFE_TODO(
-        String::Format(kImmersiveArModeNotValid, "requestSession")));
+    exception_state.ThrowTypeError(kImmersiveArModeNotValid);
 
     // We haven't created the query yet, so we can't use it to implicitly log
     // our metrics for us, so explicitly log it here, as the query requires the
@@ -1358,7 +1354,8 @@
       if (std::isnan(image->widthInMeters()) ||
           image->widthInMeters() <= 0.0f) {
         String message =
-            UNSAFE_TODO(String::Format(kTrackedImageWidthInvalid, index));
+            StrCat({"trackedImages[", String::Number(index),
+                    "].widthInMeters invalid, must be a positive number."});
         query->RejectWithTypeError(message, &exception_state);
         return promise;
       }
diff --git a/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist b/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist
index 7e14ece..c4a7cf9 100644
--- a/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist
+++ b/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist
@@ -151,6 +151,7 @@
 ../../web_tests/external/wpt/jpegxl/resources/3x3a_srgb_lossless.jxl
 ../../web_tests/external/wpt/jpegxl/resources/3x3a_srgb_lossy.jxl
 ../../web_tests/external/wpt/jpegxl/resources/8x8_noise.jxl
+../../web_tests/external/wpt/jpegxl/resources/animated-red-green.jxl
 ../../web_tests/external/wpt/jpegxl/resources/animation-1loop.jxl
 ../../web_tests/external/wpt/jpegxl/resources/animation-2loops.jxl
 ../../web_tests/external/wpt/jpegxl/resources/animation-infiniteloop.jxl
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.cc b/third_party/blink/renderer/platform/loader/fetch/resource.cc
index 6ffb8ce..afcc2060 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -79,7 +79,7 @@
 // Feature that prevents an extension resource (chrome-extension://...) from
 // being fetched across isolated worlds.
 BASE_FEATURE(kPreventExtensionResourceFetchAcrossIsolatedWorlds,
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 // Feature that prevents resources fetched via a Service Worker from being
 // reused across different script worlds.
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.h b/third_party/blink/renderer/platform/loader/fetch/resource.h
index a6abed96..c0e823db 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.h
@@ -30,6 +30,7 @@
 
 #include "base/auto_reset.h"
 #include "base/containers/span.h"
+#include "base/feature_list.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory_coordinator/memory_consumer.h"
 #include "base/task/single_thread_task_runner.h"
@@ -72,6 +73,11 @@
 
 namespace blink {
 
+PLATFORM_EXPORT BASE_DECLARE_FEATURE(
+    kPreventExtensionResourceFetchAcrossIsolatedWorlds);
+PLATFORM_EXPORT BASE_DECLARE_FEATURE(
+    kPreventCrossWorldServiceWorkerResourceReuse);
+
 class BackgroundResponseProcessorFactory;
 class BlobDataHandle;
 class FetchParameters;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
index aff18f6..b153a233 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
@@ -2478,6 +2478,12 @@
 // world) are not reused by requests originating from a different isolated
 // world. Regression test for crbug.com/461167648.
 TEST_P(ResourceFetcherTest, CrossWorldExtensionResourceMismatch) {
+  // Explicitly enable `kPreventExtensionResourceFetchAcrossIsolatedWorlds` for
+  // this test since the feature is disabled by default.
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      kPreventExtensionResourceFetchAcrossIsolatedWorlds);
+
   // Register the scheme to ensure it's recognized as an extension.
   CommonSchemeRegistry::RegisterURLSchemeAsExtension("chrome-extension");
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index a51dd91..fd9f8255 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -810,11 +810,6 @@
       status: "stable",
     },
     {
-      // Fix for https://crbug.com/471876082
-      name: "AvoidMergingStyledSpanWithSiblings",
-      status: "stable",
-    },
-    {
       // Disables the synchronous focus blur behavior when a focused form control
       // is disabled.
       name: "AvoidSynchronousBlurOnDisabledAttributeChange",
@@ -2371,11 +2366,6 @@
       base_feature: "none",
     },
     {
-      // Disables removeFormat command on Plaintext-only content editable div
-      name: "DisableRemoveFormatForPlainTextOnlyEditableDiv",
-      status: "stable",
-    },
-    {
       // Disconnected <area> elements should not trigger navigation.
       // This was added in M146, and can be removed in M148.
       name: "DisallowDisconnectedAreaNavigation",
@@ -3082,12 +3072,6 @@
       name: "FixSelectionPaintRangeNullOpt",
       status: "stable",
     },
-    {
-      // Fix for strikethrough toggle not working in empty
-      // contenteditable. See https://crbug.com/427915243
-      name: "FixStrikethroughToggleInEmptyContentEditable",
-      status: "stable",
-    },
     // Fix LayoutView viewport mapping in the GeometryMapper fast path for
     // OOPIFs.
     {
@@ -3435,10 +3419,6 @@
       status: "test",
     },
     {
-      name: "HandleDisconnectedSelectionDuringDOMChanges",
-      status: "stable",
-    },
-    {
       name: "HandleShadowDOMInSubstringUtil",
       status: "stable",
     },
@@ -3673,20 +3653,6 @@
       status: "stable",
     },
     {
-      // Enable DataTransfer propagation to input events for insertFromPaste,
-      // insertFromDrop, and insertReplacementText operations on content-editables.
-      name: "InputEventDataTransferForInsertCmd",
-      status: "stable",
-    },
-    {
-      // When enabled, deletion commands like Ctrl+Backspace (deleteWordBackward)
-      // with selected text will correctly report deleteContentBackward instead of
-      // deleteWordBackward in both beforeinput and input events, conforming to
-      // the W3C Input Events spec.
-      name: "InputEventsDeleteNonCollapsedSelection",
-      status: "stable",
-    },
-    {
       // The HTML parser inserts a </select> end tag before inserting <input>
       // when parsing inside of a <select> tag. We want to allow <input> inside
       // <select>, but it isn't web compatible enough. This flag allows <input>
@@ -4016,12 +3982,6 @@
       status: "stable",
     },
     {
-      // Enables skipping <mtr> wrapping during ancestor wrapping in
-      // serialization. When enabled duplicate <mtr> wrapping are absent.
-      name: "MathMLSkipMtrTagInAncestorWrapping",
-      status: "stable",
-    },
-    {
       name:"MeasureMemory",
       status:"stable",
     },
@@ -5119,14 +5079,6 @@
       status: "stable",
     },
     {
-      // Fix for removeFormat command incorrectly preserving background-color
-      // when removing styles from elements. Prevents background-color from being
-      // re-applied to child elements during style removal.
-      // See https://crbug.com/41438799
-      name: "RemoveFormatFilterBackgroundColor",
-      status: "stable",
-    },
-    {
       // Remove non-allowlisted event interfaces from document.createEvent().
       // This was added in M148 and can be removed after M150.
       // https://chromestatus.com/feature/5095987863486464
@@ -5590,20 +5542,6 @@
       settable_from_internals: true,
     },
     {
-      // Enables skipping user-select: none content
-      // in DOMSelection::toString()
-      name: "SelectionToStringSkipsUserSelectNone",
-      status: "stable",
-    },
-    {
-      // Conditionally update selection only when autoscroll actually scrolls.
-      // This prevents infinite selection updates during autoscroll when the
-      // scroll position doesn't change.
-      // See https://crbug.com/412578722
-      name: "SelectionUpdateOnlyAfterAutoscroll",
-      status: "stable",
-    },
-    {
       // See https://chromestatus.com/feature/5203433409871872
       name: "SelectiveClipboardFormatRead",
       status: "test",
@@ -6636,11 +6574,6 @@
       },
     },
     {
-      // Fix for https://crbug.com/454639396
-      name: "UseBoundedSelectionOffsetsInEditContextDeleteOperations",
-      status: "stable"
-    },
-    {
       name: "UseLargestPaintedImageForLCPCandidate",
       status: "stable",
     },
diff --git a/third_party/blink/renderer/platform/wtf/BUILD.gn b/third_party/blink/renderer/platform/wtf/BUILD.gn
index 7ca6c41..2d2e9b57 100644
--- a/third_party/blink/renderer/platform/wtf/BUILD.gn
+++ b/third_party/blink/renderer/platform/wtf/BUILD.gn
@@ -319,6 +319,10 @@
 
 test("wtf_unittests") {
   deps = [ ":wtf_unittests_sources" ]
+  fuzztests = [
+    "TextCodecUtf8FuzzTest.DecodeFastPathMatchesLegacy",
+    "TextCodecUtf8FuzzTest.DecodeStreamingFastPathMatchesLegacy",
+  ]
 }
 
 test("wtf_perf_tests") {
@@ -433,6 +437,7 @@
     "//base/test:test_support",
     "//testing/gmock",
     "//testing/gtest",
+    "//third_party/fuzztest",
   ]
 
   # TODO(crbug.com/40031409): Fix code that adds exit-time destructors and
diff --git a/third_party/blink/renderer/platform/wtf/DEPS b/third_party/blink/renderer/platform/wtf/DEPS
index 1ee6b4e..38d5b04 100644
--- a/third_party/blink/renderer/platform/wtf/DEPS
+++ b/third_party/blink/renderer/platform/wtf/DEPS
@@ -4,6 +4,8 @@
     "+base/apple/bridging.h",
     "+base/apple/scoped_cftyperef.h",
     "+base/apple/foundation_util.h",
+    "+base/feature_list.h",
+    "+base/features.h",
     "+base/functional/identity.h",
     "+base/i18n/string_search.h",
     "+base/numerics",
@@ -30,4 +32,8 @@
     # Refers to base containers to test compatibility.
     "+base/containers",
   ],
+  ".*_test\\.cc": [
+    "+base/test",
+    "+third_party/fuzztest",
+  ],
 }
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 c0d93e79..0f4d8e9 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
@@ -31,12 +31,16 @@
 
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
+#include "base/feature_list.h"
+#include "base/features.h"
 #include "base/memory/ptr_util.h"
 #include "base/numerics/checked_math.h"
+#include "base/strings/string_util.h"
 #include "base/types/to_address.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 #include "third_party/blink/renderer/platform/wtf/text/character_names.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_impl.h"
 #include "third_party/blink/renderer/platform/wtf/text/text_codec_ascii_fast_path.h"
 
 namespace blink {
@@ -358,6 +362,8 @@
                              bool stop_on_error,
                              bool& saw_error) {
   const bool do_flush = flush != FlushBehavior::kDoNotFlush;
+  const bool fast_ascii_range_copy =
+      base::FeatureList::IsEnabled(base::features::kUtfConversionAsciiFastPath);
 
   // Each input byte might turn into a character.
   // That includes all bytes in the partial-sequence buffer because
@@ -392,26 +398,36 @@
     while (!source.empty()) {
       if (IsAscii(source[0])) {
         // Fast path for ASCII. Most UTF-8 text will be ASCII.
-        if (IsAlignedToMachineWord(source.data())) {
-          while (source.data() < aligned_end) {
-            MachineWord chunk =
-                *reinterpret_cast_ptr<const MachineWord*>(source.data());
-            if (!IsAllAscii<LChar>(chunk)) {
-              break;
-            }
-            CopyAsciiMachineWord(
-                chunk, destination.take_first<sizeof(MachineWord)>().data());
-            source.take_first<sizeof(MachineWord)>();
-          }
+        if (fast_ascii_range_copy) {
+          size_t ascii_len = base::FindFirstNonASCII(
+              base::as_string_view(base::as_chars(source)));
+          destination.take_first(ascii_len).copy_from(
+              source.take_first(ascii_len));
           if (source.empty()) {
             break;
           }
-          if (!IsAscii(source[0])) {
-            continue;
+        } else {
+          if (IsAlignedToMachineWord(source.data())) {
+            while (source.data() < aligned_end) {
+              MachineWord chunk =
+                  *reinterpret_cast_ptr<const MachineWord*>(source.data());
+              if (!IsAllAscii<LChar>(chunk)) {
+                break;
+              }
+              CopyAsciiMachineWord(
+                  chunk, destination.take_first<sizeof(MachineWord)>().data());
+              source.take_first<sizeof(MachineWord)>();
+            }
+            if (source.empty()) {
+              break;
+            }
+            if (!IsAscii(source[0])) {
+              continue;
+            }
           }
+          destination.take_first<1u>()[0] = source.take_first_elem();
+          continue;
         }
-        destination.take_first<1u>()[0] = source.take_first_elem();
-        continue;
       }
       size_t count = kNonAsciiSequenceLength[source[0]];
       int character;
@@ -450,11 +466,8 @@
   // Copy the already converted characters
   const size_t characters_converted =
       static_cast<size_t>(destination.data() - buffer.Span().data());
-  auto dest16_converted = destination16.take_first(characters_converted);
-  auto converted8_span = buffer.Span().first(characters_converted);
-  for (size_t i = 0; i < converted8_span.size(); ++i) {
-    dest16_converted[i] = converted8_span[i];
-  }
+  StringImpl::CopyChars(destination16.take_first(characters_converted),
+                        buffer.Span().first(characters_converted));
 
   do {
     if (partial_sequence_size_) {
@@ -475,28 +488,39 @@
     while (!source.empty()) {
       if (IsAscii(source[0])) {
         // Fast path for ASCII. Most UTF-8 text will be ASCII.
-        if (IsAlignedToMachineWord(source.data())) {
-          while (source.data() < aligned_end) {
-            MachineWord chunk =
-                *reinterpret_cast_ptr<const MachineWord*>(source.data());
-            if (!IsAllAscii<LChar>(chunk)) {
-              break;
-            }
-            // SAFETY: `take_first<sizeof(MachineWord)>()` ensures sufficient
-            // size.
-            UNSAFE_BUFFERS(CopyAsciiMachineWord(
-                chunk, destination16.take_first<sizeof(MachineWord)>().data()));
-            source.take_first<sizeof(MachineWord)>();
-          }
+        if (fast_ascii_range_copy) {
+          size_t ascii_len = base::FindFirstNonASCII(
+              base::as_string_view(base::as_chars(source)));
+          StringImpl::CopyChars(destination16.take_first(ascii_len),
+                                source.take_first(ascii_len));
           if (source.empty()) {
             break;
           }
-          if (!IsAscii(source[0])) {
-            continue;
+        } else {
+          if (IsAlignedToMachineWord(source.data())) {
+            while (source.data() < aligned_end) {
+              MachineWord chunk =
+                  *reinterpret_cast_ptr<const MachineWord*>(source.data());
+              if (!IsAllAscii<LChar>(chunk)) {
+                break;
+              }
+              // SAFETY: `take_first<sizeof(MachineWord)>()` ensures sufficient
+              // size.
+              UNSAFE_BUFFERS(CopyAsciiMachineWord(
+                  chunk,
+                  destination16.take_first<sizeof(MachineWord)>().data()));
+              source.take_first<sizeof(MachineWord)>();
+            }
+            if (source.empty()) {
+              break;
+            }
+            if (!IsAscii(source[0])) {
+              continue;
+            }
           }
+          destination16.take_first<1u>()[0] = source.take_first_elem();
+          continue;
         }
-        destination16.take_first<1u>()[0] = source.take_first_elem();
-        continue;
       }
       size_t count = kNonAsciiSequenceLength[source[0]];
       int character;
diff --git a/third_party/blink/renderer/platform/wtf/text/text_codec_utf8_test.cc b/third_party/blink/renderer/platform/wtf/text/text_codec_utf8_test.cc
index 6dbbb72..64fc0998 100644
--- a/third_party/blink/renderer/platform/wtf/text/text_codec_utf8_test.cc
+++ b/third_party/blink/renderer/platform/wtf/text/text_codec_utf8_test.cc
@@ -32,19 +32,39 @@
 
 #include <limits>
 #include <memory>
+#include <string>
+#include <string_view>
+#include <vector>
 
 #include "base/compiler_specific.h"
+#include "base/features.h"
+#include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
 #include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
 #include "third_party/blink/renderer/platform/wtf/text/text_encoding_registry.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/fuzztest/src/fuzztest/fuzztest.h"
 
 namespace blink {
 
 namespace {
 
-TEST(TextCodecUtf8Test, DecodeAscii) {
+class TextCodecUtf8Test : public testing::TestWithParam<bool> {
+ public:
+  void SetUp() override {
+    scoped_feature_list_.InitWithFeatureState(
+        base::features::kUtfConversionAsciiFastPath, GetParam());
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(All, TextCodecUtf8Test, testing::Bool());
+
+TEST_P(TextCodecUtf8Test, DecodeAscii) {
   TextEncoding encoding("UTF-8");
   std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
 
@@ -59,7 +79,7 @@
   }
 }
 
-TEST(TextCodecUtf8Test, DecodeChineseCharacters) {
+TEST_P(TextCodecUtf8Test, DecodeChineseCharacters) {
   TextEncoding encoding("UTF-8");
   std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
 
@@ -76,7 +96,7 @@
   EXPECT_EQ(0x5b57U, result[1]);
 }
 
-TEST(TextCodecUtf8Test, Decode0xFF) {
+TEST_P(TextCodecUtf8Test, Decode0xFF) {
   TextEncoding encoding("UTF-8");
   std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
 
@@ -89,7 +109,7 @@
   EXPECT_EQ(0xFFFDU, result[0]);
 }
 
-TEST(TextCodecUtf8Test, DecodeOverflow) {
+TEST_P(TextCodecUtf8Test, DecodeOverflow) {
   TextEncoding encoding("UTF-8");
   std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
 
@@ -107,7 +127,7 @@
       "");
 }
 
-TEST(TextCodecUtf8Test, DecodeMultiplePartialsAfterError) {
+TEST_P(TextCodecUtf8Test, DecodeMultiplePartialsAfterError) {
   TextEncoding encoding("UTF-8");
   std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
 
@@ -137,6 +157,85 @@
   }
 }
 
+void DecodeFastPathMatchesLegacy(std::string_view input, bool stop_on_error) {
+  TextEncoding encoding("UTF-8");
+  auto input_span = base::as_bytes(base::span(input));
+
+  String out_fast;
+  bool saw_error_fast = false;
+  {
+    base::test::ScopedFeatureList features(
+        base::features::kUtfConversionAsciiFastPath);
+    std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+    out_fast = codec->Decode(input_span, FlushBehavior::kDataEof, stop_on_error,
+                             saw_error_fast);
+  }
+
+  String out_legacy;
+  bool saw_error_legacy = false;
+  {
+    base::test::ScopedFeatureList features;
+    features.InitAndDisableFeature(base::features::kUtfConversionAsciiFastPath);
+    std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+    out_legacy = codec->Decode(input_span, FlushBehavior::kDataEof,
+                               stop_on_error, saw_error_legacy);
+  }
+
+  EXPECT_EQ(saw_error_fast, saw_error_legacy);
+  EXPECT_EQ(out_fast, out_legacy);
+  EXPECT_EQ(out_fast.Is8Bit(), out_legacy.Is8Bit());
+}
+FUZZ_TEST(TextCodecUtf8FuzzTest, DecodeFastPathMatchesLegacy);
+
+void DecodeStreamingFastPathMatchesLegacy(
+    const std::vector<std::string>& chunks,
+    bool stop_on_error) {
+  TextEncoding encoding("UTF-8");
+
+  std::vector<String> outputs_fast;
+  std::vector<bool> errors_fast;
+  {
+    base::test::ScopedFeatureList features(
+        base::features::kUtfConversionAsciiFastPath);
+    std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+    for (size_t i = 0; i < chunks.size(); ++i) {
+      bool saw_error = false;
+      const bool is_last = (i + 1 == chunks.size());
+      FlushBehavior flush =
+          is_last ? FlushBehavior::kDataEof : FlushBehavior::kDoNotFlush;
+      String out = codec->Decode(base::as_bytes(base::span(chunks[i])), flush,
+                                 stop_on_error, saw_error);
+      outputs_fast.push_back(out);
+      errors_fast.push_back(saw_error);
+    }
+  }
+
+  std::vector<String> outputs_legacy;
+  std::vector<bool> errors_legacy;
+  {
+    base::test::ScopedFeatureList features;
+    features.InitAndDisableFeature(base::features::kUtfConversionAsciiFastPath);
+    std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+    for (size_t i = 0; i < chunks.size(); ++i) {
+      bool saw_error = false;
+      const bool is_last = (i + 1 == chunks.size());
+      FlushBehavior flush =
+          is_last ? FlushBehavior::kDataEof : FlushBehavior::kDoNotFlush;
+      String out = codec->Decode(base::as_bytes(base::span(chunks[i])), flush,
+                                 stop_on_error, saw_error);
+      outputs_legacy.push_back(out);
+      errors_legacy.push_back(saw_error);
+    }
+  }
+
+  EXPECT_EQ(errors_fast, errors_legacy);
+  EXPECT_EQ(outputs_fast, outputs_legacy);
+  for (size_t i = 0; i < outputs_fast.size(); ++i) {
+    EXPECT_EQ(outputs_fast[i].Is8Bit(), outputs_legacy[i].Is8Bit());
+  }
+}
+FUZZ_TEST(TextCodecUtf8FuzzTest, DecodeStreamingFastPathMatchesLegacy);
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/wtf/text/utf8_perf_test.cc b/third_party/blink/renderer/platform/wtf/text/utf8_perf_test.cc
index 222a7eb8..d9fcede8 100644
--- a/third_party/blink/renderer/platform/wtf/text/utf8_perf_test.cc
+++ b/third_party/blink/renderer/platform/wtf/text/utf8_perf_test.cc
@@ -4,17 +4,33 @@
 
 #include <vector>
 
+#include "base/features.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding_registry.h"
 #include "third_party/blink/renderer/platform/wtf/text/utf8.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_uchar.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink::unicode {
 
-class Utf8PerfTest : public testing::Test {};
+class Utf8PerfTest : public testing::TestWithParam<bool> {
+ public:
+  void SetUp() override {
+    scoped_feature_list_.InitWithFeatureState(
+        base::features::kUtfConversionAsciiFastPath, GetParam());
+  }
 
-TEST_F(Utf8PerfTest, ConvertUtf16ToUtf8_Ascii) {
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(All, Utf8PerfTest, testing::Bool());
+
+TEST_P(Utf8PerfTest, ConvertUtf16ToUtf8_Ascii) {
   const int kSize = 10000;
   const int kNumIterations = 100000;
 
@@ -31,10 +47,12 @@
   }
   base::TimeTicks end = base::TimeTicks::Now();
   double time_per_op = (end - start).InMicrosecondsF() / kNumIterations;
-  LOG(INFO) << "ConvertUtf16ToUtf8_Ascii (10k): " << time_per_op << " us/op";
+  LOG(INFO) << "ConvertUtf16ToUtf8_Ascii (10k, "
+            << (GetParam() ? "Fast" : "Legacy") << "): " << time_per_op
+            << " us/op";
 }
 
-TEST_F(Utf8PerfTest, ConvertUtf16ToUtf8_NonAscii) {
+TEST_P(Utf8PerfTest, ConvertUtf16ToUtf8_NonAscii) {
   const int kSize = 10000;
   const int kNumIterations = 100000;
 
@@ -51,10 +69,12 @@
   }
   base::TimeTicks end = base::TimeTicks::Now();
   double time_per_op = (end - start).InMicrosecondsF() / kNumIterations;
-  LOG(INFO) << "ConvertUtf16ToUtf8_NonAscii (10k): " << time_per_op << " us/op";
+  LOG(INFO) << "ConvertUtf16ToUtf8_NonAscii (10k, "
+            << (GetParam() ? "Fast" : "Legacy") << "): " << time_per_op
+            << " us/op";
 }
 
-TEST_F(Utf8PerfTest, ConvertUtf16ToUtf8_Strict_NonAscii) {
+TEST_P(Utf8PerfTest, ConvertUtf16ToUtf8_Strict_NonAscii) {
   const int kSize = 10000;
   const int kNumIterations = 100000;
 
@@ -71,7 +91,166 @@
   }
   base::TimeTicks end = base::TimeTicks::Now();
   double time_per_op = (end - start).InMicrosecondsF() / kNumIterations;
-  LOG(INFO) << "ConvertUtf16ToUtf8_Strict_NonAscii (10k): " << time_per_op
+  LOG(INFO) << "ConvertUtf16ToUtf8_Strict_NonAscii (10k, "
+            << (GetParam() ? "Fast" : "Legacy") << "): " << time_per_op
+            << " us/op";
+}
+
+TEST_P(Utf8PerfTest, Decode_Ascii) {
+  const int kSize = 10000;
+  const int kNumIterations = 100000;
+
+  std::string src(kSize, 'a');
+  auto src_span = base::as_bytes(base::span(src));
+
+  TextEncoding encoding("UTF-8");
+  std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+
+  base::TimeTicks start = base::TimeTicks::Now();
+  for (int i = 0; i < kNumIterations; ++i) {
+    bool saw_error = false;
+    codec->Decode(src_span, FlushBehavior::kDataEof, false, saw_error);
+  }
+  base::TimeTicks end = base::TimeTicks::Now();
+  double time_per_op = (end - start).InMicrosecondsF() / kNumIterations;
+  LOG(INFO) << "Decode_Ascii (10k, " << (GetParam() ? "Fast" : "Legacy")
+            << "): " << time_per_op << " us/op";
+}
+
+TEST_P(Utf8PerfTest, Decode_NonAscii) {
+  const int kNumIterations = 20000;
+
+  std::string src;
+  while (src.length() < 10000) {
+    src += "日本語の文章テキストデータです。漢字とひらがなとカタカナ。";
+  }
+  auto src_span = base::as_bytes(base::span(src));
+
+  TextEncoding encoding("UTF-8");
+  std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+
+  base::TimeTicks start = base::TimeTicks::Now();
+  for (int i = 0; i < kNumIterations; ++i) {
+    bool saw_error = false;
+    codec->Decode(src_span, FlushBehavior::kDataEof, false, saw_error);
+  }
+  base::TimeTicks end = base::TimeTicks::Now();
+  double time_per_op = (end - start).InMicrosecondsF() / kNumIterations;
+  LOG(INFO) << "Decode_NonAscii (10k CJK, " << (GetParam() ? "Fast" : "Legacy")
+            << "): " << time_per_op << " us/op";
+}
+
+TEST_P(Utf8PerfTest, Decode_MixedJson) {
+  const int kNumIterations = 50000;
+
+  std::string src = "{\"results\": [";
+  while (src.length() < 10000) {
+    src +=
+        "{\"id\": 1024, \"title\": \"Google 検索\", \"desc\": \"Bonjour le "
+        "monde!\"},";
+  }
+  src += "]}";
+  auto src_span = base::as_bytes(base::span(src));
+
+  TextEncoding encoding("UTF-8");
+  std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+
+  base::TimeTicks start = base::TimeTicks::Now();
+  for (int i = 0; i < kNumIterations; ++i) {
+    bool saw_error = false;
+    codec->Decode(src_span, FlushBehavior::kDataEof, false, saw_error);
+  }
+  base::TimeTicks end = base::TimeTicks::Now();
+  double time_per_op = (end - start).InMicrosecondsF() / kNumIterations;
+  LOG(INFO) << "Decode_MixedJson (10k, " << (GetParam() ? "Fast" : "Legacy")
+            << "): " << time_per_op << " us/op";
+}
+
+TEST_P(Utf8PerfTest, Decode_MostlyAscii_32k) {
+  const int kNumIterations = 20000;
+
+  std::string src(32700, 'A');
+  src += " // Copyright \xc2\xa9 2026 Caf\xc3\xa9 \xe6\xa4\x9c\xe7\xb4\xa2";
+  auto src_span = base::as_bytes(base::span(src));
+
+  TextEncoding encoding("UTF-8");
+  std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+
+  base::TimeTicks start = base::TimeTicks::Now();
+  for (int i = 0; i < kNumIterations; ++i) {
+    bool saw_error = false;
+    codec->Decode(src_span, FlushBehavior::kDataEof, false, saw_error);
+  }
+  base::TimeTicks end = base::TimeTicks::Now();
+  double time_per_op = (end - start).InMicrosecondsF() / kNumIterations;
+  LOG(INFO) << "Decode_MostlyAscii_32k (" << (GetParam() ? "Fast" : "Legacy")
+            << "): " << time_per_op << " us/op";
+}
+
+TEST_P(Utf8PerfTest, Decode_MostlyAscii_64k) {
+  const int kNumIterations = 10000;
+
+  std::string src;
+  src.reserve(65536);
+  while (src.length() < 65400) {
+    src +=
+        "function computeLayout(node) { if (!node) return 0; return "
+        "node.offsetWidth + 10; }\n";
+  }
+  src.resize(65400);
+  src +=
+      " // \xf0\x9f\x8c\x8d \xf0\x9f\x9a\x80 \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa"
+      "\x9e";
+  auto src_span = base::as_bytes(base::span(src));
+
+  TextEncoding encoding("UTF-8");
+  std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+
+  base::TimeTicks start = base::TimeTicks::Now();
+  for (int i = 0; i < kNumIterations; ++i) {
+    bool saw_error = false;
+    codec->Decode(src_span, FlushBehavior::kDataEof, false, saw_error);
+  }
+  base::TimeTicks end = base::TimeTicks::Now();
+  double time_per_op = (end - start).InMicrosecondsF() / kNumIterations;
+  LOG(INFO) << "Decode_MostlyAscii_64k (" << (GetParam() ? "Fast" : "Legacy")
+            << "): " << time_per_op << " us/op";
+}
+
+TEST_P(Utf8PerfTest, Decode_StreamingChunks_64k) {
+  const int kNumIterations = 5000;
+  const size_t kChunkSize = 4096;
+
+  std::string src;
+  src.reserve(65536);
+  while (src.length() < 65536) {
+    src +=
+        "function handleEvent(e) { const target = e.target; if (!target) "
+        "return; target.classList.add('active'); updateLayout(target); }\n";
+  }
+  src.resize(65536);
+  auto src_span = base::as_bytes(base::span(src));
+
+  TextEncoding encoding("UTF-8");
+
+  base::TimeTicks start = base::TimeTicks::Now();
+  for (int iter = 0; iter < kNumIterations; ++iter) {
+    std::unique_ptr<TextCodec> codec(NewTextCodec(encoding));
+    bool saw_error = false;
+    for (size_t offset = 0; offset < src_span.size(); offset += kChunkSize) {
+      size_t current_chunk_size =
+          std::min(kChunkSize, src_span.size() - offset);
+      auto chunk = src_span.subspan(offset, current_chunk_size);
+      bool is_last = (offset + current_chunk_size >= src_span.size());
+      FlushBehavior flush =
+          is_last ? FlushBehavior::kDataEof : FlushBehavior::kDoNotFlush;
+      codec->Decode(chunk, flush, false, saw_error);
+    }
+  }
+  base::TimeTicks end = base::TimeTicks::Now();
+  double time_per_op = (end - start).InMicrosecondsF() / kNumIterations;
+  LOG(INFO) << "Decode_StreamingChunks_64k (chunk 4k, "
+            << (GetParam() ? "Fast" : "Legacy") << "): " << time_per_op
             << " us/op";
 }
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 3ad7d26..57e0e969 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2715,6 +2715,14 @@
 crbug.com/540322301 external/wpt/webaudio/the-audio-api/the-oscillatornode-interface/sub-sample-start.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+[ Win ] external/wpt/WebCryptoAPI/generateKey/failures_kmac.tentative.https.any.html [ Pass Timeout ]
+external/wpt/css/css-font-loading/font-face-width-descriptor-aliasing.html [ Failure ]
+external/wpt/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-first-in-flat-tree-order.html [ Timeout ]
+external/wpt/html/semantics/forms/form-submission-0/form-submit-action.html [ Crash Timeout ]
+external/wpt/html/semantics/scripting-1/the-script-element/text-module/repeated-imports.any.html [ Failure ]
+external/wpt/html/semantics/scripting-1/the-script-element/text-module/repeated-imports.any.worker.html [ Failure ]
+external/wpt/html/semantics/scripting-1/the-script-element/text-module/repeated-imports.any.sharedworker.html [ Failure ]
+virtual/fragmented-oof-in-cb/external/wpt/css/css-multicol/crashtests/nested-multicol-spanner-reframe.html [ Timeout ]
 crbug.com/540322301 [ Win ] external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/audiobuffersource-output-channel-count.html [ Pass Timeout ]
 crbug.com/538228192 external/wpt/fetch/compression-dictionary/compressed-large-resources-002.tentative.https.html [ Skip Timeout ]
 crbug.com/538228192 virtual/produce-compile-hints/external/wpt/fetch/compression-dictionary/compressed-large-resources-002.tentative.https.html [ Skip Timeout ]
diff --git a/third_party/blink/web_tests/accessibility/in-page-link-target.html b/third_party/blink/web_tests/accessibility/in-page-link-target.html
index 6ae7e47..be92164 100644
--- a/third_party/blink/web_tests/accessibility/in-page-link-target.html
+++ b/third_party/blink/web_tests/accessibility/in-page-link-target.html
@@ -13,7 +13,7 @@
   <p><a name="emptyAnchor"></a>After empty anchor</p>
   <p><a name="anchorWithContent">Anchor with content</a></p>
   <p><a id="anchorWithID">Anchor with ID</a></p>
-  <p><span id="emptySpan"></span>After empty span</span></p>
+  <p><span id="emptySpan"></span>After empty span</p>
   <p><span id="spanWithContent">Span with content</span></p>
   <p id="paragraphWithContent">Paragraph with content</p>
 </div>
diff --git a/third_party/blink/web_tests/compositing/gestures/gesture-tapHighlight-simple-multi-line-expected.html b/third_party/blink/web_tests/compositing/gestures/gesture-tapHighlight-simple-multi-line-expected.html
index ec4cd11..8639c3e 100644
--- a/third_party/blink/web_tests/compositing/gestures/gesture-tapHighlight-simple-multi-line-expected.html
+++ b/third_party/blink/web_tests/compositing/gestures/gesture-tapHighlight-simple-multi-line-expected.html
@@ -5,7 +5,7 @@
 </head>
 <body>
 <div style="will-change: transform; position: relative; left: 10; top: 40">
-<span class="fauxHighlightSquare" href="" id="targetLink">&nbsp;Long Target&nbsp;<br>&nbsp;Link.&nbsp;</a>
+<span class="fauxHighlightSquare" href="" id="targetLink">&nbsp;Long Target&nbsp;<br>&nbsp;Link.&nbsp;</span>
 </div>
 <div style="position: relative; left: 10; top: 70">
 This test is successful if "Long Target Link" above is covered in two green rectangles with square corners.
diff --git a/third_party/blink/web_tests/editing/shadow/bold-twice-in-shadow.html b/third_party/blink/web_tests/editing/shadow/bold-twice-in-shadow.html
index b0c52fa..a042e55 100644
--- a/third_party/blink/web_tests/editing/shadow/bold-twice-in-shadow.html
+++ b/third_party/blink/web_tests/editing/shadow/bold-twice-in-shadow.html
@@ -8,7 +8,7 @@
 
 <div id="container" contenteditable>
     before host
-    <div id="host">    <span contenteditable="false">not editable<span></div>
+    <div id="host">    <span contenteditable="false">not editable</span></div>
     after host
 </div>
 <pre id="console"></pre>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 54e93e5a..31702b3 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -1165,6 +1165,13 @@
          {}
         ]
        ],
+       "float-append-child.html": [
+        "b4746887ce4a946106e9750827dd94ff5976d61c",
+        [
+         null,
+         {}
+        ]
+       ],
        "float-dynamic-change-1.html": [
         "3730445977d39014819ece61c8bab9af6ca8c407",
         [
@@ -1506,6 +1513,13 @@
        {}
       ]
      ],
+     "anchor-size-simplified-crash.html": [
+      "54326097b2d9e4036513d1e085155e8ec39dce4e",
+      [
+       null,
+       {}
+      ]
+     ],
      "chrome-1512373-2-crash.html": [
       "50d0aff5e92fea43360b7423d522db2e9e79e89b",
       [
@@ -5547,6 +5561,13 @@
         {}
        ]
       ],
+      "nested-multicol-spanner-reframe.html": [
+       "630e5a5f246c7e0e1b406e982a55b9f7be4aca33",
+       [
+        null,
+        {}
+       ]
+      ],
       "nested-multicol-with-float-between.html": [
        "3a583ed0dafa947165f514d00c1dc3e327876632",
        [
@@ -7256,6 +7277,13 @@
     },
     "css-syntax": {
      "crashtests": {
+      "atrule-with-escape-character.html": [
+       "a93787a02096ec7db2f5cff396be2904587a1f97",
+       [
+        null,
+        {}
+       ]
+      ],
       "unclosed-content-attr-bracket.html": [
        "8ac30bd9737d4dcdd3c45d07c79f2258994979cd",
        [
@@ -8201,8 +8229,8 @@
        {}
       ]
      ],
-     "top-layer-background-image-transition-crash.html": [
-      "2c98ea3a6703e3c2f7650ff93de8bfb12a423f6b",
+     "top-layer-image-transition-crash.html": [
+      "0461e7453d67625a2ccc3295520dc6c18bd36370",
       [
        null,
        {}
@@ -8816,6 +8844,13 @@
       ]
      ],
      "crashtests": {
+      "bidi-inline-continuation-first-in-flow.html": [
+       "645851dac322c3bf2272f22dc275443d5ffeaad4",
+       [
+        null,
+        {}
+       ]
+      ],
       "chrome-bug-1512988.html": [
        "babe3ca704118e1b3441868cd26cbc405f4965cc",
        [
@@ -10707,6 +10742,13 @@
        {}
       ]
      ],
+     "set-documentElement-innerHTML-when-embed-in-designMode-has-focus.html": [
+      "08e331ed30090ef8be40d3ddf16075046ffafc66",
+      [
+       null,
+       {}
+      ]
+     ],
      "set-input-value-of-editing-host-after-changing-type.html": [
       "34a15588965b1f1a975d7040f2d9a414c4d59da9",
       [
@@ -13967,6 +14009,13 @@
        {}
       ]
      ],
+     "modify-sibling-of-shadowroot-after-attach.html": [
+      "d7825631c1a8c3130fbcff8c11da32bc5cb2c980",
+      [
+       null,
+       {}
+      ]
+     ],
      "modify-style-element-in-disconnected-shadow-crash.html": [
       "3808642a97451eef40573214fbe1d1c6273c557f",
       [
@@ -14456,6 +14505,15 @@
       ]
      ]
     },
+    "embedded": {
+     "svg-zero-size-crash.html": [
+      "97b22a00158a20d0fabd8b8fcbeba32229684e69",
+      [
+       null,
+       {}
+      ]
+     ]
+    },
     "extensibility": {
      "foreignObject": {
       "foreign-object-circular-filter-reference-crash.html": [
@@ -84893,6 +84951,45 @@
        {}
       ]
      ],
+     "transform-018.html": [
+      "1c3045278021e54e5b8c1f9b303b2893e884129e",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "transform-020.html": [
+      "89d87e63583901e9db34bda8c7b96d11db0acad1",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "transform-022.html": [
+      "c707b6ff599994c6808453a0f58109ff95190644",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "under-invalidation.html": [
       "2103e1a69b683a29861a46a4c06c4452aef7322a",
       [
@@ -97584,7 +97681,7 @@
        ]
       ],
       "border-shape-polygon-miter-limit.html": [
-       "a27e1eb04b5566f4df2f090a803ec29520e07037",
+       "d881161aae0f78196eabae6a9b1513373f2bfa06",
        [
         null,
         [
@@ -97764,6 +97861,35 @@
         }
        ]
       ],
+      "border-shape-shape-arc-miter-limit.html": [
+       "eb808f2eb4a34abd7cfca5546181311a93108c8e",
+       [
+        null,
+        [
+         [
+          "/css/css-borders/border-shape/border-shape-shape-arc-miter-limit-ref.html",
+          "=="
+         ]
+        ],
+        {
+         "fuzzy": [
+          [
+           null,
+           [
+            [
+             0,
+             10
+            ],
+            [
+             0,
+             50
+            ]
+           ]
+          ]
+         ]
+        }
+       ]
+      ],
       "border-shape-stroke-from-border.html": [
        "07ed859e04e47616b095be5735a6755959b657b5",
        [
@@ -101723,6 +101849,19 @@
        {}
       ]
      ],
+     "box-decoration-break-clone-037.html": [
+      "960426bd50946a8be1ca8227ed759557d7d6c9b4",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "box-decoration-break-clone-inline-001.html": [
       "851a09678a92fd58d1f6026169448464a562c548",
       [
@@ -107733,6 +107872,110 @@
        ]
       ],
       "grid-lanes": {
+       "column-multi-lane-fragmentation-001.html": [
+        "edf347302dc6b4c7d31d0fa38e1a80d0813dfafd",
+        [
+         null,
+         [
+          [
+           "/css/reference/ref-filled-green-100px-square.xht",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "column-multi-lane-fragmentation-002.html": [
+        "1cffd95a7c50bb7da5ba94f87742acb67ffebb22",
+        [
+         null,
+         [
+          [
+           "/css/reference/ref-filled-green-100px-square.xht",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "column-multi-lane-fragmentation-003.html": [
+        "1c8d6e0a5e04230dc380a7dd959352d3beb8b6a2",
+        [
+         null,
+         [
+          [
+           "/css/reference/ref-filled-green-100px-square.xht",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "column-multi-lane-fragmentation-004.html": [
+        "0deba4b8c725a3018e265022ceff5523e62c2c29",
+        [
+         null,
+         [
+          [
+           "/css/reference/ref-filled-green-100px-square.xht",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "column-single-lane-fragmentation-001.html": [
+        "eab17ea9f43956c06d369809ce6156d218a211c2",
+        [
+         null,
+         [
+          [
+           "/css/reference/ref-filled-green-100px-square.xht",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "column-single-lane-fragmentation-002.html": [
+        "fad321a9f54a946131f1d30f4a94792ca3142a24",
+        [
+         null,
+         [
+          [
+           "/css/reference/ref-filled-green-100px-square.xht",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "column-single-lane-fragmentation-003.html": [
+        "c7380731b857c7f53bba635445ec5dc2e81e8401",
+        [
+         null,
+         [
+          [
+           "/css/reference/ref-filled-green-100px-square.xht",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "column-single-lane-fragmentation-004.html": [
+        "71d26ed3c8ad85a94207b6af518d826b6131e25d",
+        [
+         null,
+         [
+          [
+           "/css/reference/ref-filled-green-100px-square.xht",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
        "grid-lanes-container-fragmentation-001.html": [
         "0604eceaf66bf8e176f62920f9ce6128ce79d53c",
         [
@@ -107811,8 +108054,21 @@
          {}
         ]
        ],
+       "grid-lanes-container-fragmentation-007.tentative.html": [
+        "4796a42388cb89fd354bf2144198d41e31f63b1a",
+        [
+         null,
+         [
+          [
+           "/css/reference/ref-filled-green-100px-square-only.html",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
        "grid-lanes-fragmentation-001.html": [
-        "df113deb76d8916402d4c56a73ed22ffbb770707",
+        "672c96023cb236ce25f4e88536b2b0758cd35d1f",
         [
          null,
          [
@@ -113366,7 +113622,7 @@
       ]
      ],
      "layer-slotted-rule.html": [
-      "a33a5a6787a3f26d12079ac03c915797d65bd48a",
+      "1eab7b4e9f8eac2aba1636edc38438a621f99e72",
       [
        null,
        [
@@ -127523,6 +127779,19 @@
         {}
        ]
       ],
+      "symbols-function-dynamic.html": [
+       "dcfbec957886ba0f6ef3a258bd8064e745329d5e",
+       [
+        null,
+        [
+         [
+          "/css/css-counter-styles/counter-style-at-rule/symbols-function-dynamic-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "symbols-function-invalid.html": [
        "35aa83258726c89efc2c0e31a1e3927362c7f4e2",
        [
@@ -144366,6 +144635,19 @@
      ]
     },
     "css-font-loading": {
+     "font-face-width-descriptor-aliasing.html": [
+      "3ff51e8661fce45e1e1168fe1a8c103bcea4891b",
+      [
+       null,
+       [
+        [
+         "/css/css-font-loading/font-face-width-descriptor-aliasing-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "fontface-descriptor-updates.html": [
       "038b96da1e54654e1739e7034e1790c11c75a538",
       [
@@ -165152,6 +165434,45 @@
           {}
          ]
         ],
+        "column-dense-packing-flow-tolerance-001.html": [
+         "d43bfa8682de2c554b5b3fb7e14a30f2b34cb02a",
+         [
+          null,
+          [
+           [
+            "/css/css-grid/grid-lanes/item-placement/dense-packing/column-dense-packing-flow-tolerance-001-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
+        "column-dense-packing-flow-tolerance-002.html": [
+         "eb044055b5c7e972a9891ff1906787d8ab0feab9",
+         [
+          null,
+          [
+           [
+            "/css/css-grid/grid-lanes/item-placement/dense-packing/column-dense-packing-flow-tolerance-002-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
+        "column-dense-packing-flow-tolerance-003.html": [
+         "a61fe7927e686efd41c647a74bab174ffa2cf202",
+         [
+          null,
+          [
+           [
+            "/css/css-grid/grid-lanes/item-placement/dense-packing/column-dense-packing-flow-tolerance-003-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
         "column-dense-packing-multi-span-001.html": [
          "9ecb952446e3fda0ae6012fcc9574546fed4461c",
          [
@@ -165308,6 +165629,19 @@
           {}
          ]
         ],
+        "column-fill-reverse-dense-packing-flow-tolerance-001.html": [
+         "58728d796dad0cbff5683798c70a851298caf199",
+         [
+          null,
+          [
+           [
+            "/css/css-grid/grid-lanes/item-placement/dense-packing/column-fill-reverse-dense-packing-flow-tolerance-001-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
         "column-fill-reverse-dense-packing-indefinite-size-001.html": [
          "286a5cee9aabec3b746daf0dcd1c03838397918b",
          [
@@ -165321,6 +165655,32 @@
           {}
          ]
         ],
+        "column-track-reverse-dense-packing-flow-tolerance-001.html": [
+         "34d05a8c11fb9a01a0601b3e1921d306ef333cdc",
+         [
+          null,
+          [
+           [
+            "/css/css-grid/grid-lanes/item-placement/dense-packing/column-track-reverse-dense-packing-flow-tolerance-001-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
+        "column-track-reverse-dense-packing-flow-tolerance-002.html": [
+         "336f34ef98c248c04b0de7dcda1932a0a0604e5b",
+         [
+          null,
+          [
+           [
+            "/css/css-grid/grid-lanes/item-placement/dense-packing/column-track-reverse-dense-packing-flow-tolerance-002-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
         "row-dense-packing-001.html": [
          "5eb87432564c7be3058861441d3d759e9eaedec7",
          [
@@ -165373,6 +165733,45 @@
           {}
          ]
         ],
+        "row-dense-packing-flow-tolerance-001.html": [
+         "86f7106ecaa7c767e761251bbd339354abcb3c8a",
+         [
+          null,
+          [
+           [
+            "/css/css-grid/grid-lanes/item-placement/dense-packing/row-dense-packing-flow-tolerance-001-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
+        "row-dense-packing-flow-tolerance-002.html": [
+         "610cc74e686a3aec079aacd623f6cd44b2ff0c45",
+         [
+          null,
+          [
+           [
+            "/css/css-grid/grid-lanes/item-placement/dense-packing/row-dense-packing-flow-tolerance-002-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
+        "row-dense-packing-flow-tolerance-003.html": [
+         "3351f2062c55b5352d6eb1501002f8027f457e29",
+         [
+          null,
+          [
+           [
+            "/css/css-grid/grid-lanes/item-placement/dense-packing/row-dense-packing-flow-tolerance-003-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
         "row-dense-packing-multi-span-001.html": [
          "005b388d3a295c01120d2dd2816a4a36ea964705",
          [
@@ -165463,6 +165862,32 @@
           ],
           {}
          ]
+        ],
+        "row-track-reverse-dense-packing-flow-tolerance-001.html": [
+         "941bf299f3519d02459bb71dc733e9f2c74f196f",
+         [
+          null,
+          [
+           [
+            "/css/css-grid/grid-lanes/item-placement/dense-packing/row-track-reverse-dense-packing-flow-tolerance-001-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
+        ],
+        "row-track-reverse-dense-packing-flow-tolerance-002.html": [
+         "4b3b608815f7ec11d9b2b54bdb2a97f35a5f33f7",
+         [
+          null,
+          [
+           [
+            "/css/css-grid/grid-lanes/item-placement/dense-packing/row-track-reverse-dense-packing-flow-tolerance-002-ref.html",
+            "=="
+           ]
+          ],
+          {}
+         ]
         ]
        },
        "flow-tolerance": {
@@ -166253,6 +166678,97 @@
          {}
         ]
        ],
+       "grid-lanes-overflow-reverse-001.html": [
+        "78484cb3d493aefebd6f938f415c7da68c4a87f0",
+        [
+         null,
+         [
+          [
+           "/css/css-grid/grid-lanes/overflow/grid-lanes-overflow-reverse-001-ref.html",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "grid-lanes-overflow-reverse-002.html": [
+        "43346e34bf824ba71782f2eab918c605417396d4",
+        [
+         null,
+         [
+          [
+           "/css/css-grid/grid-lanes/overflow/grid-lanes-overflow-reverse-002-ref.html",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "grid-lanes-overflow-reverse-003.html": [
+        "218048c35f60f60ee0be9fea95ebbc3b49141087",
+        [
+         null,
+         [
+          [
+           "/css/css-grid/grid-lanes/overflow/grid-lanes-overflow-reverse-003-ref.html",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "grid-lanes-overflow-reverse-004.html": [
+        "436111479225e770ade29f744930dd003b47b892",
+        [
+         null,
+         [
+          [
+           "/css/css-grid/grid-lanes/overflow/grid-lanes-overflow-reverse-004-ref.html",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "grid-lanes-overflow-reverse-005.html": [
+        "263d48b54c5b8ff5a930872c8c69bbee1ad804fc",
+        [
+         null,
+         [
+          [
+           "/css/css-grid/grid-lanes/overflow/grid-lanes-overflow-reverse-005-ref.html",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "grid-lanes-overflow-reverse-baseline-001.html": [
+        "365999bf75eb1de6b41e1783bac84ca6e4f82637",
+        [
+         null,
+         [
+          [
+           "/css/css-grid/grid-lanes/overflow/grid-lanes-overflow-reverse-baseline-001-ref.html",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
+       "grid-lanes-overflow-reverse-baseline-002.html": [
+        "afc28d66bb7c69f7fb7f9f37c6467da8ad70ee19",
+        [
+         null,
+         [
+          [
+           "/css/css-grid/grid-lanes/overflow/grid-lanes-overflow-reverse-baseline-002-ref.html",
+           "=="
+          ]
+         ],
+         {}
+        ]
+       ],
        "grid-lanes-overflowing-container-001.html": [
         "821269bd9ab470a86998ca91ac0e41067437bd31",
         [
@@ -166613,6 +167129,190 @@
           ]
          ]
         },
+        "baseline": {
+         "column-grid-lanes-container-baseline-subgrid-001.html": [
+          "fb083fc8d58ad83ce44ec891cbfd38c2e51f0333",
+          [
+           null,
+           [
+            [
+             "/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/baseline/column-grid-lanes-container-baseline-subgrid-001-ref.html",
+             "=="
+            ]
+           ],
+           {}
+          ]
+         ],
+         "column-grid-lanes-item-baseline-subgrid-001.html": [
+          "bb93c3542c3edfdb8dc23f2061ed2405334def76",
+          [
+           null,
+           [
+            [
+             "/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/baseline/column-grid-lanes-item-baseline-subgrid-001-ref.html",
+             "=="
+            ]
+           ],
+           {}
+          ]
+         ],
+         "column-grid-lanes-item-baseline-subgrid-002.html": [
+          "4a65b112eba448346ffd5227bc4b882924ef2ddc",
+          [
+           null,
+           [
+            [
+             "/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/baseline/column-grid-lanes-item-baseline-subgrid-002-ref.html",
+             "=="
+            ]
+           ],
+           {}
+          ]
+         ],
+         "column-grid-lanes-item-baseline-subgrid-003.html": [
+          "0bf7aa9de2ab07f0fef39eeb1259496b97ce0e02",
+          [
+           null,
+           [
+            [
+             "/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/baseline/column-grid-lanes-item-baseline-subgrid-003-ref.html",
+             "=="
+            ]
+           ],
+           {}
+          ]
+         ],
+         "column-grid-lanes-item-baseline-subgrid-004.html": [
+          "e51c740b8fb519fecd02b995504e53871e0ca1ad",
+          [
+           null,
+           [
+            [
+             "/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/baseline/column-grid-lanes-item-baseline-subgrid-004-ref.html",
+             "=="
+            ]
+           ],
+           {}
+          ]
+         ],
+         "column-grid-lanes-item-baseline-subgrid-005.html": [
+          "eba1470157f6f0e7070615278170e1ad3c2a6a71",
+          [
+           null,
+           [
+            [
+             "/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/baseline/column-grid-lanes-item-baseline-subgrid-005-ref.html",
+             "=="
+            ]
+           ],
+           {}
+          ]
+         ],
+         "column-grid-lanes-item-baseline-subgrid-006.html": [
+          "2a112e5c2f4a3e111d78ce42c68b55aff91d9ff7",
+          [
+           null,
+           [
+            [
+             "/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/baseline/column-grid-lanes-item-baseline-subgrid-006-ref.html",
+             "=="
+            ]
+           ],
+           {}
+          ]
+         ],
+         "row-grid-lanes-container-baseline-subgrid-001.html": [
+          "9b53853c2b7df48859a9b1d61bf2180afb047c12",
+          [
+           null,
+           [
+            [
+             "/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/baseline/row-grid-lanes-container-baseline-subgrid-001-ref.html",
+             "=="
+            ]
+           ],
+           {}
+          ]
+         ],
+         "row-grid-lanes-item-baseline-subgrid-001.html": [
+          "84c2061fd1d30a1fd1210a430e85f7a942f9348b",
+          [
+           null,
+           [
+            [
+             "/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/baseline/row-grid-lanes-item-baseline-subgrid-001-ref.html",
+             "=="
+            ]
+           ],
+           {}
+          ]
+         ],
+         "row-grid-lanes-item-baseline-subgrid-002.html": [
+          "97f10622ebc88d6246835bbd8f8b4f44c623ce25",
+          [
+           null,
+           [
+            [
+             "/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/baseline/row-grid-lanes-item-baseline-subgrid-002-ref.html",
+             "=="
+            ]
+           ],
+           {}
+          ]
+         ],
+         "row-grid-lanes-item-baseline-subgrid-003.html": [
+          "c6b0b2fca18ab9b4c3cbe0e1d9d97c542d3af100",
+          [
+           null,
+           [
+            [
+             "/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/baseline/row-grid-lanes-item-baseline-subgrid-003-ref.html",
+             "=="
+            ]
+           ],
+           {}
+          ]
+         ],
+         "row-grid-lanes-item-baseline-subgrid-004.html": [
+          "38fc4bc1915c3b4c6d9c6a60dcd30d53dfa9ebfb",
+          [
+           null,
+           [
+            [
+             "/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/baseline/row-grid-lanes-item-baseline-subgrid-004-ref.html",
+             "=="
+            ]
+           ],
+           {}
+          ]
+         ],
+         "row-grid-lanes-item-baseline-subgrid-005.html": [
+          "02112cadbac48102be5794fc11a28272ff08409c",
+          [
+           null,
+           [
+            [
+             "/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/baseline/row-grid-lanes-item-baseline-subgrid-005-ref.html",
+             "=="
+            ]
+           ],
+           {}
+          ]
+         ],
+         "row-grid-lanes-item-baseline-subgrid-006.html": [
+          "385392bd78d3e0cac59356d898d766ee81eb5a6e",
+          [
+           null,
+           [
+            [
+             "/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/baseline/row-grid-lanes-item-baseline-subgrid-006-ref.html",
+             "=="
+            ]
+           ],
+           {}
+          ]
+         ]
+        },
         "column": {
          "grid-lanes-subgrid-001a.html": [
           "7fc99b2fc1312ae179f55ee5828851d309f5480f",
@@ -172793,7 +173493,7 @@
      },
      "layout-algorithm": {
       "auto-margins-ignored-during-track-sizing-001.html": [
-       "2a23b1bd2031e2a3cfc104a90f70ab6c38d2fe64",
+       "82af2d8b79e3b6bee4f673a1cb9f41374662cb13",
        [
         null,
         [
@@ -175980,7 +176680,7 @@
        ]
       ],
       "custom-highlight-painting-below-grammar.html": [
-       "93d2560040b791a0685151cc29587c771fad8c03",
+       "03c41f7e38c3c6a7f7e912f621a74033f6d60beb",
        [
         null,
         [
@@ -193787,7 +194487,7 @@
         ]
        ],
        "clip-path-transition.html": [
-        "980f7a53e2e86001b5da911c07b3f465845f3735",
+        "7ed478e470e87f77788bf2d1a5c4147a00ac8741",
         [
          null,
          [
@@ -193796,7 +194496,23 @@
            "=="
           ]
          ],
-         {}
+         {
+          "fuzzy": [
+           [
+            null,
+            [
+             [
+              0,
+              8
+             ],
+             [
+              0,
+              16
+             ]
+            ]
+           ]
+          ]
+         }
         ]
        ],
        "clip-path-xywh-interpolation-001.html": [
@@ -205683,7 +206399,7 @@
       ]
      ],
      "nested-past-fragmentation-line.html": [
-      "77f91f4a23d802bdfe1d94072825cf25b5aeb10f",
+      "389f31073a432c28f3904de17f21f6b1f5f98a3b",
       [
        null,
        [
@@ -233912,7 +234628,7 @@
        ]
       ],
       "replaced-element-002.html": [
-       "56da485a1c3ca9fd68c45cfe06685403e1dcb28c",
+       "bfb6fb8cde98d836ba1eefeaacbae0e84984b0d0",
        [
         null,
         [
@@ -253815,6 +254531,19 @@
         ],
         {}
        ]
+      ],
+      "text-transform-upperlower-108.html": [
+       "d925e826e3eb9bd8267938cd3e6c4626be78f6b0",
+       [
+        null,
+        [
+         [
+          "/css/css-text/text-transform/reference/text-transform-upperlower-108-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
       ]
      },
      "white-space": {
@@ -261435,6 +262164,19 @@
         {}
        ]
       ],
+      "text-decoration-inset-invalidation.html": [
+       "4c945b45cbed9d861277df19212f9f0620ab2e77",
+       [
+        null,
+        [
+         [
+          "/css/css-text-decor/invalidation/reference/text-decoration-inset-invalidation-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "text-decoration-invalidation-dashed.html": [
        "b92f3ddcccb407aff29d486c8c3221626617a597",
        [
@@ -261658,7 +262400,7 @@
       ]
      ],
      "text-decoration-inset-003.html": [
-      "e73f8fa6e48d65b95e98c18595f6c0a31fb9408f",
+      "bd029443eda335b40e6be7bf8d2f05bd70854e69",
       [
        null,
        [
@@ -261667,7 +262409,23 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            120
+           ],
+           [
+            0,
+            3
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "text-decoration-inset-004.html": [
@@ -261710,7 +262468,7 @@
       ]
      ],
      "text-decoration-inset-007.html": [
-      "7ee28f7db301dc0901831b3655d351919047a7da",
+      "61324458f13822531519d992d6d5bc3acd79bf10",
       [
        null,
        [
@@ -261719,11 +262477,27 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            63
+           ],
+           [
+            0,
+            6
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "text-decoration-inset-008.html": [
-      "ba2f8a22da85a083d0003cba2fdac2211b6b1783",
+      "403d717f6fcf23252175aa5eafe7fe05ab96b07a",
       [
        null,
        [
@@ -261732,11 +262506,27 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            92
+           ],
+           [
+            0,
+            9
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "text-decoration-inset-009.html": [
-      "3f4c229e67d96b16a97ab68509de78a4019a0bfc",
+      "ca2b22abdfdb602bd2dd6b24f49e8849b69d52c7",
       [
        null,
        [
@@ -261745,7 +262535,23 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            57
+           ],
+           [
+            0,
+            6
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "text-decoration-inset-010.html": [
@@ -261762,7 +262568,7 @@
       ]
      ],
      "text-decoration-inset-011.html": [
-      "f9bcbafadd3b1afb6ef026b3b42bd959e583e4c2",
+      "8f44e0cca502721a05e4234762b26b3efac9949b",
       [
        null,
        [
@@ -261771,7 +262577,23 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            95
+           ],
+           [
+            0,
+            3
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "text-decoration-inset-012.html": [
@@ -261801,7 +262623,7 @@
       ]
      ],
      "text-decoration-inset-014.html": [
-      "ef2f2007762f7bf75a154215a0219ea05017705e",
+      "bb874175755c1c11bfcfa600c43f34cc84e8ec0a",
       [
        null,
        [
@@ -261810,7 +262632,23 @@
          "=="
         ]
        ],
-       {}
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            4
+           ],
+           [
+            0,
+            8
+           ]
+          ]
+         ]
+        ]
+       }
       ]
      ],
      "text-decoration-inset-015.html": [
@@ -262034,6 +262872,106 @@
        {}
       ]
      ],
+     "text-decoration-inset-percentage-slice-multiline.html": [
+      "63d1166959c36384566945cdd35c60b412195466",
+      [
+       null,
+       [
+        [
+         "/css/css-text-decor/reference/text-decoration-inset-percentage-slice-multiline-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-decoration-inset-percentage-slice.html": [
+      "868ed7bd046d8b167224f5ea9870761a2b410ea2",
+      [
+       null,
+       [
+        [
+         "/css/css-text-decor/reference/text-decoration-inset-percentage-slice-ref.html",
+         "=="
+        ]
+       ],
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            40
+           ],
+           [
+            0,
+            8
+           ]
+          ]
+         ]
+        ]
+       }
+      ]
+     ],
+     "text-decoration-inset-svg-fragment-001.html": [
+      "51ebc2d73187f7b72dde89bfce8795f3cbf2ed8d",
+      [
+       null,
+       [
+        [
+         "/css/css-text-decor/reference/text-decoration-inset-svg-fragment-001-ref.html",
+         "=="
+        ]
+       ],
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            40
+           ],
+           [
+            0,
+            5
+           ]
+          ]
+         ]
+        ]
+       }
+      ]
+     ],
+     "text-decoration-inset-svg-fragment-002.html": [
+      "71ee10f82cb94c2c751bc169cf6aa1c56e6526b1",
+      [
+       null,
+       [
+        [
+         "/css/css-text-decor/reference/text-decoration-inset-svg-fragment-002-ref.html",
+         "=="
+        ]
+       ],
+       {
+        "fuzzy": [
+         [
+          null,
+          [
+           [
+            0,
+            33
+           ],
+           [
+            0,
+            10
+           ]
+          ]
+         ]
+        ]
+       }
+      ]
+     ],
      "text-decoration-line-010.xht": [
       "f4b16e32aa185d204c1793f9ca1b0eda502c60fd",
       [
@@ -265786,7 +266724,7 @@
        ]
       ],
       "rotate-explicit-and-implicit-keyframes.html": [
-       "2ffa43ecf5e8164a6633a85c156b542554357653",
+       "5b1e0f7ea873e4636eb70dd4362f6216f140363d",
        [
         null,
         [
@@ -265795,7 +266733,23 @@
           "=="
          ]
         ],
-        {}
+        {
+         "fuzzy": [
+          [
+           null,
+           [
+            [
+             0,
+             1
+            ],
+            [
+             0,
+             200
+            ]
+           ]
+          ]
+         ]
+        }
        ]
       ],
       "rotate-transform-equivalent.html": [
@@ -290821,7 +291775,7 @@
       ]
      ],
      "text-overflow-string-003.html": [
-      "7ab1ba6efb1c033bdcfbea06c955a7c491f6f3ed",
+      "8a03dd83d216c8742672aca6f57aa101c0cd1c0f",
       [
        null,
        [
@@ -290898,6 +291852,240 @@
        {}
       ]
      ],
+     "text-overflow-string-009.html": [
+      "6a6ad944976aa90295d1941ddbbb677f513d0b35",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-009-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-010.html": [
+      "c780c29a235a223d4631f612f24eaeac609d1d94",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-010-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-011.html": [
+      "e879d020ad202e0c1b33826bad3ca2b84bbf3599",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-011-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-012.html": [
+      "db8303eeb033a73a171c82956a11a820ff2a7cdf",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-012-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-013.html": [
+      "bcea5a714cff09c7942897e9d21fbd3ee8ac53ef",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-013-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-014.html": [
+      "16b4b80209fff13ac5655635d768e571169e3603",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-014-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-015.html": [
+      "7091bafdf1791dd0f4dbd3c7e2a3cf11fb2d7bb9",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-015-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-016.html": [
+      "98d2b0b91ad6348d1631b414dea51a3ad8097401",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-016-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-017.html": [
+      "f41781aa5e43595fb48d0d47e696451cf7893190",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-016-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-018.html": [
+      "7298bc53c957daef1467864f41fa3ffe5b7b6336",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-016-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-019.html": [
+      "6aa0d94619e5041240954dbdf6ed91f685bb1e06",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-016-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-020.html": [
+      "655d121635ca84daa7c6d106078c96583a640083",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-016-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-021.html": [
+      "3677412be7a51d25a02cedb724e760ff9fb69815",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-016-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-022.html": [
+      "98406cf567b69daf80ebc22caaaf4ad5605fa032",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-016-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-023.html": [
+      "33832f01c0f085ed3b2292f816a3f01c7a4bfcb1",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-016-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-024.tentative.html": [
+      "e9ef29e7dd6d59cd8eb32ed4466f73500baf3e03",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-024-ref.tentative.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-025.tentative.html": [
+      "a1409fe27fdefbfff80645bf935252bcc80935ff",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-025-ref.tentative.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "text-overflow-string-026.tentative.html": [
+      "9c223b6232469387927e6d1e716ca87554147901",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/reference/text-overflow-string-026-ref.tentative.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "text-overflow-with-selection.html": [
       "2ed1554b2e388656e0a4a6877a59e87434ad3c65",
       [
@@ -291777,7 +292965,7 @@
       ]
      ],
      "attr-style-sharing-2.html": [
-      "0c886227d67c3565b6277c79645623eacbc4b62a",
+      "1f24ac12b1871ad685ba734d9fd804151c2d9403",
       [
        null,
        [
@@ -299466,7 +300654,7 @@
        ]
       ],
       "normal-goes-up.tentative.html": [
-       "b027e36672fc879fd6aa3dfaa9d7af01fc2c93a5",
+       "1edacf6d000c195254a639098a22a5ebc8e0435f",
        [
         null,
         [
@@ -312162,7 +313350,7 @@
       ]
      ],
      "float-vlr-014.html": [
-      "3f0d9046c8905502c63752734dd0ec455d626f49",
+      "2028e9d548aa2219d0e2d9c552665806b668039e",
       [
        null,
        [
@@ -324513,6 +325701,19 @@
        {}
       ]
      ],
+     "svg-mutation-fedropshadow-stddeviation.html": [
+      "d5f1b1ccb96ac004bf3d0e200ed8d85546142271",
+      [
+       null,
+       [
+        [
+         "/css/filter-effects/svg-mutation-fedropshadow-stddeviation-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "svg-mutation-filter-used-by-mask.html": [
       "59ac6accbcafe5e7ba83a1e62538b10d4c4cef65",
       [
@@ -326892,6 +328093,19 @@
        {}
       ]
      ],
+     "offset-path-ray-sides-fourth-quadrant.html": [
+      "1b9552bb5373c4a9b9edaa96c21dd802be255887",
+      [
+       null,
+       [
+        [
+         "/css/motion/offset-path-ray-sides-fourth-quadrant-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "offset-path-shape-circle-001.html": [
       "5b05cb8811ba3c202790b00fd01065cd9c08ec18",
       [
@@ -332795,6 +334009,23 @@
        {}
       ]
      ]
+    },
+    "plaintext-only": {
+     "backspace-end-of-plaintext-only-pre.html": [
+      "3e61a3529ea2ca525ce1228e2c94fa4ff6aee7be",
+      [
+       null,
+       [
+        [
+         "/editing/plaintext-only/backspace-end-of-plaintext-only-pre-ref.html",
+         "=="
+        ]
+       ],
+       {
+        "testdriver": true
+       }
+      ]
+     ]
     }
    },
    "encoding": {
@@ -333491,7 +334722,7 @@
      ]
     ],
     "forced-colors-mode-43.html": [
-     "9b8c164908ea02a0693a4714024751fc7e2c1e46",
+     "9b2af90604da57c72f7615ddcf6f5089098b3470",
      [
       null,
       [
@@ -333517,7 +334748,7 @@
      ]
     ],
     "forced-colors-mode-45.html": [
-     "e667b8291417cfef3545c71d92f27ee5d35f2eee",
+     "fed0b4416654d8369f9933343dafa085f93fa024",
      [
       null,
       [
@@ -344288,7 +345519,7 @@
       },
       "the-select-element-0": {
        "listbox-abspos-descendant.html": [
-        "1abe739150ad5c64bbdb4fd9eedf53144bb57702",
+        "bbb4b2ae67c06c86f0d73510c52448da9e23a02c",
         [
          null,
          [
@@ -345519,7 +346750,7 @@
         ]
        ],
        "fieldset-dynamic-pseudo.html": [
-        "c5fbf53000e6439deeb2bd03078b886cbba4e5cb",
+        "f24e3a9235b4375ec6924633312b0b8e85f8a458",
         [
          null,
          [
@@ -352106,7 +353337,7 @@
         ]
        ],
        "backdrop-in-flow.html": [
-        "06f790979eb8118a52ffa0f0481a64d1686f46f0",
+        "a4979df4bb106ea1c0b44207124c91295fcbd228",
         [
          null,
          [
@@ -352879,7 +354110,7 @@
        ]
       ],
       "bounded-sizes-reftest.tentative.html": [
-       "27abe819bb935a46b110b423b867c744ce3b2e7e",
+       "bae2615247a0c500a2b23195d29a8a493fa7460a",
        [
         null,
         [
@@ -353366,21 +354597,8 @@
         ]
        ]
       },
-      "invalid-type-reftest.tentative.html": [
-       "3e3aa8bc9f51576af312dde55c2f4293fcd4a9db",
-       [
-        null,
-        [
-         [
-          "/html/semantics/permission-element/invalid-type-reftest-ref.html",
-          "=="
-         ]
-        ],
-        {}
-       ]
-      ],
       "large-min-size-reftest.tentative.html": [
-       "47b0baa6e50b347d30435a0b6a89495855544d52",
+       "d9a7651b2a3d700af7f690d7f962a30f92d6087a",
        [
         null,
         [
@@ -353550,7 +354768,7 @@
         ]
        ],
        "icon-unique-per-type-reftest.html": [
-        "d32dd336e10b27d4df19f96271e517d7a38893b5",
+        "17f34dfce5eaeb5caf2ff752746eaa578b116fe6",
         [
          null,
          [
@@ -353629,7 +354847,7 @@
        ]
       ],
       "unbounded-width-with-border-reftest.tentative.html": [
-       "e88e520b38a437eed2da10ca33433e5a0772f07c",
+       "8fdf5459970080f95d9d27379514ed8695669f72",
        [
         null,
         [
@@ -359626,6 +360844,19 @@
       }
      ]
     ],
+    "animated-img-timeout.html": [
+     "0640cbceb8476fc9baaf28d58e8fd44f10c1854b",
+     [
+      null,
+      [
+       [
+        "/jpegxl/animated-img-timeout-ref.html",
+        "=="
+       ]
+      ],
+      {}
+     ]
+    ],
     "basic-decode-reftest.html": [
      "c2199c6e7c1aea95bd7060ecd7de5d6caf0acaec",
      [
@@ -359810,6 +361041,58 @@
       {}
      ]
     ],
+    "css-background-image.html": [
+     "d782f427579e37c4fe7e0ad96643ee79e4113f49",
+     [
+      null,
+      [
+       [
+        "/jpegxl/css-background-image-ref.html",
+        "=="
+       ]
+      ],
+      {}
+     ]
+    ],
+    "css-before-content.html": [
+     "f623e10252001ef17f366dc89f9f842ba61ef4f8",
+     [
+      null,
+      [
+       [
+        "/jpegxl/css-before-content-ref.html",
+        "=="
+       ]
+      ],
+      {}
+     ]
+    ],
+    "css-image-set.html": [
+     "f388227bff11448916b7634c7f25655d40c1add5",
+     [
+      null,
+      [
+       [
+        "/jpegxl/css-image-set-ref.html",
+        "=="
+       ]
+      ],
+      {}
+     ]
+    ],
+    "css-list-style-image.html": [
+     "f4c7df11fd29243be14100a126c64cc18236957e",
+     [
+      null,
+      [
+       [
+        "/jpegxl/css-list-style-image-ref.html",
+        "=="
+       ]
+      ],
+      {}
+     ]
+    ],
     "gray-decode-reftest.html": [
      "109fdd022c94e7b354b0bd036a1c1fa001192eda",
      [
@@ -360044,7 +361327,7 @@
      ]
     ],
     "patches-reftest.html": [
-     "35e883f4179370504d42f61cfa16f3b43218e6b9",
+     "9870706a4e88d4510f305c8bd7dce626e8b4c2a1",
      [
       null,
       [
@@ -360053,7 +361336,23 @@
         "=="
        ]
       ],
-      {}
+      {
+       "fuzzy": [
+        [
+         null,
+         [
+          [
+           0,
+           1
+          ],
+          [
+           0,
+           480000
+          ]
+         ]
+        ]
+       ]
+      }
      ]
     ],
     "progressive-1.html": [
@@ -368844,6 +370143,45 @@
         {}
        ]
       ],
+      "non-scaling-stroke-008.html": [
+       "50770f82941f7ba81e85a0fe45d2a07300ca18ab",
+       [
+        null,
+        [
+         [
+          "/svg/struct/reftests/reference/green-100x100.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "non-scaling-stroke-009.html": [
+       "efa883836a0464dca4831685c0149387ead9b72c",
+       [
+        null,
+        [
+         [
+          "/svg/struct/reftests/reference/green-100x100.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "non-scaling-stroke-010.html": [
+       "ea0c709c57aac24a029f23c648d398e1a1c4a34d",
+       [
+        null,
+        [
+         [
+          "/svg/struct/reftests/reference/green-100x100.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "non-scaling-stroke-non-uniform-scale.html": [
        "ecea89076ad60edf0ddf3df6481ab5e1fc3719b5",
        [
@@ -377175,16 +378513,20 @@
       "93d26b5eb969808caee163eec76b4ac93d10bf41",
       []
      ],
+     "cfrg_curves.js": [
+      "25113bfe04a140961c1be8ef4c871fc8a1179bfb",
+      []
+     ],
      "cfrg_curves_bits.js": [
-      "1406e8bf0a19289a47aa9d80b8a147fa67cc5e4e",
+      "c3826a295e622cb74b0d0e3148ab736384182044",
       []
      ],
      "cfrg_curves_bits_curve448.tentative.https.any-expected.txt": [
-      "66ab63a4e41ed1cbba4838edb1641e59f3c419b0",
+      "bcf668f4a3b75e7341659b86d232887d2c0f3487",
       []
      ],
      "cfrg_curves_bits_curve448.tentative.https.any.worker-expected.txt": [
-      "66ab63a4e41ed1cbba4838edb1641e59f3c419b0",
+      "bcf668f4a3b75e7341659b86d232887d2c0f3487",
       []
      ],
      "cfrg_curves_bits_fixtures.js": [
@@ -377192,15 +378534,19 @@
       []
      ],
      "cfrg_curves_keys.js": [
-      "cefc45ac6929033beae7c9c6edaefb98d4babf38",
+      "1989c55c903fe59edb82779c2f69970a12671a04",
       []
      ],
      "cfrg_curves_keys_curve448.tentative.https.any-expected.txt": [
-      "d5317de7e76c0d7f37472222b902ca2d078dc3d8",
+      "2634f861b5bad7abe0198231a2ba10f1ddbd7869",
       []
      ],
      "cfrg_curves_keys_curve448.tentative.https.any.worker-expected.txt": [
-      "d5317de7e76c0d7f37472222b902ca2d078dc3d8",
+      "2634f861b5bad7abe0198231a2ba10f1ddbd7869",
+      []
+     ],
+     "derive.js": [
+      "16c9ec6407bbacd665d304e2385fedd780a7c26c",
       []
      ],
      "derive_key_and_encrypt.js": [
@@ -377219,12 +378565,20 @@
       "391f81d1871d7683d800b4df83b7bfa41a782874",
       []
      ],
+     "ecdh.js": [
+      "99039f250c2c59295703e62314f9fc5441bb2ea2",
+      []
+     ],
      "ecdh_bits.js": [
-      "8e79909020d398c6c04c2f4497c59e5f2e60180b",
+      "bbe48b5e2859b555aa844a5f8bf4cccfb9fbd761",
+      []
+     ],
+     "ecdh_fixtures.js": [
+      "46539d548da74f1de30c693d2d02ff14b4ff91bb",
       []
      ],
      "ecdh_keys.js": [
-      "8c3d2aeb5a497680a81f7b242531cedfdec88613",
+      "c4ee495aa6ffe707216758353efec7ec3d070737",
       []
      ],
      "hkdf.https.any.worker_1-1000-expected.txt": [
@@ -377260,13 +378614,17 @@
       []
      ],
      "hkdf.js": [
-      "08e8c0c8974617ffa03ac33f8c3bed73806b632f",
+      "a090169836e573e0f8281d8398d85e964d6b5889",
       []
      ],
      "hkdf_vectors.js": [
       "ef448ec5f2a6543cd7324443ae3691e909aeb821",
       []
      ],
+     "kdf.js": [
+      "ecbb3b4148f4a0152fcc98abccbf89be81468437",
+      []
+     ],
      "pbkdf2.https.any.worker_1-1000-expected.txt": [
       "9245ea78730757dc0339ec974f1bfecc6208bdf5",
       []
@@ -377340,7 +378698,7 @@
       []
      ],
      "pbkdf2.js": [
-      "4d5b0137a387c856cbcb3786ea59b43a62d2cfe8",
+      "57c19ae3f014809dc014fa35ce344ba223c58fa7",
       []
      ],
      "pbkdf2_vectors.js": [
@@ -377357,6 +378715,14 @@
       "b1e63d1c5afbd4ce558a96215fb3fde0bf097a1b",
       []
      ],
+     "digest.js": [
+      "9ef9c69a180b883b2239598d7412b8c857b20ed0",
+      []
+     ],
+     "digest_test_data.js": [
+      "f088e02a087021f30fc7cbf946ed38c313209b85",
+      []
+     ],
      "kangarootwelve.tentative.https.any-expected.txt": [
       "10db00ec4b8e4bf229392a2ffd061d644a0a00c1",
       []
@@ -377380,6 +378746,10 @@
      "turboshake.tentative.https.any.worker-expected.txt": [
       "7fe54c9c617b52cfb56579cd5511ae1342a22138",
       []
+     ],
+     "xof_digest.js": [
+      "f5ef5846b7960959e26e6fcaa4f9dd9169b63b17",
+      []
      ]
     },
     "encap_decap": {
@@ -377406,7 +378776,7 @@
     },
     "encrypt_decrypt": {
      "aes.js": [
-      "879a6efe257e496acb3f1c4af3ffda925a382e16",
+      "9caecf695cf7e7f20d13228c2939df9693e0d370",
       []
      ],
      "aes_cbc.https.any-expected.txt": [
@@ -377418,7 +378788,11 @@
       []
      ],
      "aes_cbc_vectors.js": [
-      "96445a96325a557e53438b8f9dfd280d9c694bb6",
+      "28ef75fece62a4be482ce487b752273cb0447fe5",
+      []
+     ],
+     "aes_common_fixtures.js": [
+      "d8c6d1666ccaeb24460ef4a3a5618ca1515a931a",
       []
      ],
      "aes_ctr.https.any-expected.txt": [
@@ -377430,7 +378804,7 @@
       []
      ],
      "aes_ctr_vectors.js": [
-      "201dff83ce9cacbd3f61f6a0b16eeb3105fa9ab2",
+      "89a19fa5fcbe5e1336e2ac8fff983f898efda35c",
       []
      ],
      "aes_gcm.https.any-expected.txt": [
@@ -377450,11 +378824,11 @@
       []
      ],
      "aes_gcm_256_iv_fixtures.js": [
-      "9cdbbbb79075c0a90d4a55caf18157fe485a5609",
+      "a081b8e864831c2ff3f9b1255873e7faf88686a5",
       []
      ],
      "aes_gcm_96_iv_fixtures.js": [
-      "bb00e2d7dd92cbd780d48273e065e95d5c38e181",
+      "e120b58d8dc493fc49a198db3d7b77c5b87e053a",
       []
      ],
      "aes_gcm_vectors.js": [
@@ -377470,7 +378844,7 @@
       []
      ],
      "aes_ocb_fixtures.js": [
-      "a5795195f163ce4c759700374b0991751bd70559",
+      "3e0c014c3e1c07edb30ddd2d18c4791bd36f249f",
       []
      ],
      "aes_ocb_vectors.js": [
@@ -377486,17 +378860,21 @@
       []
      ],
      "rsa.js": [
-      "071c086b3c4dfaeb1af21cd07e7831f2253324ab",
+      "951ffd56e58165abdda370027b056b6ab3953841",
       []
      ],
      "rsa_vectors.js": [
-      "fcc732eddc1d63d90cdb95c457c8b6e81d8cb464",
+      "ca99953524e4ff6afbd5a22932eb62aaf5c74f27",
       []
      ]
     },
     "generateKey": {
+     "algorithm_registry.js": [
+      "2c7493dfffd674bc6270db591752ce82928e645a",
+      []
+     ],
      "failures.js": [
-      "a3258ae0dd7788241a971d746e0ffa2c843e4954",
+      "e4a75c0651520c998e5589c6cde19471f4eb5ecd",
       []
      ],
      "failures_AES-CBC.https.any-expected.txt": [
@@ -377532,27 +378910,27 @@
       []
      ],
      "failures_AES-OCB.tentative.https.any-expected.txt": [
-      "df41403e48b58cf47ee17af3c5c05c27c48d40df",
+      "5f8baadffd19848e134d1398403c397eefca3149",
       []
      ],
      "failures_AES-OCB.tentative.https.any.worker-expected.txt": [
-      "df41403e48b58cf47ee17af3c5c05c27c48d40df",
+      "5f8baadffd19848e134d1398403c397eefca3149",
       []
      ],
      "failures_Ed448.tentative.https.any-expected.txt": [
-      "9e773ae68af00710e3ef5a44a921d178d7c5d4df",
+      "c840ae7f523d2ee5aed277720fe37f1c4e3fa187",
       []
      ],
      "failures_Ed448.tentative.https.any.worker-expected.txt": [
-      "9e773ae68af00710e3ef5a44a921d178d7c5d4df",
+      "c840ae7f523d2ee5aed277720fe37f1c4e3fa187",
       []
      ],
      "failures_ML-DSA.tentative.https.any-expected.txt": [
-      "934b1a8b1d7b59fbdbe27ca4ab5e947a81621e60",
+      "70d6d7c4c440ec13197208291b94555ad11935c8",
       []
      ],
      "failures_ML-DSA.tentative.https.any.worker-expected.txt": [
-      "934b1a8b1d7b59fbdbe27ca4ab5e947a81621e60",
+      "70d6d7c4c440ec13197208291b94555ad11935c8",
       []
      ],
      "failures_ML-KEM.tentative.https.any-expected.txt": [
@@ -377564,31 +378942,31 @@
       []
      ],
      "failures_X448.tentative.https.any-expected.txt": [
-      "53b8a7e9c387fc3e374b0a595d53ae66b007af36",
+      "4fbef77e0af6b886fa39e3a4c291a9a815beb8ef",
       []
      ],
      "failures_X448.tentative.https.any.worker-expected.txt": [
-      "53b8a7e9c387fc3e374b0a595d53ae66b007af36",
+      "4fbef77e0af6b886fa39e3a4c291a9a815beb8ef",
       []
      ],
      "failures_chacha20_poly1305.tentative.https.any-expected.txt": [
-      "b6ed0de20af532f456c55255460a95b3472dd712",
+      "7a8ecb9b983da450012cbbc6dc2bc613eb5b4bd5",
       []
      ],
      "failures_chacha20_poly1305.tentative.https.any.worker-expected.txt": [
-      "b6ed0de20af532f456c55255460a95b3472dd712",
+      "7a8ecb9b983da450012cbbc6dc2bc613eb5b4bd5",
       []
      ],
      "failures_kmac.tentative.https.any-expected.txt": [
-      "22b751ed5e7edf8cca8b36105e777b519ad11a1e",
+      "f354bb54a64d4d2167fb31e21fa48fdc76b02343",
       []
      ],
      "failures_kmac.tentative.https.any.worker-expected.txt": [
-      "22b751ed5e7edf8cca8b36105e777b519ad11a1e",
+      "f354bb54a64d4d2167fb31e21fa48fdc76b02343",
       []
      ],
      "successes.js": [
-      "c72384b7b93bf466a911ec1363192ab108db1353",
+      "5006cd35ad9c0f22a741f773b848f4c3fa4a7599",
       []
      ],
      "successes_AES-CBC.https.any-expected.txt": [
@@ -377729,10 +379107,6 @@
       "ac81f214cc09a44246e8e75ff832079170462c54",
       []
      ],
-     "ML-DSA_importKey.js": [
-      "d9257ac698250529a0475800ffac9045e3d84c82",
-      []
-     ],
      "ML-DSA_importKey.tentative.https.any-expected.txt": [
       "40ffeacd0562480bb077e0f7be8403076067f00d",
       []
@@ -377742,11 +379116,7 @@
       []
      ],
      "ML-DSA_importKey_fixtures.js": [
-      "ef1f5a1f51a9446c93d027dbbb66fd18a158e1eb",
-      []
-     ],
-     "ML-KEM_importKey.js": [
-      "d9257ac698250529a0475800ffac9045e3d84c82",
+      "abccc10c63fb076a750539c7e0f36099ee697ff8",
       []
      ],
      "ML-KEM_importKey.tentative.https.any-expected.txt": [
@@ -377762,11 +379132,15 @@
       []
      ],
      "ec_importKey_failures_fixtures.js": [
-      "dc0e11d551a92271e31dc9f000b18601deb9d53e",
+      "a37a72cb28d5426b4b800f79db8877ae3366c120",
       []
      ],
      "importKey_failures.js": [
-      "f45da96cf6b01ca6a01e13459619b0cc2880e007",
+      "7581614f0dcb43635b23082d5919fcc37c508158",
+      []
+     ],
+     "ml_importKey.js": [
+      "a33c3a85b912e4dfac9d29273aa2297518fb3a80",
       []
      ],
      "okp_importKey.js": [
@@ -377790,27 +379164,27 @@
       []
      ],
      "okp_importKey_failures_Ed448.tentative.https.any-expected.txt": [
-      "59e83f578305511158c2b951884d97cfa3eccbd2",
+      "a4f7c3d9501a2cb2ff9d08e0da0a0e79667df549",
       []
      ],
      "okp_importKey_failures_Ed448.tentative.https.any.worker-expected.txt": [
-      "59e83f578305511158c2b951884d97cfa3eccbd2",
+      "a4f7c3d9501a2cb2ff9d08e0da0a0e79667df549",
       []
      ],
      "okp_importKey_failures_X448.tentative.https.any-expected.txt": [
-      "903dd31e8abc800680c6856b78046d2c2acd05a4",
+      "a0b89218e304d413847c08ab4036ca194df3e8bf",
       []
      ],
      "okp_importKey_failures_X448.tentative.https.any.worker-expected.txt": [
-      "903dd31e8abc800680c6856b78046d2c2acd05a4",
+      "a0b89218e304d413847c08ab4036ca194df3e8bf",
       []
      ],
      "okp_importKey_failures_fixtures.js": [
-      "6a7f583e0165c5661baaad12afed7fbd5fe013f7",
+      "01c65d60d44d57f0f799ee82fa3f9602cb91adff",
       []
      ],
      "okp_importKey_fixtures.js": [
-      "58b41d52601eb9b7175d546a87027639887f9a75",
+      "5fe0f05c42b42ff2708eac88f6ef5e9178277d20",
       []
      ],
      "symmetric_importKey.https.any-expected.txt": [
@@ -377922,7 +379296,7 @@
     },
     "sign_verify": {
      "ecdsa.js": [
-      "cc13b1bd9c3cae79afb1b170726ea541752e44ba",
+      "a1a744aaa8244b16c80756d30be152c5ed75df06",
       []
      ],
      "ecdsa_vectors.js": [
@@ -377930,15 +379304,15 @@
       []
      ],
      "eddsa.js": [
-      "77961566d9a60202cadbd66b1d5e21973cedfdc6",
+      "a60ceda96a6c7e8b67e0ad7341a351012434adfe",
       []
      ],
      "eddsa_curve448.tentative.https.any-expected.txt": [
-      "377a39e3dd91d125c63da4961e625bd417dddfa7",
+      "28b846e865958977fbfea6fd65fa840af8361526",
       []
      ],
      "eddsa_curve448.tentative.https.any.worker-expected.txt": [
-      "377a39e3dd91d125c63da4961e625bd417dddfa7",
+      "28b846e865958977fbfea6fd65fa840af8361526",
       []
      ],
      "eddsa_small_order_points.https.any-expected.txt": [
@@ -377958,7 +379332,7 @@
       []
      ],
      "hmac.js": [
-      "929d9464e171cbaa37f24fa8ccefa0a0d79d7920",
+      "05b9cedfb94ca47420e63379b95d59f7d2bdd6de",
       []
      ],
      "hmac_vectors.js": [
@@ -377966,7 +379340,7 @@
       []
      ],
      "kmac.js": [
-      "d9d82d150b39335c5f1428549c652626176a6e4c",
+      "ac30a5559d8c003a4e5d79e02ff17c6268a69834",
       []
      ],
      "kmac.tentative.https.any-expected.txt": [
@@ -377981,32 +379355,40 @@
       "6b35cab168c9c674dbdaf3155c4dfb828c82e758",
       []
      ],
+     "mac.js": [
+      "27787c91d8a2b5c4f7e5253a9702ad459fcfa7ca",
+      []
+     ],
      "mldsa.js": [
-      "10879273c1c3f4e16faa576686f55ff8717ae9a4",
+      "a2a4ef276043c45faa5fd2694dd56b53876c01d6",
       []
      ],
      "mldsa.tentative.https.any-expected.txt": [
-      "08e0658d91944222380d9899129c889156ef14bd",
+      "a421aad6a3fe5e72c63e0e346fbed485cb4435c2",
       []
      ],
      "mldsa.tentative.https.any.worker-expected.txt": [
-      "08e0658d91944222380d9899129c889156ef14bd",
+      "a421aad6a3fe5e72c63e0e346fbed485cb4435c2",
       []
      ],
      "mldsa_vectors.js": [
-      "a035821b7e35931f24069c5b7a326f98b2e27ac0",
+      "021060a1f35bfe07acc3d53191710211401eafb9",
       []
      ],
      "rsa.js": [
-      "667f5d2793e48c07fa423113fa4f49ef6ae8fc61",
+      "8a87ba88cb4b958cde93d41d34db07dc1e6e7a94",
       []
      ],
      "rsa_pkcs_vectors.js": [
-      "71e5d8571bc8ef96ca9b23fb9887a76d9f7812c6",
+      "f1eca448a988e1b7cdd598423801bdcc92ba94a4",
       []
      ],
      "rsa_pss_vectors.js": [
-      "c3ce77960629b211836dd201dc6b318eab3a97ac",
+      "224bacc3d1bb8ba132cbd8db15ffdd70ec29ae42",
+      []
+     ],
+     "signature.js": [
+      "2d734e1d973e715a247e736233ee488cdde0d254",
       []
      ]
     },
@@ -378028,13 +379410,33 @@
     ],
     "tools": {
      "generate.py": [
-      "35ba0f0031555faa013c60b5fd6b7efb77569254",
+      "cc6c7aea9c09c9d8c7eb76b0fc946f4ec1887c49",
       []
      ]
     },
     "util": {
+     "ec_key_fixtures.js": [
+      "e298fa8f9ebef2b35e5be16cb2ba775adac16a1a",
+      []
+     ],
      "helpers.js": [
-      "3d44b7a7550e86eff62155406b62114d1c60b3ca",
+      "cc44dd16b1c9ab74d7fc1790fb8c79f9a13c8eec",
+      []
+     ],
+     "mldsa_key_fixtures.js": [
+      "7b5170cdfeac83c5c951cb5ab3146d9d549e08f0",
+      []
+     ],
+     "okp_key_fixtures.js": [
+      "569e7c7215a7c2006cbda38dc07f1588f25c7fb7",
+      []
+     ],
+     "rsa_key_fixtures.js": [
+      "13adb1a133e6b44f8b5df057b98c7a3aec4161e1",
+      []
+     ],
+     "supports.js": [
+      "18442424f4a2826920df8008834d55876975fd98",
       []
      ],
      "worker-report-crypto-subtle-presence.js": [
@@ -378044,7 +379446,7 @@
     },
     "wrapKey_unwrapKey": {
      "wrapKey_unwrapKey_vectors.js": [
-      "cf799a8a8ce3ea48abc50d1eb4e7b5465fe2c0d4",
+      "36500e4f27bad59e657b524393b84c67944a7c4e",
       []
      ]
     }
@@ -380518,14 +381920,6 @@
      "6099a18e100105f5caf3abdf7618f04b307b83d0",
      []
     ],
-    "async-clipboard-cached-getType-reject.tentative.https-expected.txt": [
-     "6d73ed0eb980c17ee82185469a6f76d11d457b8a",
-     []
-    ],
-    "async-clipboard-stale-getType-reject.tentative.https-expected.txt": [
-     "c1fc022002f6b6e78c4ce8c4b4d38e1f420d625f",
-     []
-    ],
     "async-custom-formats-write-fail.tentative.https-expected.txt": [
      "a66622a2e529d9ae181c4b41cdde11ef13bfd124",
      []
@@ -381735,10 +383129,30 @@
       "d4ead82bc80b7ea7395a3d784873fde79a9c8785",
       []
      ],
+     "reporting-navigation-blocked.https.sub.html.sub.headers": [
+      "459952b2e4e98744cf16d80b0aa1fbb5cedecfd1",
+      []
+     ],
+     "reporting-navigation-redirect-blocked.https.sub.html.sub.headers": [
+      "3906eda754dc8b434890d239ac059c56af75c8ef",
+      []
+     ],
+     "reporting-navigation-redirect-report-only.https.sub.html.sub.headers": [
+      "0824baea93034e41593fa86c629013f86e89203d",
+      []
+     ],
+     "reporting-navigation-report-only.https.sub.html.sub.headers": [
+      "633837c4e6dd8408bd30d78758cc79a448b8f431",
+      []
+     ],
      "reporting-report-only.https.sub.html.sub.headers": [
       "9c8f486c4b52f83a34af5aeddde2e5600804325e",
       []
      ],
+     "reporting-speculation-rules-prefetch-blocked.https.sub.html.sub.headers": [
+      "30ab172cd3bc14513d5da51cbf23144d970a9ecf",
+      []
+     ],
      "resources": {
       "blank-allow-same-origin.html": [
        "8d049d5e9b12ca51796fbe26818ab5929c3b26c5",
@@ -381945,6 +383359,10 @@
        []
       ]
      },
+     "service-worker-reporting-url-sanitization.https.sub.window.js.sub.headers": [
+      "15949255ec32fbfcbeec73098a12222869236b5d",
+      []
+     ],
      "service-worker-reporting.sub.https.window.js.sub.headers": [
       "4a3c3de49cfeb3ca49138a6c0515d1ba2ba9563f",
       []
@@ -381997,6 +383415,10 @@
       "03e917577a8e3149cedda6508d4be2678c808dfe",
       []
      ],
+     "webrtc-queuereport-sanitization.https.sub.window.js.headers": [
+      "274f8a9d93086e6e317547e798d86c16c303f6c3",
+      []
+     ],
      "websocket-dedicated-worker.sub.window.js.sub.headers": [
       "01fa43a977bb4c91aada7fc06ae873c127ef6b2a",
       []
@@ -382083,6 +383505,14 @@
       "567babb96d1ec3e1032d476f8f7ee1fcb2aa4875",
       []
      ]
+    },
+    "tentative": {
+     "resources": {
+      "iframe-isolation-subframe.html": [
+       "5f3416f93c18c4241cfb7b79f645781363df524e",
+       []
+      ]
+     }
     }
    },
    "content-dpr": {
@@ -383931,6 +385361,10 @@
       "519dcaacb1f6c28d09728858c3a213872d299315",
       []
      ],
+     "script-src-strict_dynamic_modulepreload_parser_inserted_module.html.headers": [
+      "b7918c93323eff9db66ad26a73b78798d35e5f7b",
+      []
+     ],
      "script-src-strict_dynamic_new_function.html.headers": [
       "b7918c93323eff9db66ad26a73b78798d35e5f7b",
       []
@@ -383959,6 +385393,10 @@
       "b7918c93323eff9db66ad26a73b78798d35e5f7b",
       []
      ],
+     "script-src-strict_dynamic_parser_inserted_module_empty_hash.html.headers": [
+      "4e53eca7e8328d32277caaadd8be6d04b35533de",
+      []
+     ],
      "script-src-trusted_types_eval_with_report_only_require_trusted_types_eval.html.headers": [
       "ae1e09e31ea4ba841d96458be2803871448e09ec",
       []
@@ -392739,6 +394177,10 @@
       "707af50766b8dd2d4a99d7824c7e8ff80ea239e3",
       []
      ],
+     "anchor-name-shadow-higher-tree-expected.txt": [
+      "766b948e5181e122605d24946e7508c334b59865",
+      []
+     ],
      "anchor-position-005-print-ref.html": [
       "2c8824bbc3536186d6bc8aed7222ded7da2c40be",
       []
@@ -393404,7 +394846,7 @@
       []
      ],
      "text-decoration-inset-auto-expected.txt": [
-      "1abb7e28bed3aec584a902e29afed90f7fc45df7",
+      "7fba5dc6e2d2594a1c3d219a60cf56e61a972e8c",
       []
      ],
      "transform-animation-under-large-scale-ref.html": [
@@ -393862,7 +395304,7 @@
       ]
      },
      "background-clip-color-ref.html": [
-      "0bd414cd067425b9604b67292fe0013b98b1b791",
+      "fdb7eb2b0dffdb98ebf25190c42c0f0d0ea55ccf",
       []
      ],
      "background-clip-color-repaint-ref.html": [
@@ -395963,7 +397405,7 @@
        []
       ],
       "border-shape-polygon-miter-limit-ref.html": [
-       "4d3dcf16bdbde02fec32715f4f8bb0390d390f07",
+       "fa85331a51d40df2483a84b261c70ecf0fb8a5f4",
        []
       ],
       "border-shape-shadow-blur-ref.html": [
@@ -395998,6 +397440,10 @@
        "2010f11bbac4530cb05b1905b5e6f597b44d7f64",
        []
       ],
+      "border-shape-shape-arc-miter-limit-ref.html": [
+       "0d3cc568305262756135d745be17799e9d38f937",
+       []
+      ],
       "border-shape-stroke-from-border-ref.html": [
        "c88b30e8ba472bfdb1e1dc2c0b928ec223cf81c9",
        []
@@ -397352,7 +398798,7 @@
       []
      ],
      "all-prop-initial-visited-ref.html": [
-      "f596b559b0e26b9c03d47b7dcab966a4d091308b",
+      "356d43f0db742193d71f5c529a11c5ffb9c59c45",
       []
      ],
      "all-prop-revert-layer-expected.txt": [
@@ -397788,6 +399234,10 @@
        "747b7a1cf292ec8786f4e3a39d1222c2bef7e32b",
        []
       ],
+      "alpha-color-computed-expected.txt": [
+       "d7b90ec20391f666686d5cb4c7546b959b60636c",
+       []
+      ],
       "color-computed-color-mix-function-expected.txt": [
        "fbcfaf6df310ec62734036e6b1b1a359e0353d9a",
        []
@@ -397817,11 +399267,7 @@
        []
       ],
       "color-valid-color-mix-function-expected.txt": [
-       "aa149edac13475e064d61d7161712da0a9b4fc07",
-       []
-      ],
-      "color-valid-contrast-color-function-expected.txt": [
-       "c9e2a9f94597401e47b2c4282dff2d06726f7f96",
+       "3be2a24a45a17e899308cfc4328748117459c6c3",
        []
       ],
       "color-valid-hsl-expected.txt": [
@@ -398301,6 +399747,10 @@
       "414d9bf6f4c83876f604d2f47bd4cfc462d314bf",
       []
      ],
+     "at-supports-at-rule-serialization-expected.txt": [
+      "2f333fb2141410322579f4ec4d8db6692e7d2e0d",
+      []
+     ],
      "at-supports-matches-expected.txt": [
       "142243ff96dd3aa744be0dcce13ad2c7f14d6cc1",
       []
@@ -399587,7 +401037,7 @@
        []
       ],
       "content-computed-expected.txt": [
-       "2541212ea95fcdba664f7d96fbee2b174811ebb4",
+       "a9b04ffd862d47c564077ad91574406f7ed585b6",
        []
       ]
      },
@@ -400026,6 +401476,10 @@
         []
        ]
       },
+      "symbols-function-dynamic-ref.html": [
+       "a5ca09fed9bb95bd6a825ccc47fc3366095a15c5",
+       []
+      ],
       "symbols-function-invalid-ref.html": [
        "e07de5f1f93e458f65f831b0de9ec47d90c102cc",
        []
@@ -403525,6 +404979,10 @@
       "efd5856fa4912f271498ba7805169012d0ac13ee",
       []
      ],
+     "font-face-width-descriptor-aliasing-ref.html": [
+      "8517bb4bca8e69f1dc74c98f60a3d3b94152dd2b",
+      []
+     ],
      "fontface-descriptor-updates-ref.html": [
       "513867b3503042886e51312bcfec20967f7384d4",
       []
@@ -403565,6 +405023,10 @@
       "Rochester.otf": [
        "79f63ced683e6342866d734b04f3c687f6dd942f",
        []
+      ],
+      "variabletest_matching.ttf": [
+       "89ed12a2c03531a1e8f764e3a2365824a8f40a12",
+       []
       ]
      },
      "support": {
@@ -414894,6 +416356,18 @@
          "2961d346d2e252f280992b802368cf3fa9762133",
          []
         ],
+        "column-dense-packing-flow-tolerance-001-ref.html": [
+         "6856a97e76d7db82376c95a670eee839170672c3",
+         []
+        ],
+        "column-dense-packing-flow-tolerance-002-ref.html": [
+         "2df0459fce7101981ee9a049bb89e9c386e0172f",
+         []
+        ],
+        "column-dense-packing-flow-tolerance-003-ref.html": [
+         "48329776a242aa2991d3357e4977648f828543ee",
+         []
+        ],
         "column-dense-packing-multi-span-001-ref.html": [
          "c00b9716de9ce998e51b85d335623190b58be3e1",
          []
@@ -414939,11 +416413,23 @@
          []
         ],
         "column-fill-reverse-dense-packing-definite-size-001-ref.html": [
-         "d791d90653b680f0b3f642e0b7b69585dff9d47d",
+         "f6be39dc8ef055dff8b2c7b42203fa016ef2cadb",
+         []
+        ],
+        "column-fill-reverse-dense-packing-flow-tolerance-001-ref.html": [
+         "028611cf4a767fbbae1b41c093e12f2d2da74be7",
          []
         ],
         "column-fill-reverse-dense-packing-indefinite-size-001-ref.html": [
-         "2ce5962c166b68463897246b362a7d64351b5e6c",
+         "59f24e74c374db0fd2d454c35debb6d51a631b9b",
+         []
+        ],
+        "column-track-reverse-dense-packing-flow-tolerance-001-ref.html": [
+         "44613393642dd4ad5ab87b703a26e51ab26061b1",
+         []
+        ],
+        "column-track-reverse-dense-packing-flow-tolerance-002-ref.html": [
+         "4d037444350f26378de9ec3c99e20a1696943483",
          []
         ],
         "row-dense-packing-001-ref.html": [
@@ -414962,6 +416448,18 @@
          "bd08b28c3eba3a3f0bd669f1ef3ec1dbb9813faa",
          []
         ],
+        "row-dense-packing-flow-tolerance-001-ref.html": [
+         "bc8fa0b3e9c8a0d1e9e89b1a8e368bfb6ac63937",
+         []
+        ],
+        "row-dense-packing-flow-tolerance-002-ref.html": [
+         "a1ba246fd0f01c93b3cb40c773fbc12971b6958b",
+         []
+        ],
+        "row-dense-packing-flow-tolerance-003-ref.html": [
+         "531e1abc8e8ea1a36878abcd9980fe6094094e86",
+         []
+        ],
         "row-dense-packing-multi-span-001-ref.html": [
          "59f8212f5c17aea3c4c19132514715167c92ce9a",
          []
@@ -414983,11 +416481,19 @@
          []
         ],
         "row-fill-reverse-dense-packing-definite-size-001-ref.html": [
-         "1dde7d215ca34e6644c1e6cbcec497646b4779b2",
+         "942b17e656e08981a11c5e60b2e0d124595a858a",
          []
         ],
         "row-fill-reverse-dense-packing-indefinite-size-001-ref.html": [
-         "2dc67c92a4cdcf00fb61425a9043ec97e912a29e",
+         "5656e6cfc0cdea69efbc4738dad5c6fc939c14a8",
+         []
+        ],
+        "row-track-reverse-dense-packing-flow-tolerance-001-ref.html": [
+         "193a425078705d6938052b9b5901316d612cff3c",
+         []
+        ],
+        "row-track-reverse-dense-packing-flow-tolerance-002-ref.html": [
+         "3d109e71c2c22b0910e18f8406136503c2021287",
          []
         ]
        },
@@ -415217,6 +416723,34 @@
         "e8edcfcd084166aa17db36eb432719ecbf28cd52",
         []
        ],
+       "grid-lanes-overflow-reverse-001-ref.html": [
+        "f92c1f51d2f9df9bd73178dfe34a95b56617a835",
+        []
+       ],
+       "grid-lanes-overflow-reverse-002-ref.html": [
+        "baeb654b329896ef985a2dcaee4d662cf5ea5aa2",
+        []
+       ],
+       "grid-lanes-overflow-reverse-003-ref.html": [
+        "60bb406e9c85a0d0c325c6ea51e09e078aedac4a",
+        []
+       ],
+       "grid-lanes-overflow-reverse-004-ref.html": [
+        "5b281910928b607b203e5c79126feb8d1f4a1c64",
+        []
+       ],
+       "grid-lanes-overflow-reverse-005-ref.html": [
+        "59b898efaf5c4967daf09fda57c3f174361617ab",
+        []
+       ],
+       "grid-lanes-overflow-reverse-baseline-001-ref.html": [
+        "f6a71f4182f05db4630284af9b2adc94c8ead441",
+        []
+       ],
+       "grid-lanes-overflow-reverse-baseline-002-ref.html": [
+        "fea095542bc0f39f4025b2e26f1372dc41b34bdc",
+        []
+       ],
        "grid-lanes-overflowing-container-005-ref.html": [
         "748533c37c9f45cc24b352deafb47847a471836f",
         []
@@ -415292,6 +416826,64 @@
           []
          ]
         },
+        "baseline": {
+         "column-grid-lanes-container-baseline-subgrid-001-ref.html": [
+          "d35bd393066d014833e0ed1ac144ac54bdb8be07",
+          []
+         ],
+         "column-grid-lanes-item-baseline-subgrid-001-ref.html": [
+          "a31a762e11d5b000d6f05a3bb5fec748fab8b546",
+          []
+         ],
+         "column-grid-lanes-item-baseline-subgrid-002-ref.html": [
+          "ef854bed1a2836ee658c7c0c8792f7404dae7d92",
+          []
+         ],
+         "column-grid-lanes-item-baseline-subgrid-003-ref.html": [
+          "49f2dc09dbb4b5720a04f0f957a3aa1e9692c891",
+          []
+         ],
+         "column-grid-lanes-item-baseline-subgrid-004-ref.html": [
+          "1ae87671122f838a6125ad59f0ccbb41b8e62cfb",
+          []
+         ],
+         "column-grid-lanes-item-baseline-subgrid-005-ref.html": [
+          "4c650673d87efc1ecd4e7d930b7dac76fc526b81",
+          []
+         ],
+         "column-grid-lanes-item-baseline-subgrid-006-ref.html": [
+          "377b775881e86f24c07b68d51982da2b56b48560",
+          []
+         ],
+         "row-grid-lanes-container-baseline-subgrid-001-ref.html": [
+          "b59182b5b7589ef04c9c645d58f40aec7556444a",
+          []
+         ],
+         "row-grid-lanes-item-baseline-subgrid-001-ref.html": [
+          "1ba31bc3d4f5d6bcddc881fce619a7038598ccbb",
+          []
+         ],
+         "row-grid-lanes-item-baseline-subgrid-002-ref.html": [
+          "c36dbf5509f94c80b665521b4a8fb087b35e5478",
+          []
+         ],
+         "row-grid-lanes-item-baseline-subgrid-003-ref.html": [
+          "64b0e3ca0bf55107c4eedd2f5e2639f40cac54a0",
+          []
+         ],
+         "row-grid-lanes-item-baseline-subgrid-004-ref.html": [
+          "78310e8045901b7d7264644c3bb42922f7b66b1d",
+          []
+         ],
+         "row-grid-lanes-item-baseline-subgrid-005-ref.html": [
+          "832eb46e7c576840d53875bba7a03c13a1498cc6",
+          []
+         ],
+         "row-grid-lanes-item-baseline-subgrid-006-ref.html": [
+          "f9fe0cf7a3a657f2411dc88b39916457581f93e4",
+          []
+         ]
+        },
         "column": {
          "grid-lanes-subgrid-001a-ref.html": [
           "5554388371f1e1e65b42c00bc5f73278d0b977de",
@@ -416584,7 +418176,7 @@
      "layout-algorithm": {
       "references": {
        "auto-margins-ignored-during-track-sizing-001-ref.html": [
-        "b06d89115178107b12493023f211f156f3e4b17d",
+        "ada8a04831cc9f5919e927b815cadf700e119347",
         []
        ],
        "grid-percent-cols-filled-shrinkwrap-001-ref.html": [
@@ -419800,7 +421392,7 @@
       []
      ],
      "link-parameters-computed-expected.txt": [
-      "b7860323da6579b0f427e10783e76e0deec449e6",
+      "f74e22409796145992c30c39cbc5ef69d4939577",
       []
      ],
      "link-parameters-valid-expected.txt": [
@@ -420358,14 +421950,6 @@
        "f3b7a8149af957f54f8e5352dc774bbd922b183c",
        []
       ],
-      "list-style-type-computed-expected.txt": [
-       "a3474ff9654922b1215ac6e2438f7679ce9516d0",
-       []
-      ],
-      "list-style-type-valid-expected.txt": [
-       "039b26b543ee7cd3ac58d06d29248eb7f434d41a",
-       []
-      ],
       "list-style-valid-expected.txt": [
        "839e3dfa4ee1f2f0126ed686717bc103a09a002c",
        []
@@ -426116,7 +427700,7 @@
       []
      ],
      "registered-property-computation-expected.txt": [
-      "8f41eb229cd7eaf7501d6fb3fa9b4953034abf00",
+      "7cdeedea5be88df72a66aa8b26a85ed78841345d",
       []
      ],
      "resources": {
@@ -426161,6 +427745,10 @@
      ]
     },
     "css-pseudo": {
+     "CSSPseudoElement-geometry-edge-cases.tentative-expected.txt": [
+      "722be7d3f238c2331135c10fe27f00c3e849285f",
+      []
+     ],
      "META.yml": [
       "ae3222595453ec46cf48810cb2f276126897bac6",
       []
@@ -426588,7 +428176,7 @@
       []
      ],
      "highlight-styling-004-ref.html": [
-      "bd023b6128e460384e3e79daeec9ca8c924fdc47",
+      "a59e0550c970d104613d3a4654f503b9b58ba401",
       []
      ],
      "highlight-styling-005-ref.html": [
@@ -429770,63 +431358,63 @@
         []
        ],
        "iframe-contents-400x200root.html": [
-        "190b9146221ee25f4f344d180c70d5c71a90d501",
+        "c8c3d443631f0d93cc45513a61259557465e76ad",
         []
        ],
-       "iframe-contents-allowed-origins-missing.html": [
+       "iframe-contents-alloworigins-missing.html": [
         "3b7406e1e4064bc0f9f829907bfe462c8b6d3891",
         []
        ],
-       "iframe-contents-allowed-origins.sub.html": [
-        "ca31c87758d3ad6e69778d29451ab1d6a6f26f4f",
+       "iframe-contents-alloworigins.sub.html": [
+        "ea23a2edfeb7b35d017baec643ed07f690911cff",
         []
        ],
        "iframe-contents-dom-content-loaded.html": [
-        "bfc8ad6cc87edcde93366b9e0d8ba441d221aded",
+        "b996f248d38e5f0ac4e443b5ea3be4cc3a13c933",
         []
        ],
        "iframe-contents-icb.html": [
-        "a8e89292d8d207034ff1c988d20603c59798a6b7",
+        "0ea5597cfb699adfa56d6b644dd9d864cd726fcd",
         []
        ],
        "iframe-contents-meta-after-head.html": [
-        "894b064c8f81becff2188c63bd5e316b89e42cee",
+        "bc354ed4ae4c02fe2c8fbf076ebf4ba2f402d681",
         []
        ],
        "iframe-contents-meta-dynamic-append-after-body.html": [
-        "f211330d1106e8c193683ed6a6358e8f1517e636",
+        "d95705f25ea7e2dfef6f81b07b88df494ceb9bec",
         []
        ],
        "iframe-contents-meta-dynamic-append-to-doc.html": [
-        "677e3e82327ccfa1d1112d04b130e4b5dd8a48dd",
+        "c49897e9d7df6828e32224e761fbae0ee28c83c5",
         []
        ],
        "iframe-contents-meta-dynamic-append.html": [
-        "f3d584d3e45ee97cf58617bef5c090cff342d602",
+        "31a9a600f199eab979512863fa9f6c3c30beecd6",
         []
        ],
        "iframe-contents-meta-dynamic-name-change-after-body.html": [
-        "2e289976b7189017473ab8c980dba66a5c93f504",
+        "47fba40cdf43e4fa3e6c647d9fdea7893a712634",
         []
        ],
        "iframe-contents-meta-dynamic-name-change.html": [
-        "72f6bf51cc1b56e1b478a8c9a443438a7efa68fc",
+        "840e50f2b952076308d7ce0b313d19a2f1cfeaf2",
         []
        ],
        "iframe-contents-meta-dynamic-remove.html": [
-        "7bd95d8bc6a425421c86fa6c0b83d0a3ff067f5f",
+        "aa5267dc05688a85169f18fe326d3b239374fa81",
         []
        ],
        "iframe-contents-meta-dynamic-write.html": [
-        "46b0337b5fc934b2a5c67a2a96aba473ad445318",
+        "f12416e392cf9688ad6425516f6e2bc18f377791",
         []
        ],
        "iframe-contents-meta-in-body.html": [
-        "22420c282e39a5e8c23fb3b043b128eada3be238",
+        "fce861a8639d825695a8801cee1c51f271539968",
         []
        ],
        "iframe-contents-navigation.html": [
-        "c57ae1beca080958b113ab6f7dc91ab74e7984b0",
+        "478f732e98c37f7450bff2571656339609518692",
         []
        ],
        "iframe-contents-request-resize-error.html": [
@@ -429834,11 +431422,11 @@
         []
        ],
        "iframe-contents-request-resize.html": [
-        "70af9ed6900cd427498fc82161d2686de1316582",
+        "afbcb763a13711dfa1c3687e45296021cfc14443",
         []
        ],
        "iframe-contents-slow.html": [
-        "7fc2a8ad793d05caba64e1bc4eaff51e78343226",
+        "56100223d10a598d421a1dfce100ac8d51331191",
         []
        ],
        "iframe-contents-unsized.html": [
@@ -429846,7 +431434,7 @@
         []
        ],
        "iframe-contents.html": [
-        "ed58a691b6fec4262b538b53dfc1146c0e6190d0",
+        "67624bae4e24686b8c7b7d0dd2509452c02399a1",
         []
        ],
        "slow-image.py": [
@@ -429888,6 +431476,10 @@
        "76e2bc35cb78208bfa193d6b3d7a6d5ce501a73c",
        []
       ],
+      "block-height-011-expected.txt": [
+       "817ab072bc69488248df9b0276fcbf646df2a7db",
+       []
+      ],
       "stretch-anonymous-block-001-ref.html": [
        "14423d71465da2ebc31a93d76e500a57c95d74c1",
        []
@@ -430712,7 +432304,7 @@
         []
        ],
        "overflow-hidden-table-ref.html": [
-        "fca7b4196cbc9a38ca01fc116e0c758c1a8e041f",
+        "cd36ebce71f0d70cf450653cd4a9942fc10d2d8e",
         []
        ]
       },
@@ -434222,6 +435814,10 @@
        "text-transform-upperlower-106-ref.html": [
         "cc3dad35de7e7ee170b620eb5b658fd915aa9bc6",
         []
+       ],
+       "text-transform-upperlower-108-ref.html": [
+        "8b7489386696f301bdee4ad4a1f9d707d47b18bf",
+        []
        ]
       },
       "text-transform-capitalize-033-ref.html": [
@@ -435403,6 +436999,10 @@
         "59351f8bed45ea5f29dcfd1d345a0a6ab49f57c9",
         []
        ],
+       "text-decoration-inset-invalidation-ref.html": [
+        "d7af699ef97093179593104bcde842489ff9636a",
+        []
+       ],
        "text-decoration-invalidation-dashed-ref.html": [
         "64356838638ce794bbf408351606bb68398bbf4c",
         []
@@ -435430,14 +437030,6 @@
        "95f3bdc59f8737252f7f81013b4c292acca49a87",
        []
       ],
-      "text-decoration-inset-computed-expected.txt": [
-       "62bccdd13ade9bdf7159e882a89c357f9c45cb34",
-       []
-      ],
-      "text-decoration-inset-valid-expected.txt": [
-       "b79d489a0dc52d9281ee1be76bbbb4f5a3140be5",
-       []
-      ],
       "text-emphasis-position-valid-expected.txt": [
        "08d5ae4e8f6cb0fd5794ccdb719f90ee41c74a15",
        []
@@ -435604,6 +437196,22 @@
        "2433b96db7652464e4094b5981a2bc941594cea2",
        []
       ],
+      "text-decoration-inset-percentage-slice-multiline-ref.html": [
+       "3ac42704c05509bc6ae07898043a6d296b0dcd9b",
+       []
+      ],
+      "text-decoration-inset-percentage-slice-ref.html": [
+       "04c43d55866af917cfd8295b0c76d78edcac4088",
+       []
+      ],
+      "text-decoration-inset-svg-fragment-001-ref.html": [
+       "d9cb314998e40db1023b61be805ba3ec335eff81",
+       []
+      ],
+      "text-decoration-inset-svg-fragment-002-ref.html": [
+       "c912b3bb2072c2a896c8c53bdfcfc75c1891080a",
+       []
+      ],
       "text-decoration-line-010-ref.xht": [
        "6bcba28f191d03b2106131f27c7a2f88667cc9ca",
        []
@@ -438300,6 +439908,30 @@
        "6da3e41f227f0c57b2e95c576cfabf14580de94a",
        []
       ],
+      "cssKeywordValue-value.any.serviceworker-expected.txt": [
+       "ca14527e1597ac5c03a75c9d18d2abaa99339ebc",
+       []
+      ],
+      "cssKeywordValue-value.any.sharedworker-expected.txt": [
+       "ca14527e1597ac5c03a75c9d18d2abaa99339ebc",
+       []
+      ],
+      "cssKeywordValue-value.any.worker-expected.txt": [
+       "ca14527e1597ac5c03a75c9d18d2abaa99339ebc",
+       []
+      ],
+      "cssKeywordValue.any.serviceworker-expected.txt": [
+       "4e869bd7439bfe67c9ac7126c87c420b7afd3e2c",
+       []
+      ],
+      "cssKeywordValue.any.sharedworker-expected.txt": [
+       "4e869bd7439bfe67c9ac7126c87c420b7afd3e2c",
+       []
+      ],
+      "cssKeywordValue.any.worker-expected.txt": [
+       "4e869bd7439bfe67c9ac7126c87c420b7afd3e2c",
+       []
+      ],
       "cssLCH-expected.txt": [
        "7755e105d55a8d7b2e87b8cd56ae2ebd2690f2fc",
        []
@@ -438324,13 +439956,109 @@
        "889da3f7b9f882cadd25bec2ddc4e7c50d3dd70b",
        []
       ],
+      "cssUnparsedValue-empty.any.serviceworker-expected.txt": [
+       "939eaa1e222fe2f2e6fde681e291b18bdfaf8a21",
+       []
+      ],
+      "cssUnparsedValue-empty.any.sharedworker-expected.txt": [
+       "939eaa1e222fe2f2e6fde681e291b18bdfaf8a21",
+       []
+      ],
+      "cssUnparsedValue-empty.any.worker-expected.txt": [
+       "939eaa1e222fe2f2e6fde681e291b18bdfaf8a21",
+       []
+      ],
       "numeric-objects": {
+       "add-two-types.tentative.any.serviceworker-expected.txt": [
+        "5e991ca488a9f14ffe05b5e3efcbb0c434dd30eb",
+        []
+       ],
+       "add-two-types.tentative.any.sharedworker-expected.txt": [
+        "83f2c550e7938d9bdd8a3d39e95b9796ca931236",
+        []
+       ],
+       "add-two-types.tentative.any.worker-expected.txt": [
+        "83f2c550e7938d9bdd8a3d39e95b9796ca931236",
+        []
+       ],
+       "arithmetic.tentative.any.serviceworker-expected.txt": [
+        "5a1e68763a53f7d0c6cf3080e81ccc1ae3d19e9b",
+        []
+       ],
+       "arithmetic.tentative.any.sharedworker-expected.txt": [
+        "83f2c550e7938d9bdd8a3d39e95b9796ca931236",
+        []
+       ],
+       "arithmetic.tentative.any.worker-expected.txt": [
+        "83f2c550e7938d9bdd8a3d39e95b9796ca931236",
+        []
+       ],
+       "create-a-type.tentative.any.serviceworker-expected.txt": [
+        "8584774ba3125a4c2d82a7f1e830d23045765cdb",
+        []
+       ],
+       "create-a-type.tentative.any.sharedworker-expected.txt": [
+        "8584774ba3125a4c2d82a7f1e830d23045765cdb",
+        []
+       ],
+       "create-a-type.tentative.any.worker-expected.txt": [
+        "8584774ba3125a4c2d82a7f1e830d23045765cdb",
+        []
+       ],
+       "cssMathInvert-type.any.serviceworker-expected.txt": [
+        "9bff1a9c0bbcc0160c293d74b0636d270e47dfeb",
+        []
+       ],
+       "cssMathInvert-type.any.sharedworker-expected.txt": [
+        "9bff1a9c0bbcc0160c293d74b0636d270e47dfeb",
+        []
+       ],
+       "cssMathInvert-type.any.worker-expected.txt": [
+        "9bff1a9c0bbcc0160c293d74b0636d270e47dfeb",
+        []
+       ],
+       "cssMathNegate-type.any.serviceworker-expected.txt": [
+        "54daa0b6d4a4dfbf5517181a9688360bbbc85af4",
+        []
+       ],
+       "cssMathNegate-type.any.sharedworker-expected.txt": [
+        "54daa0b6d4a4dfbf5517181a9688360bbbc85af4",
+        []
+       ],
+       "cssMathNegate-type.any.worker-expected.txt": [
+        "54daa0b6d4a4dfbf5517181a9688360bbbc85af4",
+        []
+       ],
+       "cssnumericvalue-multiply-two-types.tentative.any.serviceworker-expected.txt": [
+        "4d9a56e409b3da1e086e579ccde42a036d49bb5a",
+        []
+       ],
+       "cssnumericvalue-multiply-two-types.tentative.any.sharedworker-expected.txt": [
+        "4d9a56e409b3da1e086e579ccde42a036d49bb5a",
+        []
+       ],
+       "cssnumericvalue-multiply-two-types.tentative.any.worker-expected.txt": [
+        "4d9a56e409b3da1e086e579ccde42a036d49bb5a",
+        []
+       ],
        "resources": {
         "testhelper.js": [
          "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
          []
         ]
-       }
+       },
+       "to.tentative.any.serviceworker-expected.txt": [
+        "ad80abc2eeeaa0c5bdb0bbdfddfa1f9b600d8e57",
+        []
+       ],
+       "to.tentative.any.sharedworker-expected.txt": [
+        "ad80abc2eeeaa0c5bdb0bbdfddfa1f9b600d8e57",
+        []
+       ],
+       "to.tentative.any.worker-expected.txt": [
+        "ad80abc2eeeaa0c5bdb0bbdfddfa1f9b600d8e57",
+        []
+       ]
       }
      },
      "the-stylepropertymap": {
@@ -438460,7 +440188,7 @@
         []
        ],
        "list-style-type-expected.txt": [
-        "b35fd3e427c7f36cf163f96859dc39bb011bd74c",
+        "0c6d686516888db23d80ed483d6bef8b5ab4a800",
         []
        ],
        "logical-expected.txt": [
@@ -439220,6 +440948,50 @@
        "c3dcd2d599300aa7684436ce66aef24a242fbd64",
        []
       ],
+      "text-overflow-string-009-ref.html": [
+       "3a3b90047ef42c8a4927f5f2b2018f143a4780a3",
+       []
+      ],
+      "text-overflow-string-010-ref.html": [
+       "2a76f91ed1324a7d312aa86d5bbf1dd08849ad7a",
+       []
+      ],
+      "text-overflow-string-011-ref.html": [
+       "0e89803a16ea946dde92c3a2b08e3fd440837557",
+       []
+      ],
+      "text-overflow-string-012-ref.html": [
+       "6134b3efd9f62ab9436a9b3ba083eba081801f58",
+       []
+      ],
+      "text-overflow-string-013-ref.html": [
+       "cc57fa1e64415dfb6836e40fbf1a81e4b172f0ad",
+       []
+      ],
+      "text-overflow-string-014-ref.html": [
+       "2d24573aaa9e5e5fbb9e8fb18d21f6c559785f13",
+       []
+      ],
+      "text-overflow-string-015-ref.html": [
+       "3a70eecf878bfcd1796483baf87d2712ad093e7b",
+       []
+      ],
+      "text-overflow-string-016-ref.html": [
+       "1076f49397117f4b3eb47ab6cca1282edd853f66",
+       []
+      ],
+      "text-overflow-string-024-ref.tentative.html": [
+       "97a5a87438ddaa9c55d18926a3aeda8f996248a9",
+       []
+      ],
+      "text-overflow-string-025-ref.tentative.html": [
+       "ae378c472c191057b83f01546005a764a3f0b0d3",
+       []
+      ],
+      "text-overflow-string-026-ref.tentative.html": [
+       "594a188b001c2a8393ba2e8d0ab8bdbe97f20332",
+       []
+      ],
       "transparent-accent-color-001-ref.html": [
        "91a9600e180cd9636b2ec3aa0ad63f79bbc334b5",
        []
@@ -440509,10 +442281,6 @@
       "66ce81a4847b77d8edfc6e6bf5e3527c5dc4eae4",
       []
      ],
-     "random-in-container-query.tentative-expected.txt": [
-      "0842b7d40383937ea899510960cf67a3bc826b69",
-      []
-     ],
      "random-item-in-keyframe-expected.txt": [
       "af73204343bf728ebb7b31a38ac3dc85cf37f034",
       []
@@ -445246,7 +447014,7 @@
       []
      ],
      "wm-propagation-body-contain-root-ref.html": [
-      "150eba8bc932dfa079509cb422a4212a7a141238",
+      "9e97ee034fd5aa90fb1c43bb544da2dfa0b8991c",
       []
      ],
      "wm-propagation-body-dynamic-change-002-ref.html": [
@@ -445348,7 +447116,7 @@
       []
      ],
      "getComputedStyle-pseudo-expected.txt": [
-      "13de9eafcd005c67b5287112d9565a8f278ccf0d",
+      "31c484e19e0f35827c65a4dbfdf9b3c932a68122",
       []
      ],
      "getComputedStyle-pseudo-picker-expected.txt": [
@@ -445641,46 +447409,10 @@
       "92f27b0235a396c58427dde8eecef913832e0a26",
       []
      ],
-     "cssom-geometryutils-convertPointFromNode-expected.txt": [
-      "f8e2dd278fa3f20384e578f6f2086634e847a9e1",
-      []
-     ],
-     "cssom-geometryutils-convertQuadFromNode-expected.txt": [
-      "aba126ffe9d46195511fbcfec50ef64633baf137",
-      []
-     ],
-     "cssom-geometryutils-convertRectFromNode-expected.txt": [
-      "5fb822b48fccb7e7dd1af97bf320315ca1f5af35",
-      []
-     ],
-     "cssom-geometryutils-element-types-expected.txt": [
-      "8a1f8a2f9622f3836a83bb0b8984ae752cfd45c6",
-      []
-     ],
-     "cssom-geometryutils-getBoxQuads-options-expected.txt": [
-      "b532e9093d9573a8818119af374002b15460a128",
-      []
-     ],
-     "cssom-geometryutils-special-cases-expected.txt": [
-      "0f99730cf4947315192417b1ee4f2f957b4be66d",
-      []
-     ],
-     "cssom-geometryutils-transforms-expected.txt": [
-      "cf44b05cc07de5b7d09a7d3d7f2d0a1ef4742c95",
-      []
-     ],
      "cssom-getBoundingClientRect-vertical-rl-ref.html": [
       "0b68c4f210c95dde58ffac2442472321ef6480a0",
       []
      ],
-     "cssom-getBoxQuads-001-expected.txt": [
-      "6a81f0c06ce084fc2987d52b376dc175d1f9e576",
-      []
-     ],
-     "cssom-getBoxQuads-002-expected.txt": [
-      "f851132cf774efec3b13fbfc05327cc8c52dd3f2",
-      []
-     ],
      "element-scroll-promise-after-removal-expected.txt": [
       "07014b65d22ab525f13fd316e87ec4ab5cc699c5",
       []
@@ -445690,7 +447422,7 @@
       []
      ],
      "idlharness-expected.txt": [
-      "8921ecef4981e47433bdf12517e83dec8fd8a578",
+      "81fcd31563673d3b8054ac743c3d97c3d5f900a2",
       []
      ],
      "iframe.html": [
@@ -445716,6 +447448,14 @@
        "ba986ef3f568d4971eb4e84c4faaeae6e276b975",
        []
       ],
+      "geometry-utils-cross-origin-inner.html": [
+       "8cab0b4a61d3ecd6bc981628f5672346bea7e08d",
+       []
+      ],
+      "geometry-utils-cross-origin-middle.html": [
+       "397d1dc01cba0a2b78382c54ca06bf13f9cec1c4",
+       []
+      ],
       "geometry-utils.js": [
        "ea94d35ccbac511bac2dbe0d98b7a707f161d093",
        []
@@ -447105,6 +448845,10 @@
       "a11aaf36f8c098fa34d9ce8daadd4e6c9ff81c73",
       []
      ],
+     "svg-mutation-fedropshadow-stddeviation-ref.html": [
+      "8cfd3db13ae7b775a27d15e406cb8ad10db2c059",
+      []
+     ],
      "svg-unknown-input-ref.html": [
       "1fff2a6175cfe1956ff2c451aece45ec73345d81",
       []
@@ -447371,6 +449115,10 @@
       "ead5dfb8cdd6e3aaa1458e533acc8ec9a6be186d",
       []
      ],
+     "offset-path-ray-sides-fourth-quadrant-ref.html": [
+      "35f3fcac7b945a195ac41367428c4fc9736a9480",
+      []
+     ],
      "offset-path-shape-circle-001-ref.html": [
       "6d6b64d32e3c622be1b27a9dae4c7293207fcab3",
       []
@@ -450173,10 +451921,6 @@
       "3c6c50317988712fa64d26da8d31d4f91c498f2c",
       []
      ],
-     "ParentNode-querySelector-escapes-expected.txt": [
-      "bb0e5d80a38ff253b2a8a1b6f93a3b6d1e003e1f",
-      []
-     ],
      "ParentNode-replaceChildren-expected.txt": [
       "84eede74d5ca5d97ecd79c5a653a73e9d7abd32d",
       []
@@ -450522,10 +452266,6 @@
      "7e8fec9af5c13ded8bd77207e1d4655792533c22",
      []
     ],
-    "fn-id-expected.txt": [
-     "73400958f9f95edd164122bd2cf423bc794cdcc8",
-     []
-    ],
     "helpers.js": [
      "0805bd682b48461588250a22018a6bd90990d002",
      []
@@ -451177,6 +452917,10 @@
      ]
     },
     "plaintext-only": {
+     "backspace-end-of-plaintext-only-pre-ref.html": [
+      "357c82c0d2ad20d83d23383df869c1f6b3fbf30b",
+      []
+     ],
      "insertHTML_white-space=normal-expected.txt": [
       "71f15ae8eaeaa37ff0a4893fa999a9cc91a5e95e",
       []
@@ -453040,6 +454784,10 @@
      "451dc5a82ab29284469e69b465cf42db7a5adbb2",
      []
     ],
+    "filesystementry-getParent-expected.txt": [
+     "6b032209bb15cc9f15940e4ce62b00c96ab6534b",
+     []
+    ],
     "idlharness.window-expected.txt": [
      "075fab0e1a149bf06841c73a189cbe26152276b9",
      []
@@ -453093,6 +454841,10 @@
     "support.js": [
      "1cf3ad95b9aeabfb174221729b0e495504adeccf",
      []
+    ],
+    "webkitGetAsEntry-expected.txt": [
+     "e79594a3f3d64e0e3cc8ba1d3788ab7216c1ab7e",
+     []
     ]
    },
    "event-timing": {
@@ -454830,7 +456582,7 @@
       ],
       "destination": {
        "fetch-destination.https-expected.txt": [
-        "ec3b5ba00e023b90bbaf0d60b1865d4289c3e9d9",
+        "4113d917ec29f8b17111214ae94bd2074d937657",
         []
        ],
        "resources": {
@@ -455260,11 +457012,11 @@
       []
      ],
      "dictionary-fetch-with-link-connect-src-nonce.tentative.https.html.headers": [
-      "97f2d0d2c018dd6accc36aaba7a3bc85273a57bc",
+      "df072f9bbd11f7507efa910080767f3073823df4",
       []
      ],
      "dictionary-fetch-with-link-connect-src.tentative.https.html.headers": [
-      "40a6b3fca7039491b30ec8f9005530e0d75d2650",
+      "b621e2056f426222554eec11013828577b16b2a1",
       []
      ],
      "dictionary-fetch-with-link-header-in-early-hints.tentative.https.h2.html": [
@@ -455350,7 +457102,11 @@
       ],
       "static": {
        "Makefile": [
-        "03b53d9e0a4c563ce21eb48ac44d72dd5508209a",
+        "2aba01f04b7bd9c1b76041810f231d01b96d8df1",
+        []
+       ],
+       "dictionary-hashes.js": [
+        "e1439585be9ddbf31b673b848411e8970faf98aa",
         []
        ],
        "image-001.png": [
@@ -458743,6 +460499,24 @@
        []
       ]
      },
+     "cjk-kr": {
+      "LICENSE.txt": [
+       "d952d62c065f3f35fb83a173496e90b21525aef3",
+       []
+      ],
+      "NotoSansCJKkr-Regular-subset.otf": [
+       "912604cc5b4170b3fba9217446d98873a8cc7b78",
+       []
+      ],
+      "README.md": [
+       "97b699b79f133349103b375ffaff76638b8c5f19",
+       []
+      ],
+      "subset.sh": [
+       "44c994e4cdc5944f45c15e277296407df503ef09",
+       []
+      ]
+     },
      "color-emoji": {
       "LICENSE.txt": [
        "d952d62c065f3f35fb83a173496e90b21525aef3",
@@ -458935,7 +460709,7 @@
      []
     ],
     "forced-colors-mode-43-ref.html": [
-     "0b98661896f6af563d4eeaf525b74bab09e6bdbc",
+     "5fade6b95ebf7e78f513e87015d5444b93892799",
      []
     ],
     "forced-colors-mode-44-ref.html": [
@@ -458943,7 +460717,7 @@
      []
     ],
     "forced-colors-mode-45-ref.html": [
-     "c7d535c5278837fad2216965ec19b2244a392337",
+     "499778394c06ba873d3f20735eb40ed24bf356e2",
      []
     ],
     "forced-colors-mode-46-ref.html": [
@@ -459444,6 +461218,10 @@
      "9fa0a4ac9a8a161aa01a9f9f70d70499f606a25a",
      []
     ],
+    "getCurrentPosition-accuracyMode.https-expected.txt": [
+     "dd8c8401daed416637061deae4454fc5c3b86dee",
+     []
+    ],
     "resources": {
      "iframe.html": [
       "25cf3d92f3942738686a267c22d95d4a6464af7e",
@@ -459489,31 +461267,15 @@
      []
     ],
     "global_privacy_control.testdriver-expected.txt": [
-     "add9ea5e5dca94ad3d54465d71e5665c8c9bac88",
-     []
-    ],
-    "idlharness.any-expected.txt": [
-     "caaf4902ddfeebe5df5fed93110ece3f4563fff7",
-     []
-    ],
-    "idlharness.any.serviceworker-expected.txt": [
-     "93db3aa4f89a030d0cc3db53a46effc04b73d8dc",
-     []
-    ],
-    "idlharness.any.sharedworker-expected.txt": [
-     "93db3aa4f89a030d0cc3db53a46effc04b73d8dc",
-     []
-    ],
-    "idlharness.any.worker-expected.txt": [
-     "93db3aa4f89a030d0cc3db53a46effc04b73d8dc",
+     "39a6e39c7025d0d5cddb0ee5d47d2cb7ddeae8b2",
      []
     ],
     "navigator-globalPrivacyControl.https-expected.txt": [
-     "9f8824df3b98fc6f26addb3af9423e27a3cada90",
+     "b86a415aafbb0364dbcc613996ccd0ef66bca869",
      []
     ],
     "sec-gpc.https-expected.txt": [
-     "9f8824df3b98fc6f26addb3af9423e27a3cada90",
+     "85207c3f2245e7d0129f627012f956f4f6673d52",
      []
     ],
     "support": {
@@ -463652,6 +465414,14 @@
            "0368ae01896a2cadb6019ab2c1eb5346d9c3b764",
            []
           ],
+          "subframe-cross-origin-background-image-on-body.https.sub.html": [
+           "377692a43cbccb139810ac2b4a470dc7bee8f0b2",
+           []
+          ],
+          "subframe-cross-origin-background-image-on-html.https.sub.html": [
+           "cf24f0264b202fc4de732e598eba3eb315ee5ad4",
+           []
+          ],
           "subframe-cross-origin-background-image.https.sub.html": [
            "48c81b7ecf8b6affaa29fa36e8e4767e22e6917a",
            []
@@ -468812,7 +470582,7 @@
         []
        ],
        "022.html": [
-        "17e8e0c449b94056924d3372bec478dee5611ec7",
+        "b4b675b8cb56102034f60ca7d35c5e227eb2d896",
         []
        ],
        "helper-drag-me-input.xhtml": [
@@ -470946,7 +472716,7 @@
       },
       "the-select-element-0": {
        "listbox-abspos-descendant-ref.html": [
-        "c994fda2b20c9e750af78ed5a532f06d12b6d2cb",
+        "14a14db226ad182d87cfa3eeb3774c690d29d338",
         []
        ],
        "listbox-option-display-contents-ref.html": [
@@ -471211,6 +472981,10 @@
         "e465fd433c99a16c8312c98955da1afe190e596f",
         []
        ],
+       "table-border-attributes-computed-style-expected.txt": [
+        "c1b365a47de41840dc6bf5abdb192717459e0ad2",
+        []
+       ],
        "table-border-presentational-hints-ascii-case-insensitive-ref.html": [
         "c8d6a20726fd84900f192b34206954eeec57f61d",
         []
@@ -471267,6 +473041,10 @@
         "91400fcc1eafa8ab86a8254154c80286fca0cf2c",
         []
        ],
+       "table-rules-computed-style-expected.txt": [
+        "7da870a766182f5da2f675a4bf758eecb29c0e7d",
+        []
+       ],
        "table-width-150percent-ref.html": [
         "820c360e391694732cce6cac0648a04a0f89f64d",
         []
@@ -471368,7 +473146,7 @@
         []
        ],
        "fieldset-dynamic-pseudo-ref.html": [
-        "5bdf78db5573c0f67b9315b33d60ef7203091fe3",
+        "56cd532519c16ce564286bf5ba751d33b999e4ed",
         []
        ],
        "fieldset-foo-ref.html": [
@@ -472257,7 +474035,7 @@
        []
       ],
       "shadow-dom-expected.txt": [
-       "007c2f653e2f7ef0507d95e306152c9b2075c27c",
+       "546e71af1f604e841aa103995419100dcba860b1",
        []
       ],
       "text-control-flow-root-ref.html": [
@@ -474002,7 +475780,7 @@
         ],
         "support": {
          "parse-a-sizes-attribute.js": [
-          "62ad00a4682b269e52797f6e2ee8e46bf44d56ac",
+          "e5d9ab947b89184fecc2dc6e2093fad20d8defb1",
           []
          ],
          "sizes-iframed.sub.html": [
@@ -474216,6 +475994,10 @@
          "3f6079c5dc7fd912c9c7879a31dd2cd8daed4046",
          []
         ],
+        "form-with-submitter.js": [
+         "af01441476c88d8e9084c2b3097c42831fc83a37",
+         []
+        ],
         "form.html": [
          "8b16672d6b7b501e9db773666ad0f444af33e5ac",
          []
@@ -474578,7 +476360,7 @@
         []
        ],
        "dynamic-content-change-rendering-ref.html": [
-        "453bb70822f2d107705c999a8ae9282e4352bd68",
+        "73c2b159826d236328a963f62443c6e47fb34838",
         []
        ],
        "option-label-value.js": [
@@ -475729,7 +477511,7 @@
        []
       ],
       "bounded-sizes-reftest-ref.html": [
-       "5eddd96ec7910ec6c4cb816885f232b97bc3c647",
+       "57b63162d42319955e9e9eac1adaf903045feabb",
        []
       ],
       "geolocation-element": {
@@ -475860,12 +477642,8 @@
         []
        ]
       },
-      "invalid-type-reftest-ref.html": [
-       "01158fa255d428f701a4909d2526e14a9beadeaa",
-       []
-      ],
       "large-min-size-reftest-ref.html": [
-       "b4773653732a632116edfd24a163a67a657f1c13",
+       "96d19ca94261f90a35c3a782314f05fd495c823a",
        []
       ],
       "permission-icon": {
@@ -475898,7 +477676,7 @@
         []
        ],
        "standard-camera-usermedia-element-ref.html": [
-        "eb6da767499cabca444dbda726b053466e5f4905",
+        "ba965ce04259e39c041b26eeadd13efca39cbec6",
         []
        ]
       },
@@ -475923,7 +477701,7 @@
        []
       ],
       "unbounded-width-with-border-reftest-ref.html": [
-       "a7d445e1e119c4bad12a7d732985643280ad977b",
+       "d34e640bf63e7bed104c4a811c95c06571e9bb06",
        []
       ],
       "usermedia": {
@@ -475992,6 +477770,10 @@
        "2dc0d558b634279a09703798e9a531748215a563",
        []
       ],
+      "popover-hint-dialog-dismisses-unrelated-auto-expected.txt": [
+       "afd4ed3678c8ea72565affb1c5f678b0fdb65873",
+       []
+      ],
       "popover-img-backdrop-ref.html": [
        "7ee3b4db5007148e1888e6d1691084e3062a6def",
        []
@@ -478682,10 +480464,6 @@
          "1c930b1631d9abf63776554d9a6d9567c14c773f",
          []
         ],
-        "picture-source-br-img.tentative.sub-expected.txt": [
-         "6dee534d252a1cb57aeb23e1d1f5ba3b02639dde",
-         []
-        ],
         "svg-image-href.tentative.sub-expected.txt": [
          "ff9f8d16bec04a225aa4d029a10c227319a1d657",
          []
@@ -478724,10 +480502,6 @@
          "f20ae52d907fba2b3741aa1262e13dc2dfc20c5f",
          []
         ],
-        "picture-source-br-img.tentative-expected.txt": [
-         "e37ab68ada4ba20a57e395058cda43ce7c1d9caa",
-         []
-        ],
         "resources": {
          "base-href-script-src-framed.sub.html": [
           "c0913c261e01e1df835f0b63fa0e4f076278c908",
@@ -482702,6 +484476,10 @@
      "fdc4a4671548679b79febaf12a684cabc0605e44",
      []
     ],
+    "camera-element.tentative.idl": [
+     "eb512be2f34acf6d45c22a7c822f3d7592a11761",
+     []
+    ],
     "capture-handle-identity.idl": [
      "37b2c61dac94c1d571aa2f6afad24ca01696478b",
      []
@@ -483230,6 +485008,10 @@
      "68c891cdc918e7468de8add96acce35d6d63102f",
      []
     ],
+    "microphone-element.tentative.idl": [
+     "56ce0b8d0eeee437691b6011865cf176551952c2",
+     []
+    ],
     "model-element.idl": [
      "5152cfaa47cc0eb5c39a4d046176822816b7a55b",
      []
@@ -483470,6 +485252,10 @@
      "7f7ea73a574055ff1a41d6946ccc2a702f1dee8f",
      []
     ],
+    "sub-apps.tentative.idl": [
+     "cc5f00e61af3064d2d32edee62418c443ccecc71",
+     []
+    ],
     "svg-animations.idl": [
      "c77c70ad61afe21c35fbf35db979a745d075851b",
      []
@@ -483523,7 +485309,7 @@
      []
     ],
     "usermedia-element.tentative.idl": [
-     "f6b8d08e1d226e9365558594d2c832fc942bbc3d",
+     "b8a0645b59deebee65fde4a8d011c8ee45bdc226",
      []
     ],
     "vibration.idl": [
@@ -484008,6 +485794,10 @@
      "3377ddc5dfd47895a23cb5770299eaa674b232cb",
      []
     ],
+    "animated-img-timeout-ref.html": [
+     "a495de1e0e98aa5216181d5751e9ca83828769de",
+     []
+    ],
     "basic-decode-reftest-ref.html": [
      "5ac6ca6a7a3a79a9307e756fd189057713c064cd",
      []
@@ -484040,6 +485830,22 @@
      "f3c4cab5a3f879801595c9c0cc0e73778b6a1331",
      []
     ],
+    "css-background-image-ref.html": [
+     "617df03c24bfbea266eaf06e74d45a51826fe39d",
+     []
+    ],
+    "css-before-content-ref.html": [
+     "d5072fd915131f4fef0ff611e9d7bc58f1c0f627",
+     []
+    ],
+    "css-image-set-ref.html": [
+     "2c568836421fdb563b14ae9a8ec0e335116079eb",
+     []
+    ],
+    "css-list-style-image-ref.html": [
+     "afe2051a0a252787bb082c9efa39eacaeffc8a36",
+     []
+    ],
     "gray-decode-reftest-ref.html": [
      "82421ddeb2f0c290fba373b8b14a64831e608cf2",
      []
@@ -484161,6 +485967,10 @@
       "dba1fdb03bc443da9f4c8142498b7066436333e6",
       []
      ],
+     "animated-red-green.jxl": [
+      "1e458a89c69ef3423cbe7bc3a7633a4cb451db12",
+      []
+     ],
      "animation-1loop.jxl": [
       "a29a4aacbd5db612741089e9ec1132a4194eacae",
       []
@@ -484609,6 +486419,10 @@
       "cd600254805570deab8447ea843657d7f268b7c5",
       []
      ],
+     "iframe-stores-image-entry.html": [
+      "58468d0bc89424d30f3a2f9e79f2e7f6a6444ff7",
+      []
+     ],
      "invisible-images.js": [
       "bad078e35f0cd124eb688fe5937ad2686d7c5bbe",
       []
@@ -484676,7 +486490,7 @@
     }
    },
    "lint.ignore": [
-    "b9665c5ab9791998203b8ff853ed06832e40923a",
+    "7fa400b28738ce73d30fcdf77c4eb31a0a0a3ad4",
     []
    ],
    "loading": {
@@ -484986,7 +486800,7 @@
       []
      ],
      "loaf-congested-moment-worker.js": [
-      "74974b8636929baa914d4c8e4bc9aca7a44749c4",
+      "6aaa4f270129ad119224bd99dec1e4830fb41faf",
       []
      ],
      "loaf-in-microtask-after-callback.js": [
@@ -486616,6 +488430,10 @@
      "023a96bf645b4831ff082de14fd44456d0ae3ab8",
      []
     ],
+    "iamf_alternating_sine_waves_stereo.mp4": [
+     "d7b1e42e3ed0ff51058b6034f2cad1f0486bca85",
+     []
+    ],
     "movie_300.mp4": [
      "cf59777fb98fab2e46c385fa537b2afddfe138a6",
      []
@@ -486824,7 +488642,7 @@
       []
      ],
      "mediasource-worker-detach-element.js": [
-      "54b1d815f2529932a153440db3cb3f1033fd84a6",
+      "ed6c1766bd206517b6f7eadefa4051aeece51e51",
       []
      ],
      "mediasource-worker-duration.js": [
@@ -486900,6 +488718,10 @@
      "b28aa90f1f3d8135a7ea86b82820ffd414451920",
      []
     ],
+    "mediasource-iamf-playback-expected.txt": [
+     "0ee8ae6c911cd0930d464abcb37e2a3ab9c40d9b",
+     []
+    ],
     "mediasource-util.js": [
      "00105238e6bf3e37b3acccc1f78db21809b07fac",
      []
@@ -490028,11 +491850,11 @@
       []
      ],
      "permissions-policy-clipboard-read.html": [
-      "10fc45fd933ef0f77e5d53d4fac9ec70d372ce48",
+      "e6de06a865469258474399030b843337d9c68592",
       []
      ],
      "permissions-policy-clipboard-write.html": [
-      "7eb96e3db011762599b03c102505110bda37285d",
+      "0732104515cc695c7721258743b994b79274b9d6",
       []
      ],
      "permissions-policy-compute-pressure.html": [
@@ -490762,6 +492584,14 @@
      "1c9f0ec53cb117b6e6d9e600c3a8be55c0368656",
      []
     ],
+    "modulepreload-sri-expected.txt": [
+     "bc420a9966ffd94321c29e7861965303ce9b65c2",
+     []
+    ],
+    "modulepreload-sri-importmap-expected.txt": [
+     "05ff8bf75cb191a90a3aa1c0f263b04d12a7f1e6",
+     []
+    ],
     "modulepreload-text-expected.txt": [
      "88b2a8bb001059eec8b132bcb84b149c5ce5e580",
      []
@@ -490947,6 +492777,14 @@
       "d0402f490380b23a1c12b9fea045fd8d61a5a642",
       []
      ],
+     "preload-module-helper.js": [
+      "f498e256a02e428e4809b45bd25d00c5813b1392",
+      []
+     ],
+     "preload-module.py": [
+      "78a0a4dd045f956b317b142917c028c199650b6e",
+      []
+     ],
      "preload_helper.js": [
       "5b7a6eb52ba0ef89834d040d4dfb95f299416489",
       []
@@ -491152,6 +492990,18 @@
      "2cd989a70bf058ae50df7effa16f94a912384ba3",
      []
     ],
+    "cross-origin-nested.tentative.https.sub.window-expected.txt": [
+     "0ddc44426162a926425e12533c033993cb5dfec8",
+     []
+    ],
+    "cross-origin-same-site-request.tentative.https.sub.window-expected.txt": [
+     "1eca82823aa54ff5109cfcb74197ef771662f948",
+     []
+    ],
+    "cross-origin-serviceworker.tentative.https.sub.window-expected.txt": [
+     "bdb92216f94c5d7ac22404c0dd0c6833a4e6878c",
+     []
+    ],
     "idlharness.https.any-expected.txt": [
      "fb29dd0628714e42d2205912b6577c98746bb9af",
      []
@@ -491181,6 +493031,18 @@
      []
     ],
     "resources": {
+     "cross-origin-nested-child.sub.html": [
+      "b353030a74012b98799d8d044c2757c5e66ca1b0",
+      []
+     ],
+     "cross-origin-nested-parent.sub.html": [
+      "5d95e1dbddfb872d2d66fab39ee5e381558d403f",
+      []
+     ],
+     "cross-origin-serviceworker-iframe.sub.html": [
+      "d6b61be73fee2ddadbb7be12efe16aa39a008704",
+      []
+     ],
      "ece.js": [
       "f8c330e7f012679dadd1d390728c201aeb46192f",
       []
@@ -491189,6 +493051,18 @@
       "c7555531f7593cea06d2a4e361a8a7d819bdf9cf",
       []
      ],
+     "permission-helper.js": [
+      "27a16b7c4e092c3b7d87f07b0f1030cd9b5827a6",
+      []
+     ],
+     "permission-sw.js": [
+      "66782c6ce4ccb0fa9de713f21771747e5edca1db",
+      []
+     ],
+     "permission-worker.js": [
+      "a8214c40e687e9aa7975afa9ae90b933b07272df",
+      []
+     ],
      "vapid.js": [
       "ab8984199c573dabf733464f817934c5b0ad41b9",
       []
@@ -491197,6 +493071,10 @@
     "subscribe-with-faulty-applicationServerKey.https.window-expected.txt": [
      "8b8a46adc955448c0a260a2abc137f8dcbdf4b08",
      []
+    ],
+    "worker-subscribe.https.window-expected.txt": [
+     "fe10ed7ee0bdacccef1548265f34cec3365c6ad6",
+     []
     ]
    },
    "quirks": {
@@ -491935,7 +493813,7 @@
      []
     ],
     "OWNERS": [
-     "abc6202b656340edde54260daf0ea2b754cfca82",
+     "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
      []
     ],
     "README.md": [
@@ -493392,7 +495270,7 @@
       []
      ],
      "header-delay.h2.py": [
-      "27b6fd5bfbb70445454ed7ec0735b662b4c15091",
+      "913cb5c70e21525db361766da955b2ae123cd6e2",
       []
      ],
      "header-delay.py": [
@@ -493750,7 +495628,7 @@
       []
      ],
      "mock-subapps.js": [
-      "1712820c0513177465c212fd64513a97f9fa5ee6",
+      "900094b8262a2c715db31034f6b1f9234b3898ee",
       []
      ],
      "mock-textdetection.js": [
@@ -494243,6 +496121,10 @@
       "8c03d3e3350fe36498753af9021c04431738068d",
       []
      ],
+     "scroll-timeline-name-shadow-expected.txt": [
+      "cfc9228c4a89ef052a8b061696a2cf4caf9079f9",
+      []
+     ],
      "scroll-timeline-shorthand-expected.txt": [
       "b00d7672ca59c7cc64403d05abc8f258c96b8032",
       []
@@ -494261,6 +496143,10 @@
       "720a801407f77c43d623a795ef3e1998b117aa5a",
       []
      ],
+     "view-timeline-name-shadow-expected.txt": [
+      "b72a07e89114b586289c4c67c75cfee555ae9a81",
+      []
+     ],
      "view-timeline-pseudo-on-scroller-expected.txt": [
       "9a02af2f2900fcdc02a24460814da45bc90ddea9",
       []
@@ -494309,6 +496195,10 @@
       "32106d4f68cb8bbaeba494e731bbfbf0327f6f73",
       []
      ],
+     "view-timeline-inset-expected.txt": [
+      "ea6b70d4cfd686e26ff5857f3e950e7114bac795",
+      []
+     ],
      "view-timeline-snapport-expected.txt": [
       "4e90b2c13c4cc6cec9561b5ded1d9d0e53e9e940",
       []
@@ -494674,6 +496564,10 @@
      "modify-around-inline-element-boundary.tentative-expected.txt": [
       "e4e26eebf98326118a69111ff3e675eaca18e285",
       []
+     ],
+     "selection-outside-focused-editing-host.tentative-expected.txt": [
+      "733d1853ab293826e29b48c94a8fce16869f4cd8",
+      []
      ]
     },
     "extend.js": [
@@ -497031,7 +498925,7 @@
        []
       ],
       "object-image-is-not-intercepted-iframe.html": [
-       "5a20a58ab16906f42b05fd80ccf22024d59de279",
+       "e33d278ee2046a7f33b0432ad45972f049b5848e",
        []
       ],
       "object-is-not-intercepted-iframe.html": [
@@ -497352,6 +499246,10 @@
        "ec4c726331d66ca298726e45fc3573e86e227e22",
        []
       ],
+      "resource-timing-server-timing-worker.js": [
+       "86b96a8daa8f622dd4dc1d0e4eed6902737ad224",
+       []
+      ],
       "resource-timing-worker.js": [
        "b74e8cd6a236bd7c4477910a064f366e3da8470b",
        []
@@ -497454,6 +499352,10 @@
        "5ba99f075362ea30cb14ca4edf51af594d98917a",
        []
       ],
+      "server-timing.py": [
+       "c4ffcc9c1b43850aa4d102332e2f0451c2d0312d",
+       []
+      ],
       "service-worker-csp-worker.py": [
        "35a46964a7871a7ab85d4b0b181e5ff2e3f496fc",
        []
@@ -498848,8 +500750,28 @@
        "634b13160a43cd76e9a468153a8846a519f9dcad",
        []
       ],
+      "cross-origin-iframe-nesting-src-no-slm-prerender.html": [
+       "07773435b70a1e73b1c0a6e85769d5e13cdf93c2",
+       []
+      ],
+      "cross-origin-iframe-nesting-src-prerender.html": [
+       "428c1a40ea919a8df5d17845ce327087770a2497",
+       []
+      ],
+      "cross-origin-iframe-nesting-src-prerender.html.headers": [
+       "df5bb3bd34ae4dc970f1767a69eecd507c69d953",
+       []
+      ],
+      "cross-origin-iframe-prerender-nesting.html": [
+       "b32b1e8237c9a7f05e9e0fcbd7143cc1f37cb1c3",
+       []
+      ],
+      "cross-origin-iframe-prerender-nesting.html.headers": [
+       "df5bb3bd34ae4dc970f1767a69eecd507c69d953",
+       []
+      ],
       "cross-origin-iframe-prerender.html": [
-       "251f492b3c5bb2fdd0a41e920c403e67862f18c4",
+       "39032efaaf7ef53a8e0c20409c1f1093811f9783",
        []
       ],
       "cross-origin-iframe-prerender.html.headers": [
@@ -498857,7 +500779,7 @@
        []
       ],
       "cross-origin-iframe-src-prerender.html": [
-       "a421e1c881dc2a4df2623d47e4047ecbf0ebd163",
+       "8d6b0d23ff0447110fd4fe2e020fa84504379e31",
        []
       ],
       "cross-origin-iframe-src.html": [
@@ -499535,7 +501457,7 @@
        []
       ],
       "speculation-measurement-utils.js": [
-       "98d2ab79f4c7d5d160f8bc21a2d92361a36de87d",
+       "368254e56754809a2307d46550fab44a24c9434b",
        []
       ]
      }
@@ -500152,9 +502074,17 @@
      "0b17525fbd95c3d0caf41eb327cf00c9613f6ff1",
      []
     ],
+    "__dir__.headers": [
+     "252f686b40e6c99b23019b1ea922cef0ed4885f4",
+     []
+    ],
+    "disabled-by-permissions-policy.tentative.https.html.headers": [
+     "13d17d8e9ad3b3eafda3a603a8fece94c5218167",
+     []
+    ],
     "resources": {
      "subapps-helpers.js": [
-      "1b3533d79244dcd33585619cda0514c09da9d3b8",
+      "1e9fe7be9e17291c5f763f1e3642941161009a80",
       []
      ]
     }
@@ -500309,6 +502239,10 @@
       "d56c7b8cb737d21aa2df4a803fa333ad5ebd56ae",
       []
      ],
+     "animateMotion-rotate-angle-expected.txt": [
+      "fe940ff42f28be261bfaeeaf2a2d3d14c5918bb1",
+      []
+     ],
      "reference": {
       "green-100x100.svg": [
        "120941444a4898197d6b6001f9908a6cd48b62ba",
@@ -508248,7 +510182,7 @@
       },
       "browsing_context": {
        "__init__.py": [
-        "17119770cedf495c8e857eabf7f1df90e9083900",
+        "f9936db0edd98cd4904eda4e546a00d8fc6e1438",
         []
        ],
        "activate": {
@@ -509197,10 +511131,6 @@
         "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
         []
        ],
-       "back-expected.txt": [
-        "4238e891ca4795d31a5c4344bf5c89665f4fb3e7",
-        []
-       ],
        "conftest.py": [
         "bd5db0cfeba7bbc8aaf23c3151109cf48094ff09",
         []
@@ -509306,6 +511236,10 @@
         ]
        },
        "webauthn": {
+        "META.yml": [
+         "3dce173a504c035d36f1a0903d33fa10268909ed",
+         []
+        ],
         "__init__.py": [
          "584c1c9cd6f4df19f8a30874035a615acbe8bfb8",
          []
@@ -509508,10 +511442,6 @@
        "__init__.py": [
         "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
         []
-       ],
-       "get-expected.txt": [
-        "cf6e0af282781db1915ffd50a8d053ca3811f96c",
-        []
        ]
       },
       "get_title": {
@@ -509578,10 +511508,6 @@
        "__init__.py": [
         "2b20a8b5ee8a306650099f9a95ba761d29599bfc",
         []
-       ],
-       "navigate-expected.txt": [
-        "4238e891ca4795d31a5c4344bf5c89665f4fb3e7",
-        []
        ]
       },
       "new_session": {
@@ -509644,10 +511570,6 @@
        "__init__.py": [
         "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
         []
-       ],
-       "refresh-expected.txt": [
-        "4238e891ca4795d31a5c4344bf5c89665f4fb3e7",
-        []
        ]
       },
       "release_actions": {
@@ -509684,10 +511606,6 @@
        "__init__.py": [
         "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
         []
-       ],
-       "set-expected.txt": [
-        "a5b8e1c13359783df069b9bce16930a515d07c75",
-        []
        ]
       },
       "set_window_rect": {
@@ -509766,7 +511684,7 @@
         []
        ],
        "fixtures.py": [
-        "dad41f96384ede71f237204d2d9c03cd35fc9463",
+        "c79ed2811ae6be90af25dc6c7d95ca305f11b0e4",
         []
        ]
       },
@@ -511686,22 +513604,6 @@
      "b811c36d12145d164ee0b4b7ed67ce2183ee1169",
      []
     ],
-    "draining.https.any-expected.txt": [
-     "485ab6b37fa9476ebeaf73c715900def626d28a8",
-     []
-    ],
-    "draining.https.any.serviceworker-expected.txt": [
-     "485ab6b37fa9476ebeaf73c715900def626d28a8",
-     []
-    ],
-    "draining.https.any.sharedworker-expected.txt": [
-     "485ab6b37fa9476ebeaf73c715900def626d28a8",
-     []
-    ],
-    "draining.https.any.worker-expected.txt": [
-     "485ab6b37fa9476ebeaf73c715900def626d28a8",
-     []
-    ],
     "export-keying-material.https.any-expected.txt": [
      "86f315577c5e661ef6154ae6b2378337cf7f60c8",
      []
@@ -511793,19 +513695,19 @@
      []
     ],
     "idlharness.https.sub.any-expected.txt": [
-     "0d58167fe7714a2b7dfceaeab40adac691a58904",
+     "ac09231a591ee9203ff6b86336adccf2a9457d7d",
      []
     ],
     "idlharness.https.sub.any.serviceworker-expected.txt": [
-     "0d58167fe7714a2b7dfceaeab40adac691a58904",
+     "ac09231a591ee9203ff6b86336adccf2a9457d7d",
      []
     ],
     "idlharness.https.sub.any.sharedworker-expected.txt": [
-     "0d58167fe7714a2b7dfceaeab40adac691a58904",
+     "ac09231a591ee9203ff6b86336adccf2a9457d7d",
      []
     ],
     "idlharness.https.sub.any.worker-expected.txt": [
-     "0d58167fe7714a2b7dfceaeab40adac691a58904",
+     "ac09231a591ee9203ff6b86336adccf2a9457d7d",
      []
     ],
     "resources": {
@@ -517512,7 +519414,7 @@
       ]
      ],
      "filereader_abort.any.js": [
-      "c778ae55bb573bc2b7f8d5c1b76f9cd4c866d51f",
+      "2563dce5ec2d5089095582ccfe7f9fb5cf2383cd",
       [
        "FileAPI/reading-data-section/filereader_abort.any.html",
        {
@@ -517839,7 +519741,7 @@
       ]
      ],
      "url-in-tags-revoke.window.js": [
-      "1cdad79f7e34e080c62ba639f2f363efd4ed26a9",
+      "476daba6f1b38b6187bde1e052b4757174fd0a82",
       [
        "FileAPI/url/url-in-tags-revoke.window.html",
        {
@@ -517854,7 +519756,7 @@
       ]
      ],
      "url-in-tags.window.js": [
-      "8a722dcac82b66cb80e6d428afd6f15186e468b4",
+      "e14c41a5c9a8cdb4ff2f8a6f5299f8304da09226",
       [
        "FileAPI/url/url-in-tags.window.html",
        {}
@@ -535547,7 +537449,7 @@
       ]
      ],
      "cfrg_curves_bits_curve25519.https.any.js": [
-      "5684d7624076c70a769099d39a1ad68886cb4fa8",
+      "b425652088742bdb03d3ddac247577393da40581",
       [
        "WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve25519.https.any.html",
        {
@@ -535566,6 +537468,14 @@
          ],
          [
           "script",
+          "derive.js"
+         ],
+         [
+          "script",
+          "cfrg_curves.js"
+         ],
+         [
+          "script",
           "cfrg_curves_bits.js"
          ]
         ]
@@ -535589,6 +537499,14 @@
          ],
          [
           "script",
+          "derive.js"
+         ],
+         [
+          "script",
+          "cfrg_curves.js"
+         ],
+         [
+          "script",
           "cfrg_curves_bits.js"
          ]
         ]
@@ -535596,7 +537514,7 @@
       ]
      ],
      "cfrg_curves_bits_curve448.tentative.https.any.js": [
-      "5e482ef0b9d8041f0200301ff215bfa111a964df",
+      "cda5ba87fba056d098c3441eb1f1626ed639d738",
       [
        "WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any.html",
        {
@@ -535615,6 +537533,14 @@
          ],
          [
           "script",
+          "derive.js"
+         ],
+         [
+          "script",
+          "cfrg_curves.js"
+         ],
+         [
+          "script",
           "cfrg_curves_bits.js"
          ]
         ]
@@ -535638,6 +537564,14 @@
          ],
          [
           "script",
+          "derive.js"
+         ],
+         [
+          "script",
+          "cfrg_curves.js"
+         ],
+         [
+          "script",
           "cfrg_curves_bits.js"
          ]
         ]
@@ -535645,7 +537579,7 @@
       ]
      ],
      "cfrg_curves_keys_curve25519.https.any.js": [
-      "8bcc201d4e95ec15bcf6218ac37c5b2dd966a4f1",
+      "683ae21bac9f98297cc2cdad1681cccc1107bb2c",
       [
        "WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve25519.https.any.html",
        {
@@ -535664,6 +537598,14 @@
          ],
          [
           "script",
+          "derive.js"
+         ],
+         [
+          "script",
+          "cfrg_curves.js"
+         ],
+         [
+          "script",
           "cfrg_curves_keys.js"
          ]
         ]
@@ -535687,6 +537629,14 @@
          ],
          [
           "script",
+          "derive.js"
+         ],
+         [
+          "script",
+          "cfrg_curves.js"
+         ],
+         [
+          "script",
           "cfrg_curves_keys.js"
          ]
         ]
@@ -535694,7 +537644,7 @@
       ]
      ],
      "cfrg_curves_keys_curve448.tentative.https.any.js": [
-      "0ed3954ac200b53464ca1213c96341e0877dca8d",
+      "38a5ce1db5b24e76518ca242cd47735f32f02bc6",
       [
        "WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any.html",
        {
@@ -535713,6 +537663,14 @@
          ],
          [
           "script",
+          "derive.js"
+         ],
+         [
+          "script",
+          "cfrg_curves.js"
+         ],
+         [
+          "script",
           "cfrg_curves_keys.js"
          ]
         ]
@@ -535736,6 +537694,14 @@
          ],
          [
           "script",
+          "derive.js"
+         ],
+         [
+          "script",
+          "cfrg_curves.js"
+         ],
+         [
+          "script",
           "cfrg_curves_keys.js"
          ]
         ]
@@ -535841,7 +537807,7 @@
       ]
      ],
      "ecdh_bits.https.any.js": [
-      "58a0cecd5efed686be5d0792e8c16f4eb6acb51d",
+      "38058d6ce7231b709d66646a866dc46426c76a9c",
       [
        "WebCryptoAPI/derive_bits_keys/ecdh_bits.https.any.html",
        {
@@ -535856,6 +537822,18 @@
          ],
          [
           "script",
+          "ecdh_fixtures.js"
+         ],
+         [
+          "script",
+          "derive.js"
+         ],
+         [
+          "script",
+          "ecdh.js"
+         ],
+         [
+          "script",
           "ecdh_bits.js"
          ]
         ]
@@ -535875,6 +537853,18 @@
          ],
          [
           "script",
+          "ecdh_fixtures.js"
+         ],
+         [
+          "script",
+          "derive.js"
+         ],
+         [
+          "script",
+          "ecdh.js"
+         ],
+         [
+          "script",
           "ecdh_bits.js"
          ]
         ]
@@ -535882,7 +537872,7 @@
       ]
      ],
      "ecdh_keys.https.any.js": [
-      "6464dacfe3aa81463f3fa623460bd09179969efa",
+      "f9d0da921d52b1547521a958ff792b5e687b5a2a",
       [
        "WebCryptoAPI/derive_bits_keys/ecdh_keys.https.any.html",
        {
@@ -535897,6 +537887,18 @@
          ],
          [
           "script",
+          "ecdh_fixtures.js"
+         ],
+         [
+          "script",
+          "derive.js"
+         ],
+         [
+          "script",
+          "ecdh.js"
+         ],
+         [
+          "script",
           "ecdh_keys.js"
          ]
         ]
@@ -535916,6 +537918,18 @@
          ],
          [
           "script",
+          "ecdh_fixtures.js"
+         ],
+         [
+          "script",
+          "derive.js"
+         ],
+         [
+          "script",
+          "ecdh.js"
+         ],
+         [
+          "script",
           "ecdh_keys.js"
          ]
         ]
@@ -535923,7 +537937,7 @@
       ]
      ],
      "hkdf.https.any.js": [
-      "3879ddb14b903aa7ef95416de3f8f4c3bc940f6e",
+      "b283f2ba7920e64d253baded9a84e06207067a5a",
       [
        "WebCryptoAPI/derive_bits_keys/hkdf.https.any.html?1-1000",
        {
@@ -535962,6 +537976,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "hkdf.js"
          ]
         ]
@@ -536005,6 +538023,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "hkdf.js"
          ]
         ]
@@ -536048,6 +538070,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "hkdf.js"
          ]
         ]
@@ -536091,6 +538117,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "hkdf.js"
          ]
         ]
@@ -536134,6 +538164,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "hkdf.js"
          ]
         ]
@@ -536177,6 +538211,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "hkdf.js"
          ]
         ]
@@ -536220,6 +538258,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "hkdf.js"
          ]
         ]
@@ -536263,6 +538305,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "hkdf.js"
          ]
         ]
@@ -536270,7 +538316,7 @@
       ]
      ],
      "pbkdf2.https.any.js": [
-      "cc2ed9b9cef8721dd7523387c29c899981fce049",
+      "8d1299358575a2fb66f79ef884c60803e876c9c7",
       [
        "WebCryptoAPI/derive_bits_keys/pbkdf2.https.any.html?1-1000",
        {
@@ -536333,6 +538379,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -536401,6 +538451,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -536469,6 +538523,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -536537,6 +538595,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -536605,6 +538667,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -536673,6 +538739,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -536741,6 +538811,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -536809,6 +538883,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -536877,6 +538955,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -536945,6 +539027,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -537013,6 +539099,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -537081,6 +539171,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -537149,6 +539243,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -537217,6 +539315,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -537285,6 +539387,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -537353,6 +539459,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -537421,6 +539531,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -537489,6 +539603,10 @@
          ],
          [
           "script",
+          "kdf.js"
+         ],
+         [
+          "script",
           "pbkdf2.js"
          ]
         ],
@@ -537499,7 +539617,7 @@
     },
     "digest": {
      "cshake.tentative.https.any.js": [
-      "81793666294c2320cf5e1861cfa5e6fbe637960e",
+      "1bf78f581154101a0197606e59c63672a4e0cf51",
       [
        "WebCryptoAPI/digest/cshake.tentative.https.any.html",
        {
@@ -537513,6 +539631,14 @@
           "../util/helpers.js"
          ],
          [
+          "script",
+          "digest_test_data.js"
+         ],
+         [
+          "script",
+          "digest.js"
+         ],
+         [
           "timeout",
           "long"
          ]
@@ -537533,6 +539659,14 @@
           "../util/helpers.js"
          ],
          [
+          "script",
+          "digest_test_data.js"
+         ],
+         [
+          "script",
+          "digest.js"
+         ],
+         [
           "timeout",
           "long"
          ]
@@ -537542,7 +539676,7 @@
       ]
      ],
      "digest.https.any.js": [
-      "38ce85ec06cfc3231821802bc27c620d8005d617",
+      "26711b2880d94755f4fce4b4cda3869568ee24d2",
       [
        "WebCryptoAPI/digest/digest.https.any.html",
        {
@@ -537556,6 +539690,14 @@
           "../util/helpers.js"
          ],
          [
+          "script",
+          "digest_test_data.js"
+         ],
+         [
+          "script",
+          "digest.js"
+         ],
+         [
           "timeout",
           "long"
          ]
@@ -537576,6 +539718,14 @@
           "../util/helpers.js"
          ],
          [
+          "script",
+          "digest_test_data.js"
+         ],
+         [
+          "script",
+          "digest.js"
+         ],
+         [
           "timeout",
           "long"
          ]
@@ -537585,7 +539735,7 @@
       ]
      ],
      "kangarootwelve.tentative.https.any.js": [
-      "9f800b7937876aee8dcff02c051be0159b247c4b",
+      "0e38eb0542ad5f513cf5adb874c3d7e71845b327",
       [
        "WebCryptoAPI/digest/kangarootwelve.tentative.https.any.html",
        {
@@ -537599,6 +539749,10 @@
           "../util/helpers.js"
          ],
          [
+          "script",
+          "xof_digest.js"
+         ],
+         [
           "timeout",
           "long"
          ]
@@ -537619,6 +539773,10 @@
           "../util/helpers.js"
          ],
          [
+          "script",
+          "xof_digest.js"
+         ],
+         [
           "timeout",
           "long"
          ]
@@ -537628,7 +539786,7 @@
       ]
      ],
      "sha3.tentative.https.any.js": [
-      "f9f38eadc2c39a04f01ad90176e5fc34ff78777b",
+      "a6cafbb639f860c40704ad8fb1b3a25ac54c4476",
       [
        "WebCryptoAPI/digest/sha3.tentative.https.any.html",
        {
@@ -537642,6 +539800,14 @@
           "../util/helpers.js"
          ],
          [
+          "script",
+          "digest_test_data.js"
+         ],
+         [
+          "script",
+          "digest.js"
+         ],
+         [
           "timeout",
           "long"
          ]
@@ -537662,6 +539828,14 @@
           "../util/helpers.js"
          ],
          [
+          "script",
+          "digest_test_data.js"
+         ],
+         [
+          "script",
+          "digest.js"
+         ],
+         [
           "timeout",
           "long"
          ]
@@ -537671,7 +539845,7 @@
       ]
      ],
      "turboshake.tentative.https.any.js": [
-      "243931cd119802e5ec0afdabb6e47a1af7cd6bb3",
+      "356e6987956cb7eb891621327681a557679f2839",
       [
        "WebCryptoAPI/digest/turboshake.tentative.https.any.html",
        {
@@ -537685,6 +539859,10 @@
           "../util/helpers.js"
          ],
          [
+          "script",
+          "xof_digest.js"
+         ],
+         [
           "timeout",
           "long"
          ]
@@ -537705,6 +539883,10 @@
           "../util/helpers.js"
          ],
          [
+          "script",
+          "xof_digest.js"
+         ],
+         [
           "timeout",
           "long"
          ]
@@ -537716,7 +539898,7 @@
     },
     "encap_decap": {
      "encap_decap_bits.tentative.https.any.js": [
-      "5a669753cd2701bdc355eb2dfa2ce557c0ffd774",
+      "ffb78f7e534860c1aaeba69cf1d1b62f8744e15e",
       [
        "WebCryptoAPI/encap_decap/encap_decap_bits.tentative.https.any.html",
        {
@@ -537767,7 +539949,7 @@
       ]
      ],
      "encap_decap_keys.tentative.https.any.js": [
-      "0a45c1fc4e9f6fd1582577db152af9a094d30cba",
+      "a482eafbb6102100f8f730e5dfe42cfb0053dcc3",
       [
        "WebCryptoAPI/encap_decap/encap_decap_keys.tentative.https.any.html",
        {
@@ -537820,7 +540002,7 @@
     },
     "encrypt_decrypt": {
      "aes_cbc.https.any.js": [
-      "ec09aae5a954a730aff9c0f80f983834de04c81e",
+      "9dcbfbdeccbf8f1ad181d524afb5f70da30205e9",
       [
        "WebCryptoAPI/encrypt_decrypt/aes_cbc.https.any.html",
        {
@@ -537835,6 +540017,10 @@
          ],
          [
           "script",
+          "aes_common_fixtures.js"
+         ],
+         [
+          "script",
           "aes_cbc_vectors.js"
          ],
          [
@@ -537863,6 +540049,10 @@
          ],
          [
           "script",
+          "aes_common_fixtures.js"
+         ],
+         [
+          "script",
           "aes_cbc_vectors.js"
          ],
          [
@@ -537879,7 +540069,7 @@
       ]
      ],
      "aes_ctr.https.any.js": [
-      "f9d85bb6c93b7763e8726473814652fafbff1aa7",
+      "cbda32f354321f015d13d7b231ae138bc36d7e12",
       [
        "WebCryptoAPI/encrypt_decrypt/aes_ctr.https.any.html",
        {
@@ -537894,6 +540084,10 @@
          ],
          [
           "script",
+          "aes_common_fixtures.js"
+         ],
+         [
+          "script",
           "aes_ctr_vectors.js"
          ],
          [
@@ -537922,6 +540116,10 @@
          ],
          [
           "script",
+          "aes_common_fixtures.js"
+         ],
+         [
+          "script",
           "aes_ctr_vectors.js"
          ],
          [
@@ -537938,7 +540136,7 @@
       ]
      ],
      "aes_gcm.https.any.js": [
-      "b3e6b5f0d2c1ff1b643319b1beaf1ea6b7aeafea",
+      "295351fc319d656440377606f9d2a8c1783f1a4b",
       [
        "WebCryptoAPI/encrypt_decrypt/aes_gcm.https.any.html",
        {
@@ -537953,6 +540151,10 @@
          ],
          [
           "script",
+          "aes_common_fixtures.js"
+         ],
+         [
+          "script",
           "aes_gcm_96_iv_fixtures.js"
          ],
          [
@@ -537985,6 +540187,10 @@
          ],
          [
           "script",
+          "aes_common_fixtures.js"
+         ],
+         [
+          "script",
           "aes_gcm_96_iv_fixtures.js"
          ],
          [
@@ -538005,7 +540211,7 @@
       ]
      ],
      "aes_gcm_256_iv.https.any.js": [
-      "196a8ea94fb1c4ffcf6073902d741c617a3834ff",
+      "a46f4c79b891a527f71ad27e91428056e5b70fd5",
       [
        "WebCryptoAPI/encrypt_decrypt/aes_gcm_256_iv.https.any.html",
        {
@@ -538020,6 +540226,10 @@
          ],
          [
           "script",
+          "aes_common_fixtures.js"
+         ],
+         [
+          "script",
           "aes_gcm_256_iv_fixtures.js"
          ],
          [
@@ -538052,6 +540262,10 @@
          ],
          [
           "script",
+          "aes_common_fixtures.js"
+         ],
+         [
+          "script",
           "aes_gcm_256_iv_fixtures.js"
          ],
          [
@@ -538072,7 +540286,7 @@
       ]
      ],
      "aes_ocb.tentative.https.any.js": [
-      "cca34e9e54e70af21de2be9a567b5b2250a8d28a",
+      "e6a6246074c186d0abb6cac285382c37a4c6460d",
       [
        "WebCryptoAPI/encrypt_decrypt/aes_ocb.tentative.https.any.html",
        {
@@ -538087,6 +540301,10 @@
          ],
          [
           "script",
+          "aes_common_fixtures.js"
+         ],
+         [
+          "script",
           "aes_ocb_fixtures.js"
          ],
          [
@@ -538119,6 +540337,10 @@
          ],
          [
           "script",
+          "aes_common_fixtures.js"
+         ],
+         [
+          "script",
           "aes_ocb_fixtures.js"
          ],
          [
@@ -538182,7 +540404,7 @@
       ]
      ],
      "rsa_oaep.https.any.js": [
-      "3550f5eec287934dac9ef69bc175f11345d914b2",
+      "42511415f157251a03d9ae8a312237348b3fb679",
       [
        "WebCryptoAPI/encrypt_decrypt/rsa_oaep.https.any.html",
        {
@@ -538197,6 +540419,10 @@
          ],
          [
           "script",
+          "../util/rsa_key_fixtures.js"
+         ],
+         [
+          "script",
           "rsa_vectors.js"
          ],
          [
@@ -538225,6 +540451,10 @@
          ],
          [
           "script",
+          "../util/rsa_key_fixtures.js"
+         ],
+         [
+          "script",
           "rsa_vectors.js"
          ],
          [
@@ -538243,7 +540473,7 @@
     },
     "generateKey": {
      "failures_AES-CBC.https.any.js": [
-      "38bed1cc702819852e3a7185f66c18b671eb91da",
+      "5febdbb94926f782a64dd85f819bd38258027b2d",
       [
        "WebCryptoAPI/generateKey/failures_AES-CBC.https.any.html",
        {
@@ -538262,6 +540492,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538286,6 +540520,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538294,7 +540532,7 @@
       ]
      ],
      "failures_AES-CTR.https.any.js": [
-      "0e7940775fe0f51ede2264c5c96d6e4cb7033387",
+      "0eb6664f5d00d8b7e0be2b8865aac2a91a93f38b",
       [
        "WebCryptoAPI/generateKey/failures_AES-CTR.https.any.html",
        {
@@ -538313,6 +540551,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538337,6 +540579,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538345,7 +540591,7 @@
       ]
      ],
      "failures_AES-GCM.https.any.js": [
-      "a394c8b629c57906f2c5ba0a1fcb8b9199f3b4bf",
+      "3d13b78903fdf98ac119771099238374f3b079a1",
       [
        "WebCryptoAPI/generateKey/failures_AES-GCM.https.any.html",
        {
@@ -538364,6 +540610,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538388,6 +540638,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538396,7 +540650,7 @@
       ]
      ],
      "failures_AES-KW.https.any.js": [
-      "40c199b29a5c6eb8198aa1ae82968858a3ef79e0",
+      "138565e59ac190bc1c0abd204ef85ab01a0240d5",
       [
        "WebCryptoAPI/generateKey/failures_AES-KW.https.any.html",
        {
@@ -538415,6 +540669,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538439,6 +540697,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538447,7 +540709,7 @@
       ]
      ],
      "failures_AES-OCB.tentative.https.any.js": [
-      "d4a2cd868ff51a6f8d94bbabd2c997652fe84cd7",
+      "ef7c8c38e537f9e20ccb8c6521d944ae74ff2a5d",
       [
        "WebCryptoAPI/generateKey/failures_AES-OCB.tentative.https.any.html",
        {
@@ -538466,6 +540728,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538490,6 +540756,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538498,7 +540768,7 @@
       ]
      ],
      "failures_ECDH.https.any.js": [
-      "e522254d743aa469507be0919832fc74a120ce8e",
+      "f61d27e995c46724a4a1d7823f439df64da71de1",
       [
        "WebCryptoAPI/generateKey/failures_ECDH.https.any.html",
        {
@@ -538517,6 +540787,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538541,6 +540815,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538549,7 +540827,7 @@
       ]
      ],
      "failures_ECDSA.https.any.js": [
-      "e19974ff488cc05b20e04008751685bf34da2a1c",
+      "e49b5e675b3f9bcf30ce36ac2e2e9f9e68519799",
       [
        "WebCryptoAPI/generateKey/failures_ECDSA.https.any.html",
        {
@@ -538568,6 +540846,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538592,6 +540874,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538600,7 +540886,7 @@
       ]
      ],
      "failures_Ed25519.https.any.js": [
-      "8f18fb1efe09500697c976a62afef1ca219948a3",
+      "7505e97bf2e1e6e2f74f20695b870c283243bbb6",
       [
        "WebCryptoAPI/generateKey/failures_Ed25519.https.any.html",
        {
@@ -538619,6 +540905,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538643,6 +540933,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538651,7 +540945,7 @@
       ]
      ],
      "failures_Ed448.tentative.https.any.js": [
-      "b25dcd14909410125fe147a883e98f41ab281b96",
+      "6ee388e40f761aefbafea3d84bb9e086cc66fb88",
       [
        "WebCryptoAPI/generateKey/failures_Ed448.tentative.https.any.html",
        {
@@ -538670,6 +540964,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538694,6 +540992,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538702,7 +541004,7 @@
       ]
      ],
      "failures_HMAC.https.any.js": [
-      "43ce1c026fc74e618bdff87a242858519b2dfb86",
+      "e3097cd46cdc596e9367eef4ff0ff256d8da90dc",
       [
        "WebCryptoAPI/generateKey/failures_HMAC.https.any.html",
        {
@@ -538721,6 +541023,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538745,6 +541051,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538753,7 +541063,7 @@
       ]
      ],
      "failures_ML-DSA.tentative.https.any.js": [
-      "91e20bc6148fa32d01736b7412210088d48ce70a",
+      "d41636dea797464fb5f2d992ca46d411854111b6",
       [
        "WebCryptoAPI/generateKey/failures_ML-DSA.tentative.https.any.html",
        {
@@ -538772,6 +541082,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538796,6 +541110,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538804,7 +541122,7 @@
       ]
      ],
      "failures_ML-KEM.tentative.https.any.js": [
-      "9cd347d00f815c105f83c58c37b7c254ba305e06",
+      "0ece96944e7ba8a16fd0d2aeda2958e74e418983",
       [
        "WebCryptoAPI/generateKey/failures_ML-KEM.tentative.https.any.html",
        {
@@ -538823,6 +541141,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538847,6 +541169,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538855,7 +541181,7 @@
       ]
      ],
      "failures_RSA-OAEP.https.any.js": [
-      "1d2bca96b18c7e37e3c87ddc11a4935057ba43ea",
+      "a4eb4e6cbc825e334ad3f1e956f9b16a13107be4",
       [
        "WebCryptoAPI/generateKey/failures_RSA-OAEP.https.any.html",
        {
@@ -538874,6 +541200,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538898,6 +541228,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538906,7 +541240,7 @@
       ]
      ],
      "failures_RSA-PSS.https.any.js": [
-      "562f66697c9f63d052d3154cddd43ec18564d7ae",
+      "62ac442ebe2b116613cdb2c68bd22314a8d9f52f",
       [
        "WebCryptoAPI/generateKey/failures_RSA-PSS.https.any.html",
        {
@@ -538925,6 +541259,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538949,6 +541287,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -538957,7 +541299,7 @@
       ]
      ],
      "failures_RSASSA-PKCS1-v1_5.https.any.js": [
-      "fb19308de6f7a2940bed164a8d7e693237aa5b6e",
+      "f06e3402799a5993dfb28dc13b1dea79fe0b6154",
       [
        "WebCryptoAPI/generateKey/failures_RSASSA-PKCS1-v1_5.https.any.html",
        {
@@ -538976,6 +541318,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -539000,6 +541346,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -539008,7 +541358,7 @@
       ]
      ],
      "failures_X25519.https.any.js": [
-      "2662d8697a9a6f41809386f83b9af2fc2460109a",
+      "6735e85151e65cdbf0bfd639ca6f3fb6b03ba9ea",
       [
        "WebCryptoAPI/generateKey/failures_X25519.https.any.html",
        {
@@ -539027,6 +541377,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -539051,6 +541405,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -539059,7 +541417,7 @@
       ]
      ],
      "failures_X448.tentative.https.any.js": [
-      "455e260d1fe920b9d7982f1a91abd56bf842ecee",
+      "26e12b9725900f5ee7e8153a4711f26a932a4194",
       [
        "WebCryptoAPI/generateKey/failures_X448.tentative.https.any.html",
        {
@@ -539078,6 +541436,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -539102,6 +541464,61 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
+          "failures.js"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ]
+     ],
+     "failures_bad_algorithm.https.any.js": [
+      "5fe0b15784e3b7c411dcd24a320798528ed83a71",
+      [
+       "WebCryptoAPI/generateKey/failures_bad_algorithm.https.any.html",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "WebCryptoAPI: generateKey() for Failures"
+         ],
+         [
+          "timeout",
+          "long"
+         ],
+         [
+          "script",
+          "../util/helpers.js"
+         ],
+         [
+          "script",
+          "failures.js"
+         ]
+        ],
+        "timeout": "long"
+       }
+      ],
+      [
+       "WebCryptoAPI/generateKey/failures_bad_algorithm.https.any.worker.html",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "WebCryptoAPI: generateKey() for Failures"
+         ],
+         [
+          "timeout",
+          "long"
+         ],
+         [
+          "script",
+          "../util/helpers.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -539110,7 +541527,7 @@
       ]
      ],
      "failures_chacha20_poly1305.tentative.https.any.js": [
-      "c9a278cdfc2b18bf1e2dc9595c9bcc03f459f5f1",
+      "6ec0593a14b97e5b6400bad315dd3f8e42a789a7",
       [
        "WebCryptoAPI/generateKey/failures_chacha20_poly1305.tentative.https.any.html",
        {
@@ -539129,6 +541546,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -539153,6 +541574,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -539161,7 +541586,7 @@
       ]
      ],
      "failures_kmac.tentative.https.any.js": [
-      "f906038c7d3f4aad489e5c1ec8328c6419a921a9",
+      "889c17e9c23b638e41d07618675f5450be017a27",
       [
        "WebCryptoAPI/generateKey/failures_kmac.tentative.https.any.html",
        {
@@ -539180,6 +541605,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -539204,6 +541633,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "failures.js"
          ]
         ],
@@ -539212,7 +541645,7 @@
       ]
      ],
      "successes_AES-CBC.https.any.js": [
-      "80f92c2cb7d7a6bf5e47649ce9b13042f53fd54d",
+      "b2e1b2971a917407d360b17f000a97587478e308",
       [
        "WebCryptoAPI/generateKey/successes_AES-CBC.https.any.html",
        {
@@ -539235,6 +541668,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539263,6 +541700,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539271,7 +541712,7 @@
       ]
      ],
      "successes_AES-CTR.https.any.js": [
-      "243a104b606dc8f165a59b9fe356a2ed29ed6eb2",
+      "bec9e22f3205d8c54b0bbfa49a8f03a72804610f",
       [
        "WebCryptoAPI/generateKey/successes_AES-CTR.https.any.html",
        {
@@ -539294,6 +541735,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539322,6 +541767,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539330,7 +541779,7 @@
       ]
      ],
      "successes_AES-GCM.https.any.js": [
-      "f0f947c8160f90a704970c1835953fa8688fc7a2",
+      "df6e694c1e12b77aed5ff63c73133a2837dfd72b",
       [
        "WebCryptoAPI/generateKey/successes_AES-GCM.https.any.html",
        {
@@ -539353,6 +541802,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539381,6 +541834,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539389,7 +541846,7 @@
       ]
      ],
      "successes_AES-KW.https.any.js": [
-      "dbc040fdc5cb4f671bbc9633cb0f8b9e5d51b6a4",
+      "e55b24745c260d47fef401557b29f5f70a3eb7df",
       [
        "WebCryptoAPI/generateKey/successes_AES-KW.https.any.html",
        {
@@ -539412,6 +541869,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539440,6 +541901,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539448,7 +541913,7 @@
       ]
      ],
      "successes_AES-OCB.tentative.https.any.js": [
-      "b43abc4edb41c45b7989761538b6ccd801d85861",
+      "c87f875f4b1e07ceb7cca76f85973c7ee025c865",
       [
        "WebCryptoAPI/generateKey/successes_AES-OCB.tentative.https.any.html",
        {
@@ -539471,6 +541936,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539499,6 +541968,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539507,7 +541980,7 @@
       ]
      ],
      "successes_ECDH.https.any.js": [
-      "e9dee526149da6ca9cef49a8a9d65bbb50d77cef",
+      "9ec44a350605e4140388c57c559346f287271562",
       [
        "WebCryptoAPI/generateKey/successes_ECDH.https.any.html",
        {
@@ -539530,6 +542003,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539558,6 +542035,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539566,7 +542047,7 @@
       ]
      ],
      "successes_ECDSA.https.any.js": [
-      "a022f31fe9d119bb763b71622b93b001bce33313",
+      "9b0f40aa9a9b3a55da6bccb8ebe222111f96434a",
       [
        "WebCryptoAPI/generateKey/successes_ECDSA.https.any.html",
        {
@@ -539589,6 +542070,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539617,6 +542102,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539625,7 +542114,7 @@
       ]
      ],
      "successes_Ed25519.https.any.js": [
-      "6b3bc460f60f2f7e9ce2b3070b10d825a69b4268",
+      "8ecbbd23553c3c3b83392d7493156ca87ab590da",
       [
        "WebCryptoAPI/generateKey/successes_Ed25519.https.any.html",
        {
@@ -539648,6 +542137,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539676,6 +542169,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539684,7 +542181,7 @@
       ]
      ],
      "successes_Ed448.tentative.https.any.js": [
-      "8e37f57b244b582149261003109e0c2c199d6936",
+      "d017170ca1ad1f64ba2705a49e6cdf23f36328bf",
       [
        "WebCryptoAPI/generateKey/successes_Ed448.tentative.https.any.html",
        {
@@ -539707,6 +542204,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539735,6 +542236,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539743,7 +542248,7 @@
       ]
      ],
      "successes_HMAC.https.any.js": [
-      "18e0b271226ff64969ce6986845774fdf896a6fc",
+      "59fab83b08b84ceb9d1a2275b23b48a1bcc26d30",
       [
        "WebCryptoAPI/generateKey/successes_HMAC.https.any.html",
        {
@@ -539766,6 +542271,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539794,6 +542303,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539802,7 +542315,7 @@
       ]
      ],
      "successes_ML-DSA.tentative.https.any.js": [
-      "15d52f2a5059f545ba29960eae701cc6d5ca4ce8",
+      "6c81b193167c325ba3827f2cad538156d0ace99c",
       [
        "WebCryptoAPI/generateKey/successes_ML-DSA.tentative.https.any.html",
        {
@@ -539825,6 +542338,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539853,6 +542370,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539861,7 +542382,7 @@
       ]
      ],
      "successes_ML-KEM.tentative.https.any.js": [
-      "68a0d97b97553762fd78800b4d28383ccfd8db59",
+      "f41b5c34cdb535e19bc0488f6d0bad42268ac681",
       [
        "WebCryptoAPI/generateKey/successes_ML-KEM.tentative.https.any.html",
        {
@@ -539884,6 +542405,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539912,6 +542437,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -539920,7 +542449,7 @@
       ]
      ],
      "successes_RSA-OAEP.https.any.js": [
-      "d933fd981d4a33ba82fb2b000234859bdda1494e",
+      "f7c550df22224cbf39454b5e6a3221290fd4ccee",
       [
        "WebCryptoAPI/generateKey/successes_RSA-OAEP.https.any.html?1-10",
        {
@@ -540007,6 +542536,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -540099,6 +542632,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -540191,6 +542728,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -540283,6 +542824,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -540375,6 +542920,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -540467,6 +543016,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -540559,6 +543112,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -540651,6 +543208,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -540743,6 +543304,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -540835,6 +543400,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -540927,6 +543496,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -541019,6 +543592,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -541111,6 +543688,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -541203,6 +543784,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -541295,6 +543880,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -541387,6 +543976,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -541479,6 +544072,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -541571,6 +544168,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -541663,6 +544264,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -541755,6 +544360,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -541847,6 +544456,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -541939,6 +544552,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -542031,6 +544648,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -542123,6 +544744,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -542215,6 +544840,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -542307,6 +544936,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -542399,6 +545032,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -542491,6 +545128,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -542583,6 +545224,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -542675,6 +545320,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -542767,6 +545416,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -542859,6 +545512,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -542867,7 +545524,7 @@
       ]
      ],
      "successes_RSA-PSS.https.any.js": [
-      "cb43e3de3de36438b80c882c86c61bb2f1a04893",
+      "d1a9ec33ac231fc02d6fd50b8becfbe6304141fd",
       [
        "WebCryptoAPI/generateKey/successes_RSA-PSS.https.any.html?1-10",
        {
@@ -542906,6 +545563,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -542950,6 +545611,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -542994,6 +545659,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543038,6 +545707,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543082,6 +545755,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543126,6 +545803,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543170,6 +545851,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543214,6 +545899,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543222,7 +545911,7 @@
       ]
      ],
      "successes_RSASSA-PKCS1-v1_5.https.any.js": [
-      "b8db5972284d1ac6bbda0da81621d9bca5d04ee7",
+      "6db5a3703fe7d58fddc7d9adb5fe9452c32b2425",
       [
        "WebCryptoAPI/generateKey/successes_RSASSA-PKCS1-v1_5.https.any.html?1-10",
        {
@@ -543261,6 +545950,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543305,6 +545998,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543349,6 +546046,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543393,6 +546094,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543437,6 +546142,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543481,6 +546190,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543525,6 +546238,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543569,6 +546286,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543577,7 +546298,7 @@
       ]
      ],
      "successes_X25519.https.any.js": [
-      "0e87cf50108e69608c2ec76cfb424b093316a0e8",
+      "5acd5b5a1c2bcd8b0821322ece83f186d1dd6372",
       [
        "WebCryptoAPI/generateKey/successes_X25519.https.any.html",
        {
@@ -543600,6 +546321,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543628,6 +546353,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543636,7 +546365,7 @@
       ]
      ],
      "successes_X448.tentative.https.any.js": [
-      "e7dbe32696d8fe4dc47445f6f0fa10f756f958aa",
+      "f1475c16bbd2beb16d31b28d28cda3bb7b624fe3",
       [
        "WebCryptoAPI/generateKey/successes_X448.tentative.https.any.html",
        {
@@ -543659,6 +546388,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543687,6 +546420,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543695,7 +546432,7 @@
       ]
      ],
      "successes_chacha20_poly1305.tentative.https.any.js": [
-      "a1cef25c2f24fba7582b883e1d01e7986d53794a",
+      "2f3efebb6bc2ed2d643ea1dd01d8a33e5f53eebe",
       [
        "WebCryptoAPI/generateKey/successes_chacha20_poly1305.tentative.https.any.html",
        {
@@ -543718,6 +546455,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543746,6 +546487,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543754,7 +546499,7 @@
       ]
      ],
      "successes_kmac.tentative.https.any.js": [
-      "9f881f92bc17ccd859dfe73922b3c2fabec52e07",
+      "8276fa9058c478b9ce40d65c5e46a7faaea371d4",
       [
        "WebCryptoAPI/generateKey/successes_kmac.tentative.https.any.html",
        {
@@ -543777,6 +546522,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543805,6 +546554,10 @@
          ],
          [
           "script",
+          "algorithm_registry.js"
+         ],
+         [
+          "script",
           "successes.js"
          ]
         ],
@@ -543857,7 +546610,7 @@
      ]
     ],
     "getRandomValues.any.js": [
-     "aecd38efd60bac63a28673241039d2743e5b911f",
+     "8fd18a81deff398f10e3f84ea6aa806f38debf81",
      [
       "WebCryptoAPI/getRandomValues.any.html",
       {}
@@ -544195,7 +546948,7 @@
       ]
      ],
      "ML-DSA_importKey.tentative.https.any.js": [
-      "5ce13d858d6fff4ce72f743d9c0334bb80361ce5",
+      "02f745d71c2862bd32f7b3de6639eedc5ba45a10",
       [
        "WebCryptoAPI/import_export/ML-DSA_importKey.tentative.https.any.html",
        {
@@ -544214,11 +546967,15 @@
          ],
          [
           "script",
+          "../util/mldsa_key_fixtures.js"
+         ],
+         [
+          "script",
           "ML-DSA_importKey_fixtures.js"
          ],
          [
           "script",
-          "ML-DSA_importKey.js"
+          "ml_importKey.js"
          ]
         ],
         "timeout": "long"
@@ -544242,11 +546999,15 @@
          ],
          [
           "script",
+          "../util/mldsa_key_fixtures.js"
+         ],
+         [
+          "script",
           "ML-DSA_importKey_fixtures.js"
          ],
          [
           "script",
-          "ML-DSA_importKey.js"
+          "ml_importKey.js"
          ]
         ],
         "timeout": "long"
@@ -544254,7 +547015,7 @@
       ]
      ],
      "ML-KEM_importKey.tentative.https.any.js": [
-      "8b459c17e2d329d66e9af4ca5efe7892972db8bf",
+      "11d80c76bc552eee6407e926ba1b6e1640b33ba2",
       [
        "WebCryptoAPI/import_export/ML-KEM_importKey.tentative.https.any.html",
        {
@@ -544277,7 +547038,7 @@
          ],
          [
           "script",
-          "ML-KEM_importKey.js"
+          "ml_importKey.js"
          ]
         ],
         "timeout": "long"
@@ -544305,7 +547066,7 @@
          ],
          [
           "script",
-          "ML-KEM_importKey.js"
+          "ml_importKey.js"
          ]
         ],
         "timeout": "long"
@@ -544358,7 +547119,7 @@
       ]
      },
      "ec_importKey.https.any.js": [
-      "3b78bab4e74132580fed81f7acebd245dc29bac1",
+      "473fab2887e78c5f3f8e1f01e3b74690d7e05c00",
       [
        "WebCryptoAPI/import_export/ec_importKey.https.any.html",
        {
@@ -544374,6 +547135,10 @@
          [
           "script",
           "../util/helpers.js"
+         ],
+         [
+          "script",
+          "../util/ec_key_fixtures.js"
          ]
         ],
         "timeout": "long"
@@ -544394,6 +547159,10 @@
          [
           "script",
           "../util/helpers.js"
+         ],
+         [
+          "script",
+          "../util/ec_key_fixtures.js"
          ]
         ],
         "timeout": "long"
@@ -544401,7 +547170,7 @@
       ]
      ],
      "ec_importKey_failures_ECDH.https.any.js": [
-      "423d399f19def4cb2bb21696636fa839968b94d2",
+      "260e49791a51ea4c8ad85b6c0b77832099c59c07",
       [
        "WebCryptoAPI/import_export/ec_importKey_failures_ECDH.https.any.html",
        {
@@ -544420,6 +547189,10 @@
          ],
          [
           "script",
+          "../util/ec_key_fixtures.js"
+         ],
+         [
+          "script",
           "ec_importKey_failures_fixtures.js"
          ],
          [
@@ -544448,6 +547221,10 @@
          ],
          [
           "script",
+          "../util/ec_key_fixtures.js"
+         ],
+         [
+          "script",
           "ec_importKey_failures_fixtures.js"
          ],
          [
@@ -544460,7 +547237,7 @@
       ]
      ],
      "ec_importKey_failures_ECDSA.https.any.js": [
-      "527940798a42a56d831a188691ce8aade1aacb66",
+      "96116d5462df7c43fb18b30677c254728266a436",
       [
        "WebCryptoAPI/import_export/ec_importKey_failures_ECDSA.https.any.html",
        {
@@ -544479,6 +547256,10 @@
          ],
          [
           "script",
+          "../util/ec_key_fixtures.js"
+         ],
+         [
+          "script",
           "ec_importKey_failures_fixtures.js"
          ],
          [
@@ -544507,6 +547288,10 @@
          ],
          [
           "script",
+          "../util/ec_key_fixtures.js"
+         ],
+         [
+          "script",
           "ec_importKey_failures_fixtures.js"
          ],
          [
@@ -544519,7 +547304,7 @@
       ]
      ],
      "okp_importKey_Ed25519.https.any.js": [
-      "49656489d425dd4c54ec854756edfc6dacdf4765",
+      "b6b3786519512eeebcd61c56659518a9bdfc99ef",
       [
        "WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any.html",
        {
@@ -544538,6 +547323,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_fixtures.js"
          ],
          [
@@ -544566,6 +547355,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_fixtures.js"
          ],
          [
@@ -544578,7 +547371,7 @@
       ]
      ],
      "okp_importKey_Ed448.tentative.https.any.js": [
-      "5bb7460c1fbc9333435925790cf8eacad7648611",
+      "8498ce4ef8f3505e9db88804a00417e46c56862c",
       [
        "WebCryptoAPI/import_export/okp_importKey_Ed448.tentative.https.any.html",
        {
@@ -544597,6 +547390,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_fixtures.js"
          ],
          [
@@ -544625,6 +547422,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_fixtures.js"
          ],
          [
@@ -544637,7 +547438,7 @@
       ]
      ],
      "okp_importKey_X25519.https.any.js": [
-      "de1431d6cc24675e6ac7864c1e93d36b70a79765",
+      "5d629c082730ee8621d65f433ad8ab1fa37bf449",
       [
        "WebCryptoAPI/import_export/okp_importKey_X25519.https.any.html",
        {
@@ -544656,6 +547457,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_fixtures.js"
          ],
          [
@@ -544684,6 +547489,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_fixtures.js"
          ],
          [
@@ -544696,7 +547505,7 @@
       ]
      ],
      "okp_importKey_X448.tentative.https.any.js": [
-      "f8552be3c826dcf1f3511a5ad8cba295c62c1923",
+      "09361ed9b8d6ff508fa61b0ad6b889073ef20c3c",
       [
        "WebCryptoAPI/import_export/okp_importKey_X448.tentative.https.any.html",
        {
@@ -544715,6 +547524,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_fixtures.js"
          ],
          [
@@ -544743,6 +547556,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_fixtures.js"
          ],
          [
@@ -544755,7 +547572,7 @@
       ]
      ],
      "okp_importKey_failures_Ed25519.https.any.js": [
-      "e07b796df83d19bc78f17c035dcf02299c90e1fe",
+      "0b541d77016f2041017fde035425791860c55f16",
       [
        "WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.html",
        {
@@ -544774,6 +547591,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_failures_fixtures.js"
          ],
          [
@@ -544802,6 +547623,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_failures_fixtures.js"
          ],
          [
@@ -544814,7 +547639,7 @@
       ]
      ],
      "okp_importKey_failures_Ed448.tentative.https.any.js": [
-      "8ff3de5c79d3ab8f59f17051d5560b3e61371b11",
+      "726d5c1e3772346b1ef13f45d5a219395d894ab3",
       [
        "WebCryptoAPI/import_export/okp_importKey_failures_Ed448.tentative.https.any.html",
        {
@@ -544833,6 +547658,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_failures_fixtures.js"
          ],
          [
@@ -544861,6 +547690,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_failures_fixtures.js"
          ],
          [
@@ -544873,7 +547706,7 @@
       ]
      ],
      "okp_importKey_failures_X25519.https.any.js": [
-      "a0116bee8885129924cde1cd62e65b640b076595",
+      "d4c9c105bc3ba4ef43d47147a52459c8b48ea2e6",
       [
        "WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.html",
        {
@@ -544892,6 +547725,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_failures_fixtures.js"
          ],
          [
@@ -544920,6 +547757,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_failures_fixtures.js"
          ],
          [
@@ -544932,7 +547773,7 @@
       ]
      ],
      "okp_importKey_failures_X448.tentative.https.any.js": [
-      "eccce68fac731cd751f7b8819fe24401055fe3a9",
+      "2b575d7ab4d7408020f5b0072371342014583b1b",
       [
        "WebCryptoAPI/import_export/okp_importKey_failures_X448.tentative.https.any.html",
        {
@@ -544951,6 +547792,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_failures_fixtures.js"
          ],
          [
@@ -544979,6 +547824,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "okp_importKey_failures_fixtures.js"
          ],
          [
@@ -545956,7 +548805,7 @@
     },
     "sign_verify": {
      "ecdsa.https.any.js": [
-      "3f1e2e5ea9d8a7ae6954c993a08ea927ea13c4fb",
+      "2af5e05048e4445bd29aaf1cf2b2e78d3b598683",
       [
        "WebCryptoAPI/sign_verify/ecdsa.https.any.html",
        {
@@ -545975,6 +548824,10 @@
          ],
          [
           "script",
+          "signature.js"
+         ],
+         [
+          "script",
           "ecdsa.js"
          ],
          [
@@ -546003,6 +548856,10 @@
          ],
          [
           "script",
+          "signature.js"
+         ],
+         [
+          "script",
           "ecdsa.js"
          ],
          [
@@ -546015,7 +548872,7 @@
       ]
      ],
      "eddsa_curve25519.https.any.js": [
-      "b012b64733ef9d9319a1158cd2cbf99586ef4367",
+      "a4f32cbaeb7eb2a699e40e65922c34bb87c048e3",
       [
        "WebCryptoAPI/sign_verify/eddsa_curve25519.https.any.html",
        {
@@ -546034,6 +548891,10 @@
          ],
          [
           "script",
+          "signature.js"
+         ],
+         [
+          "script",
           "eddsa.js"
          ],
          [
@@ -546062,6 +548923,10 @@
          ],
          [
           "script",
+          "signature.js"
+         ],
+         [
+          "script",
           "eddsa.js"
          ],
          [
@@ -546074,7 +548939,7 @@
       ]
      ],
      "eddsa_curve448.tentative.https.any.js": [
-      "281bb63ad659f7eaa489e8db37c9e1ca0c923988",
+      "8dd43dfeb1727852a4de9c5f0566c57c7a07608f",
       [
        "WebCryptoAPI/sign_verify/eddsa_curve448.tentative.https.any.html",
        {
@@ -546093,6 +548958,10 @@
          ],
          [
           "script",
+          "signature.js"
+         ],
+         [
+          "script",
           "eddsa.js"
          ],
          [
@@ -546121,6 +548990,10 @@
          ],
          [
           "script",
+          "signature.js"
+         ],
+         [
+          "script",
           "eddsa.js"
          ],
          [
@@ -546192,7 +549065,7 @@
       ]
      ],
      "hmac.https.any.js": [
-      "a6c88f80ed99ee20495bdd3dcf0189ef002ffa38",
+      "070611181e58271599757fb499f3bd43519eaee3",
       [
        "WebCryptoAPI/sign_verify/hmac.https.any.html",
        {
@@ -546211,6 +549084,10 @@
          ],
          [
           "script",
+          "mac.js"
+         ],
+         [
+          "script",
           "hmac.js"
          ],
          [
@@ -546239,6 +549116,10 @@
          ],
          [
           "script",
+          "mac.js"
+         ],
+         [
+          "script",
           "hmac.js"
          ],
          [
@@ -546251,7 +549132,7 @@
       ]
      ],
      "kmac.tentative.https.any.js": [
-      "3d34c5752137a8c910e178d298f8038aeb0508f0",
+      "0a57ba7a3f805498df2ad281b064051b81dc313d",
       [
        "WebCryptoAPI/sign_verify/kmac.tentative.https.any.html",
        {
@@ -546270,6 +549151,10 @@
          ],
          [
           "script",
+          "mac.js"
+         ],
+         [
+          "script",
           "kmac.js"
          ],
          [
@@ -546298,6 +549183,10 @@
          ],
          [
           "script",
+          "mac.js"
+         ],
+         [
+          "script",
           "kmac.js"
          ],
          [
@@ -546310,7 +549199,7 @@
       ]
      ],
      "mldsa.tentative.https.any.js": [
-      "be336b3498462c66daf2b66646394d94440351ac",
+      "253cc85e258f67d4ec65d7065a10813c80db0043",
       [
        "WebCryptoAPI/sign_verify/mldsa.tentative.https.any.html",
        {
@@ -546325,10 +549214,18 @@
          ],
          [
           "script",
+          "../util/mldsa_key_fixtures.js"
+         ],
+         [
+          "script",
           "mldsa_vectors.js"
          ],
          [
           "script",
+          "signature.js"
+         ],
+         [
+          "script",
           "mldsa.js"
          ],
          [
@@ -546353,10 +549250,18 @@
          ],
          [
           "script",
+          "../util/mldsa_key_fixtures.js"
+         ],
+         [
+          "script",
           "mldsa_vectors.js"
          ],
          [
           "script",
+          "signature.js"
+         ],
+         [
+          "script",
           "mldsa.js"
          ],
          [
@@ -546369,7 +549274,7 @@
       ]
      ],
      "rsa_pkcs.https.any.js": [
-      "3e3a8c23bf53cf87608b5a7299c013e9e4a78457",
+      "43b87658d21f56d107a648bc009f28b959e15082",
       [
        "WebCryptoAPI/sign_verify/rsa_pkcs.https.any.html",
        {
@@ -546384,10 +549289,18 @@
          ],
          [
           "script",
+          "../util/rsa_key_fixtures.js"
+         ],
+         [
+          "script",
           "rsa_pkcs_vectors.js"
          ],
          [
           "script",
+          "signature.js"
+         ],
+         [
+          "script",
           "rsa.js"
          ],
          [
@@ -546412,10 +549325,18 @@
          ],
          [
           "script",
+          "../util/rsa_key_fixtures.js"
+         ],
+         [
+          "script",
           "rsa_pkcs_vectors.js"
          ],
          [
           "script",
+          "signature.js"
+         ],
+         [
+          "script",
           "rsa.js"
          ],
          [
@@ -546428,7 +549349,7 @@
       ]
      ],
      "rsa_pss.https.any.js": [
-      "399baba784687d921aeccf4e22cbd15122a72aee",
+      "c3ed8355f3f85f8185c0c113ba792af7f8541d82",
       [
        "WebCryptoAPI/sign_verify/rsa_pss.https.any.html",
        {
@@ -546443,10 +549364,18 @@
          ],
          [
           "script",
+          "../util/rsa_key_fixtures.js"
+         ],
+         [
+          "script",
           "rsa_pss_vectors.js"
          ],
          [
           "script",
+          "signature.js"
+         ],
+         [
+          "script",
           "rsa.js"
          ],
          [
@@ -546471,10 +549400,18 @@
          ],
          [
           "script",
+          "../util/rsa_key_fixtures.js"
+         ],
+         [
+          "script",
           "rsa_pss_vectors.js"
          ],
          [
           "script",
+          "signature.js"
+         ],
+         [
+          "script",
           "rsa.js"
          ],
          [
@@ -546488,7 +549425,7 @@
      ]
     },
     "supports-modern.tentative.https.any.js": [
-     "ca23b782317762d14243a9d58c721c41cd1aa8c9",
+     "97cf9dff48aee2d737f2b47228401cf589c155e6",
      [
       "WebCryptoAPI/supports-modern.tentative.https.any.html",
       {
@@ -546500,6 +549437,10 @@
         [
          "script",
          "util/helpers.js"
+        ],
+        [
+         "script",
+         "util/supports.js"
         ]
        ]
       }
@@ -546515,13 +549456,17 @@
         [
          "script",
          "util/helpers.js"
+        ],
+        [
+         "script",
+         "util/supports.js"
         ]
        ]
       }
      ]
     ],
     "supports.tentative.https.any.js": [
-     "ac3a32c741000cda53e2e2550f594df137d93acc",
+     "fcccbecd6916b9b5097b180d450d119adf2fc217",
      [
       "WebCryptoAPI/supports.tentative.https.any.html",
       {
@@ -546533,6 +549478,10 @@
         [
          "script",
          "util/helpers.js"
+        ],
+        [
+         "script",
+         "util/supports.js"
         ]
        ]
       }
@@ -546548,6 +549497,10 @@
         [
          "script",
          "util/helpers.js"
+        ],
+        [
+         "script",
+         "util/supports.js"
         ]
        ]
       }
@@ -546555,7 +549508,7 @@
     ],
     "wrapKey_unwrapKey": {
      "wrapKey_unwrapKey.https.any.js": [
-      "9fa3bb775d4243b75779f8a8be663567dddc79a6",
+      "d7a044030826b2bdbf9519ed82d0ea3caa162567",
       [
        "WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.html",
        {
@@ -546574,6 +549527,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "wrapKey_unwrapKey_vectors.js"
          ]
         ],
@@ -546598,6 +549555,10 @@
          ],
          [
           "script",
+          "../util/okp_key_fixtures.js"
+         ],
+         [
+          "script",
           "wrapKey_unwrapKey_vectors.js"
          ]
         ],
@@ -547144,7 +550105,7 @@
       ]
      ],
      "language-model-create-user-activation.tentative.https.window.js": [
-      "c37f0852f4108d85463d8082f56c87bfba7752cd",
+      "45ad6d01b5f9b875efa51eb14e395b5f7dbc9c28",
       [
        "ai/language-model/language-model-create-user-activation.tentative.https.window.html",
        {
@@ -559964,7 +562925,7 @@
    },
    "clipboard-apis": {
     "async-clipboard-cached-getType-reject.tentative.https.html": [
-     "ca2d5e31ccf79c832f1f28877aefdc3becdf8d75",
+     "32e3fb0990c222581aa1f742ac721a38f555e2c9",
      [
       null,
       {
@@ -559982,7 +562943,7 @@
      ]
     ],
     "async-clipboard-stale-getType-reject.tentative.https.html": [
-     "386b201297b2affcfe732f01e1086cf4ea84e412",
+     "f07de00827943db651aa5c5dc799d8e0ccf5057c",
      [
       null,
       {
@@ -564764,6 +567725,34 @@
        {}
       ]
      ],
+     "reporting-navigation-blocked.https.sub.html": [
+      "70915291ec4f437430218fe0f3af9e654461c1ce",
+      [
+       null,
+       {}
+      ]
+     ],
+     "reporting-navigation-redirect-blocked.https.sub.html": [
+      "76150ccf16530d4da7e22015ab646a4c21b8734a",
+      [
+       null,
+       {}
+      ]
+     ],
+     "reporting-navigation-redirect-report-only.https.sub.html": [
+      "71c129aac7b71015a7f63d0a1a55b1a8af849ba5",
+      [
+       null,
+       {}
+      ]
+     ],
+     "reporting-navigation-report-only.https.sub.html": [
+      "85f974e0f5e955bf1cffa909bc66c0539f2b9ccb",
+      [
+       null,
+       {}
+      ]
+     ],
      "reporting-report-only.https.sub.html": [
       "a3664e287390f401a54520dbec7f599dbafd04cd",
       [
@@ -564771,6 +567760,13 @@
        {}
       ]
      ],
+     "reporting-speculation-rules-prefetch-blocked.https.sub.html": [
+      "49c8b2976b430bfad215687e7113a8b300711ef5",
+      [
+       null,
+       {}
+      ]
+     ],
      "service-worker-dedicated-worker.https.sub.window.js": [
       "0b1c1101d01c8277b6d20d678824fb6285675b3a",
       [
@@ -564807,6 +567803,28 @@
        }
       ]
      ],
+     "service-worker-reporting-url-sanitization.https.sub.window.js": [
+      "1edd366b8353a318f50b251bbd0489403231e4c4",
+      [
+       "connection-allowlist/tentative/service-worker-reporting-url-sanitization.https.sub.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/get-host-info.sub.js"
+         ],
+         [
+          "script",
+          "/reporting/resources/report-helper.js"
+         ],
+         [
+          "script",
+          "/service-workers/service-worker/resources/test-helpers.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
      "service-worker-reporting.sub.https.window.js": [
       "9079cc9d1d4aba9185a84b10888fb368c1bd6ce7",
       [
@@ -565106,6 +568124,28 @@
        }
       ]
      ],
+     "webrtc-queuereport-sanitization.https.sub.window.js": [
+      "1cc0db5256ad48bfb0d96e9b35f672de88b4b7b1",
+      [
+       "connection-allowlist/tentative/webrtc-queuereport-sanitization.https.sub.window.html",
+       {
+        "script_metadata": [
+         [
+          "script",
+          "/common/get-host-info.sub.js"
+         ],
+         [
+          "script",
+          "/reporting/resources/report-helper.js"
+         ],
+         [
+          "script",
+          "/content-security-policy/webrtc/webrtc.js"
+         ]
+        ]
+       }
+      ]
+     ],
      "websocket-dedicated-worker.sub.window.js": [
       "40eea5e750dc454785d8b14ae7af4e0ce8d106ef",
       [
@@ -565497,14 +568537,14 @@
    "container-timing": {
     "tentative": {
      "containertiming-and-ignore-on-img-itself.html": [
-      "5d8f2c473acd0524ad8887f81e3586fa6bdaf70b",
+      "3717a63d03474328e04edde8451f844d16c9cea1",
       [
        null,
        {}
       ]
      ],
      "containertiming-and-ignore-on-text-itself.html": [
-      "96acac973792a708dcddd33abcd9eac874fa548f",
+      "9899e0bc747cf63348af5e63a612bc205b6517ce",
       [
        null,
        {}
@@ -565517,6 +568557,13 @@
        {}
       ]
      ],
+     "containertiming-img-already-loaded.html": [
+      "34d799e8bf6b673c6e89fb94826a4d86ca0dcb9f",
+      [
+       null,
+       {}
+      ]
+     ],
      "containertiming-img-itself.html": [
       "9e691ecc22a57243f75e173e63e09fb34ed86396",
       [
@@ -565560,7 +568607,7 @@
       ]
      ],
      "containertiming-with-child-ignore-and-child-img.html": [
-      "feb77cf1352c862d468a316bb06b04ac38d8c375",
+      "b9d2d0e44eed8fcbb0834f06fa71990ec2df3631",
       [
        null,
        {}
@@ -565581,14 +568628,28 @@
       ]
      ],
      "containertiming-with-ignore-and-child-img.html": [
-      "f32b0864c467659f97bfef2adf2ddbc6c951fab7",
+      "a07b95342eb8894caa3dec4e972450542cefab1f",
+      [
+       null,
+       {}
+      ]
+     ],
+     "iframe-isolation-cross-origin.sub.html": [
+      "67149d94fac92c6faafa156a4b71193ee2b38d3a",
+      [
+       null,
+       {}
+      ]
+     ],
+     "iframe-isolation-same-origin.html": [
+      "d48a3bac23d7396b8faace246b58d86fe03d4ee8",
       [
        null,
        {}
       ]
      ],
      "ignore-with-containertiming-and-child-img.html": [
-      "bbb9b52c62201a8cb485ff6c3b64e174588613a7",
+      "1f4859b8c0eda3897dbfbfdb5e92c30ea6a64d3c",
       [
        null,
        {}
@@ -565602,21 +568663,21 @@
       ]
      ],
      "nested-containertiming-and-ignore-on-img.html": [
-      "6e24c0c8b7afa283917ff803389e8bfb7bf56f1b",
+      "183de659d4a493bbec26df589b4dfaee60db3ad5",
       [
        null,
        {}
       ]
      ],
      "nested-containertiming-and-ignore-on-text.html": [
-      "36c556dc2f27fd225f277bc4421c8e28e87bb2cc",
+      "53656d33e3583f7d1c01b014b6434ed8769bcf90",
       [
        null,
        {}
       ]
      ],
      "nested-containertiming-child-img-with-ignore.html": [
-      "75265d82f25729fb0465c478a95789599e7622a9",
+      "cb588b8e1a014ecafd1220ee8fb06e1f561ce078",
       [
        null,
        {}
@@ -565637,7 +568698,7 @@
       ]
      ],
      "nested-containertiming-with-ignore.html": [
-      "7532a3798753f13b222e9c86f60ce82c05df5f7e",
+      "732a995d0cab68afd85c08ba95c47daad2535a85",
       [
        null,
        {}
@@ -565658,7 +568719,7 @@
       ]
      ],
      "pseudo-with-text-content-in-container-timing.html": [
-      "f0bde383a646c20b56593efaac84f22904643e62",
+      "58fdbee080a88cc03a9595b8e3c7ed82c6ee48d3",
       [
        null,
        {}
@@ -569392,6 +572453,13 @@
        {}
       ]
      ],
+     "source-list-wildcard-host-and-port.https.sub.html": [
+      "44fe7f7b1fab2441bcfb27993750cc06245b2536",
+      [
+       null,
+       {}
+      ]
+     ],
      "src-trailing-dot.sub.any.js": [
       "f2b45a6957c6c41bb8e1a8924db01ca6db4cd3ad",
       [
@@ -571097,6 +574165,13 @@
        {}
       ]
      ],
+     "script-src-strict_dynamic_modulepreload_parser_inserted_module.html": [
+      "dd9ad0a2d812ed3afd3cc5fe0d2b84bf1eb83dc5",
+      [
+       null,
+       {}
+      ]
+     ],
      "script-src-strict_dynamic_new_function.html": [
       "263d5d1d874260ef02547a55cc9fdad32b98a1c4",
       [
@@ -571146,6 +574221,13 @@
        {}
       ]
      ],
+     "script-src-strict_dynamic_parser_inserted_module_empty_hash.html": [
+      "ae091b96a71761ab63d64c9a852d29439f03db60",
+      [
+       null,
+       {}
+      ]
+     ],
      "script-src-strict_dynamic_worker-importScripts.https.html": [
       "681e19547ae6f10d18d0aa8640a31394fa09fc46",
       [
@@ -575048,7 +578130,7 @@
      ]
     ],
     "cors-safelisted-request-header.any.js": [
-     "a0a0417d74b3dbbb31de66b70b098eba15bcdba5",
+     "a5cff8447be29b81b63605453d856dd5b1f61c3f",
      [
       "cors/cors-safelisted-request-header.any.html",
       {
@@ -577387,7 +580469,7 @@
        ]
       ],
       "place-items-computed.html": [
-       "24a0fa32f2119c40882c86be7d756257a53818bd",
+       "ed97cc2379e5d6ce8be9c680b8033fb1e8eed5d0",
        [
         null,
         {}
@@ -577881,6 +580963,20 @@
        {}
       ]
      ],
+     "anchor-name-shadow-higher-tree.html": [
+      "21d3436a0749a6191699c515093caa633ede5c32",
+      [
+       null,
+       {}
+      ]
+     ],
+     "anchor-name-shadow-lower-tree.html": [
+      "85759e03054a114bc779d26afed5c1b7893e6260",
+      [
+       null,
+       {}
+      ]
+     ],
      "anchor-parse-invalid.html": [
       "38e1976b439d085ae4759806a3197524e052e1f2",
       [
@@ -578365,21 +581461,21 @@
       ]
      ],
      "anchor-transition-001.html": [
-      "a7db94169afc0a6f66b51e2a264b037b6edd0958",
+      "6b98b2c49c6a6649dea2de9874f7e94b986f4215",
       [
        null,
        {}
       ]
      ],
      "anchor-transition-002.html": [
-      "a5cbb675b3d975b1f384ef011685e51641cff4a8",
+      "c3719e23fac4543c5362c723e0eaf55c52aef678",
       [
        null,
        {}
       ]
      ],
      "anchor-transition-003.html": [
-      "29b459cb495bf04a9672bf026f7fc835ab430c98",
+      "10b8d47269adfa719b6acb7b2720c4b6d10545a8",
       [
        null,
        {}
@@ -579266,6 +582362,27 @@
        {}
       ]
      ],
+     "transform-019.html": [
+      "08aee4ba3ebb8440390086c50d5a4f7e26cac43a",
+      [
+       null,
+       {}
+      ]
+     ],
+     "transform-021.html": [
+      "1d46bf29bb8b095a905deef40068af22ccdce11b",
+      [
+       null,
+       {}
+      ]
+     ],
+     "transform-023.html": [
+      "9086de417c742cc3b2d8e0d01423603c0c719cf6",
+      [
+       null,
+       {}
+      ]
+     ],
      "try-tactic-alignment.html": [
       "9f4d6078d2e0ccf0e38e2b1d23c268763cad6227",
       [
@@ -583030,7 +586147,7 @@
       ]
      ],
      "scope-evaluation.html": [
-      "a70d2a59b7f54212b5e267182aac7dd5acdd1658",
+      "c09dc92fc9ae2535baa07b33aeb69f696d199ccc",
       [
        null,
        {}
@@ -583319,7 +586436,7 @@
      ],
      "parsing": {
       "alpha-color-computed.html": [
-       "e84d62818d10d9531f2bc9dc5bc075bfbf82b33e",
+       "5b1b1199ca61b4214cd44bcb6612b77e84c91f07",
        [
         null,
         {}
@@ -583649,7 +586766,7 @@
       ]
      ],
      "system-color-consistency.html": [
-      "fd73d1ab2f28984b3a229d0fb6b60718f9146f23",
+      "ecb5063467596b297d45042eae35fc28500025c4",
       [
        null,
        {}
@@ -583802,6 +586919,13 @@
      ]
     },
     "css-conditional": {
+     "at-supports-at-rule-serialization.html": [
+      "b5b60086afaab4073c146d7cdfd07ab23bf99fad",
+      [
+       null,
+       {}
+      ]
+     ],
      "at-supports-matches.html": [
       "7a6e36258c61846e3e488909ddb37fda5cb01a8d",
       [
@@ -586111,7 +589235,7 @@
      ],
      "parsing": {
       "content-computed.html": [
-       "68bbf06c177da858d2f7119445df5002cf9805ba",
+       "f17c80a6f4f83fd57335da9e0eb884582e9429a7",
        [
         null,
         {}
@@ -586125,14 +589249,14 @@
        ]
       ],
       "content-invalid.html": [
-       "efd4ea5136c489ccd38a25732864f4165a9a2b81",
+       "9872db36cf74bdae74e900e7c57196cbd8dab83c",
        [
         null,
         {}
        ]
       ],
       "content-valid.html": [
-       "80bb54f8dcb35e6dfcf7e30c475e0d30555dedde",
+       "21043c8739c8b4b7a38b50c30af4ca67d232c649",
        [
         null,
         {}
@@ -589464,7 +592588,7 @@
        ]
       ],
       "font-size-adjust-interpolation.html": [
-       "a138d7fee5a39bc925a7a4cc5224dcc3db5cc387",
+       "40e82e599d067c717cc9fe7f1c403a95a01df3b5",
        [
         null,
         {}
@@ -589865,7 +592989,7 @@
       ]
      ],
      "palette-mix-computed.html": [
-      "c82678e0ec571b7490f8b8176faa52bb802c7e38",
+      "6ed3b3b77b79a2ac31c179795c3d39f17c90de0f",
       [
        null,
        {}
@@ -595778,6 +598902,55 @@
         null,
         {}
        ]
+      ],
+      "subgrid-baseline-013.html": [
+       "e61432e212b40c098dac6abf31c7bf91210647f7",
+       [
+        null,
+        {}
+       ]
+      ],
+      "subgrid-baseline-014.html": [
+       "715013735122477142fa89a3a5807d177b438c5c",
+       [
+        null,
+        {}
+       ]
+      ],
+      "subgrid-baseline-015.html": [
+       "1868aa4565ffdf05ca70e868e2f884807a20130a",
+       [
+        null,
+        {}
+       ]
+      ],
+      "subgrid-baseline-016.html": [
+       "532af5e7bdfd1334fa8e87b4995417c8ff1caf84",
+       [
+        null,
+        {}
+       ]
+      ],
+      "subgrid-baseline-017.html": [
+       "b286047f2db4249fd9aaeb9b33fc950f7d20061c",
+       [
+        null,
+        {}
+       ]
+      ],
+      "subgrid-baseline-018.html": [
+       "4027cea33c374c780641601db51a7dc5e9a1a37f",
+       [
+        null,
+        {}
+       ]
+      ],
+      "subgrid-own-column-gap-does-not-inflate-intrinsic-width.html": [
+       "740f2591cb00e16444ab4388ee41d75c9393c193",
+       [
+        null,
+        {}
+       ]
       ]
      },
      "table-grid-item-005.html": [
@@ -596712,14 +599885,14 @@
     },
     "css-link-params": {
      "inheritance.html": [
-      "b73c6f44d56741cd9c6297e1fa794e44819426eb",
+      "965dd09e0faf15ff3f1a0328e0b25659c9f4307e",
       [
        null,
        {}
       ]
      ],
      "link-parameters-computed.html": [
-      "20dc658e60917e31f9469784f239c5af16040a2e",
+      "8f296f96e554b33989ef3240fd524ecb5184dc22",
       [
        null,
        {}
@@ -596927,7 +600100,7 @@
        ]
       ],
       "list-style-invalid.html": [
-       "0dda1630ad8f451908cc5aeddecb7549f12a40c2",
+       "94931173ee82f1be5c4516427ff7b3e46699e7d9",
        [
         null,
         {}
@@ -596969,7 +600142,7 @@
        ]
       ],
       "list-style-type-invalid.html": [
-       "c49f61ca705f83863ecc1219d24b962b8221426c",
+       "5e833da6eeb684ea10437c27758b4082e66b20fa",
        [
         null,
         {}
@@ -596983,7 +600156,7 @@
        ]
       ],
       "list-style-valid.html": [
-       "55124521f8daca21072fab063c968ec8f3b599df",
+       "f5aec513700b0f9a52de48b5bb4a00644d5475f6",
        [
         null,
         {}
@@ -598283,7 +601456,7 @@
        ]
       ],
       "mixin-cssom.tentative.html": [
-       "64ec2a71715075dc6b7abe5f1f4420f1f348dad3",
+       "61cc55e37d4b659aae46ecd245a078822d9e776e",
        [
         null,
         {}
@@ -604510,6 +607683,13 @@
          }
         ]
        ],
+       "prefer-first-in-flat-tree-order.html": [
+        "edc9224d55bb6bf78a7e84e3a3e41ec3bcb28400",
+        [
+         null,
+         {}
+        ]
+       ],
        "prefer-first-in-tree-order.html": [
         "f4b32e10c72b618268e18b63e0e50cc310a9c4f0",
         [
@@ -606861,7 +610041,7 @@
     "css-size-adjust": {
      "animations": {
       "text-size-adjust-interpolation.html": [
-       "1d7e54b734158eb234a4d653b239f9de04187aac",
+       "c20599e55f62ba6b5416b5faecbf45c8c44a610b",
        [
         null,
         {}
@@ -607722,94 +610902,94 @@
         ]
        ]
       },
-      "responsive-iframe-allowed-origins.html": [
-       "b9c2acffd6ad853ce1f08382259c92c9d497fcc2",
+      "responsive-iframe-alloworigins.html": [
+       "96ac5b6cb8248e1b654900458ed4e9e16423b720",
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?container=subdomain,origin=cross,allowed=wildcard,expect=400",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?container=subdomain,origin=cross,allowed=wildcard,expect=400",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?container=subdomain,origin=same,allowed=wildcard,expect=400",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?container=subdomain,origin=same,allowed=wildcard,expect=400",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=cross,allowed=child,expect=150",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=cross,allowed=child,expect=150",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=cross,allowed=other,expect=150",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=cross,allowed=other,expect=150",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=cross,allowed=parent,expect=400",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=cross,allowed=parent,expect=400",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=cross,allowed=self,expect=150",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=cross,allowed=self,expect=150",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=cross,allowed=star,expect=400",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=cross,allowed=star,expect=400",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=same,allowed=empty,expect=150",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=same,allowed=empty,expect=150",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=same,allowed=http,expect=400",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=same,allowed=http,expect=400",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=same,allowed=https,expect=150",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=same,allowed=https,expect=150",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=same,allowed=missing,expect=150",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=same,allowed=missing,expect=150",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=same,allowed=multiple,expect=400",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=same,allowed=multiple,expect=400",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=same,allowed=none,expect=150",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=same,allowed=none,expect=150",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=same,allowed=other,expect=150",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=same,allowed=other,expect=150",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=same,allowed=parent,expect=400",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=same,allowed=parent,expect=400",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=same,allowed=self,expect=400",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=same,allowed=self,expect=400",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=same,allowed=space,expect=150",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=same,allowed=space,expect=150",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=same,allowed=star,expect=400",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=same,allowed=star,expect=400",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=same,allowed=wildcard,expect=150",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=same,allowed=wildcard,expect=150",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=subdomain,allowed=child,expect=150",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=subdomain,allowed=child,expect=150",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=subdomain,allowed=parent,expect=400",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=subdomain,allowed=parent,expect=400",
         {}
        ],
        [
-        "css/css-sizing/responsive-iframe/responsive-iframe-allowed-origins.html?origin=subdomain,allowed=star,expect=400",
+        "css/css-sizing/responsive-iframe/responsive-iframe-alloworigins.html?origin=subdomain,allowed=star,expect=400",
         {}
        ]
       ],
@@ -607959,6 +611139,13 @@
         {}
        ]
       ],
+      "block-height-011.html": [
+       "86ef4b7e95c3e5323e5ec6e0714eabea1b2adbcf",
+       [
+        null,
+        {}
+       ]
+      ],
       "content-contribution-001.html": [
        "5f3706953c914d1ad4fa926b5e0927d8517128da",
        [
@@ -612163,7 +615350,7 @@
        ]
       ],
       "text-decoration-inset-invalid.html": [
-       "542c8a6fb0ce6b90fa8b6a98ee64d2a13ca691ae",
+       "e903ed10792ab705c04a481b5ec591654a31ae0e",
        [
         null,
         {}
@@ -614042,7 +617229,7 @@
       ]
      ],
      "transitionevent-interface.html": [
-      "b763858f46365e862df072af4af57bde21b57a7e",
+      "2c45abde6c49b9ec0b8c75e005a6129c2ce7307a",
       [
        null,
        {}
@@ -614261,7 +617448,7 @@
        ]
       ],
       "cssUnparsedValue.html": [
-       "6e9b1a0c90c23808fced4f6f80a933b8142a4fe5",
+       "691dfb024f3f032b4aa573ca4e18dbc0be787d87",
        [
         null,
         {}
@@ -614369,18 +617556,162 @@
         }
        ]
       ],
-      "cssKeywordValue-value.html": [
-       "ceb39bce5d21733cce08b9cd678a8b5ddd483f17",
+      "cssKeywordValue-value.any.js": [
+       "8c14ab4a02188c8ed7dd88d0cedc741daea86cc0",
        [
-        null,
-        {}
+        "css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.html",
+        {
+         "script_metadata": [
+          [
+           "global",
+           "window,worker"
+          ],
+          [
+           "title",
+           "CSSKeywordValue.value"
+          ],
+          [
+           "spec",
+           "https://drafts.css-houdini.org/css-typed-om-1/#dom-csskeywordvalue-csskeywordvalue"
+          ]
+         ]
+        }
+       ],
+       [
+        "css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.serviceworker.html",
+        {
+         "script_metadata": [
+          [
+           "global",
+           "window,worker"
+          ],
+          [
+           "title",
+           "CSSKeywordValue.value"
+          ],
+          [
+           "spec",
+           "https://drafts.css-houdini.org/css-typed-om-1/#dom-csskeywordvalue-csskeywordvalue"
+          ]
+         ]
+        }
+       ],
+       [
+        "css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.sharedworker.html",
+        {
+         "script_metadata": [
+          [
+           "global",
+           "window,worker"
+          ],
+          [
+           "title",
+           "CSSKeywordValue.value"
+          ],
+          [
+           "spec",
+           "https://drafts.css-houdini.org/css-typed-om-1/#dom-csskeywordvalue-csskeywordvalue"
+          ]
+         ]
+        }
+       ],
+       [
+        "css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.worker.html",
+        {
+         "script_metadata": [
+          [
+           "global",
+           "window,worker"
+          ],
+          [
+           "title",
+           "CSSKeywordValue.value"
+          ],
+          [
+           "spec",
+           "https://drafts.css-houdini.org/css-typed-om-1/#dom-csskeywordvalue-csskeywordvalue"
+          ]
+         ]
+        }
        ]
       ],
-      "cssKeywordValue.html": [
-       "1ebbf8de73bb7bb9bb4fe9b0b009f11c96ce8cea",
+      "cssKeywordValue.any.js": [
+       "776b6e2dd54e8d8f8fbc463ed8627421b3efcbdb",
        [
-        null,
-        {}
+        "css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.html",
+        {
+         "script_metadata": [
+          [
+           "global",
+           "window,worker"
+          ],
+          [
+           "title",
+           "CSSKeywordValue Constructor"
+          ],
+          [
+           "spec",
+           "https://drafts.css-houdini.org/css-typed-om-1/#dom-csskeywordvalue-value"
+          ]
+         ]
+        }
+       ],
+       [
+        "css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.serviceworker.html",
+        {
+         "script_metadata": [
+          [
+           "global",
+           "window,worker"
+          ],
+          [
+           "title",
+           "CSSKeywordValue Constructor"
+          ],
+          [
+           "spec",
+           "https://drafts.css-houdini.org/css-typed-om-1/#dom-csskeywordvalue-value"
+          ]
+         ]
+        }
+       ],
+       [
+        "css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.sharedworker.html",
+        {
+         "script_metadata": [
+          [
+           "global",
+           "window,worker"
+          ],
+          [
+           "title",
+           "CSSKeywordValue Constructor"
+          ],
+          [
+           "spec",
+           "https://drafts.css-houdini.org/css-typed-om-1/#dom-csskeywordvalue-value"
+          ]
+         ]
+        }
+       ],
+       [
+        "css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.worker.html",
+        {
+         "script_metadata": [
+          [
+           "global",
+           "window,worker"
+          ],
+          [
+           "title",
+           "CSSKeywordValue Constructor"
+          ],
+          [
+           "spec",
+           "https://drafts.css-houdini.org/css-typed-om-1/#dom-csskeywordvalue-value"
+          ]
+         ]
+        }
        ]
       ],
       "cssLCH.html": [
@@ -614433,14 +617764,14 @@
        ]
       ],
       "cssRotate.tentative.html": [
-       "6c234bc4724e70410a61e5f6397f6a3fa2d53be1",
+       "b10d8a7bacd2af5accd4021ce042c3a7fe3aac94",
        [
         null,
         {}
        ]
       ],
       "cssScale.tentative.html": [
-       "691cf4c519cbcde3f04c6636c0c96116526add11",
+       "deb487c8c116be9cde672826b1e0621c31d17070",
        [
         null,
         {}
@@ -614503,17 +617834,89 @@
        ]
       ],
       "cssTranslate.tentative.html": [
-       "0554351db3b320e5d19e65a2f2fe2e093bc739c6",
+       "8d7ac8d01d6a2a35bef8a88894dbb1586d9665d4",
        [
         null,
         {}
        ]
       ],
-      "cssUnparsedValue-empty.html": [
-       "34f77d922a3a387fd2e8a850c99b8eb18b24f81f",
+      "cssUnparsedValue-empty.any.js": [
+       "3750303dfdf03167b675891afffe85f2652782d8",
        [
-        null,
-        {}
+        "css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.html",
+        {
+         "script_metadata": [
+          [
+           "global",
+           "window,worker"
+          ],
+          [
+           "title",
+           "CSSUnparsedValue: Don't crash for empty values"
+          ],
+          [
+           "spec",
+           "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssunparsedvalue-cssunparsedvalue"
+          ]
+         ]
+        }
+       ],
+       [
+        "css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.serviceworker.html",
+        {
+         "script_metadata": [
+          [
+           "global",
+           "window,worker"
+          ],
+          [
+           "title",
+           "CSSUnparsedValue: Don't crash for empty values"
+          ],
+          [
+           "spec",
+           "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssunparsedvalue-cssunparsedvalue"
+          ]
+         ]
+        }
+       ],
+       [
+        "css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.sharedworker.html",
+        {
+         "script_metadata": [
+          [
+           "global",
+           "window,worker"
+          ],
+          [
+           "title",
+           "CSSUnparsedValue: Don't crash for empty values"
+          ],
+          [
+           "spec",
+           "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssunparsedvalue-cssunparsedvalue"
+          ]
+         ]
+        }
+       ],
+       [
+        "css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.worker.html",
+        {
+         "script_metadata": [
+          [
+           "global",
+           "window,worker"
+          ],
+          [
+           "title",
+           "CSSUnparsedValue: Don't crash for empty values"
+          ],
+          [
+           "spec",
+           "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssunparsedvalue-cssunparsedvalue"
+          ]
+         ]
+        }
        ]
       ],
       "cssUnparsedValue-indexed-getter-setter.html": [
@@ -614566,39 +617969,559 @@
        ]
       ],
       "numeric-objects": {
-       "add-two-types.tentative.html": [
-        "de263bf17175284308e1d032de11bac2f028838f",
+       "add-two-types.tentative.any.js": [
+        "a41faf0699afb2a3a7ad330c864bf517b34e1049",
         [
-         null,
-         {}
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Adding Two Numeric Types"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#add-two-types"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.serviceworker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Adding Two Numeric Types"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#add-two-types"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.sharedworker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Adding Two Numeric Types"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#add-two-types"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.worker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Adding Two Numeric Types"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#add-two-types"
+           ]
+          ]
+         }
         ]
        ],
-       "arithmetic.tentative.html": [
-        "a8a65cd9a3134f47b5eadec7cffcf535742fc617",
+       "arithmetic.tentative.any.js": [
+        "ccdd7a5b7d25b1611f74d7c57b9fa5f7ea8bd1dd",
         [
-         null,
-         {}
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Arithmetic operations on CSSNumericValue tests"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-add"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-sub"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-mul"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-div"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-min"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-max"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.serviceworker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Arithmetic operations on CSSNumericValue tests"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-add"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-sub"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-mul"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-div"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-min"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-max"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.sharedworker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Arithmetic operations on CSSNumericValue tests"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-add"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-sub"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-mul"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-div"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-min"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-max"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.worker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Arithmetic operations on CSSNumericValue tests"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-add"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-sub"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-mul"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-div"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-min"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-max"
+           ]
+          ]
+         }
         ]
        ],
-       "create-a-type.tentative.html": [
-        "8f2d7f49dcc11ae3285569f3173d69ccaeebe026",
+       "create-a-type.tentative.any.js": [
+        "de799c3e541bb130855cf6ea5004deef594e6a51",
         [
-         null,
-         {}
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Creating Type From A Unit"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#create-a-type"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.serviceworker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Creating Type From A Unit"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#create-a-type"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.sharedworker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Creating Type From A Unit"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#create-a-type"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.worker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Creating Type From A Unit"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#create-a-type"
+           ]
+          ]
+         }
         ]
        ],
-       "cssMathInvert-type.html": [
-        "74c7ceb4f06b4f0449f6340744dee061850718c8",
+       "cssMathInvert-type.any.js": [
+        "d50b76688500921d99f619b7c9efc2286e8d4cd1",
         [
-         null,
-         {}
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "CSSMathInvert.type"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#type-of-a-cssmathvalue"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.serviceworker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "CSSMathInvert.type"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#type-of-a-cssmathvalue"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.sharedworker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "CSSMathInvert.type"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#type-of-a-cssmathvalue"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.worker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "CSSMathInvert.type"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#type-of-a-cssmathvalue"
+           ]
+          ]
+         }
         ]
        ],
-       "cssMathNegate-type.html": [
-        "5f9a2d48dcd50d3049e7a58aac8ea721be019a64",
+       "cssMathNegate-type.any.js": [
+        "5c55dba05144c59fea7d7c10e3144e19b19f0446",
         [
-         null,
-         {}
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "CSSMathNegate.type"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#type-of-a-cssmathvalue"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.serviceworker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "CSSMathNegate.type"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#type-of-a-cssmathvalue"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.sharedworker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "CSSMathNegate.type"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#type-of-a-cssmathvalue"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.worker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "CSSMathNegate.type"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#type-of-a-cssmathvalue"
+           ]
+          ]
+         }
         ]
        ],
        "cssMathValue.tentative.html": [
@@ -614622,11 +618545,99 @@
          {}
         ]
        ],
-       "cssnumericvalue-multiply-two-types.tentative.html": [
-        "840964bea5ff414b307a6d1110ddb083849b0535",
+       "cssnumericvalue-multiply-two-types.tentative.any.js": [
+        "710db1e90f4c095cc8ab3bb5152413ca46795124",
         [
-         null,
-         {}
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Multiplying Two Numeric Types"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-multiply-two-types"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.serviceworker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Multiplying Two Numeric Types"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-multiply-two-types"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.sharedworker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Multiplying Two Numeric Types"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-multiply-two-types"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.worker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "Multiplying Two Numeric Types"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-multiply-two-types"
+           ]
+          ]
+         }
         ]
        ],
        "equals.tentative.html": [
@@ -614650,11 +618661,99 @@
          {}
         ]
        ],
-       "to.tentative.html": [
-        "21ff94e65cb0072b07bd90134ba0593b43b921b5",
+       "to.tentative.any.js": [
+        "9b86d5cf30757f7ae9c5ee3d5f72ab5413a24d70",
         [
-         null,
-         {}
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "CSSNumericValue.to tests"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-to"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.serviceworker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "CSSNumericValue.to tests"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-to"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.sharedworker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "CSSNumericValue.to tests"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-to"
+           ]
+          ]
+         }
+        ],
+        [
+         "css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.worker.html",
+         {
+          "script_metadata": [
+           [
+            "global",
+            "window,worker"
+           ],
+           [
+            "script",
+            "../../resources/testhelper.js"
+           ],
+           [
+            "title",
+            "CSSNumericValue.to tests"
+           ],
+           [
+            "spec",
+            "https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-to"
+           ]
+          ]
+         }
         ]
        ],
        "toSum.tentative.html": [
@@ -618663,7 +622762,7 @@
       ]
      ],
      "random-computed.tentative.html": [
-      "862c014ad98872fe6e5716d3e8404844e9ae3369",
+      "111dc50f220beb99b3498749f2268ed3738b4c15",
       [
        null,
        {}
@@ -618732,6 +622831,13 @@
        {}
       ]
      ],
+     "random-item-in-container-query.tentative.html": [
+      "bcb4ee83b20e69a8832952bafc047756bd4ff8fa",
+      [
+       null,
+       {}
+      ]
+     ],
      "random-item-in-keyframe.html": [
       "c05dc3761a5125b09a29ea1f9a12d598b56d0b3b",
       [
@@ -618873,7 +622979,7 @@
       ]
      ],
      "sign-in-keyframes-with-relative-units.html": [
-      "2b5b73332ee048aa684b7f08c110c1f985a49b80",
+      "29e976400e91fa365a588b88903b6ea019033e04",
       [
        null,
        {}
@@ -619014,7 +623120,7 @@
        ]
       ],
       "sibling-index-keyframe-palette-mix-dynamic.html": [
-       "aef994da26e8eb0819ca7a98374d60d5eedfd1e0",
+       "e210e849730d95b8de4806938497830793b82f03",
        [
         null,
         {}
@@ -619855,7 +623961,7 @@
       ]
      ],
      "document-active-view-transition.html": [
-      "6a4bcf9e385e7d1b4d7c8b8fc9fc611e88837fe8",
+      "5c98713ff5ecefdd6564185ccc246794b28d353f",
       [
        null,
        {}
@@ -620663,7 +624769,7 @@
        ]
       ],
       "element-active-view-transition.html": [
-       "f1fba29dada0ae67d61bad0464a148366e86632c",
+       "df11626247463028e3e25dc5b6cadbdeecc18223",
        [
         null,
         {}
@@ -621441,6 +625547,13 @@
        {}
       ]
      ],
+     "orthogonal-parent-shrink-to-fit-002.html": [
+      "e8be2b5dd53bf59cd88ff70253bbb52af1563361",
+      [
+       null,
+       {}
+      ]
+     ],
      "padding-percent-orthogonal-dynamic.html": [
       "402991bca61a7b57353ed353603a0af97a9d13ef",
       [
@@ -622293,7 +626406,7 @@
       ]
      ],
      "cssstyledeclaration-mutability.html": [
-      "ce5b598ca63bfdb6196e9d057558c3d366694bb4",
+      "0637df6ece8050c6532051949d73c7e962ca80ba",
       [
        null,
        {}
@@ -622622,7 +626735,7 @@
       ]
      ],
      "getComputedStyle-pseudo.html": [
-      "d98df66c86baa0c6be66c434f42d8e6d1742094a",
+      "6e3b89e8cbf115d82ab85523545eca6e22057c4e",
       [
        null,
        {}
@@ -623143,8 +627256,15 @@
        {}
       ]
      ],
+     "cssom-geometryutils-connected-callback.html": [
+      "247e461559bab711a9571fdabd609a8a4c4e47c7",
+      [
+       null,
+       {}
+      ]
+     ],
      "cssom-geometryutils-convertPointFromNode.html": [
-      "e13c822e82f9961525bb7eebaf68823d9d40203f",
+      "bdfee6d48d444476392e9874fc1252ab15b8dfd3",
       [
        null,
        {}
@@ -623164,15 +627284,50 @@
        {}
       ]
      ],
+     "cssom-geometryutils-cross-origin-path.https.html": [
+      "c13d27b051974407688e3b77d03fb1d4d2a5a2cf",
+      [
+       null,
+       {}
+      ]
+     ],
      "cssom-geometryutils-element-types.html": [
-      "dee48494e77c7e048fe04d2023ba4112a3535a47",
+      "22e9a79aa0901412496337051f8a58f5645d8c85",
+      [
+       null,
+       {}
+      ]
+     ],
+     "cssom-geometryutils-fragmentation.html": [
+      "e3d793c6b39be2ab4c0ca07cd0c6c0ae2ce39863",
       [
        null,
        {}
       ]
      ],
      "cssom-geometryutils-getBoxQuads-options.html": [
-      "9f1800341e95f6f49a574cecd8e4975992e37bfd",
+      "61d202063ea2a8af91c3991f88b1bd722e6f007d",
+      [
+       null,
+       {}
+      ]
+     ],
+     "cssom-geometryutils-lifecycle.html": [
+      "8b5c250a68b5639fa67f4d652ded7239dc06b24a",
+      [
+       null,
+       {}
+      ]
+     ],
+     "cssom-geometryutils-same-origin-iframe.html": [
+      "0f634caedc258dd38a63c83de89605c05d857d96",
+      [
+       null,
+       {}
+      ]
+     ],
+     "cssom-geometryutils-shadow-dom.html": [
+      "1c5d57cd8c9aff0a3f50445cd32d3f12549c57cd",
       [
        null,
        {}
@@ -623185,6 +627340,13 @@
        {}
       ]
      ],
+     "cssom-geometryutils-text.html": [
+      "3fa551b440e3cf7d8815e58b32e468d3833f5e02",
+      [
+       null,
+       {}
+      ]
+     ],
      "cssom-geometryutils-transforms.html": [
       "7d4a91b21d456236b28abea77e8844d7324187bc",
       [
@@ -625934,7 +630096,7 @@
       ]
      ],
      "focus-visible-003.html": [
-      "73f8fac55a5e982c1126e574f8e03d7c034eeb96",
+      "cb7d8453c93b4865a02fdaf00f22b3bc7a33c4d4",
       [
        null,
        {
@@ -625943,7 +630105,7 @@
       ]
      ],
      "focus-visible-004.html": [
-      "075ee83393f2ac8c46454f2e7191454dad5b7b08",
+      "00d0c8c5e515407fffc804301c677220e8233d5b",
       [
        null,
        {
@@ -631029,6 +635191,13 @@
       }
      ]
     ],
+    "user-agent-allows-protocol.https.html": [
+     "980d1065f9f291032d79274f9d8278f330e139fa",
+     [
+      null,
+      {}
+     ]
+    ],
     "webdriver": {
      "create.https.html": [
       "b851c9a4d6e7580628176e4c2c6bea5a916bd105",
@@ -634433,7 +638602,7 @@
       ]
      ],
      "Node-compareDocumentPosition.html": [
-      "afae60aad1e5f9543ede1d97f86cf83bf1df9d5d",
+      "158eb4d8641718fb6b82919609311fe5febf222d",
       [
        null,
        {}
@@ -635693,6 +639862,20 @@
        {}
       ]
      ],
+     "querySelector-empty-id.html": [
+      "dbfd3c2daa40b45b6e5b80b3fd67479d4828ac60",
+      [
+       null,
+       {}
+      ]
+     ],
+     "querySelector-id-nth-child.html": [
+      "077c01ae9a26bdc5711fc4c18bbfe09c9d736136",
+      [
+       null,
+       {}
+      ]
+     ],
      "querySelector-mixed-case.html": [
       "453fb70f17ad9dd4cc6a9345ff9a0d81d124c503",
       [
@@ -637070,6 +641253,13 @@
        null,
        {}
       ]
+     ],
+     "stream-sanitizer-img-ctor.html": [
+      "4592d0fc25579775482c6cab37396299f0577859",
+      [
+       null,
+       {}
+      ]
      ]
     },
     "xml-parse-serialize-roundtrip.html": [
@@ -637166,7 +641356,7 @@
      ]
     ],
     "fn-id.html": [
-     "617ff7216d84d56f8cef3f8ca08e902dd67f487d",
+     "b15d780492a889460d36319561de54d53b16eb24",
      [
       null,
       {}
@@ -654550,7 +658740,7 @@
      ]
     ],
     "missing-sinf.html": [
-     "1fabbcc8e392ccac6c0ea40b2903ec94138e56e8",
+     "10b3be2580d92ad1641e82beb732e61833c322cf",
      [
       null,
       {}
@@ -654558,6 +658748,13 @@
     ]
    },
    "entries-api": {
+    "filesystementry-getParent.html": [
+     "f7fc5c810646f1e4d108e0246592a80946fc606d",
+     [
+      null,
+      {}
+     ]
+    ],
     "idlharness.window.js": [
      "32527be514ad92a1c9d606a18cfae0061c764710",
      [
@@ -654575,6 +658772,13 @@
        ]
       }
      ]
+    ],
+    "webkitGetAsEntry.html": [
+     "bd6f07e3bfde5fb4f346d37956cb41ab89202ffd",
+     [
+      null,
+      {}
+     ]
     ]
    },
    "event-timing": {
@@ -657432,7 +661636,7 @@
      ]
     ],
     "csp-fenced-frame-src-blocked.https.html": [
-     "46f8778d47656e767e7c4c3d200945040f3ede69",
+     "9ebd67fb9bf07fe7ff65e02dbb4f83c8a8a1cc5e",
      [
       null,
       {}
@@ -657446,7 +661650,7 @@
      ]
     ],
     "csp-frame-src-blocked.https.html": [
-     "88e4a8b89f03b0deb675fc43d8317bbcefece5f5",
+     "7bb99431ab20a9a7e0ed3ee26ed486bd30c280fb",
      [
       null,
       {}
@@ -657653,7 +661857,7 @@
      ]
     ],
     "insecure-context.html": [
-     "44db6432a45674a8f0842df1ce8042149635ea4f",
+     "54e89f4a7c5bb6a9cacc66b4695dcb3baabfa9fd",
      [
       null,
       {}
@@ -657923,7 +662127,7 @@
      ]
     ],
     "sandbox-mandatory-flags.https.html": [
-     "57a5bc49cfa5ce8a21892d38dee0e0ad36ec18ab",
+     "4eba0002d4d6db968fd5eee9643c73d367a41c44",
      [
       null,
       {
@@ -658268,7 +662472,7 @@
        ]
       ],
       "general.any.js": [
-       "139f08947b15aca34fd1909e74504d929837755b",
+       "5d085cd2de20e0b55d426aff14232ccb1b0b2ebb",
        [
         "fetch/api/abort/general.any.html",
         {
@@ -666559,7 +670763,16 @@
       ]
      ],
      "compressed-large-resources-002.tentative.https.html": [
-      "8b3844281be048cfaee58c6fcd32d50069e3d2fc",
+      "6d0b32ab0df4a259e149f49f015427433f914dc0",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
+     "compressed-large-resources-dictionary-hashes.tentative.https.html": [
+      "4e90e5f97e73cf5cc2246886c49e55430f94a351",
       [
        null,
        {
@@ -676932,6 +681145,18 @@
       }
      ]
     ],
+    "getCurrentPosition-accuracyMode.https.html": [
+     "88759b0a47ee41290963842932d9508ed2b40592",
+     [
+      null,
+      {
+       "testdriver": true,
+       "testdriver_features": [
+        "bidi"
+       ]
+      }
+     ]
+    ],
     "getCurrentPosition-error.https.html": [
      "0ea929d161eeab86c1baf47682a7bdd7bdbdc268",
      [
@@ -686006,6 +690231,13 @@
         {}
        ]
       ],
+      "sandbox-disallow-forms.html": [
+       "7c925a9ebf6bc9a9a7c630b643bfb9dc794d33d1",
+       [
+        null,
+        {}
+       ]
+      ],
       "sandbox-disallow-popups.html": [
        "8e4b34eb8b950cdf1cd9ab45ef0d1325961aff01",
        [
@@ -691606,7 +695838,42 @@
           ]
          ],
          "background-image-in-iframe-ignored.tentative.https.sub.html": [
-          "5dc64d562baf1914e71b26d7931f15469c088d82",
+          "8558f9f4834938a5114b4429809ecd025d2d8e1d",
+          [
+           null,
+           {}
+          ]
+         ],
+         "background-image-in-iframe-moved-ignored.tentative.https.sub.html": [
+          "0c97599940b82d05f033010c4d5bcdf8f5ab93a4",
+          [
+           null,
+           {}
+          ]
+         ],
+         "background-image-on-body-in-iframe-ignored.tentative.https.sub.html": [
+          "10c95a6c0cf005bae1df709296d645a84577287f",
+          [
+           null,
+           {}
+          ]
+         ],
+         "background-image-on-body-in-iframe-moved-ignored.tentative.https.sub.html": [
+          "6ea6f56b2f2b5572f9d1467e1d6e93f7d38008a2",
+          [
+           null,
+           {}
+          ]
+         ],
+         "background-image-on-html-in-iframe-ignored.tentative.https.sub.html": [
+          "eda61db967bbbc41cb7886317ed05c1f823dcb97",
+          [
+           null,
+           {}
+          ]
+         ],
+         "background-image-on-html-in-iframe-moved-ignored.tentative.https.sub.html": [
+          "27e79063fa27bb30e7520ebc96cfcfff74b29c84",
           [
            null,
            {}
@@ -691725,14 +695992,14 @@
           ]
          ],
          "fragment-mask-png-ignored.tentative.https.sub.html": [
-          "4099f5402f153e81835862c254ede307fa5afdb5",
+          "7d4f5b04b922cfd8577ac4659b15748692918da3",
           [
            null,
            {}
           ]
          ],
          "fragment-mask-svg-ignored.tentative.https.sub.html": [
-          "70b2c28be9d91ed65fc5eaa3bbde686a32dffda1",
+          "4b3d2b3543b806df4ded2f05930dc31a9f711f01",
           [
            null,
            {}
@@ -691850,6 +696117,13 @@
            {}
           ]
          ],
+         "svg-mask-nested-filter.tentative.https.sub.html": [
+          "efaf94f6f54d3d1616e8da78f8370acceab7cb6a",
+          [
+           null,
+           {}
+          ]
+         ],
          "svg-pattern-cross-origin-image.tentative.https.sub.html": [
           "39ed61699ad055cbcc32d99bceb13b2355b52f00",
           [
@@ -716668,6 +720942,48 @@
           {}
          ]
         ],
+        "sanitize-boolean.html": [
+         "9d782c42d33688f180b43fffe821ca45e4068a35",
+         [
+          null,
+          {}
+         ]
+        ],
+        "sanitize-default.html": [
+         "e7edeb6542ea6f93f500eb3ffc2387347947a90b",
+         [
+          null,
+          {}
+         ]
+        ],
+        "sanitize-nested.html": [
+         "d7d877cf7472d08aadf6f5504a28633dd7381095",
+         [
+          null,
+          {}
+         ]
+        ],
+        "sanitize-regular.html": [
+         "9e8932747036c4c99af57bbc1645b91f9008bc38",
+         [
+          null,
+          {}
+         ]
+        ],
+        "sanitize-template-element.html": [
+         "c76bf9fcc39952792085c5bd8a2313f9e8bdaaee",
+         [
+          null,
+          {}
+         ]
+        ],
+        "sanitize-unsafe.html": [
+         "2a4591c9abc98c1d732700d5570f605f1a58c618",
+         [
+          null,
+          {}
+         ]
+        ],
         "streaming-target-marker-removed.html": [
          "6be426ada263c61f51829de0121f1df82cfd6823",
          [
@@ -720474,7 +724790,7 @@
     "interaction": {
      "focus": {
       "chrome-object-tab-focus-bug.html": [
-       "c4d04a2e436c818f2482c849a14ca6512cee3f9e",
+       "57eaa47fbeb4c497e3fcd87a1ce880cf1fd83011",
        [
         null,
         {
@@ -721268,7 +725584,7 @@
           ]
          ],
          "moves-across-table-sections.html": [
-          "cecf5f4b7a10b35c2a887f62f657956558e5c634",
+          "986052fefd85cc1d8d15c376a34f8a8069fb7409",
           [
            null,
            {
@@ -721731,6 +726047,15 @@
             "testdriver": true
            }
           ]
+         ],
+         "shadow-reattach.html": [
+          "36a24d807d54d253e7817e47f9f599503378cfe9",
+          [
+           null,
+           {
+            "testdriver": true
+           }
+          ]
          ]
         },
         "top-layer-dialog-excluded.html": [
@@ -722545,6 +726870,13 @@
          {}
         ]
        ],
+       "table-border-attributes-computed-style.html": [
+        "fbba6ed362d7e83a58d0f7a7756701aa04556902",
+        [
+         null,
+         {}
+        ]
+       ],
        "table-border-color-not-inherited.html": [
         "ffba6b0d6a6d8b6304b4b5de05e0f7577e54d3fc",
         [
@@ -722566,6 +726898,13 @@
          {}
         ]
        ],
+       "table-rules-computed-style.html": [
+        "20c8a259850c086ffe7ffbfed65ea48f3c8774da",
+        [
+         null,
+         {}
+        ]
+       ],
        "table-ua-stylesheet.html": [
         "dc3e45f5a468568825487f0f2874d6ba5ce2a41f",
         [
@@ -723194,7 +727533,7 @@
        ]
       ],
       "details-blockification.html": [
-       "2bd5aaf2e6259b52748776e077d3dfd5dbbf455b",
+       "4201b8a96d17a9ad7aaa31ba89f0981a8a0371d3",
        [
         null,
         {}
@@ -723360,7 +727699,7 @@
        ]
       ],
       "shadow-dom.html": [
-       "ce2650394a28b4ac27d53d24e4c2f5c86d95e4aa",
+       "79e305b8f5dc41cff39e490478361e3fb243e438",
        [
         null,
         {}
@@ -729240,7 +733579,7 @@
          ]
         ],
         "sizes-auto.html": [
-         "64493ee389ac305a0bcee1f78f583a90211baf2e",
+         "e96e6eaca597b6c5463d6d22d6ae235d1851ffea",
          [
           null,
           {}
@@ -730149,6 +734488,13 @@
          {}
         ]
        ],
+       "form-submit-action.html": [
+        "16c114b191928b7c1d61503ab507418465f82236",
+        [
+         null,
+         {}
+        ]
+       ],
        "form-submit-iframe-then-location-navigate.html": [
         "ad2943e2bb0977bb84139a7d20833e099da7370a",
         [
@@ -730242,6 +734588,13 @@
          {}
         ]
        ],
+       "submit-button-clicked-in-data-document.html": [
+        "e42c59c4f4d5acd16ba93912aa86150c67ad8533",
+        [
+         null,
+         {}
+        ]
+       ],
        "submit-entity-body.html": [
         "4467d497f111bbef5bde1d7cc7f82789de32300b",
         [
@@ -732603,7 +736956,7 @@
          ]
         ],
         "selectedcontent.html": [
-         "cb84109cc95295917faa4f149ec589fe9336ec69",
+         "71c47460768a4f5b40920cfc8627a582d940e7ea",
          [
           null,
           {
@@ -733867,7 +738220,7 @@
         ]
        ],
        "inert-node-is-unfocusable.html": [
-        "b35c82eb142e593c3c8886754a208b9acb162f39",
+        "0d29ad706082edfc78caff0b2cc1e7069626f02b",
         [
          null,
          {
@@ -734812,8 +739165,65 @@
         {}
        ]
       ],
+      "camera": {
+       "camera-element-use-cases.tentative.https.html": [
+        "4cadafb3603a57d289f3079e4e6374f0acc35fdf",
+        [
+         null,
+         {
+          "testdriver": true
+         }
+        ]
+       ],
+       "camera-error-scenarios.tentative.https.html": [
+        "8fd02ace1d3b0397e67bed0a6ee557a2f27053f6",
+        [
+         null,
+         {
+          "testdriver": true
+         }
+        ]
+       ],
+       "camera-set-constraints.tentative.https.html": [
+        "42d949e6cb60684cdb24a8cb84f0a3a64bc15071",
+        [
+         null,
+         {
+          "testdriver": true,
+          "timeout": "long"
+         }
+        ]
+       ],
+       "camera-track-attribute.tentative.https.html": [
+        "1c5c36a114d98732030507bcfcfed1664211f715",
+        [
+         null,
+         {
+          "testdriver": true
+         }
+        ]
+       ],
+       "idlharness.tentative.window.js": [
+        "226123c5fb17978a61fe52b54424d10dc4e7871d",
+        [
+         "html/semantics/permission-element/camera/idlharness.tentative.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/WebIDLParser.js"
+           ],
+           [
+            "script",
+            "/resources/idlharness.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
       "display-values.tentative.html": [
-       "89b2797e184fcbd836fc8729ca94bcee1e23e215",
+       "86ec8853feabfd49dad0e97bebb27f67c3dec7a4",
        [
         null,
         {}
@@ -734938,7 +739348,7 @@
        ]
       },
       "granted-selector.https.html": [
-       "f3e5055628440f74205fa21ab9b54b8d9b11a1fb",
+       "448597036318d36820baa3d6203c596913afde54",
        [
         null,
         {
@@ -735034,22 +739444,63 @@
         ]
        ]
       },
-      "invalid-css-properties.tentative.html": [
-       "122d7538b17dafcfa450990b4391043800a690f9",
-       [
-        null,
-        {}
+      "microphone": {
+       "idlharness.tentative.window.js": [
+        "3fa47d6c57bcb5c18b2137e7428c900b4a90227e",
+        [
+         "html/semantics/permission-element/microphone/idlharness.tentative.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/WebIDLParser.js"
+           ],
+           [
+            "script",
+            "/resources/idlharness.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "microphone-element-use-cases.tentative.https.html": [
+        "6ec0a4e542baff3bbc8b160df8f09e2a4a763608",
+        [
+         null,
+         {
+          "testdriver": true
+         }
+        ]
+       ],
+       "microphone-error-scenarios.tentative.https.html": [
+        "d970eea8cd345d2e956b4f888f8936e766d829aa",
+        [
+         null,
+         {
+          "testdriver": true
+         }
+        ]
+       ],
+       "microphone-set-constraints.tentative.https.html": [
+        "678e5e2cd6b5fb55f537620c64df963caa650a71",
+        [
+         null,
+         {
+          "testdriver": true,
+          "timeout": "long"
+         }
+        ]
+       ],
+       "microphone-track-attribute.tentative.https.html": [
+        "2b1dd6138f9a6df93ddf556d408b2d457aed8e1d",
+        [
+         null,
+         {
+          "testdriver": true
+         }
+        ]
        ]
-      ],
-      "lang-attribute-update.html": [
-       "876f196ab99f576a74f291b76d4139e65a3723d0",
-       [
-        null,
-        {
-         "testdriver": true
-        }
-       ]
-      ],
+      },
       "negative-offset.tentative.html": [
        "c2167c1f7e9b79185082c016505c99c1f57b8389",
        [
@@ -735057,31 +739508,15 @@
         {}
        ]
       ],
-      "no-children-rendered.tentative.html": [
-       "6ecbed8f6c91ddecd2f87b2cd1ab0cc70a07571e",
-       [
-        null,
-        {}
-       ]
-      ],
-      "no-focus.tentative.html": [
-       "8aaffda3b53575ed5f84d575f20f2036bc6712ff",
-       [
-        null,
-        {
-         "testdriver": true
-        }
-       ]
-      ],
       "parent-image-mask.tentative.html": [
-       "5b993dfc55fabdbfda4c38f9f18a22d4788871ee",
+       "0d5e2c1af19a1e35e0254c8634b94d659117b63f",
        [
         null,
         {}
        ]
       ],
       "parent-mask.tentative.html": [
-       "b38f97e949de6847bd34b45b269e68d8110175f8",
+       "21703e23e837b5263e0444fa704c1d48a3ae1f8a",
        [
         null,
         {}
@@ -735107,13 +739542,6 @@
         }
        ]
       ],
-      "type-supported-feature-detect.tentative.html": [
-       "571539cd5de91ba296d22d70f2cf628abf81ff95",
-       [
-        null,
-        {}
-       ]
-      ],
       "usermedia": {
        "idlharness.tentative.window.js": [
         "3d8e4168be32ab0a0a6c58bb27e6880b55132066",
@@ -735141,7 +739569,7 @@
         ]
        ],
        "lang-attribute-update.html": [
-        "21521e3b04d0dda25363a8cb90e40760e232c9f5",
+        "632df65173555f3cc6f83f8afdaee0353a8a7573",
         [
          null,
          {
@@ -735149,6 +739577,17 @@
          }
         ]
        ],
+       "legacy-mode": {
+        "legacy-mode.tentative.https.html": [
+         "972bc1de91af2f2e2872b078f05f4dd46f43510a",
+         [
+          null,
+          {
+           "testdriver": true
+          }
+         ]
+        ]
+       },
        "no-children-rendered.tentative.html": [
         "694232922fca6c9d478566c0eeb6c934ceb4b135",
         [
@@ -735157,7 +739596,7 @@
         ]
        ],
        "no-focus.tentative.html": [
-        "5de16474ac78f5bbb19762334a2c3b6137674916",
+        "f8acda9f6980bc7e49b4474a25997d985b4ddbef",
         [
          null,
          {
@@ -735214,6 +739653,16 @@
          }
         ]
        ],
+       "usermedia-set-constraints-sanitization.tentative.https.html": [
+        "136dce4a889f50825346969abcccbd6ef1933488",
+        [
+         null,
+         {
+          "testdriver": true,
+          "timeout": "long"
+         }
+        ]
+       ],
        "usermedia-untrusted-click.tentative.https.html": [
         "4bd7bd217ba9ddda26afc962d4f580ff457f19cd",
         [
@@ -735555,6 +740004,13 @@
         }
        ]
       ],
+      "popover-hint-dialog-dismisses-unrelated-auto.html": [
+       "4c8f17dc4272d0b89e5e39c3b376200d4a6607d4",
+       [
+        null,
+        {}
+       ]
+      ],
       "popover-hint-hierarchy.html": [
        "c09e3d952abb75398ae030e0b40ee02744c41766",
        [
@@ -735651,7 +740107,7 @@
        ]
       ],
       "popover-light-dismiss-hint.html": [
-       "c21397db76dc710871f695e7b217c41185906e2a",
+       "25fe6abe631a75e5830f447497f4a5666fe67187",
        [
         null,
         {
@@ -735714,6 +740170,13 @@
         }
        ]
       ],
+      "popover-manual-hide-does-not-close-auto.html": [
+       "eb46f00b01649453ac908c015ab1e0b2d1b69561",
+       [
+        null,
+        {}
+       ]
+      ],
       "popover-minimum-role.html": [
        "b46ca81fb526d828ce15806850999d002cc01ab0",
        [
@@ -735840,7 +740303,7 @@
        ]
       ],
       "popover-target-element-disabled.html": [
-       "d5c951768c083aa0fea3c6d473c9a370f386f31b",
+       "43fb02fa098ff5cb2d2c7a325ee7005fc3fd4562",
        [
         null,
         {}
@@ -739937,6 +744400,13 @@
          {}
         ]
        ],
+       "svg-script-fragment-parsed-not-executed-on-clone.html": [
+        "34cac83dbe3d49eec7e09a0651cf02fefb4c09e5",
+        [
+         null,
+         {}
+        ]
+       ],
        "text-module": {
         "charset-2.html": [
          "96bdc9964361d8a30a4d45bb46ea5cc71697d1d1",
@@ -740122,7 +744592,7 @@
          ]
         ],
         "repeated-imports.any.js": [
-         "547023157da4721f1d3b0eb9c733773f9d537b55",
+         "d2e2b0983a61e80ddfc6d78063046cbe2573a130",
          [
           "html/semantics/scripting-1/the-script-element/text-module/repeated-imports.any.html",
           {
@@ -745458,7 +749928,7 @@
         ]
        ],
        "new-scroll-event-dispatched-at-next-updating-rendering-time.html": [
-        "8a774294fa7192a80aff89450031905bd0b77d35",
+        "0de3b1c9af9429bf73038664e05d1c877d00e049",
         [
          null,
          {}
@@ -747691,7 +752161,7 @@
      ]
     ],
     "inert-iframe-tabbing.html": [
-     "a0146b74cc2abdb9590c322ec1a98d54c1adbf62",
+     "7ba83c3a935c8adee4f2849f0b366fee25844987",
      [
       null,
       {
@@ -747741,7 +752211,7 @@
      ]
     ],
     "inert-node-is-unfocusable.html": [
-     "5096c877280925b78bb4094a844c91a5fc15a199",
+     "adaddea125e6942e175e255088c87969eb36cc9a",
      [
       null,
       {
@@ -749071,7 +753541,7 @@
       ]
      ],
      "send_keys.html": [
-      "71ca4405f48dae89c820775a10f1d000866380d8",
+      "f987f29dee8a62e6a49898623a1102f18d312e7a",
       [
        null,
        {
@@ -750250,6 +754720,13 @@
      ]
     ],
     "v2": {
+     "3d-transform-flattened-preserve-3d-occlusion.html": [
+      "9999fb3b5ef2fe2fead6b59113e22596442c703e",
+      [
+       null,
+       {}
+      ]
+     ],
      "3d-transform-occlusion-2.html": [
       "9d182dca9c73c4e0bc6821f741365362408f35ab",
       [
@@ -751247,6 +755724,13 @@
       {}
      ]
     ],
+    "image-element-when-fully-active.html": [
+     "f157575b75e01613a2d1b0a59b3e28ba206f189e",
+     [
+      null,
+      {}
+     ]
+    ],
     "image-full-viewport.html": [
      "2f8e17fb3220729eb1ee8841efdb9d3bcc8256d0",
      [
@@ -751268,6 +755752,13 @@
       {}
      ]
     ],
+    "image-removed-after-paint.html": [
+     "1d4fd6d1d7bd203e40854d5888406d657987f4b6",
+     [
+      null,
+      {}
+     ]
+    ],
     "image-removed-before-load.html": [
      "08e5ee56dba1507cab5d444f01bcbaa18c675d68",
      [
@@ -753088,7 +757579,7 @@
      ]
     ],
     "loaf-congested-moment-worker.html": [
-     "0c298fc79e2d8117a2551bd326f7423f1ecdcd71",
+     "73eb0aed831520c5109dc1fce8deac926f01e558",
      [
       null,
       {
@@ -755213,6 +759704,13 @@
         }
        ]
       ],
+      "tabindex-003.html": [
+       "cc4e0d1bc223681873d01fe5f8fb3e0e87217cb6",
+       [
+        null,
+        {}
+       ]
+      ],
       "tabindex-focus-001.html": [
        "dbd21a9af167bb42d6d836ff364733572739e064",
        [
@@ -755886,10 +760384,12 @@
     ],
     "dedicated-worker": {
      "mediasource-worker-detach-element.html": [
-      "0f74d953723a40f5ff7087fdea4c4cc396f8ccad",
+      "a4da3f922cc06b605ff3e413a26ca244eee27732",
       [
        null,
-       {}
+       {
+        "timeout": "long"
+       }
       ]
      ],
      "mediasource-worker-duration.html": [
@@ -756290,6 +760790,15 @@
       }
      ]
     ],
+    "mediasource-iamf-playback.html": [
+     "2ac4fc4611f4967d1b893a5ebfec0719a68b66b2",
+     [
+      null,
+      {
+       "testdriver": true
+      }
+     ]
+    ],
     "mediasource-invalid-codec.html": [
      "19aa00c4d5f4966ef8a5de657341141f69c08ea3",
      [
@@ -770780,6 +775289,15 @@
       }
      ]
     ],
+    "request-picture-in-picture-consumes-user-activation.html": [
+     "06182d01c983ac35bbf248c0414ca63548f79610",
+     [
+      null,
+      {
+       "testdriver": true
+      }
+     ]
+    ],
     "request-picture-in-picture-exits-existing.html": [
      "4674044021655dcfd0727508e6d3dbaef7d9508b",
      [
@@ -772968,6 +777486,15 @@
        "testdriver": true
       }
      ]
+    ],
+    "touch-scroll-axis-lock-with-scroll-chain.html": [
+     "46c31b0558955849f15bef0864d3d8999ab3b4c3",
+     [
+      "pointerevents/touch-scroll-axis-lock-with-scroll-chain.html?touch",
+      {
+       "testdriver": true
+      }
+     ]
     ]
    },
    "pointerlock": {
@@ -773289,6 +777816,22 @@
       {}
      ]
     ],
+    "link-preload-as-case-insensitive.html": [
+     "429fd2a13571177c518917d3493bdc02fc6d17a9",
+     [
+      null,
+      {}
+     ]
+    ],
+    "modulepreload-already-in-module-map.html": [
+     "017d4b3bb07899adb7e6e63dc531c9b7c0af23c1",
+     [
+      null,
+      {
+       "timeout": "long"
+      }
+     ]
+    ],
     "modulepreload-as.html": [
      "47a8ede19d385ff4c76f327ced9057c4d26d7e07",
      [
@@ -773296,6 +777839,24 @@
       {}
      ]
     ],
+    "modulepreload-consumed-by-script.html": [
+     "898ba8f42be77a4afc4fb277cadfbd4a989a51de",
+     [
+      null,
+      {
+       "timeout": "long"
+      }
+     ]
+    ],
+    "modulepreload-fires-before-dependencies.html": [
+     "6ac7992025124a93f31692be33b54e500acaa6b4",
+     [
+      null,
+      {
+       "timeout": "long"
+      }
+     ]
+    ],
     "modulepreload-json.html": [
      "f0a882e7daaff891a55c60f0ca082a9eef18f1f2",
      [
@@ -773311,14 +777872,14 @@
      ]
     ],
     "modulepreload-sri-importmap.html": [
-     "d633fe1d51e113534568a79510951202ff33ff82",
+     "6c230e7e79b8585d41641205f601f7ecd1615aea",
      [
       null,
       {}
      ]
     ],
     "modulepreload-sri.html": [
-     "ea32a6a302525240573deb75b6e3f16b96b5e1eb",
+     "00c8f7b1fa580a157e4e303a46cf14ef14953f13",
      [
       null,
       {}
@@ -773490,6 +778051,15 @@
       {}
      ]
     ],
+    "preload-imagesrcset-in-markup.html": [
+     "fe744ccdf8374f75945e311d9cf937435955a6e7",
+     [
+      null,
+      {
+       "timeout": "long"
+      }
+     ]
+    ],
     "preload-imagesrcset-no-href.html": [
      "f7a796ab1343ad66a3b8c7f599077b402458406e",
      [
@@ -773857,6 +778427,72 @@
     ]
    },
    "push-api": {
+    "cross-origin-nested.tentative.https.sub.window.js": [
+     "3ce84a75e629b9050f1db69898b6a50a7bb88b7f",
+     [
+      "push-api/cross-origin-nested.tentative.https.sub.window.html",
+      {
+       "script_metadata": [
+        [
+         "script",
+         "/resources/testdriver.js"
+        ],
+        [
+         "script",
+         "/resources/testdriver-vendor.js"
+        ],
+        [
+         "script",
+         "/notifications/resources/helpers.js"
+        ]
+       ]
+      }
+     ]
+    ],
+    "cross-origin-same-site-request.tentative.https.sub.window.js": [
+     "e27708c03a24b490d1ed1e051441fd27dbe28d83",
+     [
+      "push-api/cross-origin-same-site-request.tentative.https.sub.window.html",
+      {
+       "script_metadata": [
+        [
+         "script",
+         "/resources/testdriver.js"
+        ],
+        [
+         "script",
+         "/resources/testdriver-vendor.js"
+        ],
+        [
+         "script",
+         "/notifications/resources/helpers.js"
+        ]
+       ]
+      }
+     ]
+    ],
+    "cross-origin-serviceworker.tentative.https.sub.window.js": [
+     "8bfc64156a14ee662818634b06c8b80bc6857bd1",
+     [
+      "push-api/cross-origin-serviceworker.tentative.https.sub.window.html",
+      {
+       "script_metadata": [
+        [
+         "script",
+         "/resources/testdriver.js"
+        ],
+        [
+         "script",
+         "/resources/testdriver-vendor.js"
+        ],
+        [
+         "script",
+         "/notifications/resources/helpers.js"
+        ]
+       ]
+      }
+     ]
+    ],
     "idlharness.https.any.js": [
      "d32f6fbc58cba2d2ec3cd4f9ec4d68668951ff07",
      [
@@ -774114,6 +778750,28 @@
        ]
       }
      ]
+    ],
+    "worker-subscribe.https.window.js": [
+     "b492d02708742a62201731a014d24cee039ee593",
+     [
+      "push-api/worker-subscribe.https.window.html",
+      {
+       "script_metadata": [
+        [
+         "script",
+         "/resources/testdriver.js"
+        ],
+        [
+         "script",
+         "/resources/testdriver-vendor.js"
+        ],
+        [
+         "script",
+         "/notifications/resources/helpers.js"
+        ]
+       ]
+      }
+     ]
     ]
    },
    "quirks": {
@@ -792180,7 +796838,7 @@
       ]
      ],
      "scroll-timeline-name-shadow.html": [
-      "a535b2a44fe80678f74a08564784a5ef43f593fd",
+      "b2a54cd9aedf0857e700557ded16587822697e25",
       [
        null,
        {}
@@ -792425,7 +797083,7 @@
       ]
      ],
      "view-timeline-name-shadow.html": [
-      "11902a3c6ebe07230cc80133b0e06d0215952c2e",
+      "3d8723eff9288c2b7c516eb13f74417f7815fa8a",
       [
        null,
        {}
@@ -792852,7 +797510,7 @@
       ]
      ],
      "intermediate-transform.html": [
-      "89a4a648a9ad04fdf958a5a780f9e54dba64bb15",
+      "24ec968d14c854fd49468075ff244687d9dd8313",
       [
        null,
        {}
@@ -792959,7 +797617,7 @@
       ]
      ],
      "view-timeline-inset.html": [
-      "03d762d12a0f62ad1eab37379809d8ace574c8f4",
+      "ff3abfe7ab6917f9f963c8c0e31e8d9a537b1173",
       [
        null,
        {}
@@ -792973,7 +797631,7 @@
       ]
      ],
      "view-timeline-on-display-none-element.html": [
-      "1cc23fe626ab7ee01f2a36331bb983ba12fe2a93",
+      "f56c30366df71692c20dad895dd756d36bc81a43",
       [
        null,
        {}
@@ -793942,6 +798600,13 @@
         "testdriver": true
        }
       ]
+     ],
+     "selection-outside-focused-editing-host.tentative.html": [
+      "1ab72de0bba9017cacc21c06048cf7e158979ce3",
+      [
+       null,
+       {}
+      ]
      ]
     },
     "deleteFromDocument-HTMLDetails.html": [
@@ -793976,8 +798641,17 @@
       }
      ]
     ],
+    "drag-selection-contenteditable-to-out-of-flow-user-select-none.html": [
+     "2d3a2193b1fccd0a8150290e5f54aba7fe7bb4af",
+     [
+      null,
+      {
+       "testdriver": true
+      }
+     ]
+    ],
     "drag-selection-extend-to-user-select-none.html": [
-     "e69d70152fa39e4a0f6bbef180bc116d19a48f42",
+     "6b21b65f2fb40e5ee089c4e2d82d15153d301947",
      [
       null,
       {
@@ -794354,6 +799028,13 @@
      ]
     ],
     "shadow-dom": {
+     "cross-shadow-boundary-extend.html": [
+      "7be40902eea63b21578f4765057d9ce971e7ea61",
+      [
+       null,
+       {}
+      ]
+     ],
      "selection-at-nodes-not-part-of-flattened-tree.html": [
       "f272f3977c07c44c40afc57e00c3cbc48d01b44a",
       [
@@ -797694,6 +802375,15 @@
        }
       ]
      ],
+     "resource-timing-cross-origin-server-timing.https.html": [
+      "5dda0876fb88722b4a67e64839f2391e76b81262",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
      "resource-timing-cross-origin.https.html": [
       "9184a5933a8ae4d056e9e4bff455f01eafdcc7c6",
       [
@@ -801289,7 +805979,7 @@
        ]
       ],
       "navigation-api-precommit-handler.html": [
-       "025153d0d1de38c8de29c63b609e33eefe1b7411",
+       "7dff467f8735cd50acb4f44804172e62845dde01",
        [
         null,
         {
@@ -801334,7 +806024,7 @@
        ]
       ],
       "video-srcObject-change.html": [
-       "b6b9013b1f65a307bc00cd7a96d7fabb81a7a556",
+       "6ca353c513ebca5d0e99ac1b230989f383ed3e5a",
        [
         null,
         {
@@ -801821,7 +806511,7 @@
      ]
     ],
     "navigation-type.tentative.html": [
-     "4306f51e1b00ee6d759aa7addcc5ba78a2a46369",
+     "9552a4f36779b4befabaab248fce6c9b1225a6f6",
      [
       null,
       {
@@ -801866,7 +806556,7 @@
      ]
     ],
     "replace.tentative.html": [
-     "8697f03902ac7ac3b1793de61b68613d70655efd",
+     "6ab0011c7285fda95d2daf50837c2d20ae1fbe0b",
      [
       null,
       {
@@ -801992,7 +806682,7 @@
        ]
       ],
       "video-src-change.html": [
-       "742782262503acf29da66d62c5639df7eb3d68a8",
+       "8ad51cce7c8f736b8ac91e7820ddc4a78e6988f6",
        [
         null,
         {
@@ -803425,6 +808115,21 @@
        }
       ]
      ],
+     "cross-origin-iframe-prerender-nesting.https.html": [
+      "9df5ee4c4b86d15644f931c524d76db2e08ac006",
+      [
+       "speculation-rules/prerender/cross-origin-iframe-prerender-nesting.https.html?target_hint=_blank",
+       {
+        "timeout": "long"
+       }
+      ],
+      [
+       "speculation-rules/prerender/cross-origin-iframe-prerender-nesting.https.html?target_hint=_self",
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
      "cross-origin-iframe-prerender.https.html": [
       "1b55e2148620787a35070f74af43a41d7aeb794a",
       [
@@ -805441,8 +810146,24 @@
        {}
       ]
      ],
+     "performance-speculations-navigations-eagerness.tentative.https.html": [
+      "b288a0d421f7468224b3ba3c0f4d9a0a6564b2d7",
+      [
+       null,
+       {}
+      ]
+     ],
+     "performance-speculations-navigations-pointerdown.tentative.https.html": [
+      "6fd7426ce1e294e4ddbd6c93db8ebac729c2d071",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
      "performance-speculations-navigations.tentative.https.html": [
-      "c2f56253fcb53c5d372ea5f8293efb29f410f91e",
+      "30487e256f2dbf4dd095699bf1ff0283c270e1e3",
       [
        null,
        {}
@@ -805463,7 +810184,7 @@
       ]
      ],
      "performance-speculations-preredirect-urls.tentative.https.html": [
-      "3dc424735d23d81833bde5694318a1f1d09fbb24",
+      "48b8730d8876702ec240a7f32354da2920363524",
       [
        null,
        {}
@@ -812566,7 +817287,7 @@
    },
    "subapps": {
     "add-error.tentative.https.html": [
-     "fed524e6512790a20049a60603fceb1061b0a812",
+     "a39aee225d211a1639db892a8acb9acdad1f6903",
      [
       null,
       {
@@ -812575,7 +817296,7 @@
      ]
     ],
     "add-success.tentative.https.html": [
-     "343e7754957cd8e91eedd35b8274e40414fc426f",
+     "7cee7a85a6034bd0b4abaf489a11258c1af5651e",
      [
       null,
       {
@@ -812583,6 +817304,13 @@
       }
      ]
     ],
+    "disabled-by-permissions-policy.tentative.https.html": [
+     "827bc84702ae3ba73c402a11d5e45625b41252d4",
+     [
+      null,
+      {}
+     ]
+    ],
     "idlharness.tentative.https.window.js": [
      "cda0813a9aac8d52f37b45dc10a4f3b6ac81dc11",
      [
@@ -812601,39 +817329,40 @@
       }
      ]
     ],
-    "insecure-context-error.tentative.html": [
-     "2882f8726dd613ad8b2b7d64373a7bc718004511",
-     [
-      null,
-      {}
-     ]
-    ],
     "list-error.tentative.https.html": [
-     "64a6a6484139a191df37e2806ff0d86dc69bb15e",
+     "b3f8ba8d7327b307156d0d619c8704ff17962101",
      [
       null,
-      {}
+      {
+       "testdriver": true
+      }
      ]
     ],
     "list-success.tentative.https.html": [
-     "3e864c53796de6a8a456909d3b661c32dc71fabf",
+     "bf2aacd8729b228d048737e38b5ea8f38884c12f",
      [
       null,
-      {}
+      {
+       "testdriver": true
+      }
      ]
     ],
     "remove-error.tentative.https.html": [
-     "8aaa4974dd4cd583b1b62bdaf7b2c2c6f6ec9b8d",
+     "b46f8a55a0b56d80ac455cb40014536337ef2287",
      [
       null,
-      {}
+      {
+       "testdriver": true
+      }
      ]
     ],
     "remove-success.tentative.https.html": [
-     "8cba19e49f5b792cfb3e27b8d6e89793733e4c46",
+     "a348d9a251fd4f61565f8e0253d433a0b933d0de",
      [
       null,
-      {}
+      {
+       "testdriver": true
+      }
      ]
     ]
    },
@@ -813976,6 +818705,13 @@
        {}
       ]
      ],
+     "animateMotion-rotate-angle.html": [
+      "ee8f3ef6e3f35503f60f045335cee6b0e45cc449",
+      [
+       null,
+       {}
+      ]
+     ],
      "animateMotion-spline-invalid-keyTimes.html": [
       "27e66caa9d5aec0b4231bd7ed5eb631274e4da95",
       [
@@ -814743,6 +819479,13 @@
        {}
       ]
      ],
+     "svgintegeroptionalinteger-animation-invalid-value-2.html": [
+      "f872b10967c7905b5c406d199f115ca03a6607cc",
+      [
+       null,
+       {}
+      ]
+     ],
      "svglength-additive-by-1.html": [
       "b39a21380c0e8c49fa839dbff5e3f78723226885",
       [
@@ -815074,6 +819817,20 @@
        {}
       ]
      ],
+     "svgnumber-animation-invalid-value-2.html": [
+      "80b81a5f799c3e1c67aef72c6c50ddd0a82c091d",
+      [
+       null,
+       {}
+      ]
+     ],
+     "svgnumber-to-animation-valid-value-1.html": [
+      "4fc4dd3201b558675d9a2d0449f0d8420bcd8e41",
+      [
+       null,
+       {}
+      ]
+     ],
      "svgnumberlist-animation-1.html": [
       "aabb7d7daa159409706ad2ff59e54220218f25a2",
       [
@@ -815144,6 +819901,13 @@
        {}
       ]
      ],
+     "svgnumberoptionalnumber-animation-invalid-value-2.html": [
+      "c5827f99671191d641244865e1283984d9c1b789",
+      [
+       null,
+       {}
+      ]
+     ],
      "svgpath-animation-1.tentative.html": [
       "3df821d7c310f6c1607786f14a6c6bafef272fb5",
       [
@@ -815165,6 +819929,13 @@
        {}
       ]
      ],
+     "svgpath-animation-invalid-value-2.html": [
+      "4b93ceb58f6e76d4331412de9f74fa35dba2ba9a",
+      [
+       null,
+       {}
+      ]
+     ],
      "svgpointlist-animation-1.html": [
       "a3340ba83a2a0f6846d0a9e7faa1c27dd49a1383",
       [
@@ -815827,6 +820598,13 @@
        {}
       ]
      ],
+     "svg-geometry-apis-in-non-rendered-elements.html": [
+      "afc9bdeb6ddf7c30fb81d1934fb33fddd6d8ffa8",
+      [
+       null,
+       {}
+      ]
+     ],
      "svg-get-bounding-client-rect-in-non-rendered-elements.html": [
       "a1fa7f3b925196d7d7006c52d489f7432c3152ff",
       [
@@ -816051,6 +820829,13 @@
         {}
        ]
       ],
+      "focus-tabindex-invalid-value.svg": [
+       "c005276fafe39852bd4c44132c78c9562d2d3736",
+       [
+        null,
+        {}
+       ]
+      ],
       "module-01.svg": [
        "b52ab23a5646db0608894b6baf343cf4648b14e2",
        [
@@ -817985,7 +822770,7 @@
        ]
       ],
       "SVGAnimatedRect.html": [
-       "a4842dc7d462596c09c9c4065bc3f1e3204a977f",
+       "af7ffd92a3085e6bf69ca1a70a21e67abbcfb253",
        [
         null,
         {}
@@ -820853,7 +825638,7 @@
        ]
       ],
       "focus-contained.html": [
-       "8df612c1080a82ed4ffaae0c72287735af1e8cf0",
+       "553687236ed017b7136c61275e66963cbfcbdeab",
        [
         null,
         {
@@ -825267,7 +830052,7 @@
       ]
      ],
      "fallback-roles.html": [
-      "320b5b7ddbd394eb6bac02863cf2575e073a5853",
+      "d90958e22ae4bdd7e936a9bf1ab932752504f4e5",
       [
        null,
        {
@@ -825402,7 +830187,7 @@
       ]
      ],
      "tab-roles.html": [
-      "f84504e0b45c8c831a8eb60acbf2bd7d664070b0",
+      "99c4c0cb58960d09868bd86095f29f6b87ae52a6",
       [
        null,
        {
@@ -838061,6 +842846,13 @@
         {}
        ]
       ],
+      "constant-source-onended-not-connected.html": [
+       "2d080b5de2effafbeb7307b55cba5661ef3b99a2",
+       [
+        null,
+        {}
+       ]
+      ],
       "constant-source-onended.html": [
        "64bc54f21b32fece90c58e70c22112ff24735576",
        [
@@ -849205,6 +853997,13 @@
        {}
       ]
      ],
+     "no-frame-documents.https.html": [
+      "6cdddcc76d07c70ef2f7795d7f5d850e8e577549",
+      [
+       null,
+       {}
+      ]
+     ],
      "opaque-origin-tools.https.html": [
       "491a4cd1acf2240277c48c491675689915a68831",
       [
@@ -878480,7 +883279,7 @@
      ]
     ],
     "RTCSctpTransport-events.html": [
-     "57b691a9cd620fa3836d9e645c712e4cbdd40940",
+     "cc3cdfa7c29cfd028bcfa813f01a5714f6d04518",
      [
       null,
       {}
@@ -879070,7 +883869,7 @@
       ]
      ],
      "rtp-headerextensions.html": [
-      "d1d8bb62bf8005cc8f5f9fc06b88902fd9fab05a",
+      "65650e5943f9e77435dc7d2663b7e2201aff2484",
       [
        null,
        {}
@@ -895033,7 +899832,7 @@
      ]
     ],
     "draining.https.any.js": [
-     "73a874c7e10672dbf7e51cdaf5816d2bfa78b1ae",
+     "690c1a841e4add4fe2afb8428a9b9117ddd087dc",
      [
       "webtransport/draining.https.any.html",
       {
@@ -895537,6 +900336,89 @@
       }
      ]
     ],
+    "receive-stream-pull.https.any.js": [
+     "24c2d5b8e5101d72515e1bb3135ed7bdfd9782a9",
+     [
+      "webtransport/receive-stream-pull.https.any.html",
+      {
+       "script_metadata": [
+        [
+         "global",
+         "window,worker"
+        ],
+        [
+         "script",
+         "resources/webtransport-test-helpers.sub.js"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ],
+     [
+      "webtransport/receive-stream-pull.https.any.serviceworker.html",
+      {
+       "script_metadata": [
+        [
+         "global",
+         "window,worker"
+        ],
+        [
+         "script",
+         "resources/webtransport-test-helpers.sub.js"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ],
+     [
+      "webtransport/receive-stream-pull.https.any.sharedworker.html",
+      {
+       "script_metadata": [
+        [
+         "global",
+         "window,worker"
+        ],
+        [
+         "script",
+         "resources/webtransport-test-helpers.sub.js"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ],
+     [
+      "webtransport/receive-stream-pull.https.any.worker.html",
+      {
+       "script_metadata": [
+        [
+         "global",
+         "window,worker"
+        ],
+        [
+         "script",
+         "resources/webtransport-test-helpers.sub.js"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ]
+    ],
     "sendgroup.https.any.js": [
      "ef72f16c2224d3661d806b9fd29dd003d75cfaec",
      [
@@ -903623,7 +908505,7 @@
       ]
      ],
      "constructor-submitter.html": [
-      "542357df34d6087bda3f79a240f982d443082548",
+      "553a102acf1dd8e7a631ad6055fd7d09c47edb26",
       [
        null,
        {}
@@ -903874,6 +908756,13 @@
       {}
      ]
     ],
+    "getallresponseheaders-after-reuse.html": [
+     "92f0d5c983468adf60a224a4b9bb8e7b190ad617",
+     [
+      null,
+      {}
+     ]
+    ],
     "getallresponseheaders-cookies.htm": [
      "2cd8098185963658171889ecfe226b44c02bbb9d",
      [
@@ -904589,6 +909478,31 @@
       }
      ]
     ],
+    "responseurl-after-abort.any.js": [
+     "e5ca4408fdf112fd13c0784794d69067133f572d",
+     [
+      "xhr/responseurl-after-abort.any.html",
+      {
+       "script_metadata": [
+        [
+         "title",
+         "XMLHttpRequest: responseURL is the empty string after abort()"
+        ]
+       ]
+      }
+     ],
+     [
+      "xhr/responseurl-after-abort.any.worker.html",
+      {
+       "script_metadata": [
+        [
+         "title",
+         "XMLHttpRequest: responseURL is the empty string after abort()"
+        ]
+       ]
+      }
+     ]
+    ],
     "responseurl.html": [
      "b730e045d8d5cf8799d023521cea99f3e18e0809",
      [
@@ -905194,7 +910108,7 @@
      ]
     ],
     "send-usp.any.js": [
-     "b0baf4a525ad20185d15a2e2659b9202285ce178",
+     "dcc532a7708c93e4150972ae4ffba6df264eb439",
      [
       "xhr/send-usp.any.html",
       {}
@@ -905862,7 +910776,7 @@
       ]
      ],
      "document-function.window.js": [
-      "a01f3b37c945d0fcded4a0853d32e97d60bb9696",
+      "99fab7ab823f54f25f51ba63710799ecf12e132d",
       [
        "xml/xslt/document-function.window.html",
        {}
@@ -918136,7 +923050,7 @@
       "browsing_context": {
        "activate": {
         "activate.py": [
-         "f6e5b9d27a8a90584c290555b7ba6b8b2192b77c",
+         "35e4a34892a2a283ac3beb025026a41615d3eff6",
          [
           null,
           {}
@@ -918258,7 +923172,7 @@
        },
        "create": {
         "background.py": [
-         "dc5d1e22614f9128365f977dae86e7162e41c0f0",
+         "cc060158752e4bde1fc62f734c6cb3c250424e45",
          [
           null,
           {}
@@ -918279,7 +923193,7 @@
          ]
         ],
         "type.py": [
-         "f96598fa5140c2b63d754014399059e2de2b8a19",
+         "db46704bae3debc1532f3a8744989b6137224621",
          [
           null,
           {}
@@ -921563,14 +926477,14 @@
        "webauthn": {
         "add_credential": {
          "add.py": [
-          "502a09247e1e7010ae10a7701ab1dac547281a2b",
+          "42418947b12aa263210a37e9f3986184e8bc6910",
           [
            null,
            {}
           ]
          ],
          "invalid.py": [
-          "c2a51dfa9b81a211985adf2465c202c3d96781ce",
+          "57401c56cc66e000935236d46ec9baff5858a6b7",
           [
            null,
            {}
@@ -921595,7 +926509,7 @@
         },
         "get_credentials": {
          "get.py": [
-          "91f09b1020ca46c8da6abbffe470d62291c403f2",
+          "347d7c8b293bed6a48e5e18656428f051dbbad0b",
           [
            null,
            {}
diff --git a/third_party/blink/web_tests/external/wpt/FileAPI/reading-data-section/filereader_abort.any.js b/third_party/blink/web_tests/external/wpt/FileAPI/reading-data-section/filereader_abort.any.js
index c778ae5..2563dce5 100644
--- a/third_party/blink/web_tests/external/wpt/FileAPI/reading-data-section/filereader_abort.any.js
+++ b/third_party/blink/web_tests/external/wpt/FileAPI/reading-data-section/filereader_abort.any.js
@@ -36,3 +36,32 @@
               assert_equals(readerAbort.readyState, readerAbort.DONE);
             });
       }, "Aborting after read");
+
+    promise_test(t => {
+        var reader = new FileReader();
+
+        var eventWatcher = new EventWatcher(t, reader,
+            ['abort', 'loadstart', 'loadend', 'error', 'load']);
+
+        // First read: run to completion so the reader reaches the DONE state.
+        reader.readAsText(new Blob(["first read"]));
+        return eventWatcher.wait_for('loadstart')
+          .then(() => eventWatcher.wait_for('load'))
+          .then(() => eventWatcher.wait_for('loadend'))
+          .then(() => {
+              assert_equals(reader.readyState, reader.DONE);
+
+              // Reuse the same reader for a second read and abort it while it is
+              // still LOADING. 'abort' and 'loadend' are dispatched synchronously,
+              // so call wait_for before calling abort.
+              reader.readAsText(new Blob(["second read"]));
+              assert_equals(reader.readyState, reader.LOADING);
+              var nextEvent = eventWatcher.wait_for(['abort', 'loadend']);
+              reader.abort();
+              return nextEvent;
+            })
+          .then(() => {
+              assert_equals(reader.result, null);
+              assert_equals(reader.readyState, reader.DONE);
+            });
+      }, "Aborting a reused reader after a previous read completed");
diff --git a/third_party/blink/web_tests/external/wpt/FileAPI/url/url-in-tags-revoke.window.js b/third_party/blink/web_tests/external/wpt/FileAPI/url/url-in-tags-revoke.window.js
index 1cdad79f..476daba6 100644
--- a/third_party/blink/web_tests/external/wpt/FileAPI/url/url-in-tags-revoke.window.js
+++ b/third_party/blink/web_tests/external/wpt/FileAPI/url/url-in-tags-revoke.window.js
@@ -94,12 +94,31 @@
   e.onload = t.step_func_done(() => {
     assert_equals(window.script_test_result, run_result);
   });
+  e.onerror = t.unreached_func();
 
   document.body.appendChild(e);
   URL.revokeObjectURL(url);
 }, 'Fetching a blob URL immediately before revoking it works in <script> tags.');
 
 async_test(t => {
+  const run_result = 'test_script_OK';
+  const blob_contents = 'window.script_test_result = "' + run_result + '";';
+  const blob = new Blob([blob_contents], {type: "application/javascript"});
+  const url = URL.createObjectURL(blob);
+
+  const e = document.createElement('script');
+  e.setAttribute('src', url);
+  e.setAttribute('type', 'module');
+  e.onload = t.step_func_done(() => {
+    assert_equals(window.script_test_result, run_result);
+  });
+  e.onerror = t.unreached_func();
+
+  document.body.appendChild(e);
+  URL.revokeObjectURL(url);
+}, 'Fetching a blob URL immediately before revoking it works in module <script> tags.');
+
+async_test(t => {
   const channel_name = 'a-click-test';
   const blob = new Blob([window_contents_for_channel(channel_name)], {type: 'text/html'});
   receive_message_on_channel(t, channel_name).then(t.step_func_done(t => {
diff --git a/third_party/blink/web_tests/external/wpt/FileAPI/url/url-in-tags.window.js b/third_party/blink/web_tests/external/wpt/FileAPI/url/url-in-tags.window.js
index 8a722dc..e14c41a 100644
--- a/third_party/blink/web_tests/external/wpt/FileAPI/url/url-in-tags.window.js
+++ b/third_party/blink/web_tests/external/wpt/FileAPI/url/url-in-tags.window.js
@@ -16,11 +16,29 @@
   e.onload = t.step_func_done(() => {
     assert_equals(window.test_result, run_result);
   });
+  e.onerror = t.unreached_func();
 
   document.body.appendChild(e);
 }, 'Blob URLs can be used in <script> tags');
 
 async_test(t => {
+  const run_result = 'test_script_OK';
+  const blob_contents = 'window.test_result = "' + run_result + '";';
+  const blob = new Blob([blob_contents], {type: 'application/javascript'});
+  const url = URL.createObjectURL(blob);
+
+  const e = document.createElement('script');
+  e.setAttribute('src', url);
+  e.setAttribute('type', 'module');
+  e.onload = t.step_func_done(() => {
+    assert_equals(window.test_result, run_result);
+  });
+  e.onerror = t.unreached_func();
+
+  document.body.appendChild(e);
+}, 'Blob URLs can be used in module <script> tags');
+
+async_test(t => {
   const run_result = 'test_frame_OK';
   const blob_contents = '<!doctype html>\n<meta charset="utf-8">\n' +
     '<script>window.test_result = "' + run_result + '";</script>';
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves.js
new file mode 100644
index 0000000..25113bf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves.js
@@ -0,0 +1,128 @@
+async function defineCfrgTests(algorithmName, operation) {
+    const subtle = self.crypto.subtle;
+    const isDeriveBits = operation === "deriveBits";
+
+    kSmallOrderPoint[algorithmName].forEach(test => {
+        promise_test(async() => {
+            let privateKey;
+            let publicKey;
+            let derived;
+            let error;
+
+            try {
+                privateKey = await subtle.importKey(
+                    "pkcs8",
+                    pkcs8[algorithmName],
+                    {name: algorithmName},
+                    false,
+                    ["deriveBits", "deriveKey"]
+                );
+                publicKey = await subtle.importKey(
+                    "spki",
+                    test.vector,
+                    {name: algorithmName},
+                    false,
+                    []
+                );
+                derived = isDeriveBits
+                    ? await subtle.deriveBits(
+                        {name: algorithmName, public: publicKey},
+                        privateKey,
+                        8 * sizes[algorithmName]
+                    )
+                    : await subtle.deriveKey(
+                        {name: algorithmName, public: publicKey},
+                        privateKey,
+                        {name: "HMAC", hash: "SHA-256", length: 256},
+                        true,
+                        ["sign", "verify"]
+                    );
+            } catch (caught) {
+                error = caught;
+            }
+
+            assert_not_equals(privateKey, undefined, "Private key should be valid.");
+            assert_not_equals(publicKey, undefined, "Public key should be valid.");
+            assert_not_equals(error, undefined, "Operation should fail.");
+            assert_equals(
+                error.name,
+                "OperationError",
+                "Should throw correct error, not " + error.name + ": " + error.message + "."
+            );
+            assert_equals(derived, undefined, "Operation succeeded, but should not have.");
+        }, algorithmName +
+            (isDeriveBits ? " key derivation" : " deriveBits") +
+            " checks for all-zero value result with a key of order " + test.order);
+    });
+
+    if (!isDeriveBits) {
+        promise_test(async() => {
+            const key = await subtle.generateKey(
+                {name: algorithmName},
+                true,
+                ["deriveKey", "deriveBits"]
+            );
+            const derived = await subtle.deriveKey(
+                {name: algorithmName, public: key.publicKey},
+                key.privateKey,
+                {name: "HMAC", hash: "SHA-256", length: 256},
+                true,
+                ["sign", "verify"]
+            );
+            assert_not_equals(derived, undefined, "Key derivation failed.");
+        }, "Key derivation using a " + algorithmName + " generated keys.");
+    }
+
+    const noUsage = isDeriveBits ? ["deriveKey"] : ["deriveBits"];
+    const [
+        privateKey,
+        noUsagePrivateKey,
+        publicKey,
+        ecdhPublicKey,
+    ] = await Promise.all([
+        subtle.importKey(
+            "pkcs8",
+            pkcs8[algorithmName],
+            {name: algorithmName},
+            false,
+            ["deriveBits", "deriveKey"]
+        ),
+        subtle.importKey(
+            "pkcs8",
+            pkcs8[algorithmName],
+            {name: algorithmName},
+            false,
+            noUsage
+        ),
+        subtle.importKey(
+            "spki",
+            spki[algorithmName],
+            {name: algorithmName},
+            false,
+            []
+        ),
+        subtle.importKey(
+            "spki",
+            ecSPKI,
+            {name: "ECDH", namedCurve: "P-256"},
+            false,
+            []
+        ),
+    ]);
+
+    registerDeriveTests({
+        operation,
+        algorithmName,
+        mixedCaseName: algorithmName.toLowerCase(),
+        size: sizes[algorithmName],
+        derivation: derivations[algorithmName],
+        privateKey,
+        publicKey,
+        noUsagePrivateKey,
+        invalidPublicKeys: [{
+            name: algorithmName + " mismatched algorithms",
+            key: ecdhPublicKey,
+        }],
+        missingPublicLabel: algorithmName + " missing public property",
+    });
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.js
index 1406e8b..c3826a29 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.js
@@ -1,227 +1,7 @@
 function define_tests_25519() {
-    return define_tests("X25519");
+    return defineCfrgTests("X25519", "deriveBits");
 }
 
 function define_tests_448() {
-    return define_tests("X448");
-}
-
-function define_tests(algorithmName) {
-  // May want to test prefixed implementations.
-  var subtle = self.crypto.subtle;
-
-  // Verify the derive functions perform checks against the all-zero value results,
-  // ensuring small-order points are rejected.
-  // https://www.rfc-editor.org/rfc/rfc7748#section-6.1
-  {
-      kSmallOrderPoint[algorithmName].forEach(function(test) {
-          promise_test(async() => {
-              let derived;
-              let privateKey;
-              let publicKey;
-              try {
-                  privateKey = await subtle.importKey("pkcs8", pkcs8[algorithmName],
-                                                  {name: algorithmName},
-                                                  false, ["deriveBits", "deriveKey"]);
-                  publicKey = await subtle.importKey("spki", test.vector,
-                                                 {name: algorithmName},
-                                                 false, [])
-                  derived = await subtle.deriveBits({name: algorithmName, public: publicKey}, privateKey, 8 * sizes[algorithmName]);
-              } catch (err) {
-                  assert_true(privateKey !== undefined, "Private key should be valid.");
-                  assert_true(publicKey !== undefined, "Public key should be valid.");
-                  assert_equals(err.name, "OperationError", "Should throw correct error, not " + err.name + ": " + err.message + ".");
-              }
-              assert_equals(derived, undefined, "Operation succeeded, but should not have.");
-          }, algorithmName + " key derivation checks for all-zero value result with a key of order " + test.order);
-      });
-  }
-
-  return importKeys(pkcs8, spki, sizes)
-  .then(function(results) {
-      publicKeys = results.publicKeys;
-      privateKeys = results.privateKeys;
-      noDeriveBitsKeys = results.noDeriveBitsKeys;
-      ecdhKeys = results.ecdhKeys;
-
-      {
-          // Basic success case
-          promise_test(function(test) {
-              return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName])
-              .then(function(derivation) {
-                  assert_true(equalBuffers(derivation, derivations[algorithmName]), "Derived correct bits");
-              }, function(err) {
-                  assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " good parameters");
-
-          // Case insensitivity check
-          promise_test(function(test) {
-              return subtle.deriveBits({name: algorithmName.toLowerCase(), public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName])
-              .then(function(derivation) {
-                  assert_true(equalBuffers(derivation, derivations[algorithmName]), "Derived correct bits");
-              }, function(err) {
-                  assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " mixed case parameters");
-
-          // Shorter than entire derivation per algorithm
-          promise_test(function(test) {
-              return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName] - 32)
-              .then(function(derivation) {
-                  assert_true(equalBuffers(derivation, derivations[algorithmName], 8 * sizes[algorithmName] - 32), "Derived correct bits");
-              }, function(err) {
-                  assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " short result");
-
-          // Non-multiple of 8
-          promise_test(function(test) {
-              return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName] - 11)
-              .then(function(derivation) {
-                  assert_true(equalBuffers(derivation, derivations[algorithmName], 8 * sizes[algorithmName] - 11), "Derived correct bits");
-              }, function(err) {
-                  assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " non-multiple of 8 bits");
-
-          // Errors to test:
-
-          // - missing public property TypeError
-          promise_test(function(test) {
-              return subtle.deriveBits({name: algorithmName}, privateKeys[algorithmName], 8 * sizes[algorithmName])
-              .then(function(derivation) {
-                  assert_unreached("deriveBits succeeded but should have failed with TypeError");
-              }, function(err) {
-                  assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " missing public property");
-
-          // - Non CryptoKey public property TypeError
-          promise_test(function(test) {
-              return subtle.deriveBits({name: algorithmName, public: {message: "Not a CryptoKey"}}, privateKeys[algorithmName], 8 * sizes[algorithmName])
-              .then(function(derivation) {
-                  assert_unreached("deriveBits succeeded but should have failed with TypeError");
-              }, function(err) {
-                  assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " public property of algorithm is not a CryptoKey");
-
-          // - wrong algorithm
-          promise_test(function(test) {
-              return subtle.deriveBits({name: algorithmName, public: ecdhKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName])
-              .then(function(derivation) {
-                  assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
-              }, function(err) {
-                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " mismatched algorithms");
-
-          // - No deriveBits usage in baseKey InvalidAccessError
-          promise_test(function(test) {
-              return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, noDeriveBitsKeys[algorithmName], 8 * sizes[algorithmName])
-              .then(function(derivation) {
-                  assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
-              }, function(err) {
-                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " no deriveBits usage for base key");
-
-          // - Use public key for baseKey InvalidAccessError
-          promise_test(function(test) {
-              return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, publicKeys[algorithmName], 8 * sizes[algorithmName])
-              .then(function(derivation) {
-                  assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
-              }, function(err) {
-                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " base key is not a private key");
-
-          // - Use private key for public property InvalidAccessError
-          promise_test(function(test) {
-              return subtle.deriveBits({name: algorithmName, public: privateKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName])
-              .then(function(derivation) {
-                  assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
-              }, function(err) {
-                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " public property value is a private key");
-
-          // - Use secret key for public property InvalidAccessError
-          promise_test(function(test) {
-              return subtle.generateKey({name: "AES-CBC", length: 128}, true, ["encrypt", "decrypt"])
-              .then(function(secretKey) {
-                  return subtle.deriveBits({name: algorithmName, public: secretKey}, privateKeys[algorithmName], 8 * sizes[algorithmName])
-                  .then(function(derivation) {
-                      assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
-                  }, function(err) {
-                      assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-                  });
-              });
-          }, algorithmName + " public property value is a secret key");
-
-          // - Length greater than possible for particular curves OperationError
-          promise_test(function(test) {
-              return subtle.deriveBits({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], 8 * sizes[algorithmName] + 8)
-              .then(function(derivation) {
-                  assert_unreached("deriveBits succeeded but should have failed with OperationError");
-              }, function(err) {
-                  assert_equals(err.name, "OperationError", "Should throw correct error, not " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " asking for too many bits");
-      }
-  });
-
-  function importKeys(pkcs8, spki, sizes) {
-      var privateKeys = {};
-      var publicKeys = {};
-      var noDeriveBitsKeys = {};
-      var ecdhPublicKeys = {};
-
-      var promises = [];
-      {
-          var operation = subtle.importKey("pkcs8", pkcs8[algorithmName],
-                                          {name: algorithmName},
-                                          false, ["deriveBits", "deriveKey"])
-                          .then(function(key) {
-                              privateKeys[algorithmName] = key;
-                            }, function (err) {
-                              privateKeys[algorithmName] = null;
-                          });
-          promises.push(operation);
-      }
-      {
-          var operation = subtle.importKey("pkcs8", pkcs8[algorithmName],
-                                          {name: algorithmName},
-                                          false, ["deriveKey"])
-                          .then(function(key) {
-                              noDeriveBitsKeys[algorithmName] = key;
-                            }, function (err) {
-                              noDeriveBitsKeys[algorithmName] = null;
-                          });
-          promises.push(operation);
-      }
-      {
-          var operation = subtle.importKey("spki", spki[algorithmName],
-                                          {name: algorithmName},
-                                          false, [])
-                          .then(function(key) {
-                              publicKeys[algorithmName] = key;
-                            }, function (err) {
-                              publicKeys[algorithmName] = null;
-                          });
-          promises.push(operation);
-      }
-      {
-          var operation = subtle.importKey("spki", ecSPKI,
-                                           {name: "ECDH", namedCurve: "P-256"},
-                                           false, [])
-                          .then(function(key) {
-                              ecdhPublicKeys[algorithmName] = key;
-                          });
-      }
-      return Promise.all(promises)
-             .then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, noDeriveBitsKeys: noDeriveBitsKeys, ecdhKeys: ecdhPublicKeys}});
-  }
-
+    return defineCfrgTests("X448", "deriveBits");
 }
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve25519.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve25519.https.any.js
index 5684d762..b42565208 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve25519.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve25519.https.any.js
@@ -1,6 +1,8 @@
 // META: title=WebCryptoAPI: deriveKey() Using ECDH with CFRG Elliptic Curves
 // META: script=../util/helpers.js
 // META: script=cfrg_curves_bits_fixtures.js
+// META: script=derive.js
+// META: script=cfrg_curves.js
 // META: script=cfrg_curves_bits.js
 
 // Define subtests from a `promise_test` to ensure the harness does not
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any-expected.txt
index 66ab63a4e..bcf668f 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any-expected.txt
@@ -1,33 +1,15 @@
 This is a testharness.js-based test.
+[FAIL] setup - define tests
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] X448 key derivation checks for all-zero value result with a key of order 0
-  assert_true: Private key should be valid. expected true got false
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 key derivation checks for all-zero value result with a key of order 1
-  assert_true: Private key should be valid. expected true got false
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 key derivation checks for all-zero value result with a key of order p-1 (order 2)
-  assert_true: Private key should be valid. expected true got false
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 key derivation checks for all-zero value result with a key of order p (=0, order 4)
-  assert_true: Private key should be valid. expected true got false
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 key derivation checks for all-zero value result with a key of order p+1 (=1, order 1)
-  assert_true: Private key should be valid. expected true got false
-[FAIL] X448 good parameters
-  assert_unreached: deriveBits failed with error TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. Reached unreachable code
-[FAIL] X448 mixed case parameters
-  assert_unreached: deriveBits failed with error TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. Reached unreachable code
-[FAIL] X448 short result
-  assert_unreached: deriveBits failed with error TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. Reached unreachable code
-[FAIL] X448 non-multiple of 8 bits
-  assert_unreached: deriveBits failed with error TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. Reached unreachable code
-[FAIL] X448 mismatched algorithms
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 no deriveBits usage for base key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 base key is not a private key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 public property value is a private key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 public property value is a secret key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 asking for too many bits
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "OperationError" but got "TypeError"
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any.js
index 5e482ef..cda5ba8 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any.js
@@ -1,6 +1,8 @@
 // META: title=WebCryptoAPI: deriveKey() Using ECDH with CFRG Elliptic Curves
 // META: script=../util/helpers.js
 // META: script=cfrg_curves_bits_fixtures.js
+// META: script=derive.js
+// META: script=cfrg_curves.js
 // META: script=cfrg_curves_bits.js
 
 // Define subtests from a `promise_test` to ensure the harness does not
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any.worker-expected.txt
index 66ab63a4e..bcf668f 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits_curve448.tentative.https.any.worker-expected.txt
@@ -1,33 +1,15 @@
 This is a testharness.js-based test.
+[FAIL] setup - define tests
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] X448 key derivation checks for all-zero value result with a key of order 0
-  assert_true: Private key should be valid. expected true got false
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 key derivation checks for all-zero value result with a key of order 1
-  assert_true: Private key should be valid. expected true got false
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 key derivation checks for all-zero value result with a key of order p-1 (order 2)
-  assert_true: Private key should be valid. expected true got false
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 key derivation checks for all-zero value result with a key of order p (=0, order 4)
-  assert_true: Private key should be valid. expected true got false
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 key derivation checks for all-zero value result with a key of order p+1 (=1, order 1)
-  assert_true: Private key should be valid. expected true got false
-[FAIL] X448 good parameters
-  assert_unreached: deriveBits failed with error TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. Reached unreachable code
-[FAIL] X448 mixed case parameters
-  assert_unreached: deriveBits failed with error TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. Reached unreachable code
-[FAIL] X448 short result
-  assert_unreached: deriveBits failed with error TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. Reached unreachable code
-[FAIL] X448 non-multiple of 8 bits
-  assert_unreached: deriveBits failed with error TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. Reached unreachable code
-[FAIL] X448 mismatched algorithms
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 no deriveBits usage for base key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 base key is not a private key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 public property value is a private key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 public property value is a secret key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 asking for too many bits
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveBits' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "OperationError" but got "TypeError"
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.js
index cefc45a..1989c55c 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys.js
@@ -1,224 +1,7 @@
 function define_tests_25519() {
-    return define_tests("X25519");
+    return defineCfrgTests("X25519", "deriveKey");
 }
 
 function define_tests_448() {
-    return define_tests("X448");
-}
-
-function define_tests(algorithmName) {
-  // May want to test prefixed implementations.
-  var subtle = self.crypto.subtle;
-
-  // Verify the derive functions perform checks against the all-zero value results,
-  // ensuring small-order points are rejected.
-  // https://www.rfc-editor.org/rfc/rfc7748#section-6.1
-  // TODO: The spec states that the check must be done on use, but there is discussion about doing it on import.
-  // https://github.com/WICG/webcrypto-secure-curves/pull/13
-  {
-      kSmallOrderPoint[algorithmName].forEach(function(test) {
-          promise_test(async() => {
-              let derived;
-              let privateKey;
-              let publicKey;
-              try {
-                  privateKey = await subtle.importKey("pkcs8", pkcs8[algorithmName],
-                                                  {name: algorithmName},
-                                                  false, ["deriveBits", "deriveKey"]);
-                  publicKey = await subtle.importKey("spki", test.vector,
-                                                 {name: algorithmName},
-                                                 false, [])
-                  derived = await subtle.deriveKey({name: algorithmName, public: publicKey}, privateKey,
-                                                   {name: "HMAC", hash: "SHA-256", length: 256}, true,
-                                                   ["sign", "verify"]);
-              } catch (err) {
-                  assert_false(privateKey === undefined, "Private key should be valid.");
-                  assert_false(publicKey === undefined, "Public key should be valid.");
-                  assert_equals(err.name, "OperationError", "Should throw correct error, not " + err.name + ": " + err.message + ".");
-              }
-              assert_equals(derived, undefined, "Operation succeeded, but should not have.");
-          }, algorithmName + " deriveBits checks for all-zero value result with a key of order " + test.order);
-      });
-  }
-
-  // Ensure the keys generated by each algorithm are valid for key derivation.
-  {
-      promise_test(async() => {
-          let derived;
-          try {
-              let key = await subtle.generateKey({name: algorithmName}, true, ["deriveKey", "deriveBits"]);
-              derived = await subtle.deriveKey({name: algorithmName, public: key.publicKey}, key.privateKey, {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"]);
-          } catch (err) {
-              assert_unreached("Threw an unexpected error: " + err.toString() + " -");
-          }
-          assert_false (derived === undefined, "Key derivation failed.");
-      }, "Key derivation using a " + algorithmName + " generated keys.");
-  }
-
-  return importKeys(pkcs8, spki, sizes)
-  .then(function(results) {
-      publicKeys = results.publicKeys;
-      privateKeys = results.privateKeys;
-      noDeriveKeyKeys = results.noDeriveKeyKeys;
-      ecdhKeys = results.ecdhKeys;
-
-      {
-          // Basic success case
-          promise_test(function(test) {
-              return subtle.deriveKey({name: algorithmName, public: publicKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-              .then(function(exportedKey) {
-                  assert_true(equalBuffers(exportedKey, derivations[algorithmName], 8 * exportedKey.length), "Derived correct key");
-              }, function(err) {
-                  assert_unreached("deriveKey failed with error " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " good parameters");
-
-          // Case insensitivity check
-          promise_test(function(test) {
-              return subtle.deriveKey({name: algorithmName.toLowerCase(), public: publicKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-              .then(function(exportedKey) {
-                  assert_true(equalBuffers(exportedKey, derivations[algorithmName], 8 * exportedKey.length), "Derived correct key");
-              }, function(err) {
-                  assert_unreached("deriveKey failed with error " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " mixed case parameters");
-          // Errors to test:
-
-          // - missing public property TypeError
-          promise_test(function(test) {
-              return subtle.deriveKey({name: algorithmName}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-              .then(function(exportedKey) {
-                  assert_unreached("deriveKey succeeded but should have failed with TypeError");
-              }, function(err) {
-                  assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " missing public property");
-
-          // - Non CryptoKey public property TypeError
-          promise_test(function(test) {
-              return subtle.deriveKey({name: algorithmName, public: {message: "Not a CryptoKey"}}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-              .then(function(exportedKey) {
-                  assert_unreached("deriveKey succeeded but should have failed with TypeError");
-              }, function(err) {
-                  assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " public property of algorithm is not a CryptoKey");
-
-          // - wrong algorithm
-          promise_test(function(test) {
-              return subtle.deriveKey({name: algorithmName, public: ecdhKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-              .then(function(exportedKey) {
-                  assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
-              }, function(err) {
-                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " mismatched algorithms");
-
-          // - No deriveKey usage in baseKey InvalidAccessError
-          promise_test(function(test) {
-              return subtle.deriveKey({name: algorithmName, public: publicKeys[algorithmName]}, noDeriveKeyKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-              .then(function(exportedKey) {
-                  assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
-              }, function(err) {
-                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " no deriveKey usage for base key");
-
-          // - Use public key for baseKey InvalidAccessError
-          promise_test(function(test) {
-              return subtle.deriveKey({name: algorithmName, public: publicKeys[algorithmName]}, publicKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-              .then(function(exportedKey) {
-                  assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
-              }, function(err) {
-                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " base key is not a private key");
-
-          // - Use private key for public property InvalidAccessError
-          promise_test(function(test) {
-              return subtle.deriveKey({name: algorithmName, public: privateKeys[algorithmName]}, privateKeys[algorithmName], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-              .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-              .then(function(exportedKey) {
-                  assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
-              }, function(err) {
-                  assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-              });
-          }, algorithmName + " public property value is a private key");
-
-          // - Use secret key for public property InvalidAccessError
-          promise_test(function(test) {
-              return subtle.generateKey({name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-              .then(function(secretKey) {
-                  return subtle.deriveKey({name: algorithmName, public: secretKey}, privateKeys[algorithmName], {name: "AES-CBC", length: 256}, true, ["sign", "verify"])
-                  .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-                  .then(function(exportedKey) {
-                      assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
-                  }, function(err) {
-                      assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-                  });
-              });
-          }, algorithmName + " public property value is a secret key");
-      }
-  });
-
-  function importKeys(pkcs8, spki, sizes) {
-      var privateKeys = {};
-      var publicKeys = {};
-      var noDeriveKeyKeys = {};
-      var ecdhPublicKeys = {};
-
-      var promises = [];
-      {
-          var operation = subtle.importKey("pkcs8", pkcs8[algorithmName],
-                                          {name: algorithmName},
-                                          false, ["deriveBits", "deriveKey"])
-                          .then(function(key) {
-                              privateKeys[algorithmName] = key;
-                          }, function (err) {
-                            privateKeys[algorithmName] = null;
-                          });
-          promises.push(operation);
-      }
-      {
-          var operation = subtle.importKey("pkcs8", pkcs8[algorithmName],
-                                          {name: algorithmName},
-                                          false, ["deriveBits"])
-                          .then(function(key) {
-                              noDeriveKeyKeys[algorithmName] = key;
-                          }, function (err) {
-                            noDeriveKeyKeys[algorithmName] = null;
-                          });
-          promises.push(operation);
-      }
-      {
-          var operation = subtle.importKey("spki", spki[algorithmName],
-                                          {name: algorithmName},
-                                          false, [])
-                          .then(function(key) {
-                              publicKeys[algorithmName] = key;
-                          }, function (err) {
-                            publicKeys[algorithmName] = null;
-                          });
-          promises.push(operation);
-      }
-      {
-          var operation = subtle.importKey("spki", ecSPKI,
-                                           {name: "ECDH", namedCurve: "P-256"},
-                                           false, [])
-                          .then(function(key) {
-                              ecdhPublicKeys[algorithmName] = key;
-                          });
-      }
-
-      return Promise.all(promises)
-             .then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, noDeriveKeyKeys: noDeriveKeyKeys, ecdhKeys: ecdhPublicKeys}});
-  }
-
+    return defineCfrgTests("X448", "deriveKey");
 }
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve25519.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve25519.https.any.js
index 8bcc201..683ae21b 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve25519.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve25519.https.any.js
@@ -1,6 +1,8 @@
 // META: title=WebCryptoAPI: deriveKey() Using ECDH with CFRG Elliptic Curves
 // META: script=../util/helpers.js
 // META: script=cfrg_curves_bits_fixtures.js
+// META: script=derive.js
+// META: script=cfrg_curves.js
 // META: script=cfrg_curves_keys.js
 
 // Define subtests from a `promise_test` to ensure the harness does not
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any-expected.txt
index d5317de7..2634f86 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any-expected.txt
@@ -1,29 +1,17 @@
 This is a testharness.js-based test.
+[FAIL] setup - define tests
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] X448 deriveBits checks for all-zero value result with a key of order 0
-  assert_false: Private key should be valid. expected false got true
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 deriveBits checks for all-zero value result with a key of order 1
-  assert_false: Private key should be valid. expected false got true
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 deriveBits checks for all-zero value result with a key of order p-1 (order 2)
-  assert_false: Private key should be valid. expected false got true
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 deriveBits checks for all-zero value result with a key of order p (=0, order 4)
-  assert_false: Private key should be valid. expected false got true
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 deriveBits checks for all-zero value result with a key of order p+1 (=1, order 1)
-  assert_false: Private key should be valid. expected false got true
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] Key derivation using a X448 generated keys.
-  assert_unreached: Threw an unexpected error: NotSupportedError: Failed to execute 'generateKey' on 'SubtleCrypto': Algorithm: Unrecognized name - Reached unreachable code
-[FAIL] X448 good parameters
-  assert_unreached: deriveKey failed with error TypeError: Failed to execute 'deriveKey' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. Reached unreachable code
-[FAIL] X448 mixed case parameters
-  assert_unreached: deriveKey failed with error TypeError: Failed to execute 'deriveKey' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. Reached unreachable code
-[FAIL] X448 mismatched algorithms
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveKey' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 no deriveKey usage for base key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveKey' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 base key is not a private key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveKey' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 public property value is a private key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveKey' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 public property value is a secret key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveKey' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'generateKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any.js
index 0ed3954..38a5ce1d 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any.js
@@ -1,6 +1,8 @@
 // META: title=WebCryptoAPI: deriveKey() Using ECDH with CFRG Elliptic Curves
 // META: script=../util/helpers.js
 // META: script=cfrg_curves_bits_fixtures.js
+// META: script=derive.js
+// META: script=cfrg_curves.js
 // META: script=cfrg_curves_keys.js
 
 // Define subtests from a `promise_test` to ensure the harness does not
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any.worker-expected.txt
index d5317de7..2634f86 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_keys_curve448.tentative.https.any.worker-expected.txt
@@ -1,29 +1,17 @@
 This is a testharness.js-based test.
+[FAIL] setup - define tests
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] X448 deriveBits checks for all-zero value result with a key of order 0
-  assert_false: Private key should be valid. expected false got true
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 deriveBits checks for all-zero value result with a key of order 1
-  assert_false: Private key should be valid. expected false got true
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 deriveBits checks for all-zero value result with a key of order p-1 (order 2)
-  assert_false: Private key should be valid. expected false got true
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 deriveBits checks for all-zero value result with a key of order p (=0, order 4)
-  assert_false: Private key should be valid. expected false got true
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] X448 deriveBits checks for all-zero value result with a key of order p+1 (=1, order 1)
-  assert_false: Private key should be valid. expected false got true
+  assert_not_equals: Private key should be valid. got disallowed value undefined
 [FAIL] Key derivation using a X448 generated keys.
-  assert_unreached: Threw an unexpected error: NotSupportedError: Failed to execute 'generateKey' on 'SubtleCrypto': Algorithm: Unrecognized name - Reached unreachable code
-[FAIL] X448 good parameters
-  assert_unreached: deriveKey failed with error TypeError: Failed to execute 'deriveKey' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. Reached unreachable code
-[FAIL] X448 mixed case parameters
-  assert_unreached: deriveKey failed with error TypeError: Failed to execute 'deriveKey' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. Reached unreachable code
-[FAIL] X448 mismatched algorithms
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveKey' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 no deriveKey usage for base key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveKey' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 base key is not a private key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveKey' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 public property value is a private key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveKey' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
-[FAIL] X448 public property value is a secret key
-  assert_equals: Should throw correct error, not TypeError: Failed to execute 'deriveKey' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. expected "InvalidAccessError" but got "TypeError"
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'generateKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/derive.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/derive.js
new file mode 100644
index 0000000..16c9ec6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/derive.js
@@ -0,0 +1,169 @@
+function registerDeriveTests(options) {
+    const {
+        operation,
+        algorithmName,
+        testName = algorithmName,
+        mixedCaseName,
+        size,
+        derivation,
+        privateKey,
+        publicKey,
+        noUsagePrivateKey,
+        invalidPublicKeys,
+        missingPublicLabel,
+    } = options;
+    const subtle = self.crypto.subtle;
+    const isDeriveBits = operation === "deriveBits";
+    const derivedKeyAlgorithm = {name: "HMAC", hash: "SHA-256", length: 256};
+    const derivedKeyUsages = ["sign", "verify"];
+
+    function derive(name, publicKey, baseKey, length = 8 * size, keyOptions = {}) {
+        const algorithm = {name, public: publicKey};
+        if (isDeriveBits) {
+            return subtle.deriveBits(algorithm, baseKey, length);
+        }
+
+        return subtle.deriveKey(
+            algorithm,
+            baseKey,
+            keyOptions.algorithm || derivedKeyAlgorithm,
+            true,
+            keyOptions.usages || derivedKeyUsages
+        ).then(key => subtle.exportKey("raw", key));
+    }
+
+    function assertDerived(result, length) {
+        if (isDeriveBits) {
+            assert_true(equalBuffers(result, derivation, length), "Derived correct bits");
+        } else {
+            assert_array_equals(
+                new Uint8Array(result),
+                derivation.slice(0, 32),
+                "Derived correct key"
+            );
+        }
+    }
+
+    function successTest(name, algorithm, length = 8 * size) {
+        promise_test(() => {
+            return derive(algorithm, publicKey, privateKey, length).then(
+                result => assertDerived(result, length),
+                error => assert_unreached(
+                    operation + " failed with error " + error.name + ": " + error.message
+                )
+            );
+        }, name);
+    }
+
+    function failureTest(name, expectedError, deriveOperation) {
+        promise_test(() => {
+            return Promise.resolve().then(deriveOperation).then(
+                () => assert_unreached(
+                    operation + " succeeded but should have failed with " + expectedError
+                ),
+                error => assert_equals(
+                    error.name,
+                    expectedError,
+                    "Should throw correct error, not " + error.name + ": " + error.message
+                )
+            );
+        }, name);
+    }
+
+    successTest(testName + " good parameters", algorithmName);
+    successTest(testName + " mixed case parameters", mixedCaseName);
+
+    if (isDeriveBits) {
+        successTest(testName + " short result", algorithmName, 8 * size - 32);
+        successTest(
+            testName + " non-multiple of 8 bits",
+            algorithmName,
+            8 * size - 11
+        );
+    }
+
+    failureTest(missingPublicLabel, "TypeError", () => {
+        return subtle[operation](
+            {name: algorithmName},
+            privateKey,
+            ...(isDeriveBits
+                ? [8 * size]
+                : [derivedKeyAlgorithm, true, derivedKeyUsages])
+        );
+    });
+
+    failureTest(
+        testName + " public property of algorithm is not a CryptoKey",
+        "TypeError",
+        () => derive(algorithmName, {message: "Not a CryptoKey"}, privateKey)
+    );
+
+    invalidPublicKeys.forEach(test => {
+        failureTest(
+            test.name,
+            "InvalidAccessError",
+            () => derive(algorithmName, test.key, privateKey)
+        );
+    });
+
+    failureTest(
+        testName + " no " + operation + " usage for base key",
+        "InvalidAccessError",
+        () => derive(algorithmName, publicKey, noUsagePrivateKey)
+    );
+
+    failureTest(
+        testName + " base key is not a private key",
+        "InvalidAccessError",
+        () => derive(algorithmName, publicKey, publicKey)
+    );
+
+    failureTest(
+        testName + " public property value is a private key",
+        "InvalidAccessError",
+        () => derive(algorithmName, privateKey, privateKey)
+    );
+
+    promise_test(async() => {
+        const secretKey = isDeriveBits
+            ? await subtle.generateKey(
+                {name: "AES-CBC", length: 128},
+                true,
+                ["encrypt", "decrypt"]
+            )
+            : await subtle.generateKey(
+                derivedKeyAlgorithm,
+                true,
+                derivedKeyUsages
+            );
+        const keyOptions = isDeriveBits ? {} : {
+            algorithm: {name: "AES-CBC", length: 256},
+            usages: ["sign", "verify"],
+        };
+
+        return derive(
+            algorithmName,
+            secretKey,
+            privateKey,
+            8 * size,
+            keyOptions
+        ).then(
+            () => assert_unreached(
+                operation + " succeeded but should have failed with InvalidAccessError"
+            ),
+            error => assert_equals(
+                error.name,
+                "InvalidAccessError",
+                "Should throw correct error, not " + error.name + ": " + error.message
+            )
+        );
+    }, testName + " public property value is a secret key");
+
+    if (isDeriveBits) {
+        failureTest(
+            testName + " asking for too many bits",
+            "OperationError",
+            () => derive(algorithmName, publicKey, privateKey, 8 * size + 8)
+        );
+    }
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh.js
new file mode 100644
index 0000000..99039f25
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh.js
@@ -0,0 +1,79 @@
+async function defineEcdhTests(operation) {
+    const subtle = self.crypto.subtle;
+    const fixtures = getEcdhTestFixtures();
+    const curves = Object.keys(fixtures.sizes);
+    const keys = {};
+
+    await Promise.all(curves.map(async namedCurve => {
+        const algorithm = {name: "ECDH", namedCurve};
+        const noUsage = operation === "deriveBits" ? ["deriveKey"] : ["deriveBits"];
+        const [
+            privateKey,
+            noUsagePrivateKey,
+            publicKey,
+            ecdsaKeyPair,
+        ] = await Promise.all([
+            subtle.importKey(
+                "pkcs8",
+                fixtures.pkcs8[namedCurve],
+                algorithm,
+                false,
+                ["deriveBits", "deriveKey"]
+            ),
+            subtle.importKey(
+                "pkcs8",
+                fixtures.pkcs8[namedCurve],
+                algorithm,
+                false,
+                noUsage
+            ),
+            subtle.importKey(
+                "spki",
+                fixtures.spki[namedCurve],
+                algorithm,
+                false,
+                []
+            ),
+            subtle.generateKey(
+                {name: "ECDSA", namedCurve},
+                false,
+                ["sign", "verify"]
+            ),
+        ]);
+
+        keys[namedCurve] = {
+            privateKey,
+            noUsagePrivateKey,
+            publicKey,
+            ecdsaKeyPair,
+        };
+    }));
+
+    curves.forEach(namedCurve => {
+        const otherCurve = namedCurve === "P-256" ? "P-384" : "P-256";
+        const key = keys[namedCurve];
+        registerDeriveTests({
+            operation,
+            algorithmName: "ECDH",
+            testName: namedCurve,
+            mixedCaseName: "EcDh",
+            size: fixtures.sizes[namedCurve],
+            derivation: fixtures.derivations[namedCurve],
+            privateKey: key.privateKey,
+            publicKey: key.publicKey,
+            noUsagePrivateKey: key.noUsagePrivateKey,
+            invalidPublicKeys: [
+                {
+                    name: namedCurve + " mismatched curves",
+                    key: keys[otherCurve].publicKey,
+                },
+                {
+                    name: namedCurve +
+                        " public property of algorithm is not an ECDSA public key",
+                    key: key.ecdsaKeyPair.publicKey,
+                },
+            ],
+            missingPublicLabel: namedCurve + " missing public curve",
+        });
+    });
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_bits.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_bits.https.any.js
index 58a0cec..38058d6 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_bits.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_bits.https.any.js
@@ -1,5 +1,8 @@
 // META: title=WebCryptoAPI: deriveBits() Using ECDH
 // META: script=../util/helpers.js
+// META: script=ecdh_fixtures.js
+// META: script=derive.js
+// META: script=ecdh.js
 // META: script=ecdh_bits.js
 
 // Define subtests from a `promise_test` to ensure the harness does not
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_bits.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_bits.js
index 8e79909..bbe48b5 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_bits.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_bits.js
@@ -1,233 +1,3 @@
-
 function define_tests() {
-    // May want to test prefixed implementations.
-    var subtle = self.crypto.subtle;
-
-    var pkcs8 = {
-        "P-521": new Uint8Array([48, 129, 238, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 4, 129, 214, 48, 129, 211, 2, 1, 1, 4, 66, 1, 166, 126, 211, 33, 145, 90, 100, 170, 53, 155, 125, 100, 141, 220, 38, 24, 250, 142, 141, 24, 103, 232, 247, 24, 48, 177, 13, 37, 237, 40, 145, 250, 241, 47, 60, 126, 117, 66, 26, 46, 162, 100, 249, 169, 21, 50, 13, 39, 79, 225, 71, 7, 66, 185, 132, 233, 107, 152, 145, 32, 129, 250, 205, 71, 141, 161, 129, 137, 3, 129, 134, 0, 4, 0, 32, 157, 72, 63, 40, 102, 104, 129, 198, 100, 31, 58, 18, 111, 64, 15, 81, 228, 101, 17, 112, 254, 103, 140, 117, 232, 87, 18, 226, 134, 138, 220, 133, 8, 36, 153, 123, 235, 240, 188, 130, 180, 48, 40, 166, 210, 236, 23, 119, 202, 69, 39, 159, 114, 6, 163, 234, 139, 92, 210, 7, 63, 73, 62, 69, 0, 12, 181, 76, 58, 90, 202, 162, 104, 197, 103, 16, 66, 136, 120, 217, 139, 138, 251, 246, 138, 97, 33, 83, 99, 40, 70, 216, 7, 233, 38, 114, 105, 143, 27, 156, 97, 29, 231, 211, 142, 52, 205, 108, 115, 136, 144, 146, 197, 110, 82, 214, 128, 241, 223, 208, 146, 184, 122, 200, 239, 159, 243, 200, 251, 72]),
-        "P-256": new Uint8Array([48, 129, 135, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 4, 109, 48, 107, 2, 1, 1, 4, 32, 15, 247, 79, 232, 241, 202, 175, 97, 92, 206, 241, 29, 217, 53, 114, 87, 98, 217, 216, 65, 236, 186, 185, 94, 170, 38, 68, 123, 52, 100, 245, 113, 161, 68, 3, 66, 0, 4, 140, 96, 11, 44, 102, 25, 45, 97, 158, 39, 210, 37, 107, 59, 151, 118, 178, 141, 30, 5, 246, 13, 234, 189, 98, 174, 123, 154, 211, 157, 224, 217, 59, 4, 102, 109, 199, 119, 14, 126, 207, 13, 211, 203, 203, 211, 110, 221, 107, 94, 220, 153, 81, 7, 55, 161, 237, 104, 46, 205, 112, 244, 10, 47]),
-        "P-384": new Uint8Array([48, 129, 182, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 4, 129, 158, 48, 129, 155, 2, 1, 1, 4, 48, 248, 113, 165, 102, 101, 137, 193, 74, 87, 71, 38, 62, 248, 91, 49, 156, 192, 35, 219, 110, 53, 103, 108, 61, 120, 30, 239, 139, 5, 95, 207, 190, 134, 250, 13, 6, 208, 86, 181, 25, 95, 177, 50, 58, 248, 222, 37, 179, 161, 100, 3, 98, 0, 4, 241, 25, 101, 223, 125, 212, 89, 77, 4, 25, 197, 8, 100, 130, 163, 184, 38, 185, 121, 127, 155, 224, 189, 13, 16, 156, 158, 30, 153, 137, 193, 185, 169, 43, 143, 38, 159, 152, 225, 122, 209, 132, 186, 115, 193, 247, 151, 98, 175, 69, 175, 129, 65, 96, 38, 66, 218, 39, 26, 107, 176, 255, 235, 12, 180, 71, 143, 207, 112, 126, 102, 26, 166, 214, 205, 245, 21, 73, 200, 140, 63, 19, 11, 233, 232, 32, 31, 111, 106, 9, 244, 24, 90, 175, 149, 196])
-    };
-
-    var spki = {
-        "P-521": new Uint8Array([48, 129, 155, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, 129, 134, 0, 4, 0, 238, 105, 249, 71, 21, 215, 1, 233, 226, 1, 19, 51, 212, 244, 249, 108, 186, 125, 145, 248, 139, 17, 43, 175, 117, 207, 9, 204, 31, 138, 202, 151, 97, 141, 169, 56, 152, 34, 210, 155, 111, 233, 153, 106, 97, 32, 62, 247, 82, 183, 113, 232, 149, 143, 196, 103, 123, 179, 119, 133, 101, 171, 96, 214, 237, 0, 222, 171, 103, 97, 137, 91, 147, 94, 58, 211, 37, 251, 133, 73, 229, 111, 19, 120, 106, 167, 63, 136, 162, 236, 254, 64, 147, 52, 115, 216, 174, 242, 64, 196, 223, 215, 213, 6, 242, 44, 221, 14, 85, 85, 143, 63, 191, 5, 235, 247, 239, 239, 122, 114, 215, 143, 70, 70, 155, 132, 72, 242, 110, 39, 18]),
-        "P-256": new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 154, 116, 32, 120, 126, 95, 77, 105, 211, 232, 34, 114, 115, 1, 109, 56, 224, 71, 129, 133, 223, 127, 238, 156, 142, 103, 60, 202, 211, 79, 126, 128, 254, 49, 141, 182, 221, 107, 119, 218, 99, 32, 165, 246, 151, 89, 9, 68, 23, 177, 52, 239, 138, 139, 116, 193, 101, 4, 57, 198, 115, 0, 90, 61]),
-        "P-384": new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 145, 130, 45, 194, 175, 89, 193, 143, 91, 103, 248, 13, 246, 26, 38, 3, 194, 168, 240, 179, 192, 175, 130, 45, 99, 194, 121, 112, 26, 130, 69, 96, 64, 68, 1, 221, 233, 165, 110, 229, 39, 87, 234, 139, 199, 72, 212, 200, 43, 83, 55, 180, 141, 123, 101, 88, 58, 61, 87, 36, 56, 136, 0, 54, 186, 198, 115, 15, 66, 202, 82, 120, 150, 107, 213, 242, 30, 134, 226, 29, 48, 197, 166, 208, 70, 62, 197, 19, 221, 80, 159, 252, 220, 175, 31, 245])
-    };
-
-    var sizes = {
-        "P-521": 66,
-        "P-256": 32,
-        "P-384": 48
-    };
-
-    var derivations = {
-        "P-521": new Uint8Array([0, 156, 43, 206, 87, 190, 128, 173, 171, 59, 7, 56, 91, 142, 89, 144, 235, 125, 111, 222, 189, 176, 27, 243, 83, 113, 164, 246, 7, 94, 157, 40, 138, 193, 42, 109, 254, 3, 170, 87, 67, 188, 129, 112, 157, 73, 168, 34, 148, 2, 25, 182, 75, 118, 138, 205, 82, 15, 161, 54, 142, 160, 175, 141, 71, 93]),
-        "P-256": new Uint8Array([14, 143, 60, 77, 177, 178, 162, 131, 115, 90, 0, 220, 87, 31, 26, 232, 151, 28, 227, 35, 250, 17, 131, 137, 203, 95, 65, 196, 59, 61, 181, 161]),
-        "P-384": new Uint8Array([224, 189, 107, 206, 10, 239, 140, 164, 136, 56, 166, 226, 252, 197, 126, 103, 185, 197, 232, 134, 12, 95, 11, 233, 218, 190, 197, 62, 69, 78, 24, 160, 161, 116, 196, 136, 136, 162, 100, 136, 17, 91, 45, 201, 241, 223, 165, 45])
-    };
-
-    return importKeys(pkcs8, spki, sizes)
-    .then(function(results) {
-        publicKeys = results.publicKeys;
-        privateKeys = results.privateKeys;
-        ecdsaKeyPairs = results.ecdsaKeyPairs;
-        noDeriveBitsKeys = results.noDeriveBitsKeys;
-
-        Object.keys(sizes).forEach(function(namedCurve) {
-            // Basic success case
-            promise_test(function(test) {
-                return subtle.deriveBits({name: "ECDH", public: publicKeys[namedCurve]}, privateKeys[namedCurve], 8 * sizes[namedCurve])
-                .then(function(derivation) {
-                    assert_true(equalBuffers(derivation, derivations[namedCurve]), "Derived correct bits");
-                }, function(err) {
-                    assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " good parameters");
-
-            // Case insensitivity check
-            promise_test(function(test) {
-                return subtle.deriveBits({name: "EcDh", public: publicKeys[namedCurve]}, privateKeys[namedCurve], 8 * sizes[namedCurve])
-                .then(function(derivation) {
-                    assert_true(equalBuffers(derivation, derivations[namedCurve]), "Derived correct bits");
-                }, function(err) {
-                    assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " mixed case parameters");
-
-            // Shorter than entire derivation per algorithm
-            promise_test(function(test) {
-                return subtle.deriveBits({name: "ECDH", public: publicKeys[namedCurve]}, privateKeys[namedCurve], 8 * sizes[namedCurve] - 32)
-                .then(function(derivation) {
-                    assert_true(equalBuffers(derivation, derivations[namedCurve], 8 * sizes[namedCurve] - 32), "Derived correct bits");
-                }, function(err) {
-                    assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " short result");
-
-            // Non-multiple of 8
-            promise_test(function(test) {
-                return subtle.deriveBits({name: "ECDH", public: publicKeys[namedCurve]}, privateKeys[namedCurve], 8 * sizes[namedCurve] - 11)
-                .then(function(derivation) {
-                    assert_true(equalBuffers(derivation, derivations[namedCurve], 8 * sizes[namedCurve] - 11), "Derived correct bits");
-                }, function(err) {
-                    assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " non-multiple of 8 bits");
-
-            // Errors to test:
-
-            // - missing public property TypeError
-            promise_test(function(test) {
-                return subtle.deriveBits({name: "ECDH"}, privateKeys[namedCurve], 8 * sizes[namedCurve])
-                .then(function(derivation) {
-                    assert_unreached("deriveBits succeeded but should have failed with TypeError");
-                }, function(err) {
-                    assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " missing public curve");
-
-            // - Non CryptoKey public property TypeError
-            promise_test(function(test) {
-                return subtle.deriveBits({name: "ECDH", public: {message: "Not a CryptoKey"}}, privateKeys[namedCurve], 8 * sizes[namedCurve])
-                .then(function(derivation) {
-                    assert_unreached("deriveBits succeeded but should have failed with TypeError");
-                }, function(err) {
-                    assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " public property of algorithm is not a CryptoKey");
-
-            // - wrong named curve
-            promise_test(function(test) {
-                publicKey = publicKeys["P-256"];
-                if (namedCurve === "P-256") {
-                    publicKey = publicKeys["P-384"];
-                }
-                return subtle.deriveBits({name: "ECDH", public: publicKey}, privateKeys[namedCurve], 8 * sizes[namedCurve])
-                .then(function(derivation) {
-                    assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " mismatched curves");
-
-            // - not ECDH public property InvalidAccessError
-            promise_test(function(test) {
-                return subtle.deriveBits({name: "ECDH", public: ecdsaKeyPairs[namedCurve].publicKey}, privateKeys[namedCurve], 8 * sizes[namedCurve])
-                .then(function(derivation) {
-                    assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " public property of algorithm is not an ECDSA public key");
-
-            // - No deriveBits usage in baseKey InvalidAccessError
-            promise_test(function(test) {
-                return subtle.deriveBits({name: "ECDH", public: publicKeys[namedCurve]}, noDeriveBitsKeys[namedCurve], 8 * sizes[namedCurve])
-                .then(function(derivation) {
-                    assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " no deriveBits usage for base key");
-
-            // - Use public key for baseKey InvalidAccessError
-            promise_test(function(test) {
-                return subtle.deriveBits({name: "ECDH", public: publicKeys[namedCurve]}, publicKeys[namedCurve], 8 * sizes[namedCurve])
-                .then(function(derivation) {
-                    assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " base key is not a private key");
-
-            // - Use private key for public property InvalidAccessError
-            promise_test(function(test) {
-                return subtle.deriveBits({name: "ECDH", public: privateKeys[namedCurve]}, privateKeys[namedCurve], 8 * sizes[namedCurve])
-                .then(function(derivation) {
-                    assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " public property value is a private key");
-
-            // - Use secret key for public property InvalidAccessError
-            promise_test(function(test) {
-                return subtle.generateKey({name: "AES-CBC", length: 128}, true, ["encrypt", "decrypt"])
-                .then(function(secretKey) {
-                    return subtle.deriveBits({name: "ECDH", public: secretKey}, privateKeys[namedCurve], 8 * sizes[namedCurve])
-                    .then(function(derivation) {
-                        assert_unreached("deriveBits succeeded but should have failed with InvalidAccessError");
-                    }, function(err) {
-                        assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-                    });
-                });
-            }, namedCurve + " public property value is a secret key");
-
-            // - Length greater than 256, 384, 521 for particular curves OperationError
-            promise_test(function(test) {
-                return subtle.deriveBits({name: "ECDH", public: publicKeys[namedCurve]}, privateKeys[namedCurve], 8 * sizes[namedCurve] + 8)
-                .then(function(derivation) {
-                    assert_unreached("deriveBits succeeded but should have failed with OperationError");
-                }, function(err) {
-                    assert_equals(err.name, "OperationError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " asking for too many bits");
-        });
-    });
-
-    function importKeys(pkcs8, spki, sizes) {
-        var privateKeys = {};
-        var publicKeys = {};
-        var ecdsaKeyPairs = {};
-        var noDeriveBitsKeys = {};
-
-        var promises = [];
-        Object.keys(pkcs8).forEach(function(namedCurve) {
-            var operation = subtle.importKey("pkcs8", pkcs8[namedCurve],
-                                            {name: "ECDH", namedCurve: namedCurve},
-                                            false, ["deriveBits", "deriveKey"])
-                            .then(function(key) {
-                                privateKeys[namedCurve] = key;
-                            }, function (err) {
-                                privateKeys[namedCurve] = null;
-                            });
-            promises.push(operation);
-        });
-        Object.keys(pkcs8).forEach(function(namedCurve) {
-            var operation = subtle.importKey("pkcs8", pkcs8[namedCurve],
-                                            {name: "ECDH", namedCurve: namedCurve},
-                                            false, ["deriveKey"])
-                            .then(function(key) {
-                                noDeriveBitsKeys[namedCurve] = key;
-                            }, function (err) {
-                                noDeriveBitsKeys[namedCurve] = null;
-                            });
-            promises.push(operation);
-        });
-        Object.keys(spki).forEach(function(namedCurve) {
-            var operation = subtle.importKey("spki", spki[namedCurve],
-                                            {name: "ECDH", namedCurve: namedCurve},
-                                            false, [])
-                            .then(function(key) {
-                                publicKeys[namedCurve] = key;
-                            }, function (err) {
-                                publicKeys[namedCurve] = null;
-                            });
-            promises.push(operation);
-        });
-        Object.keys(sizes).forEach(function(namedCurve) {
-            var operation = subtle.generateKey({name: "ECDSA", namedCurve: namedCurve}, false, ["sign", "verify"])
-                            .then(function(keyPair) {
-                                ecdsaKeyPairs[namedCurve] = keyPair;
-                            }, function (err) {
-                                ecdsaKeyPairs[namedCurve] = null;
-                            });
-            promises.push(operation);
-        });
-
-        return Promise.all(promises)
-               .then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, ecdsaKeyPairs: ecdsaKeyPairs, noDeriveBitsKeys: noDeriveBitsKeys}});
-    }
-
+    return defineEcdhTests("deriveBits");
 }
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_fixtures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_fixtures.js
new file mode 100644
index 0000000..46539d5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_fixtures.js
@@ -0,0 +1,32 @@
+function getEcdhTestFixtures() {
+    var pkcs8 = {
+        "P-521": new Uint8Array([48, 129, 238, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 4, 129, 214, 48, 129, 211, 2, 1, 1, 4, 66, 1, 166, 126, 211, 33, 145, 90, 100, 170, 53, 155, 125, 100, 141, 220, 38, 24, 250, 142, 141, 24, 103, 232, 247, 24, 48, 177, 13, 37, 237, 40, 145, 250, 241, 47, 60, 126, 117, 66, 26, 46, 162, 100, 249, 169, 21, 50, 13, 39, 79, 225, 71, 7, 66, 185, 132, 233, 107, 152, 145, 32, 129, 250, 205, 71, 141, 161, 129, 137, 3, 129, 134, 0, 4, 0, 32, 157, 72, 63, 40, 102, 104, 129, 198, 100, 31, 58, 18, 111, 64, 15, 81, 228, 101, 17, 112, 254, 103, 140, 117, 232, 87, 18, 226, 134, 138, 220, 133, 8, 36, 153, 123, 235, 240, 188, 130, 180, 48, 40, 166, 210, 236, 23, 119, 202, 69, 39, 159, 114, 6, 163, 234, 139, 92, 210, 7, 63, 73, 62, 69, 0, 12, 181, 76, 58, 90, 202, 162, 104, 197, 103, 16, 66, 136, 120, 217, 139, 138, 251, 246, 138, 97, 33, 83, 99, 40, 70, 216, 7, 233, 38, 114, 105, 143, 27, 156, 97, 29, 231, 211, 142, 52, 205, 108, 115, 136, 144, 146, 197, 110, 82, 214, 128, 241, 223, 208, 146, 184, 122, 200, 239, 159, 243, 200, 251, 72]),
+        "P-256": new Uint8Array([48, 129, 135, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 4, 109, 48, 107, 2, 1, 1, 4, 32, 15, 247, 79, 232, 241, 202, 175, 97, 92, 206, 241, 29, 217, 53, 114, 87, 98, 217, 216, 65, 236, 186, 185, 94, 170, 38, 68, 123, 52, 100, 245, 113, 161, 68, 3, 66, 0, 4, 140, 96, 11, 44, 102, 25, 45, 97, 158, 39, 210, 37, 107, 59, 151, 118, 178, 141, 30, 5, 246, 13, 234, 189, 98, 174, 123, 154, 211, 157, 224, 217, 59, 4, 102, 109, 199, 119, 14, 126, 207, 13, 211, 203, 203, 211, 110, 221, 107, 94, 220, 153, 81, 7, 55, 161, 237, 104, 46, 205, 112, 244, 10, 47]),
+        "P-384": new Uint8Array([48, 129, 182, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 4, 129, 158, 48, 129, 155, 2, 1, 1, 4, 48, 248, 113, 165, 102, 101, 137, 193, 74, 87, 71, 38, 62, 248, 91, 49, 156, 192, 35, 219, 110, 53, 103, 108, 61, 120, 30, 239, 139, 5, 95, 207, 190, 134, 250, 13, 6, 208, 86, 181, 25, 95, 177, 50, 58, 248, 222, 37, 179, 161, 100, 3, 98, 0, 4, 241, 25, 101, 223, 125, 212, 89, 77, 4, 25, 197, 8, 100, 130, 163, 184, 38, 185, 121, 127, 155, 224, 189, 13, 16, 156, 158, 30, 153, 137, 193, 185, 169, 43, 143, 38, 159, 152, 225, 122, 209, 132, 186, 115, 193, 247, 151, 98, 175, 69, 175, 129, 65, 96, 38, 66, 218, 39, 26, 107, 176, 255, 235, 12, 180, 71, 143, 207, 112, 126, 102, 26, 166, 214, 205, 245, 21, 73, 200, 140, 63, 19, 11, 233, 232, 32, 31, 111, 106, 9, 244, 24, 90, 175, 149, 196])
+    };
+
+    var spki = {
+        "P-521": new Uint8Array([48, 129, 155, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, 129, 134, 0, 4, 0, 238, 105, 249, 71, 21, 215, 1, 233, 226, 1, 19, 51, 212, 244, 249, 108, 186, 125, 145, 248, 139, 17, 43, 175, 117, 207, 9, 204, 31, 138, 202, 151, 97, 141, 169, 56, 152, 34, 210, 155, 111, 233, 153, 106, 97, 32, 62, 247, 82, 183, 113, 232, 149, 143, 196, 103, 123, 179, 119, 133, 101, 171, 96, 214, 237, 0, 222, 171, 103, 97, 137, 91, 147, 94, 58, 211, 37, 251, 133, 73, 229, 111, 19, 120, 106, 167, 63, 136, 162, 236, 254, 64, 147, 52, 115, 216, 174, 242, 64, 196, 223, 215, 213, 6, 242, 44, 221, 14, 85, 85, 143, 63, 191, 5, 235, 247, 239, 239, 122, 114, 215, 143, 70, 70, 155, 132, 72, 242, 110, 39, 18]),
+        "P-256": new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 154, 116, 32, 120, 126, 95, 77, 105, 211, 232, 34, 114, 115, 1, 109, 56, 224, 71, 129, 133, 223, 127, 238, 156, 142, 103, 60, 202, 211, 79, 126, 128, 254, 49, 141, 182, 221, 107, 119, 218, 99, 32, 165, 246, 151, 89, 9, 68, 23, 177, 52, 239, 138, 139, 116, 193, 101, 4, 57, 198, 115, 0, 90, 61]),
+        "P-384": new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 145, 130, 45, 194, 175, 89, 193, 143, 91, 103, 248, 13, 246, 26, 38, 3, 194, 168, 240, 179, 192, 175, 130, 45, 99, 194, 121, 112, 26, 130, 69, 96, 64, 68, 1, 221, 233, 165, 110, 229, 39, 87, 234, 139, 199, 72, 212, 200, 43, 83, 55, 180, 141, 123, 101, 88, 58, 61, 87, 36, 56, 136, 0, 54, 186, 198, 115, 15, 66, 202, 82, 120, 150, 107, 213, 242, 30, 134, 226, 29, 48, 197, 166, 208, 70, 62, 197, 19, 221, 80, 159, 252, 220, 175, 31, 245])
+    };
+
+    var sizes = {
+        "P-521": 66,
+        "P-256": 32,
+        "P-384": 48
+    };
+
+    var derivations = {
+        "P-521": new Uint8Array([0, 156, 43, 206, 87, 190, 128, 173, 171, 59, 7, 56, 91, 142, 89, 144, 235, 125, 111, 222, 189, 176, 27, 243, 83, 113, 164, 246, 7, 94, 157, 40, 138, 193, 42, 109, 254, 3, 170, 87, 67, 188, 129, 112, 157, 73, 168, 34, 148, 2, 25, 182, 75, 118, 138, 205, 82, 15, 161, 54, 142, 160, 175, 141, 71, 93]),
+        "P-256": new Uint8Array([14, 143, 60, 77, 177, 178, 162, 131, 115, 90, 0, 220, 87, 31, 26, 232, 151, 28, 227, 35, 250, 17, 131, 137, 203, 95, 65, 196, 59, 61, 181, 161]),
+        "P-384": new Uint8Array([224, 189, 107, 206, 10, 239, 140, 164, 136, 56, 166, 226, 252, 197, 126, 103, 185, 197, 232, 134, 12, 95, 11, 233, 218, 190, 197, 62, 69, 78, 24, 160, 161, 116, 196, 136, 136, 162, 100, 136, 17, 91, 45, 201, 241, 223, 165, 45])
+    };
+
+    return {
+        pkcs8: pkcs8,
+        spki: spki,
+        sizes: sizes,
+        derivations: derivations,
+    };
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_keys.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_keys.https.any.js
index 6464dacf..f9d0da92 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_keys.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_keys.https.any.js
@@ -1,5 +1,8 @@
 // META: title=WebCryptoAPI: deriveKey() Using ECDH
 // META: script=../util/helpers.js
+// META: script=ecdh_fixtures.js
+// META: script=derive.js
+// META: script=ecdh.js
 // META: script=ecdh_keys.js
 
 // Define subtests from a `promise_test` to ensure the harness does not
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_keys.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_keys.js
index 8c3d2aeb..c4ee495a 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_keys.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/ecdh_keys.js
@@ -1,212 +1,3 @@
-
 function define_tests() {
-    // May want to test prefixed implementations.
-    var subtle = self.crypto.subtle;
-
-    var pkcs8 = {
-        "P-521": new Uint8Array([48, 129, 238, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 4, 129, 214, 48, 129, 211, 2, 1, 1, 4, 66, 1, 166, 126, 211, 33, 145, 90, 100, 170, 53, 155, 125, 100, 141, 220, 38, 24, 250, 142, 141, 24, 103, 232, 247, 24, 48, 177, 13, 37, 237, 40, 145, 250, 241, 47, 60, 126, 117, 66, 26, 46, 162, 100, 249, 169, 21, 50, 13, 39, 79, 225, 71, 7, 66, 185, 132, 233, 107, 152, 145, 32, 129, 250, 205, 71, 141, 161, 129, 137, 3, 129, 134, 0, 4, 0, 32, 157, 72, 63, 40, 102, 104, 129, 198, 100, 31, 58, 18, 111, 64, 15, 81, 228, 101, 17, 112, 254, 103, 140, 117, 232, 87, 18, 226, 134, 138, 220, 133, 8, 36, 153, 123, 235, 240, 188, 130, 180, 48, 40, 166, 210, 236, 23, 119, 202, 69, 39, 159, 114, 6, 163, 234, 139, 92, 210, 7, 63, 73, 62, 69, 0, 12, 181, 76, 58, 90, 202, 162, 104, 197, 103, 16, 66, 136, 120, 217, 139, 138, 251, 246, 138, 97, 33, 83, 99, 40, 70, 216, 7, 233, 38, 114, 105, 143, 27, 156, 97, 29, 231, 211, 142, 52, 205, 108, 115, 136, 144, 146, 197, 110, 82, 214, 128, 241, 223, 208, 146, 184, 122, 200, 239, 159, 243, 200, 251, 72]),
-        "P-256": new Uint8Array([48, 129, 135, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 4, 109, 48, 107, 2, 1, 1, 4, 32, 15, 247, 79, 232, 241, 202, 175, 97, 92, 206, 241, 29, 217, 53, 114, 87, 98, 217, 216, 65, 236, 186, 185, 94, 170, 38, 68, 123, 52, 100, 245, 113, 161, 68, 3, 66, 0, 4, 140, 96, 11, 44, 102, 25, 45, 97, 158, 39, 210, 37, 107, 59, 151, 118, 178, 141, 30, 5, 246, 13, 234, 189, 98, 174, 123, 154, 211, 157, 224, 217, 59, 4, 102, 109, 199, 119, 14, 126, 207, 13, 211, 203, 203, 211, 110, 221, 107, 94, 220, 153, 81, 7, 55, 161, 237, 104, 46, 205, 112, 244, 10, 47]),
-        "P-384": new Uint8Array([48, 129, 182, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 4, 129, 158, 48, 129, 155, 2, 1, 1, 4, 48, 248, 113, 165, 102, 101, 137, 193, 74, 87, 71, 38, 62, 248, 91, 49, 156, 192, 35, 219, 110, 53, 103, 108, 61, 120, 30, 239, 139, 5, 95, 207, 190, 134, 250, 13, 6, 208, 86, 181, 25, 95, 177, 50, 58, 248, 222, 37, 179, 161, 100, 3, 98, 0, 4, 241, 25, 101, 223, 125, 212, 89, 77, 4, 25, 197, 8, 100, 130, 163, 184, 38, 185, 121, 127, 155, 224, 189, 13, 16, 156, 158, 30, 153, 137, 193, 185, 169, 43, 143, 38, 159, 152, 225, 122, 209, 132, 186, 115, 193, 247, 151, 98, 175, 69, 175, 129, 65, 96, 38, 66, 218, 39, 26, 107, 176, 255, 235, 12, 180, 71, 143, 207, 112, 126, 102, 26, 166, 214, 205, 245, 21, 73, 200, 140, 63, 19, 11, 233, 232, 32, 31, 111, 106, 9, 244, 24, 90, 175, 149, 196])
-    };
-
-    var spki = {
-        "P-521": new Uint8Array([48, 129, 155, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, 129, 134, 0, 4, 0, 238, 105, 249, 71, 21, 215, 1, 233, 226, 1, 19, 51, 212, 244, 249, 108, 186, 125, 145, 248, 139, 17, 43, 175, 117, 207, 9, 204, 31, 138, 202, 151, 97, 141, 169, 56, 152, 34, 210, 155, 111, 233, 153, 106, 97, 32, 62, 247, 82, 183, 113, 232, 149, 143, 196, 103, 123, 179, 119, 133, 101, 171, 96, 214, 237, 0, 222, 171, 103, 97, 137, 91, 147, 94, 58, 211, 37, 251, 133, 73, 229, 111, 19, 120, 106, 167, 63, 136, 162, 236, 254, 64, 147, 52, 115, 216, 174, 242, 64, 196, 223, 215, 213, 6, 242, 44, 221, 14, 85, 85, 143, 63, 191, 5, 235, 247, 239, 239, 122, 114, 215, 143, 70, 70, 155, 132, 72, 242, 110, 39, 18]),
-        "P-256": new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 154, 116, 32, 120, 126, 95, 77, 105, 211, 232, 34, 114, 115, 1, 109, 56, 224, 71, 129, 133, 223, 127, 238, 156, 142, 103, 60, 202, 211, 79, 126, 128, 254, 49, 141, 182, 221, 107, 119, 218, 99, 32, 165, 246, 151, 89, 9, 68, 23, 177, 52, 239, 138, 139, 116, 193, 101, 4, 57, 198, 115, 0, 90, 61]),
-        "P-384": new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 145, 130, 45, 194, 175, 89, 193, 143, 91, 103, 248, 13, 246, 26, 38, 3, 194, 168, 240, 179, 192, 175, 130, 45, 99, 194, 121, 112, 26, 130, 69, 96, 64, 68, 1, 221, 233, 165, 110, 229, 39, 87, 234, 139, 199, 72, 212, 200, 43, 83, 55, 180, 141, 123, 101, 88, 58, 61, 87, 36, 56, 136, 0, 54, 186, 198, 115, 15, 66, 202, 82, 120, 150, 107, 213, 242, 30, 134, 226, 29, 48, 197, 166, 208, 70, 62, 197, 19, 221, 80, 159, 252, 220, 175, 31, 245])
-    };
-
-    var sizes = {
-        "P-521": 66,
-        "P-256": 32,
-        "P-384": 48
-    };
-
-    var derivations = {
-        "P-521": new Uint8Array([0, 156, 43, 206, 87, 190, 128, 173, 171, 59, 7, 56, 91, 142, 89, 144, 235, 125, 111, 222, 189, 176, 27, 243, 83, 113, 164, 246, 7, 94, 157, 40, 138, 193, 42, 109, 254, 3, 170, 87, 67, 188, 129, 112, 157, 73, 168, 34, 148, 2, 25, 182, 75, 118, 138, 205, 82, 15, 161, 54, 142, 160, 175, 141, 71, 93]),
-        "P-256": new Uint8Array([14, 143, 60, 77, 177, 178, 162, 131, 115, 90, 0, 220, 87, 31, 26, 232, 151, 28, 227, 35, 250, 17, 131, 137, 203, 95, 65, 196, 59, 61, 181, 161]),
-        "P-384": new Uint8Array([224, 189, 107, 206, 10, 239, 140, 164, 136, 56, 166, 226, 252, 197, 126, 103, 185, 197, 232, 134, 12, 95, 11, 233, 218, 190, 197, 62, 69, 78, 24, 160, 161, 116, 196, 136, 136, 162, 100, 136, 17, 91, 45, 201, 241, 223, 165, 45])
-    };
-
-    return importKeys(pkcs8, spki, sizes)
-    .then(function(results) {
-        publicKeys = results.publicKeys;
-        privateKeys = results.privateKeys;
-        ecdsaKeyPairs = results.ecdsaKeyPairs;
-        noDeriveKeyKeys = results.noDeriveKeyKeys;
-
-        Object.keys(sizes).forEach(function(namedCurve) {
-            // Basic success case
-            promise_test(function(test) {
-                return subtle.deriveKey({name: "ECDH", public: publicKeys[namedCurve]}, privateKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-                .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-                .then(function(exportedKey) {
-                    assert_true(equalBuffers(exportedKey, derivations[namedCurve], 8 * exportedKey.length), "Derived correct key");
-                }, function(err) {
-                    assert_unreached("deriveKey failed with error " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " good parameters");
-
-            // Case insensitivity check
-            promise_test(function(test) {
-                return subtle.deriveKey({name: "EcDh", public: publicKeys[namedCurve]}, privateKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-                .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-                .then(function(exportedKey) {
-                    assert_true(equalBuffers(exportedKey, derivations[namedCurve], 8 * exportedKey.length), "Derived correct key");
-                }, function(err) {
-                    assert_unreached("deriveKey failed with error " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " mixed case parameters");
-            // Errors to test:
-
-            // - missing public property TypeError
-            promise_test(function(test) {
-                return subtle.deriveKey({name: "ECDH"}, privateKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-                .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-                .then(function(exportedKey) {
-                    assert_unreached("deriveKey succeeded but should have failed with TypeError");
-                }, function(err) {
-                    assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " missing public curve");
-
-            // - Non CryptoKey public property TypeError
-            promise_test(function(test) {
-                return subtle.deriveKey({name: "ECDH", public: {message: "Not a CryptoKey"}}, privateKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-                .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-                .then(function(exportedKey) {
-                    assert_unreached("deriveKey succeeded but should have failed with TypeError");
-                }, function(err) {
-                    assert_equals(err.name, "TypeError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " public property of algorithm is not a CryptoKey");
-
-            // - wrong named curve
-            promise_test(function(test) {
-                publicKey = publicKeys["P-256"];
-                if (namedCurve === "P-256") {
-                    publicKey = publicKeys["P-384"];
-                }
-                return subtle.deriveKey({name: "ECDH", public: publicKey}, privateKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-                .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-                .then(function(exportedKey) {
-                    assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " mismatched curves");
-
-            // - not ECDH public property InvalidAccessError
-            promise_test(function(test) {
-                return subtle.deriveKey({name: "ECDH", public: ecdsaKeyPairs[namedCurve].publicKey}, privateKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-                .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-                .then(function(exportedKey) {
-                    assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " public property of algorithm is not an ECDSA public key");
-
-            // - No deriveKey usage in baseKey InvalidAccessError
-            promise_test(function(test) {
-                return subtle.deriveKey({name: "ECDH", public: publicKeys[namedCurve]}, noDeriveKeyKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-                .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-                .then(function(exportedKey) {
-                    assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " no deriveKey usage for base key");
-
-            // - Use public key for baseKey InvalidAccessError
-            promise_test(function(test) {
-                return subtle.deriveKey({name: "ECDH", public: publicKeys[namedCurve]}, publicKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-                .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-                .then(function(exportedKey) {
-                    assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " base key is not a private key");
-
-            // - Use private key for public property InvalidAccessError
-            promise_test(function(test) {
-                return subtle.deriveKey({name: "ECDH", public: privateKeys[namedCurve]}, privateKeys[namedCurve], {name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-                .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-                .then(function(exportedKey) {
-                    assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-                });
-            }, namedCurve + " public property value is a private key");
-
-            // - Use secret key for public property InvalidAccessError
-            promise_test(function(test) {
-                return subtle.generateKey({name: "HMAC", hash: "SHA-256", length: 256}, true, ["sign", "verify"])
-                .then(function(secretKey) {
-                    return subtle.deriveKey({name: "ECDH", public: secretKey}, privateKeys[namedCurve], {name: "AES-CBC", length: 256}, true, ["sign", "verify"])
-                    .then(function(key) {return crypto.subtle.exportKey("raw", key);})
-                    .then(function(exportedKey) {
-                        assert_unreached("deriveKey succeeded but should have failed with InvalidAccessError");
-                    }, function(err) {
-                        assert_equals(err.name, "InvalidAccessError", "Should throw correct error, not " + err.name + ": " + err.message);
-                    });
-                });
-            }, namedCurve + " public property value is a secret key");
-        });
-    });
-
-    function importKeys(pkcs8, spki, sizes) {
-        var privateKeys = {};
-        var publicKeys = {};
-        var ecdsaKeyPairs = {};
-        var noDeriveKeyKeys = {};
-
-        var promises = [];
-        Object.keys(pkcs8).forEach(function(namedCurve) {
-            var operation = subtle.importKey("pkcs8", pkcs8[namedCurve],
-                                            {name: "ECDH", namedCurve: namedCurve},
-                                            false, ["deriveBits", "deriveKey"])
-                            .then(function(key) {
-                                privateKeys[namedCurve] = key;
-                            }, function (err) {
-                                privateKeys[namedCurve] = null;
-                            });
-            promises.push(operation);
-        });
-        Object.keys(pkcs8).forEach(function(namedCurve) {
-            var operation = subtle.importKey("pkcs8", pkcs8[namedCurve],
-                                            {name: "ECDH", namedCurve: namedCurve},
-                                            false, ["deriveBits"])
-                            .then(function(key) {
-                                noDeriveKeyKeys[namedCurve] = key;
-                            }, function (err) {
-                                noDeriveKeyKeys[namedCurve] = null;
-                            });
-            promises.push(operation);
-        });
-        Object.keys(spki).forEach(function(namedCurve) {
-            var operation = subtle.importKey("spki", spki[namedCurve],
-                                            {name: "ECDH", namedCurve: namedCurve},
-                                            false, [])
-                            .then(function(key) {
-                                publicKeys[namedCurve] = key;
-                            }, function (err) {
-                                publicKeys[namedCurve] = null;
-                            });
-            promises.push(operation);
-        });
-        Object.keys(sizes).forEach(function(namedCurve) {
-            var operation = subtle.generateKey({name: "ECDSA", namedCurve: namedCurve}, false, ["sign", "verify"])
-                            .then(function(keyPair) {
-                                ecdsaKeyPairs[namedCurve] = keyPair;
-                            }, function (err) {
-                                ecdsaKeyPairs[namedCurve] = null;
-                            });
-            promises.push(operation);
-        });
-
-        return Promise.all(promises)
-               .then(function(results) {return {privateKeys: privateKeys, publicKeys: publicKeys, ecdsaKeyPairs: ecdsaKeyPairs, noDeriveKeyKeys: noDeriveKeyKeys}});
-    }
-
+    return defineEcdhTests("deriveKey");
 }
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/hkdf.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/hkdf.https.any.js
index 3879ddb..b283f2b 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/hkdf.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/hkdf.https.any.js
@@ -6,6 +6,7 @@
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
 // META: script=hkdf_vectors.js
+// META: script=kdf.js
 // META: script=hkdf.js
 
 // Define subtests from a `promise_test` to ensure the harness does not
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/hkdf.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/hkdf.js
index 08e8c0c..a0901698 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/hkdf.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/hkdf.js
@@ -1,278 +1,63 @@
-
 function define_tests() {
-    // May want to test prefixed implementations.
-    var subtle = self.crypto.subtle;
+    return runKdfTests({
+        name: "HKDF",
+        getBaseKeyData: function(testData) {
+            return testData.derivedKeys;
+        },
+        registerTests: function(context) {
+            var subtle = context.subtle;
+            var testData = context.testData;
+            var derivations = testData.derivations;
+            var salts = testData.salts;
+            var infos = testData.infos;
 
-    // hkdf2_vectors sets up test data with the correct derivations for each
-    // test case.
-    var testData = getTestData();
-    var derivedKeys = testData.derivedKeys;
-    var salts = testData.salts;
-    var derivations = testData.derivations;
-    var infos = testData.infos;
+            Object.keys(derivations).forEach(function(derivedKeySize) {
+                Object.keys(derivations[derivedKeySize]).forEach(function(saltSize) {
+                    Object.keys(derivations[derivedKeySize][saltSize]).forEach(function(hashName) {
+                        Object.keys(derivations[derivedKeySize][saltSize][hashName]).forEach(function(infoSize) {
+                            var testName = derivedKeySize + " derivedKey, " + saltSize + " salt, " + hashName + ", with " + infoSize + " info";
+                            var testCase = {
+                                name: testName,
+                                keyName: derivedKeySize,
+                                hash: hashName,
+                                algorithm: {name: "HKDF", salt: salts[saltSize], info: infos[infoSize], hash: hashName},
+                                expected: derivations[derivedKeySize][saltSize][hashName][infoSize]
+                            };
 
-    // What kinds of keys can be created with deriveKey? The following:
-    var derivedKeyTypes = testData.derivedKeyTypes;
-
-    return setUpBaseKeys(derivedKeys)
-    .then(function(allKeys) {
-        // We get several kinds of base keys. Normal ones that can be used for
-        // derivation operations, ones that lack the deriveBits usage, ones
-        // that lack the deriveKeys usage, and one key that is for the wrong
-        // algorithm (not HKDF in this case).
-        var baseKeys = allKeys.baseKeys;
-        var noBits = allKeys.noBits;
-        var noKey = allKeys.noKey;
-        var wrongKey = allKeys.wrongKey;
-
-        // Test each combination of derivedKey size, salt size, hash function,
-        // and number of iterations. The derivations object is structured in
-        // that way, so navigate it to run tests and compare with correct results.
-        Object.keys(derivations).forEach(function(derivedKeySize) {
-            Object.keys(derivations[derivedKeySize]).forEach(function(saltSize) {
-                Object.keys(derivations[derivedKeySize][saltSize]).forEach(function(hashName) {
-                    Object.keys(derivations[derivedKeySize][saltSize][hashName]).forEach(function(infoSize) {
-                        var testName = derivedKeySize + " derivedKey, " + saltSize + " salt, " + hashName + ", with " + infoSize + " info";
-                        var algorithm = {name: "HKDF", salt: salts[saltSize], info: infos[infoSize], hash: hashName};
-
-                        // Check for correct deriveBits result
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveBits(algorithm, baseKeys[derivedKeySize], 256)
-                            .then(function(derivation) {
-                                assert_true(equalBuffers(derivation, derivations[derivedKeySize][saltSize][hashName][infoSize]), "Derived correct key");
-                            }, function(err) {
-                                assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
-                            });
-                        }, testName);
-
-                        // 0 length
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveBits(algorithm, baseKeys[derivedKeySize], 0)
-                            .then(function(derivation) {
-                                assert_equals(derivation.byteLength, 0, "Derived correctly empty key");
-                            }, function(err) {
-                                assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
-                            });
-                        }, testName + " with 0 length");
-
-                        // Check for correct deriveKey results for every kind of
-                        // key that can be created by the deriveKeys operation.
-                        derivedKeyTypes.forEach(function(derivedKeyType) {
-                            var testName = "Derived key of type ";
-                            Object.keys(derivedKeyType.algorithm).forEach(function(prop) {
-                                testName += prop + ": " + derivedKeyType.algorithm[prop] + " ";
-                            });
-                            testName += " using " + derivedKeySize + " derivedKey, " + saltSize + " salt, " + hashName + ", with " + infoSize + " info";
-
-                            // Test the particular key derivation.
-                            subsetTest(promise_test, function(test) {
-                                return subtle.deriveKey(algorithm, baseKeys[derivedKeySize], derivedKeyType.algorithm, true, derivedKeyType.usages)
-                                .then(function(key) {
-                                    // Need to export the key to see that the correct bits were set.
-                                    return subtle.exportKey("raw", key)
-                                    .then(function(buffer) {
-                                        assert_true(equalBuffers(buffer, derivations[derivedKeySize][saltSize][hashName][infoSize].slice(0, derivedKeyType.algorithm.length/8)), "Exported key matches correct value");
+                            context.registerCase(testCase, function() {
+                                subsetTest(promise_test, function(test) {
+                                    return subtle.deriveBits({name: "HKDF", info: infos[infoSize], hash: hashName}, context.keys.baseKeys[derivedKeySize], 0)
+                                    .then(function(derivation) {
+                                        assert_equals(derivation.byteLength, 0, "Derived even with missing salt");
                                     }, function(err) {
-                                        assert_unreached("Exporting derived key failed with error " + err.name + ": " + err.message);
+                                        assert_equals(err.name, "TypeError", "deriveBits missing salt correctly threw OperationError: " + err.message);
                                     });
-                                }, function(err) {
-                                    assert_unreached("deriveKey failed with error " + err.name + ": " + err.message);
+                                }, testName + " with missing salt");
 
-                                });
-                            }, testName);
-
-                            // Test various error conditions for deriveKey:
-
-                            // - illegal name for hash algorithm (NotSupportedError)
-                            var badHash = hashName.substring(0, 3) + hashName.substring(4);
-                            subsetTest(promise_test, function(test) {
-                                var badAlgorithm = {name: "HKDF", salt: salts[saltSize], hash: badHash, info: algorithm.info};
-                                return subtle.deriveKey(badAlgorithm, baseKeys[derivedKeySize], derivedKeyType.algorithm, true, derivedKeyType.usages)
-                                .then(function(key) {
-                                    assert_unreached("bad hash name should have thrown an NotSupportedError");
-                                }, function(err) {
-                                    assert_equals(err.name, "NotSupportedError", "deriveKey with bad hash name correctly threw NotSupportedError: " + err.message);
-                                });
-                            }, testName + " with bad hash name " + badHash);
-
-                            // - baseKey usages missing "deriveKey" (InvalidAccessError)
-                            subsetTest(promise_test, function(test) {
-                                return subtle.deriveKey(algorithm, noKey[derivedKeySize], derivedKeyType.algorithm, true, derivedKeyType.usages)
-                                .then(function(key) {
-                                    assert_unreached("missing deriveKey usage should have thrown an InvalidAccessError");
-                                }, function(err) {
-                                    assert_equals(err.name, "InvalidAccessError", "deriveKey with missing deriveKey usage correctly threw InvalidAccessError: " + err.message);
-                                });
-                            }, testName + " with missing deriveKey usage");
-
-                            // - baseKey algorithm does not match HKDF (InvalidAccessError)
-                            subsetTest(promise_test, function(test) {
-                                return subtle.deriveKey(algorithm, wrongKey, derivedKeyType.algorithm, true, derivedKeyType.usages)
-                                .then(function(key) {
-                                    assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError");
-                                }, function(err) {
-                                    assert_equals(err.name, "InvalidAccessError", "deriveKey with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message);
-                                });
-                            }, testName + " with wrong (ECDH) key");
-
+                                subsetTest(promise_test, function(test) {
+                                    return subtle.deriveBits({name: "HKDF", salt: salts[saltSize], hash: hashName}, context.keys.baseKeys[derivedKeySize], 0)
+                                    .then(function(derivation) {
+                                        assert_equals(derivation.byteLength, 0, "Derived even with missing info");
+                                    }, function(err) {
+                                        assert_equals(err.name, "TypeError", "deriveBits missing info correctly threw OperationError: " + err.message);
+                                    });
+                                }, testName + " with missing info");
+                            });
                         });
-
-                        // Test various error conditions for deriveBits below:
-
-                        // missing salt (TypeError)
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveBits({name: "HKDF", info: infos[infoSize], hash: hashName}, baseKeys[derivedKeySize], 0)
-                            .then(function(derivation) {
-                                assert_equals(derivation.byteLength, 0, "Derived even with missing salt");
-                            }, function(err) {
-                                assert_equals(err.name, "TypeError", "deriveBits missing salt correctly threw OperationError: " + err.message);
-                            });
-                        }, testName + " with missing salt");
-
-                        // missing info (TypeError)
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveBits({name: "HKDF", salt: salts[saltSize], hash: hashName}, baseKeys[derivedKeySize], 0)
-                            .then(function(derivation) {
-                                assert_equals(derivation.byteLength, 0, "Derived even with missing info");
-                            }, function(err) {
-                                assert_equals(err.name, "TypeError", "deriveBits missing info correctly threw OperationError: " + err.message);
-                            });
-                        }, testName + " with missing info");
-
-                        // length not multiple of 8 (OperationError)
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveBits(algorithm, baseKeys[derivedKeySize], 44)
-                            .then(function(derivation) {
-                                assert_unreached("non-multiple of 8 length should have thrown an OperationError");
-                            }, function(err) {
-                                assert_equals(err.name, "OperationError", "deriveBits with non-multiple of 8 length correctly threw OperationError: " + err.message);
-                            });
-                        }, testName + " with non-multiple of 8 length");
-
-                        // - illegal name for hash algorithm (NotSupportedError)
-                        var badHash = hashName.substring(0, 3) + hashName.substring(4);
-                        subsetTest(promise_test, function(test) {
-                            var badAlgorithm = {name: "HKDF", salt: salts[saltSize], hash: badHash, info: algorithm.info};
-                            return subtle.deriveBits(badAlgorithm, baseKeys[derivedKeySize], 256)
-                            .then(function(derivation) {
-                                assert_unreached("bad hash name should have thrown an NotSupportedError");
-                            }, function(err) {
-                                assert_equals(err.name, "NotSupportedError", "deriveBits with bad hash name correctly threw NotSupportedError: " + err.message);
-                            });
-                        }, testName + " with bad hash name " + badHash);
-
-                        // - baseKey usages missing "deriveBits" (InvalidAccessError)
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveBits(algorithm, noBits[derivedKeySize], 256)
-                            .then(function(derivation) {
-                                assert_unreached("missing deriveBits usage should have thrown an InvalidAccessError");
-                            }, function(err) {
-                                assert_equals(err.name, "InvalidAccessError", "deriveBits with missing deriveBits usage correctly threw InvalidAccessError: " + err.message);
-                            });
-                        }, testName + " with missing deriveBits usage");
-
-                        // - baseKey algorithm does not match HKDF (InvalidAccessError)
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveBits(algorithm, wrongKey, 256)
-                            .then(function(derivation) {
-                                assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError");
-                            }, function(err) {
-                                assert_equals(err.name, "InvalidAccessError", "deriveBits with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message);
-                            });
-                        }, testName + " with wrong (ECDH) key");
-                    });
-                });
-
-                // - legal algorithm name but not digest one (e.g., PBKDF2) (NotSupportedError)
-                var nonDigestHash = "PBKDF2";
-                Object.keys(infos).forEach(function(infoSize) {
-                    var testName = derivedKeySize + " derivedKey, " + saltSize + " salt, " + nonDigestHash + ", with " + infoSize + " info";
-                    var algorithm = {name: "HKDF", salt: salts[saltSize], hash: nonDigestHash};
-                    if (infoSize !== "missing") {
-                        algorithm.info = infos[infoSize];
-                    }
-
-                    subsetTest(promise_test, function(test) {
-                        return subtle.deriveBits(algorithm, baseKeys[derivedKeySize], 256)
-                        .then(function(derivation) {
-                            assert_unreached("non-digest algorithm should have thrown an NotSupportedError");
-                        }, function(err) {
-                            assert_equals(err.name, "NotSupportedError", "deriveBits with non-digest algorithm correctly threw NotSupportedError: " + err.message);
-                        });
-                    }, testName + " with non-digest algorithm " + nonDigestHash);
-
-                    derivedKeyTypes.forEach(function(derivedKeyType) {
-                        var testName = "Derived key of type ";
-                        Object.keys(derivedKeyType.algorithm).forEach(function(prop) {
-                            testName += prop + ": " + derivedKeyType.algorithm[prop] + " ";
-                        });
-                        testName += " using " + derivedKeySize + " derivedKey, " + saltSize + " salt, " + nonDigestHash + ", with " + infoSize + " info";
-
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveKey(algorithm, baseKeys[derivedKeySize], derivedKeyType.algorithm, true, derivedKeyType.usages)
-                            .then(function(derivation) {
-                                assert_unreached("non-digest algorithm should have thrown an NotSupportedError");
-                            }, function(err) {
-                                assert_equals(err.name, "NotSupportedError", "derivekey with non-digest algorithm correctly threw NotSupportedError: " + err.message);
-                            });
-                        }, testName);
                     });
 
+                    var nonDigestHash = "PBKDF2";
+                    Object.keys(infos).forEach(function(infoSize) {
+                        var testName = derivedKeySize + " derivedKey, " + saltSize + " salt, " + nonDigestHash + ", with " + infoSize + " info";
+                        context.registerNonDigestCase({
+                            name: testName,
+                            keyName: derivedKeySize,
+                            hash: nonDigestHash,
+                            algorithm: {name: "HKDF", salt: salts[saltSize], hash: nonDigestHash, info: infos[infoSize]}
+                        });
+                    });
                 });
-
             });
-        });
+        }
     });
-
-    // Deriving bits and keys requires starting with a base key, which is created
-    // by importing a derivedKey. setUpBaseKeys returns a promise that yields the
-    // necessary base keys.
-    function setUpBaseKeys(derivedKeys) {
-        var promises = [];
-
-        var baseKeys = {};
-        var noBits = {};
-        var noKey = {};
-        var wrongKey = null;
-
-        Object.keys(derivedKeys).forEach(function(derivedKeySize) {
-            var promise = subtle.importKey("raw", derivedKeys[derivedKeySize], {name: "HKDF"}, false, ["deriveKey", "deriveBits"])
-            .then(function(baseKey) {
-                baseKeys[derivedKeySize] = baseKey;
-            }, function(err) {
-                baseKeys[derivedKeySize] = null;
-            });
-            promises.push(promise);
-
-            promise = subtle.importKey("raw", derivedKeys[derivedKeySize], {name: "HKDF"}, false, ["deriveBits"])
-            .then(function(baseKey) {
-                noKey[derivedKeySize] = baseKey;
-            }, function(err) {
-                noKey[derivedKeySize] = null;
-            });
-            promises.push(promise);
-
-            promise = subtle.importKey("raw", derivedKeys[derivedKeySize], {name: "HKDF"}, false, ["deriveKey"])
-            .then(function(baseKey) {
-                noBits[derivedKeySize] = baseKey;
-            }, function(err) {
-                noBits[derivedKeySize] = null;
-            });
-            promises.push(promise);
-        });
-
-        var promise = subtle.generateKey({name: "ECDH", namedCurve: "P-256"}, false, ["deriveKey", "deriveBits"])
-        .then(function(baseKey) {
-            wrongKey = baseKey.privateKey;
-        }, function(err) {
-            wrongKey = null;
-        });
-        promises.push(promise);
-
-
-        return Promise.all(promises).then(function() {
-            return {baseKeys: baseKeys, noBits: noBits, noKey: noKey, wrongKey: wrongKey};
-        });
-    }
-
 }
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/kdf.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/kdf.js
new file mode 100644
index 0000000..ecbb3b4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/kdf.js
@@ -0,0 +1,209 @@
+function runKdfTests(options) {
+    var subtle = self.crypto.subtle;
+    var testData = getTestData();
+    var derivedKeyTypes = testData.derivedKeyTypes;
+
+    return setUpBaseKeys(options.getBaseKeyData(testData))
+    .then(function(allKeys) {
+        function derivedKeyTestName(derivedKeyType, caseName) {
+            var testName = "Derived key of type ";
+            Object.keys(derivedKeyType.algorithm).forEach(function(prop) {
+                testName += prop + ": " + derivedKeyType.algorithm[prop] + " ";
+            });
+            return testName + " using " + caseName;
+        }
+
+        function withHash(algorithm, hash) {
+            return Object.assign({}, algorithm, {hash: hash});
+        }
+
+        function registerCase(testCase, registerAdditionalBitsTests) {
+            var algorithm = testCase.algorithm;
+            var baseKey = allKeys.baseKeys[testCase.keyName];
+            var badHash = testCase.hash.substring(0, 3) + testCase.hash.substring(4);
+
+            subsetTest(promise_test, function(test) {
+                return subtle.deriveBits(algorithm, baseKey, 256)
+                .then(function(derivation) {
+                    assert_true(equalBuffers(derivation, testCase.expected), "Derived correct key");
+                }, function(err) {
+                    assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
+                });
+            }, testCase.name);
+
+            subsetTest(promise_test, function(test) {
+                return subtle.deriveBits(algorithm, baseKey, 0)
+                .then(function(derivation) {
+                    assert_equals(derivation.byteLength, 0, "Derived correctly empty key");
+                }, function(err) {
+                    assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
+                });
+            }, testCase.name + " with 0 length");
+
+            derivedKeyTypes.forEach(function(derivedKeyType) {
+                var testName = derivedKeyTestName(derivedKeyType, testCase.name);
+
+                subsetTest(promise_test, function(test) {
+                    return subtle.deriveKey(algorithm, baseKey, derivedKeyType.algorithm, true, derivedKeyType.usages)
+                    .then(function(key) {
+                        return subtle.exportKey("raw", key)
+                        .then(function(buffer) {
+                            assert_true(equalBuffers(buffer, testCase.expected.slice(0, derivedKeyType.algorithm.length/8)), "Exported key matches correct value");
+                        }, function(err) {
+                            assert_unreached("Exporting derived key failed with error " + err.name + ": " + err.message);
+                        });
+                    }, function(err) {
+                        assert_unreached("deriveKey failed with error " + err.name + ": " + err.message);
+                    });
+                }, testName);
+
+                subsetTest(promise_test, function(test) {
+                    return subtle.deriveKey(withHash(algorithm, badHash), baseKey, derivedKeyType.algorithm, true, derivedKeyType.usages)
+                    .then(function(key) {
+                        assert_unreached("bad hash name should have thrown an NotSupportedError");
+                    }, function(err) {
+                        assert_equals(err.name, "NotSupportedError", "deriveKey with bad hash name correctly threw NotSupportedError: " + err.message);
+                    });
+                }, testName + " with bad hash name " + badHash);
+
+                subsetTest(promise_test, function(test) {
+                    return subtle.deriveKey(algorithm, allKeys.noKey[testCase.keyName], derivedKeyType.algorithm, true, derivedKeyType.usages)
+                    .then(function(key) {
+                        assert_unreached("missing deriveKey usage should have thrown an InvalidAccessError");
+                    }, function(err) {
+                        assert_equals(err.name, "InvalidAccessError", "deriveKey with missing deriveKey usage correctly threw InvalidAccessError: " + err.message);
+                    });
+                }, testName + " with missing deriveKey usage");
+
+                subsetTest(promise_test, function(test) {
+                    return subtle.deriveKey(algorithm, allKeys.wrongKey, derivedKeyType.algorithm, true, derivedKeyType.usages)
+                    .then(function(key) {
+                        assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError");
+                    }, function(err) {
+                        assert_equals(err.name, "InvalidAccessError", "deriveKey with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message);
+                    });
+                }, testName + " with wrong (ECDH) key");
+            });
+
+            if (registerAdditionalBitsTests !== undefined) {
+                registerAdditionalBitsTests();
+            }
+
+            subsetTest(promise_test, function(test) {
+                return subtle.deriveBits(algorithm, baseKey, 44)
+                .then(function(derivation) {
+                    assert_unreached("non-multiple of 8 length should have thrown an OperationError");
+                }, function(err) {
+                    assert_equals(err.name, "OperationError", "deriveBits with non-multiple of 8 length correctly threw OperationError: " + err.message);
+                });
+            }, testCase.name + " with non-multiple of 8 length");
+
+            subsetTest(promise_test, function(test) {
+                return subtle.deriveBits(withHash(algorithm, badHash), baseKey, 256)
+                .then(function(derivation) {
+                    assert_unreached("bad hash name should have thrown an NotSupportedError");
+                }, function(err) {
+                    assert_equals(err.name, "NotSupportedError", "deriveBits with bad hash name correctly threw NotSupportedError: " + err.message);
+                });
+            }, testCase.name + " with bad hash name " + badHash);
+
+            subsetTest(promise_test, function(test) {
+                return subtle.deriveBits(algorithm, allKeys.noBits[testCase.keyName], 256)
+                .then(function(derivation) {
+                    assert_unreached("missing deriveBits usage should have thrown an InvalidAccessError");
+                }, function(err) {
+                    assert_equals(err.name, "InvalidAccessError", "deriveBits with missing deriveBits usage correctly threw InvalidAccessError: " + err.message);
+                });
+            }, testCase.name + " with missing deriveBits usage");
+
+            subsetTest(promise_test, function(test) {
+                return subtle.deriveBits(algorithm, allKeys.wrongKey, 256)
+                .then(function(derivation) {
+                    assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError");
+                }, function(err) {
+                    assert_equals(err.name, "InvalidAccessError", "deriveBits with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message);
+                });
+            }, testCase.name + " with wrong (ECDH) key");
+        }
+
+        function registerNonDigestCase(testCase) {
+            var baseKey = allKeys.baseKeys[testCase.keyName];
+
+            subsetTest(promise_test, function(test) {
+                return subtle.deriveBits(testCase.algorithm, baseKey, 256)
+                .then(function(derivation) {
+                    assert_unreached("non-digest algorithm should have thrown an NotSupportedError");
+                }, function(err) {
+                    assert_equals(err.name, "NotSupportedError", "deriveBits with non-digest algorithm correctly threw NotSupportedError: " + err.message);
+                });
+            }, testCase.name + " with non-digest algorithm " + testCase.hash);
+
+            derivedKeyTypes.forEach(function(derivedKeyType) {
+                subsetTest(promise_test, function(test) {
+                    return subtle.deriveKey(testCase.algorithm, baseKey, derivedKeyType.algorithm, true, derivedKeyType.usages)
+                    .then(function(derivation) {
+                        assert_unreached("non-digest algorithm should have thrown an NotSupportedError");
+                    }, function(err) {
+                        assert_equals(err.name, "NotSupportedError", "derivekey with non-digest algorithm correctly threw NotSupportedError: " + err.message);
+                    });
+                }, derivedKeyTestName(derivedKeyType, testCase.name));
+            });
+        }
+
+        options.registerTests({
+            subtle: subtle,
+            testData: testData,
+            derivedKeyTypes: derivedKeyTypes,
+            keys: allKeys,
+            registerCase: registerCase,
+            registerNonDigestCase: registerNonDigestCase,
+            derivedKeyTestName: derivedKeyTestName
+        });
+    });
+
+    function setUpBaseKeys(baseKeyData) {
+        var promises = [];
+        var baseKeys = {};
+        var noBits = {};
+        var noKey = {};
+        var wrongKey = null;
+
+        Object.keys(baseKeyData).forEach(function(keyName) {
+            var promise = subtle.importKey("raw", baseKeyData[keyName], {name: options.name}, false, ["deriveKey", "deriveBits"])
+            .then(function(baseKey) {
+                baseKeys[keyName] = baseKey;
+            }, function(err) {
+                baseKeys[keyName] = null;
+            });
+            promises.push(promise);
+
+            promise = subtle.importKey("raw", baseKeyData[keyName], {name: options.name}, false, ["deriveBits"])
+            .then(function(baseKey) {
+                noKey[keyName] = baseKey;
+            }, function(err) {
+                noKey[keyName] = null;
+            });
+            promises.push(promise);
+
+            promise = subtle.importKey("raw", baseKeyData[keyName], {name: options.name}, false, ["deriveKey"])
+            .then(function(baseKey) {
+                noBits[keyName] = baseKey;
+            }, function(err) {
+                noBits[keyName] = null;
+            });
+            promises.push(promise);
+        });
+
+        var promise = subtle.generateKey({name: "ECDH", namedCurve: "P-256"}, false, ["deriveKey", "deriveBits"])
+        .then(function(baseKey) {
+            wrongKey = baseKey.privateKey;
+        }, function(err) {
+            wrongKey = null;
+        });
+        promises.push(promise);
+
+        return Promise.all(promises).then(function() {
+            return {baseKeys: baseKeys, noBits: noBits, noKey: noKey, wrongKey: wrongKey};
+        });
+    }
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/pbkdf2.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/pbkdf2.https.any.js
index cc2ed9b..8d12993 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/pbkdf2.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/pbkdf2.https.any.js
@@ -12,6 +12,7 @@
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
 // META: script=pbkdf2_vectors.js
+// META: script=kdf.js
 // META: script=pbkdf2.js
 
 // Define subtests from a `promise_test` to ensure the harness does not
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/pbkdf2.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/pbkdf2.js
index 4d5b013..57c19ae 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/pbkdf2.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/derive_bits_keys/pbkdf2.js
@@ -1,274 +1,64 @@
 function define_tests() {
-    // May want to test prefixed implementations.
-    var subtle = self.crypto.subtle;
+    return runKdfTests({
+        name: "PBKDF2",
+        getBaseKeyData: function(testData) {
+            return testData.passwords;
+        },
+        registerTests: function(context) {
+            var subtle = context.subtle;
+            var testData = context.testData;
+            var derivations = testData.derivations;
+            var salts = testData.salts;
 
-    // pbkdf2_vectors sets up test data with the correct derivations for each
-    // test case.
-    var testData = getTestData();
-    var passwords = testData.passwords;
-    var salts = testData.salts;
-    var derivations = testData.derivations;
-
-    // What kinds of keys can be created with deriveKey? The following:
-    var derivedKeyTypes = testData.derivedKeyTypes;
-
-    return setUpBaseKeys(passwords)
-    .then(function(allKeys) {
-        // We get several kinds of base keys. Normal ones that can be used for
-        // derivation operations, ones that lack the deriveBits usage, ones
-        // that lack the deriveKeys usage, and one key that is for the wrong
-        // algorithm (not PBKDF2 in this case).
-        var baseKeys = allKeys.baseKeys;
-        var noBits = allKeys.noBits;
-        var noKey = allKeys.noKey;
-        var wrongKey = allKeys.wrongKey;
-
-        // Test each combination of password size, salt size, hash function,
-        // and number of iterations. The derivations object is structured in
-        // that way, so navigate it to run tests and compare with correct results.
-        Object.keys(derivations).forEach(function(passwordSize) {
-            Object.keys(derivations[passwordSize]).forEach(function(saltSize) {
-                Object.keys(derivations[passwordSize][saltSize]).forEach(function(hashName) {
-                    Object.keys(derivations[passwordSize][saltSize][hashName]).forEach(function(iterations) {
-                        var testName = passwordSize + " password, " + saltSize + " salt, " + hashName + ", with " + iterations + " iterations";
-
-                        // Check for correct deriveBits result
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256)
-                            .then(function(derivation) {
-                                assert_true(equalBuffers(derivation, derivations[passwordSize][saltSize][hashName][iterations]), "Derived correct key");
-                            }, function(err) {
-                                assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
+            Object.keys(derivations).forEach(function(passwordSize) {
+                Object.keys(derivations[passwordSize]).forEach(function(saltSize) {
+                    Object.keys(derivations[passwordSize][saltSize]).forEach(function(hashName) {
+                        Object.keys(derivations[passwordSize][saltSize][hashName]).forEach(function(iterations) {
+                            var testName = passwordSize + " password, " + saltSize + " salt, " + hashName + ", with " + iterations + " iterations";
+                            context.registerCase({
+                                name: testName,
+                                keyName: passwordSize,
+                                hash: hashName,
+                                algorithm: {name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)},
+                                expected: derivations[passwordSize][saltSize][hashName][iterations]
                             });
-                        }, testName);
-
-                        // 0 length
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 0)
-                            .then(function(derivation) {
-                                assert_true(equalBuffers(derivation.byteLength, 0, "Derived correctly empty key"));
-                            }, function(err) {
-                                assert_unreached("deriveBits failed with error " + err.name + ": " + err.message);
-                            });
-                        }, testName + " with 0 length");
-
-                        // Check for correct deriveKey results for every kind of
-                        // key that can be created by the deriveKeys operation.
-                        derivedKeyTypes.forEach(function(derivedKeyType) {
-                            var testName = "Derived key of type ";
-                            Object.keys(derivedKeyType.algorithm).forEach(function(prop) {
-                                testName += prop + ": " + derivedKeyType.algorithm[prop] + " ";
-                            });
-                            testName += " using " + passwordSize + " password, " + saltSize + " salt, " + hashName + ", with " + iterations + " iterations";
-
-                            // Test the particular key derivation.
-                            subsetTest(promise_test, function(test) {
-                                return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
-                                .then(function(key) {
-                                    // Need to export the key to see that the correct bits were set.
-                                    return subtle.exportKey("raw", key)
-                                    .then(function(buffer) {
-                                        assert_true(equalBuffers(buffer, derivations[passwordSize][saltSize][hashName][iterations].slice(0, derivedKeyType.algorithm.length/8)), "Exported key matches correct value");
-                                    }, function(err) {
-                                        assert_unreached("Exporting derived key failed with error " + err.name + ": " + err.message);
-                                    });
-                                }, function(err) {
-                                    assert_unreached("deriveKey failed with error " + err.name + ": " + err.message);
-
-                                });
-                            }, testName);
-
-                            // Test various error conditions for deriveKey:
-
-                            // - illegal name for hash algorithm (NotSupportedError)
-                            var badHash = hashName.substring(0, 3) + hashName.substring(4);
-                            subsetTest(promise_test, function(test) {
-                                return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: badHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
-                                .then(function(key) {
-                                    assert_unreached("bad hash name should have thrown an NotSupportedError");
-                                }, function(err) {
-                                    assert_equals(err.name, "NotSupportedError", "deriveKey with bad hash name correctly threw NotSupportedError: " + err.message);
-                                });
-                            }, testName + " with bad hash name " + badHash);
-
-                            // - baseKey usages missing "deriveKey" (InvalidAccessError)
-                            subsetTest(promise_test, function(test) {
-                                return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, noKey[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
-                                .then(function(key) {
-                                    assert_unreached("missing deriveKey usage should have thrown an InvalidAccessError");
-                                }, function(err) {
-                                    assert_equals(err.name, "InvalidAccessError", "deriveKey with missing deriveKey usage correctly threw InvalidAccessError: " + err.message);
-                                });
-                            }, testName + " with missing deriveKey usage");
-
-                            // - baseKey algorithm does not match PBKDF2 (InvalidAccessError)
-                            subsetTest(promise_test, function(test) {
-                                return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, wrongKey, derivedKeyType.algorithm, true, derivedKeyType.usages)
-                                .then(function(key) {
-                                    assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError");
-                                }, function(err) {
-                                    assert_equals(err.name, "InvalidAccessError", "deriveKey with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message);
-                                });
-                            }, testName + " with wrong (ECDH) key");
-
                         });
 
-                        // length not multiple of 8 (OperationError)
+                        var zeroIterationName = passwordSize + " password, " + saltSize + " salt, " + hashName + ", with 0 iterations";
+                        var zeroIterationAlgorithm = {name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: 0};
                         subsetTest(promise_test, function(test) {
-                            return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, baseKeys[passwordSize], 44)
-                            .then(function(derivation) {
-                                assert_unreached("non-multiple of 8 length should have thrown an OperationError");
-                            }, function(err) {
-                                assert_equals(err.name, "OperationError", "deriveBits with non-multiple of 8 length correctly threw OperationError: " + err.message);
-                            });
-                        }, testName + " with non-multiple of 8 length");
-
-                        // - illegal name for hash algorithm (NotSupportedError)
-                        var badHash = hashName.substring(0, 3) + hashName.substring(4);
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: badHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256)
-                            .then(function(derivation) {
-                                assert_unreached("bad hash name should have thrown an NotSupportedError");
-                            }, function(err) {
-                                assert_equals(err.name, "NotSupportedError", "deriveBits with bad hash name correctly threw NotSupportedError: " + err.message);
-                            });
-                        }, testName + " with bad hash name " + badHash);
-
-                        // - baseKey usages missing "deriveBits" (InvalidAccessError)
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, noBits[passwordSize], 256)
-                            .then(function(derivation) {
-                                assert_unreached("missing deriveBits usage should have thrown an InvalidAccessError");
-                            }, function(err) {
-                                assert_equals(err.name, "InvalidAccessError", "deriveBits with missing deriveBits usage correctly threw InvalidAccessError: " + err.message);
-                            });
-                        }, testName + " with missing deriveBits usage");
-
-                        // - baseKey algorithm does not match PBKDF2 (InvalidAccessError)
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: parseInt(iterations)}, wrongKey, 256)
-                            .then(function(derivation) {
-                                assert_unreached("wrong (ECDH) key should have thrown an InvalidAccessError");
-                            }, function(err) {
-                                assert_equals(err.name, "InvalidAccessError", "deriveBits with wrong (ECDH) key correctly threw InvalidAccessError: " + err.message);
-                            });
-                        }, testName + " with wrong (ECDH) key");
-                    });
-
-                    // Check that 0 iterations throws proper error
-                    subsetTest(promise_test, function(test) {
-                        return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: 0}, baseKeys[passwordSize], 256)
-                        .then(function(derivation) {
-                            assert_unreached("0 iterations should have thrown an error");
-                        }, function(err) {
-                            assert_equals(err.name, "OperationError", "deriveBits with 0 iterations correctly threw OperationError: " + err.message);
-                        });
-                    }, passwordSize + " password, " + saltSize + " salt, " + hashName + ", with 0 iterations");
-
-                    derivedKeyTypes.forEach(function(derivedKeyType) {
-                        var testName = "Derived key of type ";
-                        Object.keys(derivedKeyType.algorithm).forEach(function(prop) {
-                            testName += prop + ": " + derivedKeyType.algorithm[prop] + " ";
-                        });
-                        testName += " using " + passwordSize + " password, " + saltSize + " salt, " + hashName + ", with 0 iterations";
-
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: hashName, iterations: 0}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
+                            return subtle.deriveBits(zeroIterationAlgorithm, context.keys.baseKeys[passwordSize], 256)
                             .then(function(derivation) {
                                 assert_unreached("0 iterations should have thrown an error");
                             }, function(err) {
-                                assert_equals(err.name, "OperationError", "derivekey with 0 iterations correctly threw OperationError: " + err.message);
+                                assert_equals(err.name, "OperationError", "deriveBits with 0 iterations correctly threw OperationError: " + err.message);
                             });
-                        }, testName);
-                    });
-                });
+                        }, zeroIterationName);
 
-                // - legal algorithm name but not digest one (e.g., PBKDF2) (NotSupportedError)
-                var nonDigestHash = "PBKDF2";
-                [1, 1000, 100000].forEach(function(iterations) {
-                    var testName = passwordSize + " password, " + saltSize + " salt, " + nonDigestHash + ", with " + iterations + " iterations";
-
-                    subsetTest(promise_test, function(test) {
-                        return subtle.deriveBits({name: "PBKDF2", salt: salts[saltSize], hash: nonDigestHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], 256)
-                        .then(function(derivation) {
-                            assert_unreached("non-digest algorithm should have thrown an NotSupportedError");
-                        }, function(err) {
-                            assert_equals(err.name, "NotSupportedError", "deriveBits with non-digest algorithm correctly threw NotSupportedError: " + err.message);
+                        context.derivedKeyTypes.forEach(function(derivedKeyType) {
+                            subsetTest(promise_test, function(test) {
+                                return subtle.deriveKey(zeroIterationAlgorithm, context.keys.baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
+                                .then(function(derivation) {
+                                    assert_unreached("0 iterations should have thrown an error");
+                                }, function(err) {
+                                    assert_equals(err.name, "OperationError", "derivekey with 0 iterations correctly threw OperationError: " + err.message);
+                                });
+                            }, context.derivedKeyTestName(derivedKeyType, zeroIterationName));
                         });
-                    }, testName + " with non-digest algorithm " + nonDigestHash);
-
-                    derivedKeyTypes.forEach(function(derivedKeyType) {
-                        var testName = "Derived key of type ";
-                        Object.keys(derivedKeyType.algorithm).forEach(function(prop) {
-                            testName += prop + ": " + derivedKeyType.algorithm[prop] + " ";
-                        });
-                        testName += " using " + passwordSize + " password, " + saltSize + " salt, " + nonDigestHash + ", with " + iterations + " iterations";
-
-                        subsetTest(promise_test, function(test) {
-                            return subtle.deriveKey({name: "PBKDF2", salt: salts[saltSize], hash: nonDigestHash, iterations: parseInt(iterations)}, baseKeys[passwordSize], derivedKeyType.algorithm, true, derivedKeyType.usages)
-                            .then(function(derivation) {
-                                assert_unreached("non-digest algorithm should have thrown an NotSupportedError");
-                            }, function(err) {
-                                assert_equals(err.name, "NotSupportedError", "derivekey with non-digest algorithm correctly threw NotSupportedError: " + err.message);
-                            });
-                        }, testName);
                     });
 
+                    var nonDigestHash = "PBKDF2";
+                    [1, 1000, 100000].forEach(function(iterations) {
+                        var testName = passwordSize + " password, " + saltSize + " salt, " + nonDigestHash + ", with " + iterations + " iterations";
+                        context.registerNonDigestCase({
+                            name: testName,
+                            keyName: passwordSize,
+                            hash: nonDigestHash,
+                            algorithm: {name: "PBKDF2", salt: salts[saltSize], hash: nonDigestHash, iterations: parseInt(iterations)}
+                        });
+                    });
                 });
-
             });
-        });
+        }
     });
-
-    // Deriving bits and keys requires starting with a base key, which is created
-    // by importing a password. setUpBaseKeys returns a promise that yields the
-    // necessary base keys.
-    function setUpBaseKeys(passwords) {
-        var promises = [];
-
-        var baseKeys = {};
-        var noBits = {};
-        var noKey = {};
-        var wrongKey = null;
-
-        Object.keys(passwords).forEach(function(passwordSize) {
-            var promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveKey", "deriveBits"])
-            .then(function(baseKey) {
-                baseKeys[passwordSize] = baseKey;
-            }, function(err) {
-                baseKeys[passwordSize] = null;
-            });
-            promises.push(promise);
-
-            promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveBits"])
-            .then(function(baseKey) {
-                noKey[passwordSize] = baseKey;
-            }, function(err) {
-                noKey[passwordSize] = null;
-            });
-            promises.push(promise);
-
-            promise = subtle.importKey("raw", passwords[passwordSize], {name: "PBKDF2"}, false, ["deriveKey"])
-            .then(function(baseKey) {
-                noBits[passwordSize] = baseKey;
-            }, function(err) {
-                noBits[passwordSize] = null;
-            });
-            promises.push(promise);
-        });
-
-        var promise = subtle.generateKey({name: "ECDH", namedCurve: "P-256"}, false, ["deriveKey", "deriveBits"])
-        .then(function(baseKey) {
-            wrongKey = baseKey.privateKey;
-        }, function(err) {
-            wrongKey = null;
-        });
-        promises.push(promise);
-
-
-        return Promise.all(promises).then(function() {
-            return {baseKeys: baseKeys, noBits: noBits, noKey: noKey, wrongKey: wrongKey};
-        });
-    }
-
 }
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/cshake.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/cshake.tentative.https.any.js
index 8179366..1bf78f5 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/cshake.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/cshake.tentative.https.any.js
@@ -1,23 +1,12 @@
 // META: title=WebCryptoAPI: digest() cSHAKE algorithms
 // META: script=../util/helpers.js
+// META: script=digest_test_data.js
+// META: script=digest.js
 // META: timeout=long
 
 var subtle = crypto.subtle; // Change to test prefixed implementations
 
-var sourceData = {
-  empty: new Uint8Array(0),
-  short: new Uint8Array([
-    21, 110, 234, 124, 193, 76, 86, 203, 148, 219, 3, 10, 74, 157, 149, 255,
-  ]),
-  medium: new Uint8Array([
-    182, 200, 249, 223, 100, 140, 208, 136, 183, 15, 56, 231, 65, 151, 177, 140,
-    184, 30, 30, 67, 80, 213, 11, 204, 184, 251, 90, 115, 121, 200, 123, 178,
-    227, 214, 237, 84, 97, 237, 30, 159, 54, 243, 64, 163, 150, 42, 68, 107,
-    129, 91, 121, 75, 75, 212, 58, 68, 3, 80, 32, 119, 178, 37, 108, 200, 7,
-    131, 127, 58, 172, 209, 24, 235, 75, 156, 43, 174, 184, 151, 6, 134, 37,
-    171, 172, 161, 147,
-  ]),
-};
+var sourceData = getDigestSourceData(false);
 
 // Test different output lengths for cSHAKE
 var digestLengths = [0, 256, 384, 512];
@@ -158,91 +147,19 @@
 };
 
 // Test cSHAKE digest algorithms with variable output lengths
-Object.keys(digestedData).forEach(function (alg) {
-  digestLengths.forEach(function (length) {
-    Object.keys(sourceData).forEach(function (size) {
-      promise_test(function (test) {
-        return crypto.subtle
-          .digest({ name: alg, outputLength: length }, sourceData[size])
-          .then(function (result) {
-            assert_true(
-              equalBuffers(result, digestedData[alg][length][size]),
-              'digest matches expected'
-            );
-          });
-      }, alg + ' with ' + length + ' bit output and ' + size + ' source data');
-
-      if (sourceData[size].length > 0) {
-        promise_test(function (test) {
-          var buffer = new Uint8Array(sourceData[size]);
-          // Alter the buffer before calling digest
-          buffer[0] = ~buffer[0];
-          return crypto.subtle
-            .digest({
-              get name() {
-                // Alter the buffer back while calling digest
-                buffer[0] = sourceData[size][0];
-                return alg;
-              },
-              outputLength: length
-            }, buffer)
-            .then(function (result) {
-              assert_true(
-                equalBuffers(result, digestedData[alg][length][size]),
-                'digest matches expected'
-              );
-            });
-        }, alg + ' with ' + length + ' bit output and ' + size + ' source data and altered buffer during call');
-
-        promise_test(function (test) {
-          var buffer = new Uint8Array(sourceData[size]);
-          var promise = crypto.subtle
-            .digest({ name: alg, outputLength: length }, buffer)
-            .then(function (result) {
-              assert_true(
-                equalBuffers(result, digestedData[alg][length][size]),
-                'digest matches expected'
-              );
-            });
-          // Alter the buffer after calling digest
-          buffer[0] = ~buffer[0];
-          return promise;
-        }, alg + ' with ' + length + ' bit output and ' + size + ' source data and altered buffer after call');
-
-        promise_test(function (test) {
-          var buffer = new Uint8Array(sourceData[size]);
-          return crypto.subtle
-            .digest({
-              get name() {
-                // Transfer the buffer while calling digest
-                buffer.buffer.transfer();
-                return alg;
-              },
-              outputLength: length
-            }, buffer)
-            .then(function (result) {
-              assert_true(
-                equalBuffers(result, digestedData[alg][length].empty),
-                'digest on transferred buffer should match result for empty buffer'
-              );
-            });
-        }, alg + ' with ' + length + ' bit output and ' + size + ' source data and transferred buffer during call');
-
-        promise_test(function (test) {
-          var buffer = new Uint8Array(sourceData[size]);
-          var promise = crypto.subtle
-            .digest({ name: alg, outputLength: length }, buffer)
-            .then(function (result) {
-              assert_true(
-                equalBuffers(result, digestedData[alg][length][size]),
-                'digest matches expected'
-              );
-            });
-          // Transfer the buffer after calling digest
-          buffer.buffer.transfer();
-          return promise;
-        }, alg + ' with ' + length + ' bit output and ' + size + ' source data and transferred buffer after call');
-      }
+runDigestTests(subtle, sourceData, function (size) {
+  var vectors = [];
+  Object.keys(digestedData).forEach(function (alg) {
+    digestLengths.forEach(function (length) {
+      vectors.push({
+        algorithm: {name: alg, outputLength: length},
+        expected: digestedData[alg][length][size],
+        emptyExpected: digestedData[alg][length].empty,
+        label: alg + ' with ' + length + ' bit output and ' +
+          size + ' source data',
+        mutations: true,
+      });
     });
   });
+  return vectors;
 });
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/digest.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/digest.https.any.js
index 38ce85ec..26711b288 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/digest.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/digest.https.any.js
@@ -1,19 +1,12 @@
 // META: title=WebCryptoAPI: digest()
 // META: script=../util/helpers.js
+// META: script=digest_test_data.js
+// META: script=digest.js
 // META: timeout=long
 
     var subtle = crypto.subtle; // Change to test prefixed implementations
 
-    var sourceData = {
-        empty: new Uint8Array(0),
-        short: new Uint8Array([21, 110, 234, 124, 193, 76, 86, 203, 148, 219, 3, 10, 74, 157, 149, 255]),
-        medium: new Uint8Array([182, 200, 249, 223, 100, 140, 208, 136, 183, 15, 56, 231, 65, 151, 177, 140, 184, 30, 30, 67, 80, 213, 11, 204, 184, 251, 90, 115, 121, 200, 123, 178, 227, 214, 237, 84, 97, 237, 30, 159, 54, 243, 64, 163, 150, 42, 68, 107, 129, 91, 121, 75, 75, 212, 58, 68, 3, 80, 32, 119, 178, 37, 108, 200, 7, 131, 127, 58, 172, 209, 24, 235, 75, 156, 43, 174, 184, 151, 6, 134, 37, 171, 172, 161, 147])
-    };
-
-    sourceData.long = new Uint8Array(1024 * sourceData.medium.byteLength);
-    for (var i=0; i<1024; i++) {
-        sourceData.long.set(sourceData.medium, i * sourceData.medium.byteLength);
-    }
+    var sourceData = getDigestSourceData(true);
 
     var digestedData = {
         "sha-1": {
@@ -43,105 +36,36 @@
     }
 
     // Try every combination of hash with source data size. Variations tested are
-    // hash name in upper, lower, or mixed case, and upper-case version with the
-    // source buffer altered after call.
-    Object.keys(sourceData).forEach(function(size) {
-        Object.keys(digestedData).forEach(function(alg) {
+    // hash name in upper, lower, or mixed case, plus buffer mutation and transfer.
+    runDigestTests(subtle, sourceData, function (size) {
+        var vectors = [];
+        Object.keys(digestedData).forEach(function (alg) {
             var upCase = alg.toUpperCase();
             var downCase = alg.toLowerCase();
-            var mixedCase = upCase.substr(0, 1) + downCase.substr(1);
+            var mixedCase = upCase.slice(0, 1) + downCase.slice(1);
+            var expected = digestedData[alg][size];
 
-            promise_test(function(test) {
-                var promise = subtle.digest({name: upCase}, sourceData[size])
-                .then(function(result) {
-                    assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size);
-                }, function(err) {
-                    assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message);
-                });
-
-                return promise;
-            }, upCase + " with " + size + " source data");
-
-            promise_test(function(test) {
-                var promise = subtle.digest({name: downCase}, sourceData[size])
-                .then(function(result) {
-                    assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size);
-                }, function(err) {
-                    assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message);
-                });
-
-                return promise;
-            }, downCase + " with " + size + " source data");
-
-            promise_test(function(test) {
-                var promise = subtle.digest({name: mixedCase}, sourceData[size])
-                .then(function(result) {
-                    assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size);
-                }, function(err) {
-                    assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message);
-                });
-
-                return promise;
-            }, mixedCase + " with " + size + " source data");
-
-            if (sourceData[size].length > 0) {
-                promise_test(function(test) {
-                    var copiedBuffer = copyBuffer(sourceData[size]);
-                    copiedBuffer[0] = 255 - copiedBuffer[0];
-                    var promise = subtle.digest({
-                        get name() {
-                            copiedBuffer[0] = sourceData[size][0];
-                            return upCase;
-                        }
-                    }, copiedBuffer)
-                    .then(function(result) {
-                        assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size);
-                    }, function(err) {
-                        assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message);
-                    });
-                    return promise;
-                }, upCase + " with " + size + " source data and altered buffer during call");
-
-                promise_test(function(test) {
-                    var copiedBuffer = copyBuffer(sourceData[size]);
-                    var promise = subtle.digest({name: upCase}, copiedBuffer)
-                    .then(function(result) {
-                        assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size);
-                    }, function(err) {
-                        assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message);
-                    });
-
-                    copiedBuffer[0] = 255 - copiedBuffer[0];
-                    return promise;
-                }, upCase + " with " + size + " source data and altered buffer after call");
-
-                promise_test(function(test) {
-                    var copiedBuffer = copyBuffer(sourceData[size]);
-                    copiedBuffer.buffer.transfer();
-                    return subtle.digest({name: upCase}, copiedBuffer)
-                    .then(function(result) {
-                        assert_true(equalBuffers(result, digestedData[alg].empty), "digest() on transferred buffer should yield result for empty buffer for " + alg + ":" + size);
-                    }, function(err) {
-                        assert_unreached("digest() threw an error for transferred buffer for " + alg + ":" + size + ": " + err.message);
-                    });
-                }, upCase + " with " + size + " source data and transferred buffer during call");
-
-                promise_test(function(test) {
-                    var copiedBuffer = copyBuffer(sourceData[size]);
-                    var promise = subtle.digest({name: upCase}, copiedBuffer)
-                    .then(function(result) {
-                        assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size);
-                    }, function(err) {
-                        assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message);
-                    });
-
-                    copiedBuffer.buffer.transfer();
-                    return promise;
-                }, upCase + " with " + size + " source data and transferred buffer after call");
-            }
+            vectors.push({
+                algorithm: {name: upCase},
+                expected: expected,
+                emptyExpected: digestedData[alg].empty,
+                label: upCase + " with " + size + " source data",
+                mutations: true,
+                transferBeforeCall: true,
+            });
+            vectors.push({
+                algorithm: {name: downCase},
+                expected: expected,
+                label: downCase + " with " + size + " source data",
+            });
+            vectors.push({
+                algorithm: {name: mixedCase},
+                expected: expected,
+                label: mixedCase + " with " + size + " source data",
+            });
         });
+        return vectors;
     });
-
     // Call digest() with bad algorithm names to get an error
     var badNames = ["AES-GCM", "RSA-OAEP", "PBKDF2", "AES-KW"];
     Object.keys(sourceData).forEach(function(size) {
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/digest.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/digest.js
new file mode 100644
index 0000000..9ef9c69
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/digest.js
@@ -0,0 +1,92 @@
+function runDigestTests(subtle, sourceData, getVectors) {
+  function algorithmName(algorithm) {
+    return typeof algorithm === 'string' ? algorithm : algorithm.name;
+  }
+
+  function withNameGetter(algorithm, getter) {
+    var result = typeof algorithm === 'string' ? {} : { ...algorithm };
+    Object.defineProperty(result, 'name', {
+      enumerable: true,
+      get: getter,
+    });
+    return result;
+  }
+
+  Object.keys(sourceData).forEach(function (size) {
+    getVectors(size).forEach(function (vector) {
+      promise_test(function () {
+        return subtle.digest(vector.algorithm, sourceData[size])
+          .then(function (result) {
+            assert_true(
+              equalBuffers(result, vector.expected),
+              'digest matches expected'
+            );
+          });
+      }, vector.label);
+
+      if (!vector.mutations || sourceData[size].length === 0) {
+        return;
+      }
+
+      promise_test(function () {
+        var buffer = new Uint8Array(sourceData[size]);
+        buffer[0] = ~buffer[0];
+        var algorithm = withNameGetter(vector.algorithm, function () {
+          buffer[0] = sourceData[size][0];
+          return algorithmName(vector.algorithm);
+        });
+        return subtle.digest(algorithm, buffer).then(function (result) {
+          assert_true(
+            equalBuffers(result, vector.expected),
+            'digest matches expected'
+          );
+        });
+      }, vector.label + ' and altered buffer during call');
+
+      promise_test(function () {
+        var buffer = new Uint8Array(sourceData[size]);
+        var promise = subtle.digest(vector.algorithm, buffer)
+          .then(function (result) {
+            assert_true(
+              equalBuffers(result, vector.expected),
+              'digest matches expected'
+            );
+          });
+        buffer[0] = ~buffer[0];
+        return promise;
+      }, vector.label + ' and altered buffer after call');
+
+      promise_test(function () {
+        var buffer = new Uint8Array(sourceData[size]);
+        var algorithm = vector.transferBeforeCall
+          ? vector.algorithm
+          : withNameGetter(vector.algorithm, function () {
+              buffer.buffer.transfer();
+              return algorithmName(vector.algorithm);
+            });
+        if (vector.transferBeforeCall) {
+          buffer.buffer.transfer();
+        }
+        return subtle.digest(algorithm, buffer).then(function (result) {
+          assert_true(
+            equalBuffers(result, vector.emptyExpected),
+            'digest on transferred buffer should match result for empty buffer'
+          );
+        });
+      }, vector.label + ' and transferred buffer during call');
+
+      promise_test(function () {
+        var buffer = new Uint8Array(sourceData[size]);
+        var promise = subtle.digest(vector.algorithm, buffer)
+          .then(function (result) {
+            assert_true(
+              equalBuffers(result, vector.expected),
+              'digest matches expected'
+            );
+          });
+        buffer.buffer.transfer();
+        return promise;
+      }, vector.label + ' and transferred buffer after call');
+    });
+  });
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/digest_test_data.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/digest_test_data.js
new file mode 100644
index 0000000..f088e02
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/digest_test_data.js
@@ -0,0 +1,25 @@
+function getDigestSourceData(includeLong) {
+  var sourceData = {
+    empty: new Uint8Array(0),
+    short: new Uint8Array([
+      21, 110, 234, 124, 193, 76, 86, 203, 148, 219, 3, 10, 74, 157, 149, 255,
+    ]),
+    medium: new Uint8Array([
+      182, 200, 249, 223, 100, 140, 208, 136, 183, 15, 56, 231, 65, 151, 177,
+      140, 184, 30, 30, 67, 80, 213, 11, 204, 184, 251, 90, 115, 121, 200, 123,
+      178, 227, 214, 237, 84, 97, 237, 30, 159, 54, 243, 64, 163, 150, 42, 68,
+      107, 129, 91, 121, 75, 75, 212, 58, 68, 3, 80, 32, 119, 178, 37, 108,
+      200, 7, 131, 127, 58, 172, 209, 24, 235, 75, 156, 43, 174, 184, 151, 6,
+      134, 37, 171, 172, 161, 147,
+    ]),
+  };
+
+  if (includeLong) {
+    sourceData.long = new Uint8Array(1024 * sourceData.medium.byteLength);
+    for (var i = 0; i < 1024; i++) {
+      sourceData.long.set(sourceData.medium, i * sourceData.medium.byteLength);
+    }
+  }
+
+  return sourceData;
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/kangarootwelve.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/kangarootwelve.tentative.https.any.js
index 9f800b7..0e38eb0 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/kangarootwelve.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/kangarootwelve.tentative.https.any.js
@@ -1,24 +1,10 @@
 // META: title=WebCryptoAPI: digest() KangarooTwelve algorithms
 // META: script=../util/helpers.js
+// META: script=xof_digest.js
 // META: timeout=long
 
 var subtle = crypto.subtle; // Change to test prefixed implementations
 
-// Generates a Uint8Array of length n by repeating the pattern 00 01 02 .. F9 FA.
-function ptn(n) {
-  var buf = new Uint8Array(n);
-  for (var i = 0; i < n; i++)
-    buf[i] = i % 251;
-  return buf;
-}
-
-function hexToBytes(hex) {
-  var bytes = new Uint8Array(hex.length / 2);
-  for (var i = 0; i < hex.length; i += 2)
-    bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
-  return bytes;
-}
-
 // RFC 9861 Section 5 test vectors
 // [input, outputLengthBits, expected hex(, customization)]
 var kt128Vectors = [
@@ -182,143 +168,21 @@
    '2752f3ccd855288efee5fcbb8b563069'],
 ];
 
-largeOutputTests.forEach(function (entry) {
-  var alg = entry[0];
-  var outputLength = entry[1];
-  var lastN = entry[2];
-  var expected = entry[3];
-
-  promise_test(function (test) {
-    return subtle
-      .digest({ name: alg, outputLength: outputLength }, new Uint8Array(0))
-      .then(function (result) {
-        var full = new Uint8Array(result);
-        var last = full.slice(full.length - lastN);
-        assert_true(
-          equalBuffers(last.buffer, hexToBytes(expected)),
-          'last ' + lastN + ' bytes of digest match expected'
-        );
-      });
-  }, alg + ' with ' + outputLength + ' bit output, verify last ' + lastN + ' bytes');
-});
-
-function customizationEqual(emptyDataVector, customization) {
-  return equalBuffers(customization ?? new Uint8Array(0), emptyDataVector[3] ?? new Uint8Array(0));
-}
-
-function outputLengthLessOrEqual(emptyDataVector, outputLength) {
-  return outputLength <= emptyDataVector[1];
-}
-
-var allVectors = {
-  KT128: kt128Vectors,
-  KT256: kt256Vectors,
-};
-
-Object.keys(allVectors).forEach(function (alg) {
-  var emptyDataVector = allVectors[alg][0];
-  allVectors[alg].forEach(function (vector, i) {
-    var input = vector[0];
-    var outputLength = vector[1];
-    var expected = vector[2];
-    var customization = vector[3];
-
-    var algorithmParams = { name: alg, outputLength: outputLength };
-    if (customization !== undefined)
-      algorithmParams.customization = customization;
-
-    var label = alg + ' vector #' + (i + 1) +
-      ' (' + outputLength + ' bit output, ' + input.length + ' byte input' +
-      (customization !== undefined ? ', C=' + customization.length + ' bytes' : '') + ')';
-
-    promise_test(function (test) {
-      return subtle
-        .digest(algorithmParams, input)
-        .then(function (result) {
-          assert_true(
-            equalBuffers(result, hexToBytes(expected)),
-            'digest matches expected'
-          );
-        });
-    }, label);
-
-    if (input.length > 0) {
-      promise_test(function (test) {
-        var buffer = new Uint8Array(input);
-        // Alter the buffer before calling digest
-        buffer[0] = ~buffer[0];
-        return subtle
-          .digest({
-            get name() {
-              // Alter the buffer back while calling digest
-              buffer[0] = input[0];
-              return alg;
-            },
-            outputLength: outputLength,
-            customization: customization,
-          }, buffer)
-          .then(function (result) {
-            assert_true(
-              equalBuffers(result, hexToBytes(expected)),
-              'digest matches expected'
-            );
-          });
-      }, label + ' and altered buffer during call');
-
-      promise_test(function (test) {
-        var buffer = new Uint8Array(input);
-        var promise = subtle
-          .digest(algorithmParams, buffer)
-          .then(function (result) {
-            assert_true(
-              equalBuffers(result, hexToBytes(expected)),
-              'digest matches expected'
-            );
-          });
-        // Alter the buffer after calling digest
-        buffer[0] = ~buffer[0];
-        return promise;
-      }, label + ' and altered buffer after call');
-
-      promise_test(function (test) {
-        var buffer = new Uint8Array(input);
-        return subtle
-          .digest({
-            get name() {
-              // Transfer the buffer while calling digest
-              buffer.buffer.transfer();
-              return alg;
-            },
-            outputLength: outputLength,
-            customization: customization,
-          }, buffer)
-          .then(function (result) {
-            if (customizationEqual(emptyDataVector, customization) && outputLengthLessOrEqual(emptyDataVector, outputLength)) {
-              assert_true(
-                equalBuffers(result, Uint8Array.fromHex(emptyDataVector[2]).subarray(0, outputLength / 8)),
-                'digest on transferred buffer should match result for empty buffer'
-              );
-            } else {
-              assert_equals(result.byteLength, outputLength / 8,
-                'digest on transferred buffer should have correct output length');
-            }
-          });
-      }, label + ' and transferred buffer during call');
-
-      promise_test(function (test) {
-        var buffer = new Uint8Array(input);
-        var promise = subtle
-          .digest(algorithmParams, buffer)
-          .then(function (result) {
-            assert_true(
-              equalBuffers(result, hexToBytes(expected)),
-              'digest matches expected'
-            );
-          });
-        // Transfer the buffer after calling digest
-        buffer.buffer.transfer();
-        return promise;
-      }, label + ' and transferred buffer after call');
-    }
-  });
+runXofDigestTests(subtle, {
+  vectors: {
+    KT128: kt128Vectors,
+    KT256: kt256Vectors,
+  },
+  largeOutputTests: largeOutputTests,
+  parameterName: 'customization',
+  formatParameter: function (customization) {
+    return customization !== undefined ?
+      ', C=' + customization.length + ' bytes' : '';
+  },
+  parameterEquals: function (emptyDataVector, customization) {
+    return equalBuffers(
+      customization ?? new Uint8Array(0),
+      emptyDataVector[3] ?? new Uint8Array(0)
+    );
+  },
 });
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/sha3.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/sha3.tentative.https.any.js
index f9f38ead..a6cafbb 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/sha3.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/sha3.tentative.https.any.js
@@ -1,28 +1,12 @@
 // META: title=WebCryptoAPI: digest() SHA-3 algorithms
 // META: script=../util/helpers.js
+// META: script=digest_test_data.js
+// META: script=digest.js
 // META: timeout=long
 
 var subtle = crypto.subtle; // Change to test prefixed implementations
 
-var sourceData = {
-  empty: new Uint8Array(0),
-  short: new Uint8Array([
-    21, 110, 234, 124, 193, 76, 86, 203, 148, 219, 3, 10, 74, 157, 149, 255,
-  ]),
-  medium: new Uint8Array([
-    182, 200, 249, 223, 100, 140, 208, 136, 183, 15, 56, 231, 65, 151, 177, 140,
-    184, 30, 30, 67, 80, 213, 11, 204, 184, 251, 90, 115, 121, 200, 123, 178,
-    227, 214, 237, 84, 97, 237, 30, 159, 54, 243, 64, 163, 150, 42, 68, 107,
-    129, 91, 121, 75, 75, 212, 58, 68, 3, 80, 32, 119, 178, 37, 108, 200, 7,
-    131, 127, 58, 172, 209, 24, 235, 75, 156, 43, 174, 184, 151, 6, 134, 37,
-    171, 172, 161, 147,
-  ]),
-};
-
-sourceData.long = new Uint8Array(1024 * sourceData.medium.byteLength);
-for (var i = 0; i < 1024; i++) {
-  sourceData.long.set(sourceData.medium, i * sourceData.medium.byteLength);
-}
+var sourceData = getDigestSourceData(true);
 
 var digestedData = {
   'SHA3-256': {
@@ -97,83 +81,14 @@
 };
 
 // Test SHA-3 digest algorithms
-Object.keys(sourceData).forEach(function (size) {
-  Object.keys(digestedData).forEach(function (alg) {
-    promise_test(function (test) {
-      return crypto.subtle
-        .digest(alg, sourceData[size])
-        .then(function (result) {
-          assert_true(
-            equalBuffers(result, digestedData[alg][size]),
-            'digest matches expected'
-          );
-        });
-    }, alg + ' with ' + size + ' source data');
-
-    if (sourceData[size].length > 0) {
-      promise_test(function (test) {
-        var buffer = new Uint8Array(sourceData[size]);
-        // Alter the buffer before calling digest
-        buffer[0] = ~buffer[0];
-        return crypto.subtle
-          .digest({
-            get name() {
-              // Alter the buffer back while calling digest
-              buffer[0] = sourceData[size][0];
-              return alg;
-            }
-          }, buffer)
-          .then(function (result) {
-            assert_true(
-              equalBuffers(result, digestedData[alg][size]),
-              'digest matches expected'
-            );
-          });
-      }, alg + ' with ' + size + ' source data and altered buffer during call');
-
-      promise_test(function (test) {
-        var buffer = new Uint8Array(sourceData[size]);
-        var promise = crypto.subtle.digest(alg, buffer).then(function (result) {
-          assert_true(
-            equalBuffers(result, digestedData[alg][size]),
-            'digest matches expected'
-          );
-        });
-        // Alter the buffer after calling digest
-        buffer[0] = ~buffer[0];
-        return promise;
-      }, alg + ' with ' + size + ' source data and altered buffer after call');
-
-      promise_test(function (test) {
-        var buffer = new Uint8Array(sourceData[size]);
-        return crypto.subtle
-          .digest({
-            get name() {
-              // Transfer the buffer while calling digest
-              buffer.buffer.transfer();
-              return alg;
-            }
-          }, buffer)
-          .then(function (result) {
-            assert_true(
-              equalBuffers(result, digestedData[alg].empty),
-              'digest on transferred buffer should match result for empty buffer'
-            );
-          });
-      }, alg + ' with ' + size + ' source data and transferred buffer during call');
-
-      promise_test(function (test) {
-        var buffer = new Uint8Array(sourceData[size]);
-        var promise = crypto.subtle.digest(alg, buffer).then(function (result) {
-          assert_true(
-            equalBuffers(result, digestedData[alg][size]),
-            'digest matches expected'
-          );
-        });
-        // Transfer the buffer after calling digest
-        buffer.buffer.transfer();
-        return promise;
-      }, alg + ' with ' + size + ' source data and transferred buffer after call');
-    }
+runDigestTests(subtle, sourceData, function (size) {
+  return Object.keys(digestedData).map(function (alg) {
+    return {
+      algorithm: alg,
+      expected: digestedData[alg][size],
+      emptyExpected: digestedData[alg].empty,
+      label: alg + ' with ' + size + ' source data',
+      mutations: true,
+    };
   });
 });
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/turboshake.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/turboshake.tentative.https.any.js
index 243931c..356e698 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/turboshake.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/turboshake.tentative.https.any.js
@@ -1,24 +1,10 @@
 // META: title=WebCryptoAPI: digest() TurboSHAKE algorithms
 // META: script=../util/helpers.js
+// META: script=xof_digest.js
 // META: timeout=long
 
 var subtle = crypto.subtle; // Change to test prefixed implementations
 
-// Generates a Uint8Array of length n by repeating the pattern 00 01 02 .. F9 FA.
-function ptn(n) {
-  var buf = new Uint8Array(n);
-  for (var i = 0; i < n; i++)
-    buf[i] = i % 251;
-  return buf;
-}
-
-function hexToBytes(hex) {
-  var bytes = new Uint8Array(hex.length / 2);
-  for (var i = 0; i < hex.length; i += 2)
-    bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
-  return bytes;
-}
-
 // RFC 9861 Section 5 test vectors
 // [input, outputLengthBits, expected hex(, domainSeparation)]
 var turboSHAKE128Vectors = [
@@ -155,143 +141,18 @@
    '207265dccf2f43534e9c61ba0c9d1d75'],
 ];
 
-largeOutputTests.forEach(function (entry) {
-  var alg = entry[0];
-  var outputLength = entry[1];
-  var lastN = entry[2];
-  var expected = entry[3];
-
-  promise_test(function (test) {
-    return subtle
-      .digest({ name: alg, outputLength: outputLength }, new Uint8Array(0))
-      .then(function (result) {
-        var full = new Uint8Array(result);
-        var last = full.slice(full.length - lastN);
-        assert_true(
-          equalBuffers(last.buffer, hexToBytes(expected)),
-          'last ' + lastN + ' bytes of digest match expected'
-        );
-      });
-  }, alg + ' with ' + outputLength + ' bit output, verify last ' + lastN + ' bytes');
-});
-
-function domainSeparationEqual(emptyDataVector, domainSeparation) {
-  return (domainSeparation ?? 0x1f) === (emptyDataVector[3] ?? 0x1f);
-}
-
-function outputLengthLessOrEqual(emptyDataVector, outputLength) {
-  return outputLength <= emptyDataVector[1];
-}
-
-var allVectors = {
-  TurboSHAKE128: turboSHAKE128Vectors,
-  TurboSHAKE256: turboSHAKE256Vectors,
-};
-
-Object.keys(allVectors).forEach(function (alg) {
-  var emptyDataVector = allVectors[alg][0];
-  allVectors[alg].forEach(function (vector, i) {
-    var input = vector[0];
-    var outputLength = vector[1];
-    var expected = vector[2];
-    var domainSeparation = vector[3];
-
-    var algorithmParams = { name: alg, outputLength: outputLength };
-    if (domainSeparation !== undefined)
-      algorithmParams.domainSeparation = domainSeparation;
-
-    var label = alg + ' vector #' + (i + 1) +
-      ' (' + outputLength + ' bit output, ' + input.length + ' byte input' +
-      (domainSeparation !== undefined ? ', D=0x' + domainSeparation.toString(16) : '') + ')';
-
-    promise_test(function (test) {
-      return subtle
-        .digest(algorithmParams, input)
-        .then(function (result) {
-          assert_true(
-            equalBuffers(result, hexToBytes(expected)),
-            'digest matches expected'
-          );
-        });
-    }, label);
-
-    if (input.length > 0) {
-      promise_test(function (test) {
-        var buffer = new Uint8Array(input);
-        // Alter the buffer before calling digest
-        buffer[0] = ~buffer[0];
-        return subtle
-          .digest({
-            get name() {
-              // Alter the buffer back while calling digest
-              buffer[0] = input[0];
-              return alg;
-            },
-            outputLength: outputLength,
-            domainSeparation: domainSeparation,
-          }, buffer)
-          .then(function (result) {
-            assert_true(
-              equalBuffers(result, hexToBytes(expected)),
-              'digest matches expected'
-            );
-          });
-      }, label + ' and altered buffer during call');
-
-      promise_test(function (test) {
-        var buffer = new Uint8Array(input);
-        var promise = subtle
-          .digest(algorithmParams, buffer)
-          .then(function (result) {
-            assert_true(
-              equalBuffers(result, hexToBytes(expected)),
-              'digest matches expected'
-            );
-          });
-        // Alter the buffer after calling digest
-        buffer[0] = ~buffer[0];
-        return promise;
-      }, label + ' and altered buffer after call');
-
-      promise_test(function (test) {
-        var buffer = new Uint8Array(input);
-        return subtle
-          .digest({
-            get name() {
-              // Transfer the buffer while calling digest
-              buffer.buffer.transfer();
-              return alg;
-            },
-            outputLength: outputLength,
-            domainSeparation: domainSeparation,
-          }, buffer)
-          .then(function (result) {
-            if (domainSeparationEqual(emptyDataVector, domainSeparation) && outputLengthLessOrEqual(emptyDataVector, outputLength)) {
-              assert_true(
-                equalBuffers(result, Uint8Array.fromHex(emptyDataVector[2]).subarray(0, outputLength / 8)),
-                'digest on transferred buffer should match result for empty buffer'
-              );
-            } else {
-              assert_equals(result.byteLength, outputLength / 8,
-                'digest on transferred buffer should have correct output length');
-            }
-          });
-      }, label + ' and transferred buffer during call');
-
-      promise_test(function (test) {
-        var buffer = new Uint8Array(input);
-        var promise = subtle
-          .digest(algorithmParams, buffer)
-          .then(function (result) {
-            assert_true(
-              equalBuffers(result, hexToBytes(expected)),
-              'digest matches expected'
-            );
-          });
-        // Transfer the buffer after calling digest
-        buffer.buffer.transfer();
-        return promise;
-      }, label + ' and transferred buffer after call');
-    }
-  });
+runXofDigestTests(subtle, {
+  vectors: {
+    TurboSHAKE128: turboSHAKE128Vectors,
+    TurboSHAKE256: turboSHAKE256Vectors,
+  },
+  largeOutputTests: largeOutputTests,
+  parameterName: 'domainSeparation',
+  formatParameter: function (domainSeparation) {
+    return domainSeparation !== undefined ?
+      ', D=0x' + domainSeparation.toString(16) : '';
+  },
+  parameterEquals: function (emptyDataVector, domainSeparation) {
+    return (domainSeparation ?? 0x1f) === (emptyDataVector[3] ?? 0x1f);
+  },
 });
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/xof_digest.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/xof_digest.js
new file mode 100644
index 0000000..f5ef584
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/digest/xof_digest.js
@@ -0,0 +1,146 @@
+// Generates a Uint8Array of length n by repeating the pattern 00 01 02 .. F9 FA.
+function ptn(n) {
+  var buf = new Uint8Array(n);
+  for (var i = 0; i < n; i++)
+    buf[i] = i % 251;
+  return buf;
+}
+
+function runXofDigestTests(subtle, options) {
+  options.largeOutputTests.forEach(function (entry) {
+    var alg = entry[0];
+    var outputLength = entry[1];
+    var lastN = entry[2];
+    var expected = entry[3];
+
+    promise_test(function (test) {
+      return subtle
+        .digest({ name: alg, outputLength: outputLength }, new Uint8Array(0))
+        .then(function (result) {
+          var full = new Uint8Array(result);
+          var last = full.slice(full.length - lastN);
+          assert_true(
+            equalBuffers(last.buffer, hexStringToUint8Array(expected)),
+            'last ' + lastN + ' bytes of digest match expected'
+          );
+        });
+    }, alg + ' with ' + outputLength + ' bit output, verify last ' + lastN + ' bytes');
+  });
+
+  Object.keys(options.vectors).forEach(function (alg) {
+    var emptyDataVector = options.vectors[alg][0];
+    options.vectors[alg].forEach(function (vector, i) {
+      var input = vector[0];
+      var outputLength = vector[1];
+      var expected = vector[2];
+      var parameter = vector[3];
+
+      var algorithmParams = { name: alg, outputLength: outputLength };
+      if (parameter !== undefined)
+        algorithmParams[options.parameterName] = parameter;
+
+      var label = alg + ' vector #' + (i + 1) +
+        ' (' + outputLength + ' bit output, ' + input.length + ' byte input' +
+        options.formatParameter(parameter) + ')';
+
+      promise_test(function (test) {
+        return subtle
+          .digest(algorithmParams, input)
+          .then(function (result) {
+            assert_true(
+              equalBuffers(result, hexStringToUint8Array(expected)),
+              'digest matches expected'
+            );
+          });
+      }, label);
+
+      if (input.length > 0) {
+        promise_test(function (test) {
+          var buffer = new Uint8Array(input);
+          // Alter the buffer before calling digest
+          buffer[0] = ~buffer[0];
+          var duringCallParams = {
+            get name() {
+              // Alter the buffer back while calling digest
+              buffer[0] = input[0];
+              return alg;
+            },
+            outputLength: outputLength,
+          };
+          duringCallParams[options.parameterName] = parameter;
+          return subtle
+            .digest(duringCallParams, buffer)
+            .then(function (result) {
+              assert_true(
+                equalBuffers(result, hexStringToUint8Array(expected)),
+                'digest matches expected'
+              );
+            });
+        }, label + ' and altered buffer during call');
+
+        promise_test(function (test) {
+          var buffer = new Uint8Array(input);
+          var promise = subtle
+            .digest(algorithmParams, buffer)
+            .then(function (result) {
+              assert_true(
+                equalBuffers(result, hexStringToUint8Array(expected)),
+                'digest matches expected'
+              );
+            });
+          // Alter the buffer after calling digest
+          buffer[0] = ~buffer[0];
+          return promise;
+        }, label + ' and altered buffer after call');
+
+        promise_test(function (test) {
+          var buffer = new Uint8Array(input);
+          var duringCallParams = {
+            get name() {
+              // Transfer the buffer while calling digest
+              buffer.buffer.transfer();
+              return alg;
+            },
+            outputLength: outputLength,
+          };
+          duringCallParams[options.parameterName] = parameter;
+          return subtle
+            .digest(duringCallParams, buffer)
+            .then(function (result) {
+              if (
+                options.parameterEquals(emptyDataVector, parameter) &&
+                outputLength <= emptyDataVector[1]
+              ) {
+                assert_true(
+                  equalBuffers(
+                    result,
+                    hexStringToUint8Array(emptyDataVector[2])
+                      .subarray(0, outputLength / 8)
+                  ),
+                  'digest on transferred buffer should match result for empty buffer'
+                );
+              } else {
+                assert_equals(result.byteLength, outputLength / 8,
+                  'digest on transferred buffer should have correct output length');
+              }
+            });
+        }, label + ' and transferred buffer during call');
+
+        promise_test(function (test) {
+          var buffer = new Uint8Array(input);
+          var promise = subtle
+            .digest(algorithmParams, buffer)
+            .then(function (result) {
+              assert_true(
+                equalBuffers(result, hexStringToUint8Array(expected)),
+                'digest matches expected'
+              );
+            });
+          // Transfer the buffer after calling digest
+          buffer.buffer.transfer();
+          return promise;
+        }, label + ' and transferred buffer after call');
+      }
+    });
+  });
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encap_decap/encap_decap_bits.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encap_decap/encap_decap_bits.tentative.https.any.js
index 5a66975..ffb78f7 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encap_decap/encap_decap_bits.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encap_decap/encap_decap_bits.tentative.https.any.js
@@ -27,11 +27,11 @@
         'encapsulateBits should return an object'
       );
       assert_true(
-        encapsulatedBits.hasOwnProperty('sharedKey'),
+        Object.hasOwn(encapsulatedBits, 'sharedKey'),
         'Result should have sharedKey property'
       );
       assert_true(
-        encapsulatedBits.hasOwnProperty('ciphertext'),
+        Object.hasOwn(encapsulatedBits, 'ciphertext'),
         'Result should have ciphertext property'
       );
       assert_true(
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encap_decap/encap_decap_keys.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encap_decap/encap_decap_keys.tentative.https.any.js
index 0a45c1f..a482eafb 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encap_decap/encap_decap_keys.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encap_decap/encap_decap_keys.tentative.https.any.js
@@ -61,11 +61,11 @@
             'encapsulateKey should return an object'
           );
           assert_true(
-            encapsulatedKey.hasOwnProperty('sharedKey'),
+            Object.hasOwn(encapsulatedKey, 'sharedKey'),
             'Result should have sharedKey property'
           );
           assert_true(
-            encapsulatedKey.hasOwnProperty('ciphertext'),
+            Object.hasOwn(encapsulatedKey, 'ciphertext'),
             'Result should have ciphertext property'
           );
           assert_true(
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes.js
index 879a6ef..9caecf6 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes.js
@@ -458,9 +458,7 @@
     });
 
     promise_test(function() {
-        return Promise.all(all_promises)
-            .then(function() {done();})
-            .catch(function() {done();})
+        return Promise.all(all_promises).finally(done);
     }, "setup");
 
     // A test vector has all needed fields for encryption, EXCEPT that the
@@ -470,9 +468,7 @@
     // Returns a Promise that yields an updated vector on success.
     function importVectorKey(vector, usages) {
         if (vector.key !== null) {
-            return new Promise(function(resolve, reject) {
-                resolve(vector);
-            });
+            return Promise.resolve(vector);
         } else {
             return subtle.importKey(vector.algorithm.name.toUpperCase() === "AES-OCB" ? "raw-secret" : "raw", vector.keyBuffer, {name: vector.algorithm.name}, false, usages)
             .then(function(key) {
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_cbc.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_cbc.https.any.js
index ec09aae..9dcbfbd 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_cbc.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_cbc.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: encrypt() Using AES-CBC
 // META: script=../util/helpers.js
+// META: script=aes_common_fixtures.js
 // META: script=aes_cbc_vectors.js
 // META: script=aes.js
 // META: timeout=long
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_cbc_vectors.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_cbc_vectors.js
index 96445a9..28ef75f 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_cbc_vectors.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_cbc_vectors.js
@@ -12,59 +12,9 @@
 //     plaintext - the text to encrypt
 //     result - the expected result (usually just ciphertext, sometimes with added authentication)
 function getTestVectors() {
-    // Before we can really start, we need to fill a bunch of buffers with data
-    var plaintext = new Uint8Array([84, 104, 105, 115, 32, 115,
-        112, 101, 99, 105, 102, 105, 99, 97, 116, 105, 111, 110,
-        32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, 32,
-        74, 97, 118, 97, 83, 99, 114, 105, 112, 116, 32, 65, 80,
-        73, 32, 102, 111, 114, 32, 112, 101, 114, 102, 111, 114,
-        109, 105, 110, 103, 32, 98, 97, 115, 105, 99, 32, 99, 114,
-        121, 112, 116, 111, 103, 114, 97, 112, 104, 105, 99, 32,
-        111, 112, 101, 114, 97, 116, 105, 111, 110, 115, 32, 105,
-        110, 32, 119, 101, 98, 32, 97, 112, 112, 108, 105, 99, 97,
-        116, 105, 111, 110, 115, 44, 32, 115, 117, 99, 104, 32, 97,
-        115, 32, 104, 97, 115, 104, 105, 110, 103, 44, 32, 115,
-        105, 103, 110, 97, 116, 117, 114, 101, 32, 103, 101, 110,
-        101, 114, 97, 116, 105, 111, 110, 32, 97, 110, 100, 32,
-        118, 101, 114, 105, 102, 105, 99, 97, 116, 105, 111, 110,
-        44, 32, 97, 110, 100, 32, 101, 110, 99, 114, 121, 112,
-        116, 105, 111, 110, 32, 97, 110, 100, 32, 100, 101, 99,
-        114, 121, 112, 116, 105, 111, 110, 46, 32, 65, 100, 100,
-        105, 116, 105, 111, 110, 97, 108, 108, 121, 44, 32, 105,
-        116, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97,
-        110, 32, 65, 80, 73, 32, 102, 111, 114, 32, 97, 112, 112,
-        108, 105, 99, 97, 116, 105, 111, 110, 115, 32, 116, 111,
-        32, 103, 101, 110, 101, 114, 97, 116, 101, 32, 97, 110,
-        100, 47, 111, 114, 32, 109, 97, 110, 97, 103, 101, 32, 116,
-        104, 101, 32, 107, 101, 121, 105, 110, 103, 32, 109, 97,
-        116, 101, 114, 105, 97, 108, 32, 110, 101, 99, 101, 115,
-        115, 97, 114, 121, 32, 116, 111, 32, 112, 101, 114, 102,
-        111, 114, 109, 32, 116, 104, 101, 115, 101, 32, 111, 112,
-        101, 114, 97, 116, 105, 111, 110, 115, 46, 32, 85, 115,
-        101, 115, 32, 102, 111, 114, 32, 116, 104, 105, 115, 32,
-        65, 80, 73, 32, 114, 97, 110, 103, 101, 32, 102, 114, 111,
-        109, 32, 117, 115, 101, 114, 32, 111, 114, 32, 115, 101,
-        114, 118, 105, 99, 101, 32, 97, 117, 116, 104, 101, 110,
-        116, 105, 99, 97, 116, 105, 111, 110, 44, 32, 100, 111,
-        99, 117, 109, 101, 110, 116, 32, 111, 114, 32, 99, 111,
-        100, 101, 32, 115, 105, 103, 110, 105, 110, 103, 44, 32,
-        97, 110, 100, 32, 116, 104, 101, 32, 99, 111, 110, 102,
-        105, 100, 101, 110, 116, 105, 97, 108, 105, 116, 121, 32,
-        97, 110, 100, 32, 105, 110, 116, 101, 103, 114, 105, 116,
-        121, 32, 111, 102, 32, 99, 111, 109, 109, 117, 110, 105,
-        99, 97, 116, 105, 111, 110, 115, 46]);
-
-    // We want some random key bytes of various sizes.
-    // These were randomly generated from a script.
-    var keyBytes = {
-        128: new Uint8Array([222, 192, 212, 252, 191, 60, 71,
-            65, 200, 146, 218, 189, 28, 212, 192, 78]),
-        192: new Uint8Array([208, 238, 131, 65, 63, 68, 196, 63, 186, 208,
-            61, 207, 166, 18, 99, 152, 29, 109, 221, 95, 240, 30, 28, 246]),
-        256: new Uint8Array([103, 105, 56, 35, 251, 29, 88, 7, 63, 145, 236,
-            233, 204, 58, 249, 16, 229, 83, 38, 22, 164, 210, 123, 19, 235, 123, 116,
-            216, 0, 11, 191, 48])
-    }
+    var commonFixtures = getAesCommonFixtures();
+    var plaintext = commonFixtures.plaintext;
+    var keyBytes = commonFixtures.keyBytes;
 
     // AES-CBC needs a 16 byte (128 bit) IV.
     var iv = new Uint8Array([85, 170, 248, 155, 168, 148, 19, 213, 78, 167, 39,
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_common_fixtures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_common_fixtures.js
new file mode 100644
index 0000000..d8c6d166
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_common_fixtures.js
@@ -0,0 +1,57 @@
+function getAesCommonFixtures() {
+  var plaintext = new Uint8Array([
+    84, 104, 105, 115, 32, 115, 112, 101, 99, 105, 102, 105, 99, 97, 116, 105,
+    111, 110, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, 32, 74, 97,
+    118, 97, 83, 99, 114, 105, 112, 116, 32, 65, 80, 73, 32, 102, 111, 114, 32,
+    112, 101, 114, 102, 111, 114, 109, 105, 110, 103, 32, 98, 97, 115, 105, 99,
+    32, 99, 114, 121, 112, 116, 111, 103, 114, 97, 112, 104, 105, 99, 32, 111,
+    112, 101, 114, 97, 116, 105, 111, 110, 115, 32, 105, 110, 32, 119, 101, 98,
+    32, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 115, 44, 32, 115,
+    117, 99, 104, 32, 97, 115, 32, 104, 97, 115, 104, 105, 110, 103, 44, 32,
+    115, 105, 103, 110, 97, 116, 117, 114, 101, 32, 103, 101, 110, 101, 114, 97,
+    116, 105, 111, 110, 32, 97, 110, 100, 32, 118, 101, 114, 105, 102, 105, 99,
+    97, 116, 105, 111, 110, 44, 32, 97, 110, 100, 32, 101, 110, 99, 114, 121,
+    112, 116, 105, 111, 110, 32, 97, 110, 100, 32, 100, 101, 99, 114, 121, 112,
+    116, 105, 111, 110, 46, 32, 65, 100, 100, 105, 116, 105, 111, 110, 97, 108,
+    108, 121, 44, 32, 105, 116, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115,
+    32, 97, 110, 32, 65, 80, 73, 32, 102, 111, 114, 32, 97, 112, 112, 108, 105,
+    99, 97, 116, 105, 111, 110, 115, 32, 116, 111, 32, 103, 101, 110, 101, 114,
+    97, 116, 101, 32, 97, 110, 100, 47, 111, 114, 32, 109, 97, 110, 97, 103,
+    101, 32, 116, 104, 101, 32, 107, 101, 121, 105, 110, 103, 32, 109, 97, 116,
+    101, 114, 105, 97, 108, 32, 110, 101, 99, 101, 115, 115, 97, 114, 121, 32,
+    116, 111, 32, 112, 101, 114, 102, 111, 114, 109, 32, 116, 104, 101, 115,
+    101, 32, 111, 112, 101, 114, 97, 116, 105, 111, 110, 115, 46, 32, 85, 115,
+    101, 115, 32, 102, 111, 114, 32, 116, 104, 105, 115, 32, 65, 80, 73, 32,
+    114, 97, 110, 103, 101, 32, 102, 114, 111, 109, 32, 117, 115, 101, 114, 32,
+    111, 114, 32, 115, 101, 114, 118, 105, 99, 101, 32, 97, 117, 116, 104, 101,
+    110, 116, 105, 99, 97, 116, 105, 111, 110, 44, 32, 100, 111, 99, 117, 109,
+    101, 110, 116, 32, 111, 114, 32, 99, 111, 100, 101, 32, 115, 105, 103, 110,
+    105, 110, 103, 44, 32, 97, 110, 100, 32, 116, 104, 101, 32, 99, 111, 110,
+    102, 105, 100, 101, 110, 116, 105, 97, 108, 105, 116, 121, 32, 97, 110, 100,
+    32, 105, 110, 116, 101, 103, 114, 105, 116, 121, 32, 111, 102, 32, 99, 111,
+    109, 109, 117, 110, 105, 99, 97, 116, 105, 111, 110, 115, 46,
+  ]);
+
+  var keyBytes = {
+    128: new Uint8Array([
+      222, 192, 212, 252, 191, 60, 71, 65, 200, 146, 218, 189, 28, 212, 192, 78,
+    ]),
+    192: new Uint8Array([
+      208, 238, 131, 65, 63, 68, 196, 63, 186, 208, 61, 207, 166, 18, 99, 152,
+      29, 109, 221, 95, 240, 30, 28, 246,
+    ]),
+    256: new Uint8Array([
+      103, 105, 56, 35, 251, 29, 88, 7, 63, 145, 236, 233, 204, 58, 249, 16,
+      229, 83, 38, 22, 164, 210, 123, 19, 235, 123, 116, 216, 0, 11, 191, 48,
+    ]),
+  };
+
+  var additionalData = new Uint8Array([
+    84, 104, 101, 114, 101, 32, 97, 114, 101, 32, 55, 32, 102, 117, 114, 116,
+    104, 101, 114, 32, 101, 100, 105, 116, 111, 114, 105, 97, 108, 32, 110, 111,
+    116, 101, 115, 32, 105, 110, 32, 116, 104, 101, 32, 100, 111, 99, 117, 109,
+    101, 110, 116, 46,
+  ]);
+
+  return { plaintext, keyBytes, additionalData };
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ctr.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ctr.https.any.js
index f9d85bb..cbda32f3 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ctr.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ctr.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: encrypt() Using AES-CTR
 // META: script=../util/helpers.js
+// META: script=aes_common_fixtures.js
 // META: script=aes_ctr_vectors.js
 // META: script=aes.js
 // META: timeout=long
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ctr_vectors.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ctr_vectors.js
index 201dff8..89a19fa 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ctr_vectors.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ctr_vectors.js
@@ -12,59 +12,9 @@
 //     plaintext - the text to encrypt
 //     result - the expected result (usually just ciphertext, sometimes with added authentication)
 function getTestVectors() {
-    // Before we can really start, we need to fill a bunch of buffers with data
-    var plaintext = new Uint8Array([84, 104, 105, 115, 32, 115,
-        112, 101, 99, 105, 102, 105, 99, 97, 116, 105, 111, 110,
-        32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, 32,
-        74, 97, 118, 97, 83, 99, 114, 105, 112, 116, 32, 65, 80,
-        73, 32, 102, 111, 114, 32, 112, 101, 114, 102, 111, 114,
-        109, 105, 110, 103, 32, 98, 97, 115, 105, 99, 32, 99, 114,
-        121, 112, 116, 111, 103, 114, 97, 112, 104, 105, 99, 32,
-        111, 112, 101, 114, 97, 116, 105, 111, 110, 115, 32, 105,
-        110, 32, 119, 101, 98, 32, 97, 112, 112, 108, 105, 99, 97,
-        116, 105, 111, 110, 115, 44, 32, 115, 117, 99, 104, 32, 97,
-        115, 32, 104, 97, 115, 104, 105, 110, 103, 44, 32, 115,
-        105, 103, 110, 97, 116, 117, 114, 101, 32, 103, 101, 110,
-        101, 114, 97, 116, 105, 111, 110, 32, 97, 110, 100, 32,
-        118, 101, 114, 105, 102, 105, 99, 97, 116, 105, 111, 110,
-        44, 32, 97, 110, 100, 32, 101, 110, 99, 114, 121, 112,
-        116, 105, 111, 110, 32, 97, 110, 100, 32, 100, 101, 99,
-        114, 121, 112, 116, 105, 111, 110, 46, 32, 65, 100, 100,
-        105, 116, 105, 111, 110, 97, 108, 108, 121, 44, 32, 105,
-        116, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97,
-        110, 32, 65, 80, 73, 32, 102, 111, 114, 32, 97, 112, 112,
-        108, 105, 99, 97, 116, 105, 111, 110, 115, 32, 116, 111,
-        32, 103, 101, 110, 101, 114, 97, 116, 101, 32, 97, 110,
-        100, 47, 111, 114, 32, 109, 97, 110, 97, 103, 101, 32, 116,
-        104, 101, 32, 107, 101, 121, 105, 110, 103, 32, 109, 97,
-        116, 101, 114, 105, 97, 108, 32, 110, 101, 99, 101, 115,
-        115, 97, 114, 121, 32, 116, 111, 32, 112, 101, 114, 102,
-        111, 114, 109, 32, 116, 104, 101, 115, 101, 32, 111, 112,
-        101, 114, 97, 116, 105, 111, 110, 115, 46, 32, 85, 115,
-        101, 115, 32, 102, 111, 114, 32, 116, 104, 105, 115, 32,
-        65, 80, 73, 32, 114, 97, 110, 103, 101, 32, 102, 114, 111,
-        109, 32, 117, 115, 101, 114, 32, 111, 114, 32, 115, 101,
-        114, 118, 105, 99, 101, 32, 97, 117, 116, 104, 101, 110,
-        116, 105, 99, 97, 116, 105, 111, 110, 44, 32, 100, 111,
-        99, 117, 109, 101, 110, 116, 32, 111, 114, 32, 99, 111,
-        100, 101, 32, 115, 105, 103, 110, 105, 110, 103, 44, 32,
-        97, 110, 100, 32, 116, 104, 101, 32, 99, 111, 110, 102,
-        105, 100, 101, 110, 116, 105, 97, 108, 105, 116, 121, 32,
-        97, 110, 100, 32, 105, 110, 116, 101, 103, 114, 105, 116,
-        121, 32, 111, 102, 32, 99, 111, 109, 109, 117, 110, 105,
-        99, 97, 116, 105, 111, 110, 115, 46]);
-
-    // We want some random key bytes of various sizes.
-    // These were randomly generated from a script.
-    var keyBytes = {
-        128: new Uint8Array([222, 192, 212, 252, 191, 60, 71,
-            65, 200, 146, 218, 189, 28, 212, 192, 78]),
-        192: new Uint8Array([208, 238, 131, 65, 63, 68, 196, 63, 186, 208,
-            61, 207, 166, 18, 99, 152, 29, 109, 221, 95, 240, 30, 28, 246]),
-        256: new Uint8Array([103, 105, 56, 35, 251, 29, 88, 7, 63, 145, 236,
-            233, 204, 58, 249, 16, 229, 83, 38, 22, 164, 210, 123, 19, 235, 123, 116,
-            216, 0, 11, 191, 48])
-    }
+    var commonFixtures = getAesCommonFixtures();
+    var plaintext = commonFixtures.plaintext;
+    var keyBytes = commonFixtures.keyBytes;
 
     // AES-CTR needs a 16 byte (128 bit) counter.
     var counter = new Uint8Array([85, 170, 248, 155, 168, 148, 19, 213, 78, 167, 39,
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm.https.any.js
index b3e6b5f..295351f 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: encrypt() Using AES-GCM w/ 96-bit iv
 // META: script=../util/helpers.js
+// META: script=aes_common_fixtures.js
 // META: script=aes_gcm_96_iv_fixtures.js
 // META: script=aes_gcm_vectors.js
 // META: script=aes.js
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm_256_iv.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm_256_iv.https.any.js
index 196a8ea9..a46f4c7 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm_256_iv.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm_256_iv.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: encrypt() Using AES-GCM w/ 256-bit iv
 // META: script=../util/helpers.js
+// META: script=aes_common_fixtures.js
 // META: script=aes_gcm_256_iv_fixtures.js
 // META: script=aes_gcm_vectors.js
 // META: script=aes.js
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm_256_iv_fixtures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm_256_iv_fixtures.js
index 9cdbbbb..a081b8e8 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm_256_iv_fixtures.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm_256_iv_fixtures.js
@@ -1,53 +1,7 @@
 function getFixtures() {
-  // Before we can really start, we need to fill a bunch of buffers with data
-  var plaintext = new Uint8Array([
-    84, 104, 105, 115, 32, 115, 112, 101, 99, 105, 102, 105, 99, 97, 116, 105,
-    111, 110, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, 32, 74, 97,
-    118, 97, 83, 99, 114, 105, 112, 116, 32, 65, 80, 73, 32, 102, 111, 114, 32,
-    112, 101, 114, 102, 111, 114, 109, 105, 110, 103, 32, 98, 97, 115, 105, 99,
-    32, 99, 114, 121, 112, 116, 111, 103, 114, 97, 112, 104, 105, 99, 32, 111,
-    112, 101, 114, 97, 116, 105, 111, 110, 115, 32, 105, 110, 32, 119, 101, 98,
-    32, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 115, 44, 32, 115,
-    117, 99, 104, 32, 97, 115, 32, 104, 97, 115, 104, 105, 110, 103, 44, 32,
-    115, 105, 103, 110, 97, 116, 117, 114, 101, 32, 103, 101, 110, 101, 114, 97,
-    116, 105, 111, 110, 32, 97, 110, 100, 32, 118, 101, 114, 105, 102, 105, 99,
-    97, 116, 105, 111, 110, 44, 32, 97, 110, 100, 32, 101, 110, 99, 114, 121,
-    112, 116, 105, 111, 110, 32, 97, 110, 100, 32, 100, 101, 99, 114, 121, 112,
-    116, 105, 111, 110, 46, 32, 65, 100, 100, 105, 116, 105, 111, 110, 97, 108,
-    108, 121, 44, 32, 105, 116, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115,
-    32, 97, 110, 32, 65, 80, 73, 32, 102, 111, 114, 32, 97, 112, 112, 108, 105,
-    99, 97, 116, 105, 111, 110, 115, 32, 116, 111, 32, 103, 101, 110, 101, 114,
-    97, 116, 101, 32, 97, 110, 100, 47, 111, 114, 32, 109, 97, 110, 97, 103,
-    101, 32, 116, 104, 101, 32, 107, 101, 121, 105, 110, 103, 32, 109, 97, 116,
-    101, 114, 105, 97, 108, 32, 110, 101, 99, 101, 115, 115, 97, 114, 121, 32,
-    116, 111, 32, 112, 101, 114, 102, 111, 114, 109, 32, 116, 104, 101, 115,
-    101, 32, 111, 112, 101, 114, 97, 116, 105, 111, 110, 115, 46, 32, 85, 115,
-    101, 115, 32, 102, 111, 114, 32, 116, 104, 105, 115, 32, 65, 80, 73, 32,
-    114, 97, 110, 103, 101, 32, 102, 114, 111, 109, 32, 117, 115, 101, 114, 32,
-    111, 114, 32, 115, 101, 114, 118, 105, 99, 101, 32, 97, 117, 116, 104, 101,
-    110, 116, 105, 99, 97, 116, 105, 111, 110, 44, 32, 100, 111, 99, 117, 109,
-    101, 110, 116, 32, 111, 114, 32, 99, 111, 100, 101, 32, 115, 105, 103, 110,
-    105, 110, 103, 44, 32, 97, 110, 100, 32, 116, 104, 101, 32, 99, 111, 110,
-    102, 105, 100, 101, 110, 116, 105, 97, 108, 105, 116, 121, 32, 97, 110, 100,
-    32, 105, 110, 116, 101, 103, 114, 105, 116, 121, 32, 111, 102, 32, 99, 111,
-    109, 109, 117, 110, 105, 99, 97, 116, 105, 111, 110, 115, 46,
-  ]);
-
-  // We want some random key bytes of various sizes.
-  // These were randomly generated from a script.
-  var keyBytes = {
-    128: new Uint8Array([
-      222, 192, 212, 252, 191, 60, 71, 65, 200, 146, 218, 189, 28, 212, 192, 78,
-    ]),
-    192: new Uint8Array([
-      208, 238, 131, 65, 63, 68, 196, 63, 186, 208, 61, 207, 166, 18, 99, 152,
-      29, 109, 221, 95, 240, 30, 28, 246,
-    ]),
-    256: new Uint8Array([
-      103, 105, 56, 35, 251, 29, 88, 7, 63, 145, 236, 233, 204, 58, 249, 16,
-      229, 83, 38, 22, 164, 210, 123, 19, 235, 123, 116, 216, 0, 11, 191, 48,
-    ]),
-  };
+  var commonFixtures = getAesCommonFixtures();
+  var plaintext = commonFixtures.plaintext;
+  var keyBytes = commonFixtures.keyBytes;
 
   // AES-GCM needs an IV of no more than 2^64 - 1 bytes. Arbitrary 32 bytes is okay then.
   var iv = new Uint8Array([
@@ -55,15 +9,7 @@
     33, 117, 56, 94, 248, 173, 234, 194, 200, 115, 53, 235, 146, 141, 212,
   ]);
 
-  // Authenticated encryption via AES-GCM requires additional data that
-  // will be checked. We use the ASCII encoded Editorial Note
-  // following the Abstract of the Web Cryptography API recommendation.
-  var additionalData = new Uint8Array([
-    84, 104, 101, 114, 101, 32, 97, 114, 101, 32, 55, 32, 102, 117, 114, 116,
-    104, 101, 114, 32, 101, 100, 105, 116, 111, 114, 105, 97, 108, 32, 110, 111,
-    116, 101, 115, 32, 105, 110, 32, 116, 104, 101, 32, 100, 111, 99, 117, 109,
-    101, 110, 116, 46,
-  ]);
+  var additionalData = commonFixtures.additionalData;
 
   //  The length of the tag defaults to 16 bytes (128 bit).
   var tag = {
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm_96_iv_fixtures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm_96_iv_fixtures.js
index bb00e2d..e120b58 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm_96_iv_fixtures.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_gcm_96_iv_fixtures.js
@@ -1,68 +1,14 @@
 function getFixtures() {
-  // Before we can really start, we need to fill a bunch of buffers with data
-  var plaintext = new Uint8Array([
-    84, 104, 105, 115, 32, 115, 112, 101, 99, 105, 102, 105, 99, 97, 116, 105,
-    111, 110, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, 32, 74, 97,
-    118, 97, 83, 99, 114, 105, 112, 116, 32, 65, 80, 73, 32, 102, 111, 114, 32,
-    112, 101, 114, 102, 111, 114, 109, 105, 110, 103, 32, 98, 97, 115, 105, 99,
-    32, 99, 114, 121, 112, 116, 111, 103, 114, 97, 112, 104, 105, 99, 32, 111,
-    112, 101, 114, 97, 116, 105, 111, 110, 115, 32, 105, 110, 32, 119, 101, 98,
-    32, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 115, 44, 32, 115,
-    117, 99, 104, 32, 97, 115, 32, 104, 97, 115, 104, 105, 110, 103, 44, 32,
-    115, 105, 103, 110, 97, 116, 117, 114, 101, 32, 103, 101, 110, 101, 114, 97,
-    116, 105, 111, 110, 32, 97, 110, 100, 32, 118, 101, 114, 105, 102, 105, 99,
-    97, 116, 105, 111, 110, 44, 32, 97, 110, 100, 32, 101, 110, 99, 114, 121,
-    112, 116, 105, 111, 110, 32, 97, 110, 100, 32, 100, 101, 99, 114, 121, 112,
-    116, 105, 111, 110, 46, 32, 65, 100, 100, 105, 116, 105, 111, 110, 97, 108,
-    108, 121, 44, 32, 105, 116, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115,
-    32, 97, 110, 32, 65, 80, 73, 32, 102, 111, 114, 32, 97, 112, 112, 108, 105,
-    99, 97, 116, 105, 111, 110, 115, 32, 116, 111, 32, 103, 101, 110, 101, 114,
-    97, 116, 101, 32, 97, 110, 100, 47, 111, 114, 32, 109, 97, 110, 97, 103,
-    101, 32, 116, 104, 101, 32, 107, 101, 121, 105, 110, 103, 32, 109, 97, 116,
-    101, 114, 105, 97, 108, 32, 110, 101, 99, 101, 115, 115, 97, 114, 121, 32,
-    116, 111, 32, 112, 101, 114, 102, 111, 114, 109, 32, 116, 104, 101, 115,
-    101, 32, 111, 112, 101, 114, 97, 116, 105, 111, 110, 115, 46, 32, 85, 115,
-    101, 115, 32, 102, 111, 114, 32, 116, 104, 105, 115, 32, 65, 80, 73, 32,
-    114, 97, 110, 103, 101, 32, 102, 114, 111, 109, 32, 117, 115, 101, 114, 32,
-    111, 114, 32, 115, 101, 114, 118, 105, 99, 101, 32, 97, 117, 116, 104, 101,
-    110, 116, 105, 99, 97, 116, 105, 111, 110, 44, 32, 100, 111, 99, 117, 109,
-    101, 110, 116, 32, 111, 114, 32, 99, 111, 100, 101, 32, 115, 105, 103, 110,
-    105, 110, 103, 44, 32, 97, 110, 100, 32, 116, 104, 101, 32, 99, 111, 110,
-    102, 105, 100, 101, 110, 116, 105, 97, 108, 105, 116, 121, 32, 97, 110, 100,
-    32, 105, 110, 116, 101, 103, 114, 105, 116, 121, 32, 111, 102, 32, 99, 111,
-    109, 109, 117, 110, 105, 99, 97, 116, 105, 111, 110, 115, 46,
-  ]);
-
-  // We want some random key bytes of various sizes.
-  // These were randomly generated from a script.
-  var keyBytes = {
-    128: new Uint8Array([
-      222, 192, 212, 252, 191, 60, 71, 65, 200, 146, 218, 189, 28, 212, 192, 78,
-    ]),
-    192: new Uint8Array([
-      208, 238, 131, 65, 63, 68, 196, 63, 186, 208, 61, 207, 166, 18, 99, 152,
-      29, 109, 221, 95, 240, 30, 28, 246,
-    ]),
-    256: new Uint8Array([
-      103, 105, 56, 35, 251, 29, 88, 7, 63, 145, 236, 233, 204, 58, 249, 16,
-      229, 83, 38, 22, 164, 210, 123, 19, 235, 123, 116, 216, 0, 11, 191, 48,
-    ]),
-  };
+  var commonFixtures = getAesCommonFixtures();
+  var plaintext = commonFixtures.plaintext;
+  var keyBytes = commonFixtures.keyBytes;
 
   // AES-GCM specification recommends that the IV should be 96 bits long.
   var iv = new Uint8Array([
     58, 146, 115, 42, 166, 234, 57, 191, 57, 134, 224, 199,
   ]);
 
-  // Authenticated encryption via AES-GCM requires additional data that
-  // will be checked. We use the ASCII encoded Editorial Note
-  // following the Abstract of the Web Cryptography API recommendation.
-  var additionalData = new Uint8Array([
-    84, 104, 101, 114, 101, 32, 97, 114, 101, 32, 55, 32, 102, 117, 114, 116,
-    104, 101, 114, 32, 101, 100, 105, 116, 111, 114, 105, 97, 108, 32, 110, 111,
-    116, 101, 115, 32, 105, 110, 32, 116, 104, 101, 32, 100, 111, 99, 117, 109,
-    101, 110, 116, 46,
-  ]);
+  var additionalData = commonFixtures.additionalData;
 
   //  The length of the tag defaults to 16 bytes (128 bit).
   var tag = {
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ocb.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ocb.tentative.https.any.js
index cca34e9..e6a6246 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ocb.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ocb.tentative.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: encrypt() Using AES-OCB w/ 120-bit iv
 // META: script=../util/helpers.js
+// META: script=aes_common_fixtures.js
 // META: script=aes_ocb_fixtures.js
 // META: script=aes_ocb_vectors.js
 // META: script=aes.js
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ocb_fixtures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ocb_fixtures.js
index a579519..3e0c014 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ocb_fixtures.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/aes_ocb_fixtures.js
@@ -1,64 +1,13 @@
 function getFixtures() {
-  // Before we can really start, we need to fill a bunch of buffers with data
-  var plaintext = new Uint8Array([
-    84, 104, 105, 115, 32, 115, 112, 101, 99, 105, 102, 105, 99, 97, 116, 105,
-    111, 110, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115, 32, 97, 32, 74, 97,
-    118, 97, 83, 99, 114, 105, 112, 116, 32, 65, 80, 73, 32, 102, 111, 114, 32,
-    112, 101, 114, 102, 111, 114, 109, 105, 110, 103, 32, 98, 97, 115, 105, 99,
-    32, 99, 114, 121, 112, 116, 111, 103, 114, 97, 112, 104, 105, 99, 32, 111,
-    112, 101, 114, 97, 116, 105, 111, 110, 115, 32, 105, 110, 32, 119, 101, 98,
-    32, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 115, 44, 32, 115,
-    117, 99, 104, 32, 97, 115, 32, 104, 97, 115, 104, 105, 110, 103, 44, 32,
-    115, 105, 103, 110, 97, 116, 117, 114, 101, 32, 103, 101, 110, 101, 114, 97,
-    116, 105, 111, 110, 32, 97, 110, 100, 32, 118, 101, 114, 105, 102, 105, 99,
-    97, 116, 105, 111, 110, 44, 32, 97, 110, 100, 32, 101, 110, 99, 114, 121,
-    112, 116, 105, 111, 110, 32, 97, 110, 100, 32, 100, 101, 99, 114, 121, 112,
-    116, 105, 111, 110, 46, 32, 65, 100, 100, 105, 116, 105, 111, 110, 97, 108,
-    108, 121, 44, 32, 105, 116, 32, 100, 101, 115, 99, 114, 105, 98, 101, 115,
-    32, 97, 110, 32, 65, 80, 73, 32, 102, 111, 114, 32, 97, 112, 112, 108, 105,
-    99, 97, 116, 105, 111, 110, 115, 32, 116, 111, 32, 103, 101, 110, 101, 114,
-    97, 116, 101, 32, 97, 110, 100, 47, 111, 114, 32, 109, 97, 110, 97, 103,
-    101, 32, 116, 104, 101, 32, 107, 101, 121, 105, 110, 103, 32, 109, 97, 116,
-    101, 114, 105, 97, 108, 32, 110, 101, 99, 101, 115, 115, 97, 114, 121, 32,
-    116, 111, 32, 112, 101, 114, 102, 111, 114, 109, 32, 116, 104, 101, 115,
-    101, 32, 111, 112, 101, 114, 97, 116, 105, 111, 110, 115, 46, 32, 85, 115,
-    101, 115, 32, 102, 111, 114, 32, 116, 104, 105, 115, 32, 65, 80, 73, 32,
-    114, 97, 110, 103, 101, 32, 102, 114, 111, 109, 32, 117, 115, 101, 114, 32,
-    111, 114, 32, 115, 101, 114, 118, 105, 99, 101, 32, 97, 117, 116, 104, 101,
-    110, 116, 105, 99, 97, 116, 105, 111, 110, 44, 32, 100, 111, 99, 117, 109,
-    101, 110, 116, 32, 111, 114, 32, 99, 111, 100, 101, 32, 115, 105, 103, 110,
-    105, 110, 103, 44, 32, 97, 110, 100, 32, 116, 104, 101, 32, 99, 111, 110,
-    102, 105, 100, 101, 110, 116, 105, 97, 108, 105, 116, 121, 32, 97, 110, 100,
-    32, 105, 110, 116, 101, 103, 114, 105, 116, 121, 32, 111, 102, 32, 99, 111,
-    109, 109, 117, 110, 105, 99, 97, 116, 105, 111, 110, 115, 46,
-  ]);
-
-  // We want some random key bytes of various sizes.
-  // These were randomly generated from a script.
-  var keyBytes = {
-    128: new Uint8Array([
-      222, 192, 212, 252, 191, 60, 71, 65, 200, 146, 218, 189, 28, 212, 192, 78,
-    ]),
-    192: new Uint8Array([
-      208, 238, 131, 65, 63, 68, 196, 63, 186, 208, 61, 207, 166, 18, 99, 152,
-      29, 109, 221, 95, 240, 30, 28, 246,
-    ]),
-    256: new Uint8Array([
-      103, 105, 56, 35, 251, 29, 88, 7, 63, 145, 236, 233, 204, 58, 249, 16,
-      229, 83, 38, 22, 164, 210, 123, 19, 235, 123, 116, 216, 0, 11, 191, 48,
-    ]),
-  };
+  var commonFixtures = getAesCommonFixtures();
+  var plaintext = commonFixtures.plaintext;
+  var keyBytes = commonFixtures.keyBytes;
 
   var iv = new Uint8Array([
     58, 146, 115, 42, 166, 234, 57, 191, 57, 134, 224, 199, 108, 116, 46,
   ]);
 
-  var additionalData = new Uint8Array([
-    84, 104, 101, 114, 101, 32, 97, 114, 101, 32, 55, 32, 102, 117, 114, 116,
-    104, 101, 114, 32, 101, 100, 105, 116, 111, 114, 105, 97, 108, 32, 110, 111,
-    116, 101, 115, 32, 105, 110, 32, 116, 104, 101, 32, 100, 111, 99, 117, 109,
-    101, 110, 116, 46,
-  ]);
+  var additionalData = commonFixtures.additionalData;
 
   var ciphertext = {
     128: {
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/rsa.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/rsa.js
index 071c086b..951ffd5 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/rsa.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/rsa.js
@@ -23,7 +23,7 @@
             promise_test(function(test) {
                 return subtle.decrypt(vector.algorithm, vector.privateKey, vector.ciphertext)
                 .then(function(plaintext) {
-                    assert_true(equalBuffers(plaintext, vector.plaintext, "Decryption works"));
+                    assert_true(equalBuffers(plaintext, vector.plaintext), "Decryption works");
                 }, function(err) {
                     assert_unreached("Decryption should not throw error " + vector.name + ": '" + err.message + "'");
                 });
@@ -60,7 +60,7 @@
                     }
                 }, vector.privateKey, ciphertext)
                 .then(function(plaintext) {
-                    assert_true(equalBuffers(plaintext, vector.plaintext, "Decryption works"));
+                    assert_true(equalBuffers(plaintext, vector.plaintext), "Decryption works");
                 }, function(err) {
                     assert_unreached("Decryption should not throw error " + vector.name + ": '" + err.message + "'");
                 });
@@ -91,7 +91,7 @@
                 var ciphertext = copyBuffer(vector.ciphertext);
                 var operation = subtle.decrypt(vector.algorithm, vector.privateKey, ciphertext)
                 .then(function(plaintext) {
-                    assert_true(equalBuffers(plaintext, vector.plaintext, "Decryption works"));
+                    assert_true(equalBuffers(plaintext, vector.plaintext), "Decryption works");
                 }, function(err) {
                     assert_unreached("Decryption should not throw error " + vector.name + ": '" + err.message + "'");
                 });
@@ -160,7 +160,7 @@
                 var ciphertext = copyBuffer(vector.ciphertext);
                 var operation = subtle.decrypt(vector.algorithm, vector.privateKey, ciphertext)
                 .then(function(plaintext) {
-                    assert_true(equalBuffers(plaintext, vector.plaintext, "Decryption works"));
+                    assert_true(equalBuffers(plaintext, vector.plaintext), "Decryption works");
                 }, function(err) {
                     assert_unreached("Decryption should not throw error " + vector.name + ": '" + err.message + "'");
                 });
@@ -538,9 +538,7 @@
     });
 
     promise_test(function() {
-        return Promise.all(all_promises)
-            .then(function() {done();})
-            .catch(function() {done();})
+        return Promise.all(all_promises).finally(done);
     }, "setup");
 
     // A test vector has all needed fields for encryption, EXCEPT that the
@@ -552,9 +550,7 @@
         var publicPromise, privatePromise;
 
         if (vector.publicKey !== null) {
-            publicPromise = new Promise(function(resolve, reject) {
-                resolve(vector);
-            });
+            publicPromise = Promise.resolve(vector);
         } else {
             publicPromise = subtle.importKey(vector.publicKeyFormat, vector.publicKeyBuffer, {name: vector.algorithm.name, hash: vector.hash}, false, publicKeyUsages)
             .then(function(key) {
@@ -564,9 +560,7 @@
         }
 
         if (vector.privateKey !== null) {
-            privatePromise = new Promise(function(resolve, reject) {
-                resolve(vector);
-            });
+            privatePromise = Promise.resolve(vector);
         } else {
             privatePromise = subtle.importKey(vector.privateKeyFormat, vector.privateKeyBuffer, {name: vector.algorithm.name, hash: vector.hash}, false, privateKeyUsages)
             .then(function(key) {
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/rsa_oaep.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/rsa_oaep.https.any.js
index 3550f5e..4251141 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/rsa_oaep.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/rsa_oaep.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: encrypt() Using RSA-OAEP
 // META: script=../util/helpers.js
+// META: script=../util/rsa_key_fixtures.js
 // META: script=rsa_vectors.js
 // META: script=rsa.js
 // META: timeout=long
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/rsa_vectors.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/rsa_vectors.js
index fcc732e..ca99953 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/rsa_vectors.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/encrypt_decrypt/rsa_vectors.js
@@ -18,8 +18,9 @@
 //     plaintext - the text to encrypt
 //     result - the expected result (usually just ciphertext, sometimes with added authentication)
 function getTestVectors() {
-    var pkcs8 = new Uint8Array([48, 130, 4, 191, 2, 1, 0, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 4, 130, 4, 169, 48, 130, 4, 165, 2, 1, 0, 2, 130, 1, 1, 0, 211, 87, 96, 146, 230, 41, 87, 54, 69, 68, 231, 228, 35, 59, 123, 219, 41, 61, 178, 8, 81, 34, 196, 121, 50, 133, 70, 249, 240, 247, 18, 246, 87, 196, 177, 120, 104, 201, 48, 144, 140, 197, 148, 247, 237, 0, 192, 20, 66, 193, 175, 4, 194, 246, 120, 164, 139, 162, 200, 15, 209, 113, 62, 48, 181, 172, 80, 120, 122, 195, 81, 101, 137, 241, 113, 150, 127, 99, 134, 173, 163, 73, 0, 166, 187, 4, 238, 206, 164, 43, 240, 67, 206, 217, 160, 249, 77, 12, 192, 158, 145, 155, 157, 113, 102, 192, 138, 182, 206, 32, 70, 64, 174, 164, 196, 146, 13, 182, 216, 110, 185, 22, 208, 220, 192, 244, 52, 26, 16, 56, 4, 41, 231, 225, 3, 33, 68, 234, 148, 157, 232, 246, 192, 204, 191, 149, 250, 142, 146, 141, 112, 216, 163, 140, 225, 104, 219, 69, 246, 241, 52, 102, 61, 111, 101, 111, 92, 234, 188, 114, 93, 168, 192, 42, 171, 234, 170, 19, 172, 54, 167, 92, 192, 186, 225, 53, 223, 49, 20, 182, 101, 137, 199, 237, 60, 182, 21, 89, 174, 90, 56, 79, 22, 43, 250, 128, 219, 228, 97, 127, 134, 195, 241, 208, 16, 201, 79, 226, 201, 191, 1, 154, 110, 99, 179, 239, 192, 40, 212, 60, 238, 97, 28, 133, 236, 38, 60, 144, 108, 70, 55, 114, 198, 145, 27, 25, 238, 192, 150, 202, 118, 236, 94, 49, 225, 227, 2, 3, 1, 0, 1, 2, 130, 1, 1, 0, 139, 55, 92, 203, 135, 200, 37, 197, 255, 61, 83, 208, 9, 145, 110, 150, 65, 5, 126, 24, 82, 114, 39, 160, 122, 178, 38, 190, 16, 136, 129, 58, 59, 56, 187, 123, 72, 243, 119, 5, 81, 101, 250, 42, 147, 57, 210, 77, 198, 103, 213, 197, 186, 52, 39, 230, 164, 129, 23, 110, 172, 21, 255, 212, 144, 104, 49, 30, 28, 40, 59, 159, 58, 142, 12, 184, 9, 180, 99, 12, 80, 170, 143, 62, 69, 166, 11, 53, 158, 25, 191, 140, 187, 94, 202, 214, 78, 118, 31, 16, 149, 116, 63, 243, 106, 175, 92, 240, 236, 185, 127, 237, 173, 221, 166, 11, 91, 243, 93, 129, 26, 117, 184, 34, 35, 12, 250, 160, 25, 47, 173, 64, 84, 126, 39, 84, 72, 170, 51, 22, 191, 142, 43, 76, 224, 133, 79, 199, 112, 139, 83, 123, 162, 45, 19, 33, 11, 9, 174, 195, 122, 39, 89, 239, 192, 130, 161, 83, 27, 35, 169, 23, 48, 3, 125, 222, 78, 242, 107, 95, 150, 239, 220, 195, 159, 211, 76, 52, 90, 213, 28, 187, 228, 79, 229, 139, 138, 59, 78, 201, 151, 134, 108, 8, 109, 255, 27, 136, 49, 239, 10, 31, 234, 38, 60, 247, 218, 205, 3, 192, 76, 188, 194, 178, 121, 229, 127, 165, 185, 83, 153, 107, 251, 29, 214, 136, 23, 175, 127, 180, 44, 222, 247, 165, 41, 74, 87, 250, 194, 184, 173, 115, 159, 27, 2, 153, 2, 129, 129, 0, 251, 248, 51, 194, 198, 49, 201, 112, 36, 12, 142, 116, 133, 240, 106, 62, 162, 168, 72, 34, 81, 26, 134, 39, 221, 70, 78, 248, 175, 175, 113, 72, 209, 164, 37, 182, 184, 101, 125, 221, 82, 70, 131, 43, 142, 83, 48, 32, 197, 187, 181, 104, 133, 90, 106, 236, 62, 66, 33, 215, 147, 241, 220, 91, 47, 37, 132, 226, 65, 94, 72, 233, 162, 189, 41, 43, 19, 64, 49, 249, 156, 142, 180, 47, 192, 188, 208, 68, 155, 242, 44, 230, 222, 201, 112, 20, 239, 229, 172, 147, 235, 232, 53, 135, 118, 86, 37, 44, 187, 177, 108, 65, 91, 103, 177, 132, 210, 40, 69, 104, 162, 119, 213, 147, 53, 88, 92, 253, 2, 129, 129, 0, 214, 184, 206, 39, 199, 41, 93, 93, 22, 252, 53, 112, 237, 100, 200, 218, 147, 3, 250, 210, 148, 136, 193, 166, 94, 154, 215, 17, 249, 3, 112, 24, 125, 187, 253, 129, 49, 109, 105, 100, 139, 200, 140, 197, 200, 53, 81, 175, 255, 69, 222, 186, 207, 182, 17, 5, 247, 9, 228, 195, 8, 9, 185, 0, 49, 235, 214, 134, 36, 68, 150, 198, 246, 158, 105, 46, 189, 200, 20, 246, 66, 57, 244, 173, 21, 117, 110, 203, 120, 197, 165, 176, 153, 49, 219, 24, 48, 119, 197, 70, 163, 140, 76, 116, 56, 137, 173, 61, 62, 208, 121, 181, 98, 46, 208, 18, 15, 160, 225, 249, 59, 89, 61, 183, 216, 82, 224, 95, 2, 129, 128, 56, 135, 75, 157, 131, 247, 129, 120, 206, 45, 158, 252, 23, 92, 131, 137, 127, 214, 127, 48, 107, 191, 166, 159, 100, 238, 52, 35, 104, 206, 212, 124, 128, 195, 241, 206, 23, 122, 117, 141, 100, 186, 251, 12, 151, 134, 164, 66, 133, 250, 1, 205, 236, 53, 7, 205, 238, 125, 201, 183, 226, 178, 29, 60, 187, 204, 16, 14, 238, 153, 103, 132, 59, 5, 115, 41, 253, 204, 166, 41, 152, 237, 15, 17, 179, 140, 232, 176, 171, 199, 222, 57, 1, 124, 113, 207, 208, 174, 87, 84, 108, 85, 145, 68, 205, 208, 175, 208, 100, 95, 126, 168, 255, 7, 185, 116, 209, 237, 68, 253, 31, 142, 0, 245, 96, 191, 109, 69, 2, 129, 129, 0, 133, 41, 239, 144, 115, 207, 143, 123, 95, 249, 226, 26, 186, 223, 58, 65, 115, 211, 144, 6, 112, 223, 175, 89, 66, 106, 188, 223, 4, 147, 193, 61, 47, 29, 27, 70, 184, 36, 166, 172, 24, 148, 179, 217, 37, 37, 12, 24, 30, 52, 114, 193, 96, 120, 5, 110, 177, 154, 141, 40, 247, 31, 48, 128, 146, 117, 52, 129, 212, 148, 68, 253, 247, 140, 158, 166, 194, 68, 7, 220, 1, 142, 119, 211, 175, 239, 56, 91, 47, 247, 67, 158, 150, 35, 121, 65, 51, 45, 212, 70, 206, 190, 255, 219, 68, 4, 254, 79, 113, 89, 81, 97, 208, 22, 64, 44, 51, 77, 15, 87, 198, 26, 190, 79, 249, 244, 203, 249, 2, 129, 129, 0, 135, 216, 119, 8, 212, 103, 99, 228, 204, 190, 178, 209, 233, 113, 46, 91, 240, 33, 109, 112, 222, 148, 32, 165, 178, 6, 155, 116, 89, 185, 159, 93, 159, 127, 47, 173, 124, 215, 154, 174, 230, 122, 127, 154, 52, 67, 126, 60, 121, 168, 74, 240, 205, 141, 233, 223, 242, 104, 235, 12, 71, 147, 245, 1, 249, 136, 213, 64, 246, 211, 71, 92, 32, 121, 184, 34, 122, 35, 217, 104, 222, 196, 227, 198, 101, 3, 24, 113, 147, 69, 150, 48, 71, 43, 253, 182, 186, 29, 231, 134, 199, 151, 250, 111, 78, 166, 90, 42, 132, 25, 38, 47, 41, 103, 136, 86, 203, 115, 201, 189, 75, 200, 155, 94, 4, 27, 34, 119]);
-    var spki = new Uint8Array([48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2, 130, 1, 1, 0, 211, 87, 96, 146, 230, 41, 87, 54, 69, 68, 231, 228, 35, 59, 123, 219, 41, 61, 178, 8, 81, 34, 196, 121, 50, 133, 70, 249, 240, 247, 18, 246, 87, 196, 177, 120, 104, 201, 48, 144, 140, 197, 148, 247, 237, 0, 192, 20, 66, 193, 175, 4, 194, 246, 120, 164, 139, 162, 200, 15, 209, 113, 62, 48, 181, 172, 80, 120, 122, 195, 81, 101, 137, 241, 113, 150, 127, 99, 134, 173, 163, 73, 0, 166, 187, 4, 238, 206, 164, 43, 240, 67, 206, 217, 160, 249, 77, 12, 192, 158, 145, 155, 157, 113, 102, 192, 138, 182, 206, 32, 70, 64, 174, 164, 196, 146, 13, 182, 216, 110, 185, 22, 208, 220, 192, 244, 52, 26, 16, 56, 4, 41, 231, 225, 3, 33, 68, 234, 148, 157, 232, 246, 192, 204, 191, 149, 250, 142, 146, 141, 112, 216, 163, 140, 225, 104, 219, 69, 246, 241, 52, 102, 61, 111, 101, 111, 92, 234, 188, 114, 93, 168, 192, 42, 171, 234, 170, 19, 172, 54, 167, 92, 192, 186, 225, 53, 223, 49, 20, 182, 101, 137, 199, 237, 60, 182, 21, 89, 174, 90, 56, 79, 22, 43, 250, 128, 219, 228, 97, 127, 134, 195, 241, 208, 16, 201, 79, 226, 201, 191, 1, 154, 110, 99, 179, 239, 192, 40, 212, 60, 238, 97, 28, 133, 236, 38, 60, 144, 108, 70, 55, 114, 198, 145, 27, 25, 238, 192, 150, 202, 118, 236, 94, 49, 225, 227, 2, 3, 1, 0, 1]);
+    var keyFixtures = getRsaKeyFixtures();
+    var pkcs8 = keyFixtures.pkcs8;
+    var spki = keyFixtures.spki;
 
     // Can optionally provide a label for encryption. We use the ASCII-encoded
     // abstract from the candidate recommendation.
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/algorithm_registry.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/algorithm_registry.js
new file mode 100644
index 0000000..2c7493d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/algorithm_registry.js
@@ -0,0 +1,36 @@
+// Generated by WebCryptoAPI/tools/generate.py. Do not edit directly.
+const generateKeyTestVectors = [
+    {name: "AES-CTR", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
+    {name: "AES-CBC", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
+    {name: "AES-GCM", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
+    {name: "AES-OCB", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
+    {name: "ChaCha20-Poly1305", resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
+    {name: "AES-KW", resultType: CryptoKey, usages: ["wrapKey", "unwrapKey"], mandatoryUsages: []},
+    {name: "HMAC", resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []},
+    {name: "RSASSA-PKCS1-v1_5", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
+    {name: "RSA-PSS", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
+    {name: "RSA-OAEP", resultType: "CryptoKeyPair", usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: ["decrypt", "unwrapKey"]},
+    {name: "ECDSA", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
+    {name: "ECDH", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
+    {name: "Ed25519", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
+    {name: "Ed448", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
+    {name: "ML-DSA-44", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
+    {name: "ML-DSA-65", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
+    {name: "ML-DSA-87", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
+    {name: "ML-KEM-512", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]},
+    {name: "ML-KEM-768", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]},
+    {name: "ML-KEM-1024", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]},
+    {name: "X25519", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
+    {name: "X448", resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
+    {name: "KMAC128", resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []},
+    {name: "KMAC256", resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []},
+];
+
+function getGenerateKeyTestVectors(algorithmNames) {
+    if (algorithmNames && !Array.isArray(algorithmNames)) {
+        algorithmNames = [algorithmNames];
+    }
+
+    return generateKeyTestVectors.filter(
+        vector => !algorithmNames || algorithmNames.includes(vector.name));
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures.js
index a3258ae0..e4a75c0 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures.js
@@ -1,8 +1,3 @@
-function run_test(algorithmNames) {
-    var subtle = crypto.subtle; // Change to test prefixed implementations
-
-    setup({explicit_timeout: true});
-
 // These tests check that generateKey throws an error, and that
 // the error is of the right type, for a wide set of incorrect parameters.
 //
@@ -21,135 +16,45 @@
 // helper functions that generate all possible test parameters for
 // different situations.
 
-    var allTestVectors = [ // Parameters that should work for generateKey
-        {name: "AES-CTR",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
-        {name: "AES-CBC",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
-        {name: "AES-GCM",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
-        {name: "AES-OCB",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
-        {name: "ChaCha20-Poly1305",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
-        {name: "AES-KW",   resultType: CryptoKey, usages: ["wrapKey", "unwrapKey"], mandatoryUsages: []},
-        {name: "HMAC",     resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []},
-        {name: "RSASSA-PKCS1-v1_5", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "RSA-PSS",  resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "RSA-OAEP", resultType: "CryptoKeyPair", usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: ["decrypt", "unwrapKey"]},
-        {name: "ECDSA",    resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "ECDH",     resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
-        {name: "Ed25519",  resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "Ed448",    resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "ML-DSA-44", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "ML-DSA-65", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "ML-DSA-87", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "ML-KEM-512", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]},
-        {name: "ML-KEM-768", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]},
-        {name: "ML-KEM-1024", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]},
-        {name: "X25519",   resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
-        {name: "X448",     resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
-        {name: "KMAC128",  resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []},
-        {name: "KMAC256",  resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []},
-    ];
-
-    var testVectors = [];
-    if (algorithmNames && !Array.isArray(algorithmNames)) {
-        algorithmNames = [algorithmNames];
-    };
-    allTestVectors.forEach(function(vector) {
-        if (!algorithmNames || algorithmNames.includes(vector.name)) {
-            testVectors.push(vector);
-        }
-    });
-
-
-    function parameterString(algorithm, extractable, usages) {
-        if (typeof algorithm !== "object" && typeof algorithm !== "string") {
-            alert(algorithm);
-        }
-
-        var result = "(" +
-                        objectToString(algorithm) + ", " +
-                        objectToString(extractable) + ", " +
-                        objectToString(usages) +
-                     ")";
-
-        return result;
+function parameterString(algorithm, extractable, usages) {
+    if (typeof algorithm !== "object" && typeof algorithm !== "string") {
+        alert(algorithm);
     }
 
-    // Test that a given combination of parameters results in an error,
-    // AND that it is the correct kind of error.
-    //
-    // Expected error is either a number, tested against the error code,
-    // or a string, tested against the error name.
-    function testError(algorithm, extractable, usages, expectedError, testTag) {
-        promise_test(function(test) {
-            return crypto.subtle.generateKey(algorithm, extractable, usages)
-            .then(function(result) {
-                assert_unreached("Operation succeeded, but should not have");
-            }, function(err) {
-                if (typeof expectedError === "number") {
-                    assert_equals(err.code, expectedError, testTag + " not supported");
-                } else {
-                    assert_equals(err.name, expectedError, testTag + " not supported");
-                }
-            });
-        }, testTag + ": generateKey" + parameterString(algorithm, extractable, usages));
-    }
+    var result = "(" +
+                    objectToString(algorithm) + ", " +
+                    objectToString(extractable) + ", " +
+                    objectToString(usages) +
+                 ")";
 
+    return result;
+}
 
-    // Given an algorithm name, create several invalid parameters.
-    function badAlgorithmPropertySpecifiersFor(algorithmName) {
-        var results = [];
-
-        if (algorithmName.toUpperCase().substring(0, 3) === "AES") {
-            // Specifier properties are name and length
-            [64, 127, 129, 255, 257, 512].forEach(function(length) {
-                results.push({name: algorithmName, length: length});
-            });
-        } else if (algorithmName.toUpperCase().substring(0, 3) === "RSA") {
-            [new Uint8Array([1]), new Uint8Array([1,0,0])].forEach(function(publicExponent) {
-                results.push({name: algorithmName, hash: "SHA-256", modulusLength: 1024, publicExponent: publicExponent});
-            });
-        } else if (algorithmName.toUpperCase().substring(0, 2) === "EC") {
-            ["P-512", "Curve25519"].forEach(function(curveName) {
-                results.push({name: algorithmName, namedCurve: curveName});
-            });
-        }
-
-        return results;
-    }
-
-
-    // Don't create an exhaustive list of all invalid usages,
-    // because there would usually be nearly 2**8 of them,
-    // way too many to test. Instead, create every singleton
-    // of an illegal usage, and "poison" every valid usage
-    // with an illegal one.
-    function invalidUsages(validUsages, mandatoryUsages) {
-        var results = [];
-
-        var illegalUsages = [];
-        ["encrypt", "decrypt", "sign", "verify", "wrapKey", "unwrapKey", "deriveKey", "deriveBits"].forEach(function(usage) {
-            if (!validUsages.includes(usage)) {
-                illegalUsages.push(usage);
+// Test that a given combination of parameters results in an error,
+// AND that it is the correct kind of error.
+//
+// Expected error is either a number, tested against the error code,
+// or a string, tested against the error name.
+function testError(algorithm, extractable, usages, expectedError, testTag) {
+    promise_test(function(test) {
+        return crypto.subtle.generateKey(algorithm, extractable, usages)
+        .then(function(result) {
+            assert_unreached("Operation succeeded, but should not have");
+        }, function(err) {
+            if (typeof expectedError === "number") {
+                assert_equals(err.code, expectedError, testTag + " not supported");
+            } else {
+                assert_equals(err.name, expectedError, testTag + " not supported");
             }
         });
-
-        var goodUsageCombinations = allValidUsages(validUsages, false, mandatoryUsages);
-
-        illegalUsages.forEach(function(illegalUsage) {
-            results.push([illegalUsage]);
-            goodUsageCombinations.forEach(function(usageCombination) {
-                results.push(usageCombination.concat([illegalUsage]));
-            });
-        });
-
-        return results;
-    }
+    }, testTag + ": generateKey" + parameterString(algorithm, extractable, usages));
+}
 
 
-// Now test for properly handling errors
-// - Unsupported algorithm
-// - Bad usages for algorithm
-// - Bad key lengths
-
+// Algorithm normalization happens before generateKey looks at any other
+// argument, so these cases are independent of the algorithm under test and
+// only need to run once for the whole suite.
+function run_bad_algorithm_test() {
     // Algorithm normalization should fail with "Not supported"
     var badAlgorithmNames = [
         "AES",
@@ -183,8 +88,67 @@
                 testError({}, extractable, usages, "TypeError", "Empty algorithm");
             });
         });
+}
 
 
+function run_test(algorithmNames) {
+    var testVectors = getGenerateKeyTestVectors(algorithmNames);
+
+
+    // Given an algorithm name, create several invalid parameters.
+    function badAlgorithmPropertySpecifiersFor(algorithmName) {
+        var results = [];
+
+        if (algorithmName.toUpperCase().substring(0, 3) === "AES") {
+            // Specifier properties are name and length
+            [64, 127, 129, 255, 257, 512].forEach(function(length) {
+                results.push({name: algorithmName, length: length});
+            });
+        } else if (algorithmName.toUpperCase().substring(0, 3) === "RSA") {
+            [new Uint8Array([1]), new Uint8Array([1,0,0])].forEach(function(publicExponent) {
+                results.push({name: algorithmName, hash: "SHA-256", modulusLength: 1024, publicExponent: publicExponent});
+            });
+        } else if (algorithmName.toUpperCase().substring(0, 2) === "EC") {
+            ["P-512", "Curve25519"].forEach(function(curveName) {
+                results.push({name: algorithmName, namedCurve: curveName});
+            });
+        }
+
+        return results;
+    }
+
+
+    // Don't create an exhaustive list of all invalid usages because
+    // there would be too many to test. Instead, create every singleton
+    // of an illegal usage, and "poison" every valid usage
+    // with an illegal one.
+    function invalidUsages(validUsages, mandatoryUsages) {
+        var results = [];
+
+        var illegalUsages = [];
+        allKeyUsages.forEach(function(usage) {
+            if (!validUsages.includes(usage)) {
+                illegalUsages.push(usage);
+            }
+        });
+
+        var goodUsageCombinations = allValidUsages(validUsages, false, mandatoryUsages);
+
+        illegalUsages.forEach(function(illegalUsage) {
+            results.push([illegalUsage]);
+            goodUsageCombinations.forEach(function(usageCombination) {
+                results.push(usageCombination.concat([illegalUsage]));
+            });
+        });
+
+        return results;
+    }
+
+
+// Now test for properly handling errors
+// - Bad usages for algorithm
+// - Bad key lengths
+
     // Algorithms normalize okay, but usages bad (though not empty).
     // It shouldn't matter what other extractable is. Should fail
     // due to SyntaxError
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-CBC.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-CBC.https.any.js
index 38bed1c..5febdbb 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-CBC.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-CBC.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["AES-CBC"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-CTR.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-CTR.https.any.js
index 0e79407..0eb6664 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-CTR.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-CTR.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["AES-CTR"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-GCM.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-GCM.https.any.js
index a394c8b6..3d13b78 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-GCM.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-GCM.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["AES-GCM"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-KW.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-KW.https.any.js
index 40c199b..138565e5 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-KW.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-KW.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["AES-KW"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-OCB.tentative.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-OCB.tentative.https.any-expected.txt
index df41403e..5f8baadf 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-OCB.tentative.https.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-OCB.tentative.https.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 414 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 618 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [sign])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, sign])
@@ -136,6 +136,142 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [sign])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, sign])
@@ -272,6 +408,142 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [sign])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, sign])
@@ -408,6 +680,142 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad algorithm property: generateKey({length: 64, name: AES-OCB}, false, [encrypt])
   assert_equals: Bad algorithm property not supported expected "OperationError" but got "NotSupportedError"
 [FAIL] Bad algorithm property: generateKey({length: 64, name: AES-OCB}, true, [encrypt])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-OCB.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-OCB.tentative.https.any.js
index d4a2cd8..ef7c8c3 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-OCB.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-OCB.tentative.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["AES-OCB"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-OCB.tentative.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-OCB.tentative.https.any.worker-expected.txt
index df41403e..5f8baadf 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-OCB.tentative.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_AES-OCB.tentative.https.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 414 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 618 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [sign])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, sign])
@@ -136,6 +136,142 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [sign])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, sign])
@@ -272,6 +408,142 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 192, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [sign])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, sign])
@@ -408,6 +680,142 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: AES-OCB}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad algorithm property: generateKey({length: 64, name: AES-OCB}, false, [encrypt])
   assert_equals: Bad algorithm property not supported expected "OperationError" but got "NotSupportedError"
 [FAIL] Bad algorithm property: generateKey({length: 64, name: AES-OCB}, true, [encrypt])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ECDH.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ECDH.https.any.js
index e522254..f61d27e99 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ECDH.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ECDH.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["ECDH"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ECDSA.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ECDSA.https.any.js
index e19974f..e49b5e675 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ECDSA.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ECDSA.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["ECDSA"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed25519.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed25519.https.any.js
index 8f18fb1..7505e97b 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed25519.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed25519.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["Ed25519"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed448.tentative.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed448.tentative.https.any-expected.txt
index 9e773ae6..c840ae7 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed448.tentative.https.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed448.tentative.https.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 52 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 84 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: generateKey(Ed448, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(Ed448, true, [sign, encrypt])
@@ -48,6 +48,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(Ed448, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: Ed448}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, encrypt])
@@ -96,6 +128,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(Ed448, false, [])
   assert_equals: Empty usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(Ed448, true, [])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed448.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed448.tentative.https.any.js
index b25dcd1..6ee388e 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed448.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed448.tentative.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["Ed448"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed448.tentative.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed448.tentative.https.any.worker-expected.txt
index 9e773ae6..c840ae7 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed448.tentative.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_Ed448.tentative.https.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 52 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 84 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: generateKey(Ed448, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(Ed448, true, [sign, encrypt])
@@ -48,6 +48,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(Ed448, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(Ed448, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: Ed448}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, encrypt])
@@ -96,6 +128,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: Ed448}, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(Ed448, false, [])
   assert_equals: Empty usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(Ed448, true, [])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_HMAC.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_HMAC.https.any.js
index 43ce1c0..e3097cd 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_HMAC.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_HMAC.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["HMAC"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-DSA.tentative.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-DSA.tentative.https.any-expected.txt
index 934b1a8..70d6d7c 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-DSA.tentative.https.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-DSA.tentative.https.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 156 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 252 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: generateKey(ML-DSA-44, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, encrypt])
@@ -48,6 +48,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, encrypt])
@@ -96,6 +128,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-65, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, encrypt])
@@ -144,6 +208,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, encrypt])
@@ -192,6 +288,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-87, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, encrypt])
@@ -240,6 +368,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, encrypt])
@@ -288,6 +448,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(ML-DSA-44, false, [])
   assert_equals: Empty usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(ML-DSA-44, true, [])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-DSA.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-DSA.tentative.https.any.js
index 91e20bc6..d41636de 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-DSA.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-DSA.tentative.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["ML-DSA-44", "ML-DSA-65", "ML-DSA-87"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-DSA.tentative.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-DSA.tentative.https.any.worker-expected.txt
index 934b1a8..70d6d7c 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-DSA.tentative.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-DSA.tentative.https.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 156 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 252 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: generateKey(ML-DSA-44, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, encrypt])
@@ -48,6 +48,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-44, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, encrypt])
@@ -96,6 +128,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-44}, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-65, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, encrypt])
@@ -144,6 +208,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-65, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, encrypt])
@@ -192,6 +288,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-65}, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-87, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, encrypt])
@@ -240,6 +368,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ML-DSA-87, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, encrypt])
@@ -288,6 +448,38 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, verify, sign, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, verify, sign, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, verify, sign, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, verify, sign, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ML-DSA-87}, true, [sign, verify, sign, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(ML-DSA-44, false, [])
   assert_equals: Empty usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(ML-DSA-44, true, [])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-KEM.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-KEM.tentative.https.any.js
index 9cd347d..0ece9694 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-KEM.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_ML-KEM.tentative.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["ML-KEM-512", "ML-KEM-768", "ML-KEM-1024"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_RSA-OAEP.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_RSA-OAEP.https.any.js
index 1d2bca9..a4eb4e6 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_RSA-OAEP.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_RSA-OAEP.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["RSA-OAEP"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_RSA-PSS.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_RSA-PSS.https.any.js
index 562f6669..62ac442e 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_RSA-PSS.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_RSA-PSS.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["RSA-PSS"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_RSASSA-PKCS1-v1_5.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_RSASSA-PKCS1-v1_5.https.any.js
index fb19308..f06e3402 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_RSASSA-PKCS1-v1_5.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_RSASSA-PKCS1-v1_5.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["RSASSA-PKCS1-v1_5"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X25519.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X25519.https.any.js
index 2662d86..6735e85 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X25519.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X25519.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["X25519"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X448.tentative.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X448.tentative.https.any-expected.txt
index 53b8a7e9..4fbef77 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X448.tentative.https.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X448.tentative.https.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 64 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 104 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: generateKey(X448, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(X448, true, [deriveKey, encrypt])
@@ -60,6 +60,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: X448}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, encrypt])
@@ -120,6 +160,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(X448, false, [])
   assert_equals: Empty usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(X448, true, [])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X448.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X448.tentative.https.any.js
index 455e260..26e12b9 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X448.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X448.tentative.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["X448"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X448.tentative.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X448.tentative.https.any.worker-expected.txt
index 53b8a7e9..4fbef77 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X448.tentative.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_X448.tentative.https.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 64 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 104 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: generateKey(X448, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(X448, true, [deriveKey, encrypt])
@@ -60,6 +60,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: X448}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, encrypt])
@@ -120,6 +160,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(X448, false, [])
   assert_equals: Empty usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(X448, true, [])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_bad_algorithm.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_bad_algorithm.https.any.js
new file mode 100644
index 0000000..5fe0b15
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_bad_algorithm.https.any.js
@@ -0,0 +1,5 @@
+// META: title=WebCryptoAPI: generateKey() for Failures
+// META: timeout=long
+// META: script=../util/helpers.js
+// META: script=failures.js
+run_bad_algorithm_test();
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_chacha20_poly1305.tentative.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_chacha20_poly1305.tentative.https.any-expected.txt
index b6ed0de..7a8ecb9 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_chacha20_poly1305.tentative.https.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_chacha20_poly1305.tentative.https.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 140 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 276 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [sign])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, sign])
@@ -136,6 +136,142 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [sign])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, sign])
@@ -272,6 +408,142 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(ChaCha20-Poly1305, false, [])
   assert_equals: Empty usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(ChaCha20-Poly1305, true, [])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_chacha20_poly1305.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_chacha20_poly1305.tentative.https.any.js
index c9a278cd..6ec0593 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_chacha20_poly1305.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_chacha20_poly1305.tentative.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["ChaCha20-Poly1305"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_chacha20_poly1305.tentative.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_chacha20_poly1305.tentative.https.any.worker-expected.txt
index b6ed0de..7a8ecb9 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_chacha20_poly1305.tentative.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_chacha20_poly1305.tentative.https.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 140 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 276 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [sign])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, sign])
@@ -136,6 +136,142 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey(ChaCha20-Poly1305, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [sign])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, sign])
@@ -272,6 +408,142 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, encrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, encrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, encrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, encrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decrypt, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, wrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({name: ChaCha20-Poly1305}, true, [encrypt, decrypt, wrapKey, unwrapKey, encrypt, decrypt, wrapKey, unwrapKey, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(ChaCha20-Poly1305, false, [])
   assert_equals: Empty usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey(ChaCha20-Poly1305, true, [])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_kmac.tentative.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_kmac.tentative.https.any-expected.txt
index 22b751ed..f354bb5 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_kmac.tentative.https.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_kmac.tentative.https.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 192 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 312 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, encrypt])
@@ -60,6 +60,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, verify, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, verify, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, verify, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, verify, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, verify, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, encrypt])
@@ -120,6 +160,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, verify, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, verify, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, verify, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, verify, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, verify, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, encrypt])
@@ -180,6 +260,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, verify, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, verify, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, verify, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, verify, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, verify, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, encrypt])
@@ -240,6 +360,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, verify, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, verify, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, verify, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, verify, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, verify, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, encrypt])
@@ -300,6 +460,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, verify, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, verify, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, verify, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, verify, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, verify, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, encrypt])
@@ -360,6 +560,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, verify, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, verify, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, verify, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, verify, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, verify, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey({length: 128, name: KMAC128}, false, [])
   assert_equals: Empty usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey({length: 128, name: KMAC128}, true, [])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_kmac.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_kmac.tentative.https.any.js
index f906038..889c17e9 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_kmac.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_kmac.tentative.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCryptoAPI: generateKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=algorithm_registry.js
 // META: script=failures.js
 run_test(["KMAC128", "KMAC256"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_kmac.tentative.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_kmac.tentative.https.any.worker-expected.txt
index 22b751ed..f354bb5 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_kmac.tentative.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/failures_kmac.tentative.https.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 192 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 312 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, encrypt])
@@ -60,6 +60,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, verify, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, verify, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, verify, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, verify, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC128}, true, [sign, verify, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, encrypt])
@@ -120,6 +160,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, verify, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, verify, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, verify, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, verify, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC128}, true, [sign, verify, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, encrypt])
@@ -180,6 +260,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, verify, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, verify, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, verify, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, verify, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC128}, true, [sign, verify, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, encrypt])
@@ -240,6 +360,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, verify, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, verify, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, verify, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, verify, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 128, name: KMAC256}, true, [sign, verify, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, encrypt])
@@ -300,6 +460,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, verify, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, verify, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, verify, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, verify, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 160, name: KMAC256}, true, [sign, verify, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [encrypt])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, encrypt])
@@ -360,6 +560,46 @@
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, verify, sign, verify, deriveBits])
   assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, sign, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, verify, sign, verify, encapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, sign, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, verify, sign, verify, encapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, sign, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, verify, sign, verify, decapsulateKey])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, sign, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: generateKey({length: 256, name: KMAC256}, true, [sign, verify, sign, verify, decapsulateBits])
+  assert_equals: Bad usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey({length: 128, name: KMAC128}, false, [])
   assert_equals: Empty usages not supported expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: generateKey({length: 128, name: KMAC128}, true, [])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes.js
index c72384b7..5006cd35 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes.js
@@ -2,8 +2,6 @@
 function run_test(algorithmNames, slowTest) {
     var subtle = crypto.subtle; // Change to test prefixed implementations
 
-    setup({explicit_timeout: true});
-
 // These tests check that generateKey successfully creates keys
 // when provided any of a wide set of correct parameters
 // and that they can be exported afterwards.
@@ -17,42 +15,7 @@
 // helper functions that generate all possible test parameters for
 // different situations.
 
-    var allTestVectors = [ // Parameters that should work for generateKey
-        {name: "AES-CTR",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
-        {name: "AES-CBC",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
-        {name: "AES-GCM",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
-        {name: "AES-OCB",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
-        {name: "ChaCha20-Poly1305",  resultType: CryptoKey, usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: []},
-        {name: "AES-KW",   resultType: CryptoKey, usages: ["wrapKey", "unwrapKey"], mandatoryUsages: []},
-        {name: "HMAC",     resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []},
-        {name: "RSASSA-PKCS1-v1_5", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "RSA-PSS",  resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "RSA-OAEP", resultType: "CryptoKeyPair", usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"], mandatoryUsages: ["decrypt", "unwrapKey"]},
-        {name: "ECDSA",    resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "ECDH",     resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
-        {name: "Ed25519",  resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "Ed448",    resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "ML-DSA-44", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "ML-DSA-65", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "ML-DSA-87", resultType: "CryptoKeyPair", usages: ["sign", "verify"], mandatoryUsages: ["sign"]},
-        {name: "ML-KEM-512", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]},
-        {name: "ML-KEM-768", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]},
-        {name: "ML-KEM-1024", resultType: "CryptoKeyPair", usages: ["decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"], mandatoryUsages: ["decapsulateBits", "decapsulateKey"]},
-        {name: "X25519",   resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
-        {name: "X448",     resultType: "CryptoKeyPair", usages: ["deriveKey", "deriveBits"], mandatoryUsages: ["deriveKey", "deriveBits"]},
-        {name: "KMAC128",  resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []},
-        {name: "KMAC256",  resultType: CryptoKey, usages: ["sign", "verify"], mandatoryUsages: []},
-    ];
-
-    var testVectors = [];
-    if (algorithmNames && !Array.isArray(algorithmNames)) {
-        algorithmNames = [algorithmNames];
-    };
-    allTestVectors.forEach(function(vector) {
-        if (!algorithmNames || algorithmNames.includes(vector.name)) {
-            testVectors.push(vector);
-        }
-    });
+    var testVectors = getGenerateKeyTestVectors(algorithmNames);
 
     function parameterString(algorithm, extractable, usages) {
         var result = "(" +
@@ -93,6 +56,7 @@
                     ];
                     if (extractable)
                         promises.push(subtle.exportKey('raw-seed', result.privateKey));
+                    await Promise.all(promises);
                 } else if (resultType === "CryptoKeyPair") {
                     const promises = [
                         subtle.exportKey('jwk', result.publicKey),
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-CBC.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-CBC.https.any.js
index 80f92c2..b2e1b29 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-CBC.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-CBC.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["AES-CBC"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-CTR.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-CTR.https.any.js
index 243a104b..bec9e22f3 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-CTR.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-CTR.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["AES-CTR"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-GCM.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-GCM.https.any.js
index f0f947c8..df6e694 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-GCM.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-GCM.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["AES-GCM"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-KW.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-KW.https.any.js
index dbc040fd..e55b247 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-KW.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-KW.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["AES-KW"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-OCB.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-OCB.tentative.https.any.js
index b43abc4..c87f875f 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-OCB.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_AES-OCB.tentative.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["AES-OCB"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ECDH.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ECDH.https.any.js
index e9dee52..9ec44a3 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ECDH.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ECDH.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["ECDH"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ECDSA.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ECDSA.https.any.js
index a022f31..9b0f40a 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ECDSA.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ECDSA.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["ECDSA"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_Ed25519.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_Ed25519.https.any.js
index 6b3bc46..8ecbbd2 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_Ed25519.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_Ed25519.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["Ed25519"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_Ed448.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_Ed448.tentative.https.any.js
index 8e37f57..d017170 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_Ed448.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_Ed448.tentative.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["Ed448"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_HMAC.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_HMAC.https.any.js
index 18e0b271..59fab83 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_HMAC.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_HMAC.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["HMAC"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ML-DSA.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ML-DSA.tentative.https.any.js
index 15d52f2..6c81b193 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ML-DSA.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ML-DSA.tentative.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["ML-DSA-44", "ML-DSA-65", "ML-DSA-87"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ML-KEM.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ML-KEM.tentative.https.any.js
index 68a0d97..f41b5c34 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ML-KEM.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_ML-KEM.tentative.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["ML-KEM-512", "ML-KEM-768", "ML-KEM-1024"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_RSA-OAEP.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_RSA-OAEP.https.any.js
index d933fd98..f7c550d 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_RSA-OAEP.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_RSA-OAEP.https.any.js
@@ -18,5 +18,6 @@
 // META: variant=?151-last
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["RSA-OAEP"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_RSA-PSS.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_RSA-PSS.https.any.js
index cb43e3d..d1a9ec33 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_RSA-PSS.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_RSA-PSS.https.any.js
@@ -6,5 +6,6 @@
 // META: variant=?31-last
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["RSA-PSS"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_RSASSA-PKCS1-v1_5.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_RSASSA-PKCS1-v1_5.https.any.js
index b8db5972..6db5a37 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_RSASSA-PKCS1-v1_5.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_RSASSA-PKCS1-v1_5.https.any.js
@@ -6,5 +6,6 @@
 // META: variant=?31-last
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["RSASSA-PKCS1-v1_5"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_X25519.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_X25519.https.any.js
index 0e87cf5..5acd5b5 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_X25519.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_X25519.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["X25519"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_X448.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_X448.tentative.https.any.js
index e7dbe32..f1475c1 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_X448.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_X448.tentative.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["X448"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_chacha20_poly1305.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_chacha20_poly1305.tentative.https.any.js
index a1cef25..2f3efeb 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_chacha20_poly1305.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_chacha20_poly1305.tentative.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["ChaCha20-Poly1305"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_kmac.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_kmac.tentative.https.any.js
index 9f881f9..8276fa9 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_kmac.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/generateKey/successes_kmac.tentative.https.any.js
@@ -2,5 +2,6 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=/common/subset-tests.js
+// META: script=algorithm_registry.js
 // META: script=successes.js
 run_test(["KMAC128", "KMAC256"]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/getRandomValues.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/getRandomValues.any.js
index aecd38e..8fd18a8 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/getRandomValues.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/getRandomValues.any.js
@@ -66,7 +66,7 @@
     }, "Large length: " + array);
 
     test(function() {
-        assert_true(self.crypto.getRandomValues(new ctor(0)).length == 0)
+        assert_true(self.crypto.getRandomValues(new ctor(0)).length === 0)
     }, "Null arrays: " + array);
 
     test(function() {
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-DSA_importKey.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-DSA_importKey.tentative.https.any.js
index 5ce13d8..02f745d 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-DSA_importKey.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-DSA_importKey.tentative.https.any.js
@@ -1,8 +1,9 @@
 // META: title=WebCryptoAPI: importKey() for ML-DSA keys
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=../util/mldsa_key_fixtures.js
 // META: script=ML-DSA_importKey_fixtures.js
-// META: script=ML-DSA_importKey.js
+// META: script=ml_importKey.js
 
 runTests("ML-DSA-44");
 runTests("ML-DSA-65");
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-DSA_importKey_fixtures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-DSA_importKey_fixtures.js
index ef1f5a1..abccc10c 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-DSA_importKey_fixtures.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-DSA_importKey_fixtures.js
@@ -1,99 +1,11 @@
+var mldsaKeyFixtures = getMldsaKeyFixtures();
+
 var keyData = {
   'ML-DSA-44': {
     privateUsages: ['sign'],
     publicUsages: ['verify'],
-    pkcs8: new Uint8Array([
-      48, 52, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 17, 4, 34,
-      128, 32, 153, 21, 95, 99, 48, 150, 218, 124, 190, 8, 122, 137, 72, 184,
-      79, 118, 123, 16, 249, 1, 200, 35, 194, 64, 177, 221, 43, 200, 112, 5,
-      201, 62,
-    ]),
-    spki: new Uint8Array([
-      48, 130, 5, 50, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 17, 3, 130, 5,
-      33, 0, 85, 152, 213, 68, 198, 20, 177, 84, 200, 188, 33, 104, 36, 161,
-      193, 127, 226, 151, 48, 107, 122, 191, 89, 86, 188, 3, 245, 199, 18, 79,
-      168, 192, 218, 218, 223, 227, 84, 133, 41, 187, 9, 183, 14, 222, 155, 137,
-      84, 111, 138, 97, 234, 152, 52, 15, 163, 47, 227, 218, 19, 20, 229, 93,
-      99, 252, 155, 213, 30, 241, 158, 211, 213, 191, 155, 73, 211, 145, 59,
-      194, 75, 140, 3, 161, 149, 139, 48, 172, 155, 172, 120, 234, 142, 79, 78,
-      116, 215, 184, 50, 182, 162, 192, 93, 221, 179, 216, 70, 186, 92, 93, 51,
-      190, 61, 173, 105, 206, 9, 208, 52, 54, 208, 252, 115, 176, 146, 118, 23,
-      0, 5, 41, 151, 121, 241, 246, 193, 206, 212, 160, 181, 7, 99, 220, 78, 96,
-      123, 142, 35, 107, 37, 218, 138, 139, 228, 207, 166, 132, 87, 36, 116,
-      251, 174, 10, 41, 196, 171, 26, 212, 90, 213, 223, 141, 87, 232, 59, 46,
-      173, 16, 247, 173, 4, 219, 165, 234, 8, 217, 233, 163, 13, 10, 160, 22,
-      87, 232, 2, 9, 92, 177, 35, 67, 55, 104, 51, 204, 231, 81, 209, 62, 31,
-      117, 56, 141, 78, 109, 148, 118, 191, 93, 193, 59, 30, 88, 237, 19, 169,
-      14, 11, 150, 102, 215, 144, 69, 246, 32, 159, 110, 49, 75, 78, 225, 121,
-      184, 206, 234, 126, 171, 164, 43, 237, 244, 63, 83, 70, 136, 213, 244,
-      121, 184, 34, 201, 249, 95, 64, 165, 99, 14, 195, 231, 18, 121, 20, 156,
-      80, 231, 133, 197, 0, 94, 195, 22, 251, 255, 174, 213, 103, 75, 88, 58,
-      138, 66, 37, 85, 162, 15, 242, 29, 49, 147, 191, 163, 170, 109, 195, 22,
-      63, 197, 149, 117, 100, 36, 81, 115, 203, 126, 62, 86, 103, 193, 182, 69,
-      238, 233, 162, 62, 248, 132, 158, 212, 208, 187, 127, 212, 198, 169, 245,
-      26, 153, 28, 207, 85, 65, 145, 200, 23, 196, 38, 64, 49, 54, 233, 185, 72,
-      156, 86, 230, 127, 152, 108, 184, 240, 109, 8, 103, 228, 206, 142, 73, 74,
-      214, 52, 182, 203, 82, 201, 175, 188, 111, 42, 232, 8, 177, 131, 47, 237,
-      92, 120, 210, 124, 48, 185, 215, 137, 36, 70, 162, 164, 119, 0, 155, 20,
-      57, 33, 27, 220, 77, 254, 233, 190, 106, 135, 21, 194, 57, 238, 95, 162,
-      78, 164, 33, 210, 215, 43, 100, 96, 232, 87, 19, 43, 65, 240, 183, 17,
-      131, 247, 97, 241, 7, 111, 159, 40, 252, 45, 228, 76, 71, 46, 85, 181, 54,
-      41, 135, 162, 144, 227, 128, 121, 173, 207, 119, 54, 19, 197, 23, 231,
-      176, 125, 190, 32, 76, 60, 34, 24, 160, 42, 73, 190, 124, 99, 5, 9, 214,
-      6, 220, 177, 206, 79, 222, 10, 142, 250, 63, 179, 67, 21, 159, 57, 126,
-      239, 28, 238, 240, 107, 79, 185, 174, 232, 168, 146, 128, 229, 119, 19,
-      21, 33, 239, 193, 42, 178, 152, 124, 24, 203, 131, 193, 93, 162, 2, 208,
-      231, 20, 203, 232, 47, 54, 114, 255, 236, 97, 99, 156, 15, 160, 75, 60,
-      111, 59, 35, 25, 230, 43, 91, 170, 161, 74, 70, 179, 180, 251, 71, 197,
-      240, 104, 42, 39, 202, 206, 12, 115, 249, 138, 55, 252, 216, 61, 185, 121,
-      76, 137, 172, 166, 88, 92, 130, 18, 52, 67, 13, 187, 49, 147, 212, 77, 74,
-      56, 59, 123, 99, 205, 36, 137, 96, 111, 148, 121, 157, 2, 83, 246, 86,
-      156, 152, 98, 172, 77, 244, 214, 225, 184, 240, 121, 144, 56, 213, 161,
-      43, 67, 4, 161, 104, 202, 91, 134, 247, 108, 24, 46, 187, 63, 216, 50,
-      125, 120, 17, 216, 22, 228, 156, 253, 7, 180, 15, 130, 180, 72, 71, 169,
-      3, 236, 247, 11, 170, 32, 76, 236, 225, 133, 250, 20, 235, 200, 143, 89,
-      25, 158, 49, 158, 164, 213, 221, 81, 120, 241, 150, 210, 19, 79, 165, 24,
-      35, 170, 182, 242, 255, 143, 171, 148, 140, 41, 207, 186, 98, 224, 16,
-      152, 224, 38, 110, 175, 169, 111, 132, 201, 178, 114, 25, 196, 50, 149,
-      158, 193, 180, 101, 183, 92, 109, 131, 102, 119, 123, 78, 107, 223, 4, 1,
-      206, 140, 130, 237, 205, 36, 18, 180, 197, 154, 26, 236, 140, 173, 230,
-      101, 35, 189, 121, 104, 21, 75, 81, 224, 186, 212, 81, 107, 66, 244, 235,
-      64, 90, 206, 39, 44, 43, 162, 187, 63, 229, 217, 154, 185, 157, 24, 125,
-      252, 91, 136, 59, 47, 182, 200, 73, 19, 137, 132, 81, 16, 234, 227, 210,
-      32, 16, 160, 188, 250, 27, 190, 164, 53, 244, 219, 199, 177, 146, 117, 50,
-      99, 72, 235, 37, 154, 72, 51, 203, 61, 39, 230, 34, 132, 117, 217, 167,
-      201, 42, 17, 76, 72, 103, 172, 93, 169, 29, 76, 88, 178, 226, 32, 53, 190,
-      60, 210, 132, 113, 198, 26, 70, 179, 47, 34, 184, 88, 178, 208, 1, 196,
-      89, 136, 167, 33, 38, 9, 255, 89, 202, 27, 93, 229, 100, 192, 24, 234,
-      200, 186, 125, 231, 212, 188, 11, 29, 51, 189, 70, 147, 176, 231, 81, 16,
-      114, 152, 159, 21, 124, 185, 208, 50, 74, 211, 113, 207, 35, 54, 173, 205,
-      133, 52, 167, 199, 87, 158, 120, 33, 204, 163, 146, 233, 21, 61, 28, 102,
-      48, 232, 184, 15, 219, 238, 240, 215, 222, 239, 3, 110, 180, 95, 103, 147,
-      236, 10, 57, 195, 159, 231, 50, 92, 145, 165, 44, 204, 121, 187, 9, 210,
-      80, 86, 251, 169, 132, 236, 248, 23, 207, 222, 227, 13, 53, 1, 88, 69,
-      105, 13, 238, 202, 251, 194, 245, 14, 38, 1, 245, 157, 212, 162, 182, 217,
-      230, 115, 139, 175, 219, 199, 74, 124, 186, 158, 3, 220, 87, 220, 177, 85,
-      13, 58, 168, 223, 6, 238, 156, 80, 121, 44, 166, 176, 0, 48, 98, 70, 93,
-      78, 50, 192, 16, 186, 2, 233, 105, 105, 120, 195, 17, 100, 141, 214, 158,
-      144, 63, 4, 88, 190, 101, 209, 55, 119, 46, 128, 117, 225, 121, 204, 195,
-      19, 61, 170, 116, 251, 225, 230, 28, 27, 249, 84, 195, 224, 190, 60, 212,
-      83, 3, 148, 204, 4, 103, 50, 233, 175, 207, 47, 51, 216, 79, 251, 150, 81,
-      171, 7, 145, 55, 188, 187, 217, 200, 155, 246, 85, 42, 123, 18, 112, 192,
-      116, 163, 40, 187, 132, 192, 210, 188, 106, 117, 217, 185, 183, 202, 33,
-      11, 205, 10, 61, 52, 15, 66, 131, 121, 112, 26, 96, 166, 241, 1, 68, 206,
-      80, 92, 132, 83, 89, 126, 135, 157, 4, 202, 16, 5, 131, 112, 62, 56, 234,
-      176, 213, 119, 205, 203, 17, 106, 156, 117, 251, 135, 21, 73, 219, 238, 3,
-      88, 21, 71, 136, 118, 0, 7, 106, 151, 200, 221, 179, 206, 50, 198, 27,
-      118, 181, 27, 85, 35, 248, 44, 57, 15, 221, 97, 121, 4, 167, 111, 182,
-      207, 195, 52, 134, 195, 128, 173, 111, 98, 152, 29, 138, 197, 175, 171,
-      247, 194, 20, 134, 209, 232, 94, 17, 203, 139, 30, 237, 187, 243, 16, 180,
-      43, 105, 236, 66, 175, 170, 139, 24, 99, 28, 225, 141, 96, 250, 119, 0,
-      111, 212, 34, 217, 42, 134, 88, 78, 76, 126, 169, 168, 59, 154, 93, 54,
-      43, 161, 29, 111, 124, 59, 225, 52, 86, 147, 38, 151, 161, 36, 119, 204,
-      164, 121, 46, 186, 65, 84, 70, 38, 15, 203, 48, 168, 235, 231, 30, 55, 95,
-      36, 10, 20, 166, 109, 8, 18, 109, 251, 213, 82, 142, 240, 49, 249, 180,
-      78, 69,
-    ]),
+    pkcs8: mldsaKeyFixtures.pkcs8['ML-DSA-44'],
+    spki: mldsaKeyFixtures.spki['ML-DSA-44'],
     'raw-public': hexStringToUint8Array(
       '5598d544c614b154c8bc216824a1c17fe297306b7abf5956bc03f5c7124fa8c0dadadfe3548529bb09b70ede9b89546f8a61ea98340fa32fe3da1314e55d63fc9bd51ef19ed3d5bf9b49d3913bc24b8c03a1958b30ac9bac78ea8e4f4e74d7b832b6a2c05dddb3d846ba5c5d33be3dad69ce09d03436d0fc73b09276170005299779f1f6c1ced4a0b50763dc4e607b8e236b25da8a8be4cfa684572474fbae0a29c4ab1ad45ad5df8d57e83b2ead10f7ad04dba5ea08d9e9a30d0aa01657e802095cb12343376833cce751d13e1f75388d4e6d9476bf5dc13b1e58ed13a90e0b9666d79045f6209f6e314b4ee179b8ceea7eaba42bedf43f534688d5f479b822c9f95f40a5630ec3e71279149c50e785c5005ec316fbffaed5674b583a8a422555a20ff21d3193bfa3aa6dc3163fc5957564245173cb7e3e5667c1b645eee9a23ef8849ed4d0bb7fd4c6a9f51a991ccf554191c817c426403136e9b9489c56e67f986cb8f06d0867e4ce8e494ad634b6cb52c9afbc6f2ae808b1832fed5c78d27c30b9d7892446a2a477009b1439211bdc4dfee9be6a8715c239ee5fa24ea421d2d72b6460e857132b41f0b71183f761f1076f9f28fc2de44c472e55b5362987a290e38079adcf773613c517e7b07dbe204c3c2218a02a49be7c630509d606dcb1ce4fde0a8efa3fb343159f397eef1ceef06b4fb9aee8a89280e577131521efc12ab2987c18cb83c15da202d0e714cbe82f3672ffec61639c0fa04b3c6f3b2319e62b5baaa14a46b3b4fb47c5f0682a27cace0c73f98a37fcd83db9794c89aca6585c821234430dbb3193d44d4a383b7b63cd2489606f94799d0253f6569c9862ac4df4d6e1b8f0799038d5a12b4304a168ca5b86f76c182ebb3fd8327d7811d816e49cfd07b40f82b44847a903ecf70baa204cece185fa14ebc88f59199e319ea4d5dd5178f196d2134fa51823aab6f2ff8fab948c29cfba62e01098e0266eafa96f84c9b27219c432959ec1b465b75c6d8366777b4e6bdf0401ce8c82edcd2412b4c59a1aec8cade66523bd7968154b51e0bad4516b42f4eb405ace272c2ba2bb3fe5d99ab99d187dfc5b883b2fb6c8491389845110eae3d22010a0bcfa1bbea435f4dbc7b19275326348eb259a4833cb3d27e6228475d9a7c92a114c4867ac5da91d4c58b2e22035be3cd28471c61a46b32f22b858b2d001c45988a7212609ff59ca1b5de564c018eac8ba7de7d4bc0b1d33bd4693b0e7511072989f157cb9d0324ad371cf2336adcd8534a7c7579e7821cca392e9153d1c6630e8b80fdbeef0d7deef036eb45f6793ec0a39c39fe7325c91a52ccc79bb09d25056fba984ecf817cfdee30d35015845690deecafbc2f50e2601f59dd4a2b6d9e6738bafdbc74a7cba9e03dc57dcb1550d3aa8df06ee9c50792ca6b0003062465d4e32c010ba02e9696978c311648dd69e903f0458be65d137772e8075e179ccc3133daa74fbe1e61c1bf954c3e0be3cd4530394cc046732e9afcf2f33d84ffb9651ab079137bcbbd9c89bf6552a7b1270c074a328bb84c0d2bc6a75d9b9b7ca210bcd0a3d340f428379701a60a6f10144ce505c8453597e879d04ca100583703e38eab0d577cdcb116a9c75fb871549dbee03581547887600076a97c8ddb3ce32c61b76b51b5523f82c390fdd617904a76fb6cfc33486c380ad6f62981d8ac5afabf7c21486d1e85e11cb8b1eedbbf310b42b69ec42afaa8b18631ce18d60fa77006fd422d92a86584e4c7ea9a83b9a5d362ba11d6f7c3be13456932697a12477cca4792eba415446260fcb30a8ebe71e375f240a14a66d08126dfbd5528ef031f9b44e45'
     ),
@@ -111,138 +23,8 @@
   'ML-DSA-65': {
     privateUsages: ['sign'],
     publicUsages: ['verify'],
-    pkcs8: new Uint8Array([
-      48, 52, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 18, 4, 34,
-      128, 32, 132, 164, 137, 75, 123, 70, 164, 178, 3, 156, 206, 16, 195, 26,
-      133, 186, 176, 195, 102, 48, 254, 35, 29, 66, 103, 17, 67, 152, 38, 7,
-      130, 139,
-    ]),
-    spki: new Uint8Array([
-      48, 130, 7, 178, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 18, 3, 130,
-      7, 161, 0, 216, 177, 233, 60, 151, 24, 246, 66, 175, 161, 192, 118, 9,
-      177, 87, 232, 5, 216, 49, 254, 251, 160, 51, 216, 250, 171, 229, 104, 149,
-      117, 135, 107, 5, 239, 244, 167, 83, 57, 49, 153, 29, 135, 108, 138, 117,
-      236, 120, 65, 89, 65, 17, 201, 127, 147, 246, 103, 238, 204, 25, 83, 240,
-      29, 173, 127, 140, 1, 88, 40, 43, 134, 51, 19, 125, 131, 224, 50, 167, 82,
-      20, 217, 215, 162, 41, 95, 215, 56, 130, 226, 177, 216, 20, 169, 133, 85,
-      140, 9, 105, 172, 54, 121, 0, 87, 202, 18, 93, 96, 147, 103, 127, 164, 63,
-      197, 97, 49, 230, 125, 53, 80, 64, 20, 239, 29, 90, 118, 102, 221, 177, 6,
-      220, 220, 170, 107, 107, 18, 138, 125, 138, 70, 66, 60, 185, 232, 209, 4,
-      100, 17, 118, 24, 181, 110, 241, 50, 250, 138, 209, 19, 84, 220, 61, 96,
-      80, 128, 81, 101, 160, 9, 5, 53, 33, 112, 60, 59, 195, 79, 115, 190, 101,
-      93, 56, 171, 146, 51, 82, 233, 66, 225, 226, 211, 72, 58, 9, 74, 222, 185,
-      175, 211, 156, 164, 68, 188, 119, 46, 14, 64, 160, 13, 207, 60, 81, 68,
-      108, 148, 59, 88, 88, 92, 88, 171, 249, 204, 160, 137, 222, 234, 168, 233,
-      3, 86, 224, 174, 105, 224, 1, 177, 184, 230, 236, 30, 85, 249, 56, 105,
-      211, 50, 83, 168, 139, 28, 175, 181, 22, 89, 166, 215, 152, 218, 184, 111,
-      65, 24, 82, 71, 185, 100, 172, 66, 178, 6, 215, 164, 252, 56, 40, 198,
-      207, 152, 110, 186, 207, 39, 235, 179, 38, 215, 88, 105, 109, 177, 130,
-      91, 5, 223, 164, 78, 241, 176, 125, 197, 94, 100, 40, 214, 251, 239, 253,
-      88, 154, 14, 75, 254, 94, 210, 86, 244, 51, 95, 168, 139, 213, 73, 87,
-      151, 209, 150, 126, 88, 69, 251, 116, 157, 122, 96, 212, 206, 42, 218, 38,
-      85, 184, 50, 246, 188, 35, 72, 60, 226, 155, 246, 86, 209, 110, 133, 161,
-      129, 126, 80, 154, 238, 64, 64, 170, 156, 177, 225, 52, 87, 42, 88, 154,
-      174, 246, 30, 84, 143, 221, 51, 27, 65, 60, 40, 22, 120, 42, 145, 34, 82,
-      235, 79, 20, 215, 211, 231, 151, 108, 119, 53, 116, 190, 13, 139, 199,
-      189, 84, 54, 186, 222, 90, 116, 253, 227, 207, 104, 209, 118, 42, 144, 94,
-      22, 219, 198, 211, 216, 171, 75, 245, 61, 240, 157, 108, 81, 19, 238, 39,
-      120, 210, 242, 63, 126, 6, 15, 18, 97, 232, 207, 184, 84, 239, 72, 115,
-      90, 95, 112, 78, 45, 201, 13, 102, 208, 151, 146, 253, 153, 177, 115, 14,
-      194, 184, 107, 118, 120, 63, 105, 89, 144, 60, 242, 80, 106, 32, 11, 204,
-      170, 165, 221, 182, 31, 187, 159, 216, 118, 218, 207, 182, 107, 136, 211,
-      162, 231, 196, 128, 83, 251, 192, 189, 149, 1, 245, 36, 146, 159, 11, 116,
-      30, 191, 141, 182, 157, 227, 91, 88, 72, 224, 55, 195, 96, 245, 135, 249,
-      142, 16, 229, 237, 27, 60, 2, 91, 138, 82, 197, 210, 40, 8, 123, 172, 202,
-      207, 119, 199, 190, 102, 40, 141, 174, 93, 211, 219, 134, 105, 26, 103,
-      100, 102, 62, 130, 224, 135, 173, 82, 224, 200, 213, 76, 211, 38, 181, 16,
-      97, 99, 75, 14, 42, 162, 223, 237, 141, 1, 29, 140, 191, 146, 49, 236,
-      179, 88, 202, 220, 124, 239, 231, 131, 101, 134, 111, 28, 54, 151, 172,
-      67, 167, 113, 96, 204, 63, 76, 169, 95, 14, 175, 149, 189, 173, 39, 177,
-      102, 23, 158, 102, 236, 224, 70, 203, 190, 132, 230, 136, 3, 28, 201, 131,
-      148, 164, 219, 181, 208, 26, 239, 111, 86, 35, 102, 133, 114, 113, 180,
-      184, 82, 180, 106, 124, 248, 126, 87, 177, 165, 176, 204, 128, 141, 179,
-      141, 34, 119, 160, 57, 206, 168, 95, 197, 110, 79, 60, 48, 170, 125, 206,
-      147, 137, 156, 85, 46, 7, 193, 228, 150, 22, 46, 147, 51, 153, 157, 248,
-      151, 253, 114, 58, 126, 38, 127, 19, 225, 99, 183, 235, 175, 90, 166, 8,
-      233, 141, 10, 85, 232, 147, 7, 20, 137, 240, 84, 231, 86, 134, 50, 212,
-      201, 6, 253, 18, 150, 114, 160, 140, 2, 254, 189, 152, 14, 68, 110, 50,
-      134, 199, 73, 237, 56, 116, 110, 135, 36, 40, 14, 167, 50, 71, 81, 9, 178,
-      54, 182, 247, 64, 32, 204, 116, 92, 131, 40, 30, 246, 188, 236, 182, 187,
-      132, 239, 124, 136, 238, 146, 137, 247, 87, 52, 15, 78, 108, 135, 178,
-      101, 70, 199, 193, 192, 144, 196, 106, 186, 141, 42, 101, 214, 196, 67,
-      175, 38, 8, 189, 148, 166, 253, 221, 144, 119, 81, 1, 232, 175, 81, 237,
-      13, 188, 220, 230, 47, 115, 184, 179, 0, 51, 118, 39, 22, 114, 232, 88,
-      15, 121, 216, 130, 107, 173, 108, 175, 225, 113, 40, 223, 185, 6, 244,
-      227, 73, 38, 64, 84, 10, 200, 53, 81, 179, 217, 106, 172, 59, 161, 70,
-      180, 70, 48, 56, 46, 233, 133, 245, 57, 120, 154, 167, 49, 109, 188, 152,
-      245, 181, 1, 159, 247, 44, 167, 152, 6, 191, 246, 38, 120, 141, 57, 241,
-      168, 60, 38, 6, 82, 248, 87, 85, 0, 10, 22, 19, 44, 178, 63, 38, 78, 1,
-      210, 166, 140, 3, 199, 112, 135, 155, 36, 13, 204, 124, 47, 5, 190, 103,
-      91, 205, 147, 248, 115, 177, 196, 180, 234, 85, 35, 24, 182, 16, 207, 107,
-      167, 10, 54, 193, 146, 71, 95, 45, 109, 184, 43, 101, 155, 184, 55, 171,
-      196, 136, 89, 227, 84, 183, 31, 0, 137, 230, 146, 250, 6, 27, 225, 241,
-      11, 95, 124, 248, 133, 171, 149, 67, 73, 19, 110, 104, 173, 86, 6, 246,
-      195, 196, 200, 175, 53, 18, 198, 223, 140, 85, 208, 253, 204, 220, 255,
-      232, 88, 238, 20, 97, 24, 52, 13, 3, 179, 62, 151, 101, 67, 33, 79, 253,
-      157, 253, 233, 197, 109, 130, 78, 11, 165, 214, 200, 41, 157, 63, 46, 175,
-      251, 246, 225, 227, 199, 92, 107, 216, 58, 124, 226, 202, 153, 92, 31,
-      250, 182, 92, 99, 0, 110, 18, 78, 228, 166, 190, 67, 88, 245, 155, 139,
-      82, 152, 39, 204, 74, 140, 95, 82, 82, 160, 147, 144, 33, 0, 242, 200, 22,
-      102, 160, 233, 102, 224, 42, 205, 240, 56, 95, 197, 226, 175, 235, 240,
-      125, 10, 51, 12, 246, 150, 234, 173, 95, 132, 173, 174, 1, 161, 23, 39,
-      52, 168, 87, 54, 4, 66, 201, 34, 155, 82, 133, 170, 76, 52, 226, 109, 163,
-      19, 34, 184, 226, 39, 20, 75, 72, 201, 70, 188, 71, 182, 230, 6, 19, 255,
-      8, 145, 193, 63, 81, 150, 24, 72, 89, 168, 74, 98, 173, 133, 67, 227, 44,
-      252, 252, 227, 192, 125, 20, 227, 144, 24, 31, 48, 67, 67, 48, 94, 163,
-      52, 219, 163, 225, 214, 214, 109, 211, 122, 213, 198, 90, 204, 40, 97,
-      211, 121, 17, 28, 132, 246, 110, 230, 51, 197, 42, 162, 143, 199, 158,
-      215, 210, 133, 60, 65, 127, 1, 153, 193, 22, 171, 250, 114, 204, 246, 255,
-      126, 25, 96, 44, 164, 102, 172, 211, 23, 245, 122, 48, 221, 249, 138, 148,
-      134, 206, 135, 246, 42, 235, 198, 89, 189, 45, 125, 204, 69, 193, 48, 29,
-      144, 125, 224, 127, 66, 1, 134, 141, 224, 211, 193, 141, 69, 128, 75, 167,
-      244, 160, 120, 54, 191, 214, 29, 40, 249, 15, 46, 68, 141, 91, 242, 91,
-      80, 252, 109, 122, 154, 64, 153, 56, 65, 254, 106, 18, 4, 172, 171, 136,
-      80, 98, 79, 133, 255, 4, 100, 191, 144, 171, 219, 46, 132, 181, 130, 228,
-      107, 68, 32, 123, 201, 17, 67, 35, 144, 180, 160, 30, 125, 9, 55, 184,
-      172, 0, 159, 250, 232, 83, 168, 162, 102, 158, 121, 208, 177, 116, 163,
-      160, 80, 241, 46, 156, 58, 203, 240, 67, 176, 244, 170, 160, 115, 122,
-      141, 154, 101, 218, 178, 119, 130, 195, 32, 207, 51, 149, 98, 51, 219, 57,
-      121, 216, 156, 101, 218, 184, 220, 204, 41, 181, 149, 63, 80, 194, 11,
-      143, 164, 219, 23, 123, 141, 119, 43, 94, 78, 175, 89, 165, 48, 167, 44,
-      45, 219, 197, 15, 202, 118, 116, 245, 151, 218, 14, 199, 96, 27, 102, 206,
-      198, 123, 222, 178, 210, 20, 200, 38, 25, 124, 58, 102, 92, 197, 107, 51,
-      5, 236, 125, 173, 198, 113, 144, 108, 177, 22, 104, 223, 165, 39, 9, 84,
-      87, 124, 80, 153, 8, 212, 76, 2, 28, 12, 90, 212, 129, 148, 212, 229, 63,
-      29, 200, 113, 171, 154, 107, 189, 202, 22, 147, 7, 32, 253, 70, 37, 224,
-      98, 199, 129, 21, 54, 49, 52, 124, 84, 40, 190, 194, 108, 73, 26, 15, 124,
-      87, 87, 198, 217, 122, 127, 82, 167, 131, 59, 8, 172, 49, 162, 61, 64, 79,
-      196, 205, 65, 110, 75, 130, 128, 197, 182, 251, 110, 141, 197, 184, 166,
-      244, 246, 217, 20, 105, 85, 42, 80, 251, 77, 59, 204, 247, 179, 218, 181,
-      124, 209, 4, 28, 118, 234, 145, 237, 140, 106, 54, 88, 82, 28, 235, 68,
-      221, 109, 139, 11, 166, 182, 63, 142, 194, 255, 213, 219, 116, 158, 31,
-      224, 119, 126, 232, 160, 144, 1, 177, 92, 219, 162, 49, 181, 116, 163,
-      104, 245, 193, 188, 26, 172, 15, 190, 135, 207, 106, 246, 13, 132, 76,
-      189, 160, 25, 123, 26, 20, 48, 203, 59, 209, 69, 235, 103, 253, 160, 108,
-      83, 206, 70, 98, 0, 2, 57, 162, 202, 63, 45, 89, 173, 201, 254, 254, 253,
-      143, 21, 77, 131, 184, 234, 37, 68, 206, 69, 186, 179, 145, 147, 135, 42,
-      137, 152, 253, 213, 240, 13, 122, 161, 218, 186, 180, 213, 162, 150, 231,
-      63, 112, 182, 233, 86, 12, 225, 195, 133, 37, 28, 22, 147, 201, 200, 197,
-      115, 3, 138, 194, 86, 79, 247, 27, 241, 149, 128, 197, 8, 11, 134, 53,
-      118, 175, 248, 253, 114, 91, 31, 192, 253, 209, 111, 31, 228, 244, 184,
-      179, 146, 145, 167, 137, 155, 184, 218, 38, 62, 187, 22, 181, 193, 93, 16,
-      9, 195, 42, 198, 225, 100, 144, 148, 223, 184, 40, 117, 32, 131, 94, 93,
-      83, 88, 125, 95, 220, 20, 206, 7, 228, 78, 54, 238, 178, 196, 38, 245, 95,
-      8, 235, 106, 17, 175, 142, 193, 60, 179, 53, 244, 92, 147, 244, 218, 95,
-      127, 251, 128, 42, 105, 82, 243, 224, 213, 25, 91, 151, 22, 201, 18, 25,
-      230, 165, 85, 25, 249, 170, 160, 171, 210, 209, 32, 154, 124, 245, 60, 30,
-      255, 138, 154, 29, 85, 156, 232, 177, 78, 14, 137, 52, 215, 247, 26, 211,
-      115, 72, 20, 133, 232, 1, 151, 251, 63, 45, 120, 69, 49, 209, 130, 255, 2,
-      218, 21, 251, 16, 86, 30, 62, 136, 92, 149, 60, 6, 125, 129, 145, 235,
-      102, 190, 144, 248, 1, 53, 135, 21, 158, 44, 158, 230, 246, 172, 249, 161,
-      105, 204, 49, 60, 70, 63, 127, 163, 231, 175, 174, 234, 147, 185, 62, 5,
-      244, 156, 4, 31, 39, 156, 176, 154, 251, 166, 143, 212, 43, 30, 97, 50,
-      37, 176, 155, 77, 149, 102,
-    ]),
+    pkcs8: mldsaKeyFixtures.pkcs8['ML-DSA-65'],
+    spki: mldsaKeyFixtures.spki['ML-DSA-65'],
     'raw-public': hexStringToUint8Array(
       'd8b1e93c9718f642afa1c07609b157e805d831fefba033d8faabe5689575876b05eff4a7533931991d876c8a75ec7841594111c97f93f667eecc1953f01dad7f8c0158282b8633137d83e032a75214d9d7a2295fd73882e2b1d814a985558c0969ac36790057ca125d6093677fa43fc56131e67d35504014ef1d5a7666ddb106dcdcaa6b6b128a7d8a46423cb9e8d10464117618b56ef132fa8ad11354dc3d6050805165a009053521703c3bc34f73be655d38ab923352e942e1e2d3483a094adeb9afd39ca444bc772e0e40a00dcf3c51446c943b58585c58abf9cca089deeaa8e90356e0ae69e001b1b8e6ec1e55f93869d33253a88b1cafb51659a6d798dab86f41185247b964ac42b206d7a4fc3828c6cf986ebacf27ebb326d758696db1825b05dfa44ef1b07dc55e6428d6fbeffd589a0e4bfe5ed256f4335fa88bd5495797d1967e5845fb749d7a60d4ce2ada2655b832f6bc23483ce29bf656d16e85a1817e509aee4040aa9cb1e134572a589aaef61e548fdd331b413c2816782a912252eb4f14d7d3e7976c773574be0d8bc7bd5436bade5a74fde3cf68d1762a905e16dbc6d3d8ab4bf53df09d6c5113ee2778d2f23f7e060f1261e8cfb854ef48735a5f704e2dc90d66d09792fd99b1730ec2b86b76783f6959903cf2506a200bccaaa5ddb61fbb9fd876dacfb66b88d3a2e7c48053fbc0bd9501f524929f0b741ebf8db69de35b5848e037c360f587f98e10e5ed1b3c025b8a52c5d228087baccacf77c7be66288dae5dd3db86691a6764663e82e087ad52e0c8d54cd326b51061634b0e2aa2dfed8d011d8cbf9231ecb358cadc7cefe78365866f1c3697ac43a77160cc3f4ca95f0eaf95bdad27b166179e66ece046cbbe84e688031cc98394a4dbb5d01aef6f562366857271b4b852b46a7cf87e57b1a5b0cc808db38d2277a039cea85fc56e4f3c30aa7dce93899c552e07c1e496162e9333999df897fd723a7e267f13e163b7ebaf5aa608e98d0a55e893071489f054e7568632d4c906fd129672a08c02febd980e446e3286c749ed38746e8724280ea732475109b236b6f74020cc745c83281ef6bcecb6bb84ef7c88ee9289f757340f4e6c87b26546c7c1c090c46aba8d2a65d6c443af2608bd94a6fddd90775101e8af51ed0dbcdce62f73b8b3003376271672e8580f79d8826bad6cafe17128dfb906f4e3492640540ac83551b3d96aac3ba146b44630382ee985f539789aa7316dbc98f5b5019ff72ca79806bff626788d39f1a83c260652f85755000a16132cb23f264e01d2a68c03c770879b240dcc7c2f05be675bcd93f873b1c4b4ea552318b610cf6ba70a36c192475f2d6db82b659bb837abc48859e354b71f0089e692fa061be1f10b5f7cf885ab954349136e68ad5606f6c3c4c8af3512c6df8c55d0fdccdcffe858ee146118340d03b33e976543214ffd9dfde9c56d824e0ba5d6c8299d3f2eaffbf6e1e3c75c6bd83a7ce2ca995c1ffab65c63006e124ee4a6be4358f59b8b529827cc4a8c5f5252a093902100f2c81666a0e966e02acdf0385fc5e2afebf07d0a330cf696eaad5f84adae01a1172734a857360442c9229b5285aa4c34e26da31322b8e227144b48c946bc47b6e60613ff0891c13f5196184859a84a62ad8543e32cfcfce3c07d14e390181f304343305ea334dba3e1d6d66dd37ad5c65acc2861d379111c84f66ee633c52aa28fc79ed7d2853c417f0199c116abfa72ccf6ff7e19602ca466acd317f57a30ddf98a9486ce87f62aebc659bd2d7dcc45c1301d907de07f4201868de0d3c18d45804ba7f4a07836bfd61d28f90f2e448d5bf25b50fc6d7a9a40993841fe6a1204acab8850624f85ff0464bf90abdb2e84b582e46b44207bc911432390b4a01e7d0937b8ac009ffae853a8a2669e79d0b174a3a050f12e9c3acbf043b0f4aaa0737a8d9a65dab27782c320cf33956233db3979d89c65dab8dccc29b5953f50c20b8fa4db177b8d772b5e4eaf59a530a72c2ddbc50fca7674f597da0ec7601b66cec67bdeb2d214c826197c3a665cc56b3305ec7dadc671906cb11668dfa5270954577c509908d44c021c0c5ad48194d4e53f1dc871ab9a6bbdca16930720fd4625e062c781153631347c5428bec26c491a0f7c5757c6d97a7f52a7833b08ac31a23d404fc4cd416e4b8280c5b6fb6e8dc5b8a6f4f6d91469552a50fb4d3bccf7b3dab57cd1041c76ea91ed8c6a3658521ceb44dd6d8b0ba6b63f8ec2ffd5db749e1fe0777ee8a09001b15cdba231b574a368f5c1bc1aac0fbe87cf6af60d844cbda0197b1a1430cb3bd145eb67fda06c53ce4662000239a2ca3f2d59adc9fefefd8f154d83b8ea2544ce45bab39193872a8998fdd5f00d7aa1dabab4d5a296e73f70b6e9560ce1c385251c1693c9c8c573038ac2564ff71bf19580c5080b863576aff8fd725b1fc0fdd16f1fe4f4b8b39291a7899bb8da263ebb16b5c15d1009c32ac6e1649094dfb8287520835e5d53587d5fdc14ce07e44e36eeb2c426f55f08eb6a11af8ec13cb335f45c93f4da5f7ffb802a6952f3e0d5195b9716c91219e6a55519f9aaa0abd2d1209a7cf53c1eff8a9a1d559ce8b14e0e8934d7f71ad373481485e80197fb3f2d784531d182ff02da15fb10561e3e885c953c067d8191eb66be90f8013587159e2c9ee6f6acf9a169cc313c463f7fa3e7afaeea93b93e05f49c041f279cb09afba68fd42b1e613225b09b4d9566'
     ),
@@ -260,178 +42,8 @@
   'ML-DSA-87': {
     privateUsages: ['sign'],
     publicUsages: ['verify'],
-    pkcs8: new Uint8Array([
-      48, 52, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 19, 4, 34,
-      128, 32, 161, 80, 109, 145, 76, 109, 101, 140, 117, 39, 228, 51, 151, 221,
-      109, 76, 37, 246, 164, 121, 116, 51, 90, 76, 208, 59, 254, 105, 131, 68,
-      18, 81,
-    ]),
-    spki: new Uint8Array([
-      48, 130, 10, 50, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 19, 3, 130,
-      10, 33, 0, 146, 184, 67, 40, 172, 27, 204, 27, 135, 22, 109, 223, 127, 23,
-      97, 130, 198, 228, 199, 62, 178, 21, 173, 20, 88, 180, 205, 168, 17, 164,
-      135, 92, 85, 146, 76, 194, 152, 80, 239, 157, 33, 21, 239, 182, 176, 254,
-      124, 80, 135, 138, 87, 169, 103, 99, 87, 5, 91, 161, 239, 59, 18, 246,
-      148, 20, 32, 60, 117, 245, 182, 156, 196, 230, 82, 117, 228, 211, 81, 45,
-      115, 217, 23, 95, 138, 95, 103, 124, 32, 89, 187, 48, 251, 103, 81, 234,
-      76, 4, 57, 197, 71, 48, 125, 169, 195, 147, 183, 142, 166, 8, 147, 7, 77,
-      240, 111, 44, 168, 23, 89, 107, 95, 220, 75, 202, 44, 25, 244, 200, 160,
-      0, 174, 111, 107, 204, 22, 36, 122, 191, 101, 33, 52, 252, 14, 47, 25,
-      109, 135, 48, 131, 253, 195, 237, 60, 86, 14, 119, 85, 28, 182, 139, 136,
-      191, 59, 187, 90, 126, 207, 168, 238, 184, 83, 77, 254, 208, 21, 57, 104,
-      188, 228, 134, 82, 249, 190, 249, 163, 120, 111, 68, 225, 210, 131, 29,
-      151, 153, 146, 71, 163, 104, 225, 194, 33, 64, 213, 92, 145, 253, 42, 32,
-      233, 95, 224, 72, 184, 119, 192, 113, 253, 177, 153, 246, 240, 49, 103,
-      170, 229, 82, 69, 186, 36, 252, 29, 124, 255, 25, 148, 5, 160, 159, 105,
-      157, 185, 25, 158, 122, 145, 30, 245, 51, 205, 62, 56, 154, 23, 52, 0,
-      225, 177, 105, 195, 131, 177, 151, 197, 114, 110, 128, 227, 127, 176, 28,
-      252, 217, 56, 61, 136, 148, 190, 124, 108, 129, 62, 243, 235, 37, 70, 228,
-      233, 10, 67, 124, 120, 58, 164, 83, 57, 80, 39, 204, 98, 28, 199, 136, 75,
-      12, 51, 255, 24, 130, 127, 19, 239, 160, 94, 137, 75, 15, 82, 158, 252,
-      163, 43, 236, 193, 234, 27, 74, 69, 14, 179, 171, 10, 169, 27, 138, 172,
-      213, 189, 222, 167, 110, 24, 11, 118, 151, 223, 94, 154, 125, 9, 21, 137,
-      70, 128, 51, 102, 52, 105, 104, 157, 54, 186, 75, 217, 226, 169, 71, 125,
-      177, 253, 218, 85, 236, 242, 25, 215, 147, 181, 104, 242, 82, 75, 50, 165,
-      49, 102, 128, 82, 237, 170, 134, 162, 196, 56, 247, 199, 138, 102, 118,
-      37, 66, 114, 165, 177, 156, 251, 39, 199, 12, 167, 246, 169, 150, 108, 91,
-      192, 42, 68, 192, 244, 255, 107, 18, 191, 7, 73, 19, 174, 28, 116, 66,
-      131, 96, 173, 60, 131, 190, 249, 219, 246, 71, 182, 124, 183, 129, 101,
-      109, 95, 180, 78, 17, 140, 182, 62, 80, 195, 184, 253, 176, 169, 153, 1,
-      29, 83, 39, 252, 150, 41, 219, 176, 48, 151, 108, 60, 255, 239, 0, 85,
-      101, 193, 110, 204, 12, 254, 22, 136, 15, 154, 217, 88, 13, 28, 252, 232,
-      48, 32, 33, 41, 221, 103, 227, 228, 177, 54, 175, 195, 106, 174, 140, 54,
-      128, 108, 214, 228, 215, 118, 226, 206, 171, 21, 155, 162, 152, 135, 203,
-      16, 170, 130, 100, 173, 155, 243, 80, 40, 98, 157, 248, 7, 101, 88, 199,
-      218, 224, 141, 82, 238, 121, 92, 82, 97, 49, 31, 155, 61, 63, 84, 227, 90,
-      143, 164, 59, 216, 101, 19, 35, 134, 2, 20, 18, 197, 97, 215, 169, 85,
-      220, 75, 254, 91, 125, 144, 43, 65, 128, 29, 66, 77, 184, 172, 89, 123,
-      203, 231, 254, 59, 18, 249, 204, 249, 220, 153, 84, 166, 63, 23, 108, 120,
-      145, 216, 67, 223, 123, 248, 15, 253, 63, 191, 126, 84, 95, 98, 141, 66,
-      200, 129, 194, 174, 30, 80, 48, 75, 113, 14, 102, 101, 218, 249, 14, 203,
-      24, 6, 11, 89, 99, 56, 49, 17, 148, 160, 242, 101, 191, 102, 227, 115, 33,
-      107, 71, 235, 6, 141, 68, 161, 162, 165, 227, 159, 82, 253, 54, 157, 214,
-      255, 141, 151, 154, 134, 150, 93, 141, 97, 158, 244, 180, 135, 42, 10, 3,
-      148, 9, 152, 149, 20, 18, 237, 253, 180, 136, 165, 13, 80, 195, 220, 43,
-      3, 178, 10, 127, 144, 2, 207, 209, 246, 40, 110, 38, 133, 9, 22, 180, 11,
-      223, 116, 236, 205, 186, 25, 183, 155, 165, 77, 206, 101, 255, 106, 223,
-      70, 10, 22, 176, 119, 142, 163, 197, 178, 251, 216, 112, 237, 63, 201, 77,
-      219, 196, 2, 176, 48, 15, 207, 91, 74, 236, 21, 8, 205, 126, 77, 28, 13,
-      26, 28, 202, 250, 100, 79, 134, 209, 193, 78, 202, 58, 248, 124, 215, 64,
-      143, 244, 17, 23, 163, 162, 241, 249, 49, 210, 168, 203, 213, 135, 77, 14,
-      29, 35, 193, 234, 58, 12, 106, 165, 163, 251, 192, 115, 251, 7, 198, 189,
-      93, 207, 178, 246, 50, 189, 185, 51, 45, 84, 140, 194, 34, 46, 92, 90,
-      136, 81, 81, 53, 52, 253, 128, 247, 131, 243, 21, 207, 245, 141, 49, 178,
-      213, 214, 211, 8, 17, 234, 133, 225, 104, 197, 186, 224, 117, 1, 173, 104,
-      17, 177, 161, 223, 71, 195, 159, 194, 45, 177, 116, 26, 187, 193, 161,
-      213, 179, 158, 8, 122, 186, 122, 191, 158, 5, 12, 178, 235, 78, 132, 78,
-      189, 16, 86, 110, 73, 51, 129, 255, 242, 110, 63, 6, 53, 209, 110, 132,
-      236, 43, 239, 192, 221, 138, 1, 128, 176, 0, 113, 85, 119, 201, 36, 188,
-      202, 9, 223, 98, 196, 42, 130, 158, 149, 200, 150, 202, 132, 118, 153, 97,
-      54, 195, 154, 15, 45, 246, 129, 144, 255, 231, 75, 25, 56, 47, 178, 98,
-      10, 106, 84, 56, 113, 161, 227, 37, 246, 1, 245, 129, 173, 70, 186, 11,
-      28, 125, 198, 243, 225, 113, 130, 5, 138, 52, 251, 194, 152, 105, 48, 214,
-      13, 246, 228, 75, 236, 194, 223, 177, 64, 150, 97, 167, 20, 27, 74, 76,
-      166, 199, 239, 91, 240, 8, 22, 54, 41, 63, 35, 112, 255, 251, 88, 252,
-      147, 173, 185, 43, 98, 83, 155, 230, 176, 55, 161, 8, 83, 15, 110, 151,
-      241, 110, 168, 203, 190, 198, 218, 152, 144, 150, 58, 123, 11, 123, 250,
-      104, 113, 198, 93, 1, 152, 51, 172, 140, 246, 115, 113, 229, 218, 166,
-      200, 35, 123, 16, 133, 169, 191, 59, 103, 12, 114, 15, 60, 37, 4, 92, 208,
-      54, 31, 79, 56, 225, 6, 11, 74, 107, 79, 225, 239, 16, 73, 249, 234, 197,
-      129, 230, 97, 39, 115, 49, 96, 141, 41, 242, 225, 177, 214, 18, 103, 34,
-      229, 37, 176, 241, 99, 82, 227, 195, 77, 32, 64, 4, 120, 49, 199, 209,
-      139, 2, 4, 222, 233, 45, 151, 141, 142, 252, 41, 124, 231, 13, 144, 63,
-      212, 252, 145, 34, 142, 232, 152, 84, 135, 87, 175, 46, 53, 139, 60, 168,
-      135, 167, 101, 253, 127, 152, 138, 154, 31, 231, 198, 77, 89, 182, 9, 54,
-      103, 119, 100, 218, 245, 44, 191, 74, 30, 152, 84, 22, 62, 159, 131, 163,
-      223, 3, 51, 194, 241, 49, 9, 213, 43, 214, 201, 75, 158, 198, 22, 203,
-      209, 190, 199, 189, 75, 4, 6, 72, 60, 241, 113, 171, 30, 42, 143, 73, 51,
-      72, 206, 110, 175, 203, 195, 199, 15, 155, 208, 166, 121, 26, 132, 59, 44,
-      72, 155, 7, 48, 122, 132, 224, 142, 3, 7, 21, 207, 11, 30, 112, 18, 149,
-      146, 127, 41, 104, 197, 169, 86, 213, 108, 253, 111, 160, 5, 11, 202, 172,
-      233, 32, 5, 52, 92, 124, 152, 162, 11, 88, 28, 166, 248, 141, 251, 38,
-      161, 53, 49, 136, 246, 183, 85, 9, 165, 115, 108, 18, 208, 218, 129, 165,
-      163, 131, 34, 32, 94, 226, 121, 93, 24, 87, 226, 105, 25, 242, 128, 198,
-      78, 26, 237, 21, 92, 33, 121, 8, 119, 131, 140, 193, 14, 60, 139, 130, 19,
-      65, 96, 50, 5, 249, 12, 27, 51, 23, 195, 89, 255, 47, 23, 109, 62, 202,
-      190, 5, 131, 92, 91, 39, 27, 209, 132, 146, 95, 98, 66, 26, 230, 186, 236,
-      186, 162, 149, 81, 143, 221, 21, 79, 171, 236, 21, 161, 19, 135, 10, 213,
-      168, 200, 135, 19, 221, 177, 207, 106, 214, 194, 124, 220, 53, 45, 176,
-      245, 178, 189, 49, 242, 22, 230, 154, 241, 146, 230, 187, 180, 244, 158,
-      145, 197, 225, 51, 150, 140, 26, 175, 78, 142, 243, 232, 221, 137, 205,
-      130, 178, 54, 216, 136, 118, 212, 33, 90, 206, 18, 224, 158, 44, 23, 144,
-      204, 24, 184, 48, 81, 233, 124, 20, 222, 77, 119, 144, 22, 145, 241, 19,
-      206, 135, 252, 164, 114, 84, 37, 175, 107, 67, 243, 183, 137, 213, 53,
-      236, 41, 73, 50, 34, 146, 218, 204, 68, 235, 158, 150, 139, 133, 75, 248,
-      157, 7, 0, 53, 68, 44, 156, 110, 56, 197, 209, 201, 0, 155, 225, 85, 35,
-      169, 193, 231, 36, 31, 142, 101, 168, 29, 192, 31, 128, 81, 191, 253, 184,
-      153, 226, 214, 1, 27, 6, 0, 192, 36, 101, 72, 105, 150, 226, 3, 21, 12,
-      127, 72, 250, 223, 240, 136, 249, 157, 129, 247, 91, 178, 157, 208, 86,
-      146, 23, 176, 150, 35, 211, 155, 22, 207, 119, 168, 167, 122, 180, 183,
-      106, 97, 138, 87, 96, 34, 141, 110, 145, 231, 226, 35, 115, 232, 129, 54,
-      186, 128, 190, 238, 37, 169, 194, 37, 24, 237, 211, 164, 111, 54, 88, 245,
-      125, 67, 78, 194, 11, 194, 235, 217, 87, 203, 14, 220, 189, 107, 68, 94,
-      139, 66, 230, 5, 54, 64, 28, 246, 143, 75, 127, 239, 243, 20, 251, 189,
-      246, 246, 151, 90, 219, 227, 242, 223, 205, 40, 124, 245, 4, 189, 72, 39,
-      145, 110, 96, 120, 164, 27, 117, 146, 177, 30, 76, 173, 106, 130, 228,
-      192, 189, 59, 167, 30, 127, 178, 164, 5, 133, 80, 199, 207, 216, 189, 156,
-      93, 222, 150, 129, 81, 193, 180, 38, 59, 8, 214, 210, 95, 150, 182, 175,
-      148, 43, 153, 169, 66, 62, 135, 140, 159, 89, 195, 253, 3, 132, 180, 164,
-      244, 243, 232, 71, 196, 200, 158, 194, 114, 115, 193, 88, 161, 185, 235,
-      247, 14, 126, 225, 176, 111, 17, 79, 77, 245, 80, 229, 174, 6, 180, 156,
-      217, 85, 174, 201, 255, 237, 85, 83, 53, 188, 153, 98, 91, 193, 21, 211,
-      49, 145, 196, 252, 52, 66, 226, 131, 203, 214, 130, 237, 194, 208, 174,
-      102, 133, 220, 235, 222, 79, 232, 36, 21, 161, 248, 51, 185, 164, 156, 95,
-      121, 65, 57, 233, 183, 29, 199, 105, 104, 12, 90, 195, 186, 81, 199, 176,
-      1, 87, 16, 226, 192, 206, 185, 197, 46, 34, 75, 174, 153, 238, 15, 50, 87,
-      226, 96, 26, 197, 202, 73, 235, 19, 140, 9, 79, 12, 52, 83, 76, 85, 146,
-      234, 24, 151, 179, 178, 118, 58, 162, 223, 103, 69, 244, 231, 135, 167,
-      204, 117, 166, 60, 237, 82, 63, 11, 4, 207, 10, 146, 54, 126, 191, 79,
-      133, 60, 128, 34, 136, 170, 23, 84, 81, 38, 94, 9, 130, 245, 100, 115,
-      209, 34, 228, 158, 101, 216, 135, 208, 207, 191, 169, 115, 252, 144, 139,
-      226, 252, 6, 44, 221, 134, 170, 87, 11, 46, 124, 58, 219, 179, 238, 6, 98,
-      216, 18, 174, 42, 12, 97, 126, 85, 245, 81, 220, 232, 135, 114, 21, 125,
-      135, 225, 182, 245, 228, 223, 242, 62, 158, 35, 76, 6, 110, 25, 184, 206,
-      124, 237, 54, 252, 199, 44, 78, 89, 0, 135, 44, 176, 57, 168, 36, 221,
-      173, 77, 214, 209, 60, 1, 202, 238, 237, 61, 90, 47, 114, 230, 92, 238,
-      235, 18, 151, 220, 243, 225, 163, 159, 139, 189, 253, 62, 225, 182, 202,
-      59, 7, 83, 99, 129, 118, 175, 37, 246, 85, 119, 251, 246, 69, 180, 247,
-      37, 24, 194, 89, 158, 97, 230, 247, 254, 145, 102, 89, 77, 68, 245, 3,
-      103, 83, 28, 45, 168, 30, 189, 151, 112, 120, 215, 84, 90, 198, 85, 85,
-      129, 55, 127, 124, 28, 137, 229, 139, 54, 88, 229, 105, 81, 212, 83, 28,
-      107, 250, 82, 164, 43, 24, 15, 57, 206, 156, 19, 145, 95, 57, 169, 8, 128,
-      211, 29, 213, 195, 148, 27, 73, 186, 221, 242, 11, 167, 78, 246, 133, 18,
-      118, 67, 236, 59, 19, 112, 254, 93, 168, 157, 118, 0, 107, 248, 57, 149,
-      67, 222, 123, 225, 207, 251, 69, 218, 56, 110, 162, 19, 25, 209, 209, 213,
-      200, 158, 70, 9, 100, 208, 77, 48, 255, 151, 200, 0, 68, 230, 70, 120,
-      209, 29, 86, 225, 188, 189, 226, 101, 42, 176, 32, 147, 121, 72, 151, 130,
-      217, 13, 66, 148, 84, 129, 215, 249, 164, 174, 187, 80, 85, 185, 245, 108,
-      169, 119, 59, 178, 144, 229, 63, 194, 132, 250, 131, 82, 161, 125, 126,
-      255, 252, 220, 204, 104, 231, 201, 136, 246, 116, 43, 88, 233, 0, 43, 128,
-      214, 40, 59, 81, 147, 139, 132, 69, 24, 233, 21, 14, 91, 230, 241, 19,
-      138, 163, 55, 13, 221, 47, 64, 140, 248, 6, 38, 164, 16, 219, 63, 33, 180,
-      71, 151, 0, 234, 103, 58, 213, 222, 163, 95, 132, 1, 178, 146, 66, 124,
-      242, 223, 102, 129, 192, 214, 194, 117, 162, 252, 246, 143, 42, 70, 139,
-      97, 168, 64, 141, 190, 115, 126, 93, 175, 59, 49, 9, 184, 88, 201, 100,
-      182, 142, 145, 244, 72, 128, 203, 49, 196, 5, 5, 18, 46, 34, 87, 171, 132,
-      158, 128, 75, 194, 8, 242, 52, 156, 229, 245, 56, 245, 88, 14, 195, 110,
-      166, 51, 158, 245, 195, 120, 17, 166, 66, 100, 212, 188, 243, 2, 236, 90,
-      8, 16, 35, 151, 122, 175, 115, 168, 186, 191, 60, 71, 23, 81, 217, 79,
-      203, 239, 61, 146, 247, 168, 112, 83, 102, 146, 222, 178, 45, 247, 63, 23,
-      181, 3, 136, 208, 62, 154, 203, 35, 250, 238, 61, 98, 207, 90, 169, 175,
-      36, 227, 9, 182, 78, 226, 99, 89, 67, 105, 185, 35, 242, 162, 54, 99, 60,
-      148, 14, 118, 1, 26, 120, 62, 82, 62, 222, 34, 99, 58, 174, 145, 199, 190,
-      21, 182, 117, 238, 13, 170, 29, 67, 149, 44, 90, 94, 181, 125, 182, 186,
-      82, 55, 105, 253, 29, 212, 67, 134, 204, 227, 94, 255, 127, 72, 157, 140,
-      142, 224, 77, 149, 29, 170, 45, 163, 214, 209, 46, 28, 125, 177, 111, 2,
-      92, 121, 252, 166, 204, 227, 173, 51, 60, 162, 243, 202, 207, 103, 30,
-      153, 182, 116, 182, 130, 98, 59, 25, 141, 239, 49, 224, 176, 27, 237, 218,
-      76, 68, 189, 108, 185, 136, 255, 105, 150, 11, 153, 44, 248, 139, 199,
-      178, 235, 85, 115, 121, 144, 87, 221, 50, 222, 238, 16, 20, 51, 190, 93,
-      248, 228, 84, 228, 115, 31, 229, 227, 137, 180, 44, 115, 224, 119, 129,
-      181, 134, 224, 144, 186, 123, 208, 118, 96, 101, 177, 191, 232, 171, 6,
-      17, 247, 187, 173, 84, 70, 249, 19, 191, 116, 172, 126, 131, 216, 123,
-      225, 151, 55, 205, 177, 93, 139, 117,
-    ]),
+    pkcs8: mldsaKeyFixtures.pkcs8['ML-DSA-87'],
+    spki: mldsaKeyFixtures.spki['ML-DSA-87'],
     'raw-public': hexStringToUint8Array(
       '92b84328ac1bcc1b87166ddf7f176182c6e4c73eb215ad1458b4cda811a4875c55924cc29850ef9d2115efb6b0fe7c50878a57a9676357055ba1ef3b12f69414203c75f5b69cc4e65275e4d3512d73d9175f8a5f677c2059bb30fb6751ea4c0439c547307da9c393b78ea60893074df06f2ca817596b5fdc4bca2c19f4c8a000ae6f6bcc16247abf652134fc0e2f196d873083fdc3ed3c560e77551cb68b88bf3bbb5a7ecfa8eeb8534dfed0153968bce48652f9bef9a3786f44e1d2831d97999247a368e1c22140d55c91fd2a20e95fe048b877c071fdb199f6f03167aae55245ba24fc1d7cff199405a09f699db9199e7a911ef533cd3e389a173400e1b169c383b197c5726e80e37fb01cfcd9383d8894be7c6c813ef3eb2546e4e90a437c783aa453395027cc621cc7884b0c33ff18827f13efa05e894b0f529efca32becc1ea1b4a450eb3ab0aa91b8aacd5bddea76e180b7697df5e9a7d091589468033663469689d36ba4bd9e2a9477db1fdda55ecf219d793b568f2524b32a531668052edaa86a2c438f7c78a6676254272a5b19cfb27c70ca7f6a9966c5bc02a44c0f4ff6b12bf074913ae1c74428360ad3c83bef9dbf647b67cb781656d5fb44e118cb63e50c3b8fdb0a999011d5327fc9629dbb030976c3cffef005565c16ecc0cfe16880f9ad9580d1cfce830202129dd67e3e4b136afc36aae8c36806cd6e4d776e2ceab159ba29887cb10aa8264ad9bf35028629df8076558c7dae08d52ee795c5261311f9b3d3f54e35a8fa43bd865132386021412c561d7a955dc4bfe5b7d902b41801d424db8ac597bcbe7fe3b12f9ccf9dc9954a63f176c7891d843df7bf80ffd3fbf7e545f628d42c881c2ae1e50304b710e6665daf90ecb18060b596338311194a0f265bf66e373216b47eb068d44a1a2a5e39f52fd369dd6ff8d979a86965d8d619ef4b4872a0a03940998951412edfdb488a50d50c3dc2b03b20a7f9002cfd1f6286e26850916b40bdf74eccdba19b79ba54dce65ff6adf460a16b0778ea3c5b2fbd870ed3fc94ddbc402b0300fcf5b4aec1508cd7e4d1c0d1a1ccafa644f86d1c14eca3af87cd7408ff41117a3a2f1f931d2a8cbd5874d0e1d23c1ea3a0c6aa5a3fbc073fb07c6bd5dcfb2f632bdb9332d548cc2222e5c5a8851513534fd80f783f315cff58d31b2d5d6d30811ea85e168c5bae07501ad6811b1a1df47c39fc22db1741abbc1a1d5b39e087aba7abf9e050cb2eb4e844ebd10566e493381fff26e3f0635d16e84ec2befc0dd8a0180b000715577c924bcca09df62c42a829e95c896ca8476996136c39a0f2df68190ffe74b19382fb2620a6a543871a1e325f601f581ad46ba0b1c7dc6f3e17182058a34fbc2986930d60df6e44becc2dfb1409661a7141b4a4ca6c7ef5bf0081636293f2370fffb58fc93adb92b62539be6b037a108530f6e97f16ea8cbbec6da9890963a7b0b7bfa6871c65d019833ac8cf67371e5daa6c8237b1085a9bf3b670c720f3c25045cd0361f4f38e1060b4a6b4fe1ef1049f9eac581e661277331608d29f2e1b1d6126722e525b0f16352e3c34d2040047831c7d18b0204dee92d978d8efc297ce70d903fd4fc91228ee898548757af2e358b3ca887a765fd7f988a9a1fe7c64d59b60936677764daf52cbf4a1e9854163e9f83a3df0333c2f13109d52bd6c94b9ec616cbd1bec7bd4b0406483cf171ab1e2a8f493348ce6eafcbc3c70f9bd0a6791a843b2c489b07307a84e08e030715cf0b1e701295927f2968c5a956d56cfd6fa0050bcaace92005345c7c98a20b581ca6f88dfb26a1353188f6b75509a5736c12d0da81a5a38322205ee2795d1857e26919f280c64e1aed155c21790877838cc10e3c8b821341603205f90c1b3317c359ff2f176d3ecabe05835c5b271bd184925f62421ae6baecbaa295518fdd154fabec15a113870ad5a8c88713ddb1cf6ad6c27cdc352db0f5b2bd31f216e69af192e6bbb4f49e91c5e133968c1aaf4e8ef3e8dd89cd82b236d88876d4215ace12e09e2c1790cc18b83051e97c14de4d77901691f113ce87fca4725425af6b43f3b789d535ec2949322292dacc44eb9e968b854bf89d070035442c9c6e38c5d1c9009be15523a9c1e7241f8e65a81dc01f8051bffdb899e2d6011b0600c02465486996e203150c7f48fadff088f99d81f75bb29dd0569217b09623d39b16cf77a8a77ab4b76a618a5760228d6e91e7e22373e88136ba80beee25a9c22518edd3a46f3658f57d434ec20bc2ebd957cb0edcbd6b445e8b42e60536401cf68f4b7feff314fbbdf6f6975adbe3f2dfcd287cf504bd4827916e6078a41b7592b11e4cad6a82e4c0bd3ba71e7fb2a4058550c7cfd8bd9c5dde968151c1b4263b08d6d25f96b6af942b99a9423e878c9f59c3fd0384b4a4f4f3e847c4c89ec27273c158a1b9ebf70e7ee1b06f114f4df550e5ae06b49cd955aec9ffed555335bc99625bc115d33191c4fc3442e283cbd682edc2d0ae6685dcebde4fe82415a1f833b9a49c5f794139e9b71dc769680c5ac3ba51c7b0015710e2c0ceb9c52e224bae99ee0f3257e2601ac5ca49eb138c094f0c34534c5592ea1897b3b2763aa2df6745f4e787a7cc75a63ced523f0b04cf0a92367ebf4f853c802288aa175451265e0982f56473d122e49e65d887d0cfbfa973fc908be2fc062cdd86aa570b2e7c3adbb3ee0662d812ae2a0c617e55f551dce88772157d87e1b6f5e4dff23e9e234c066e19b8ce7ced36fcc72c4e5900872cb039a824ddad4dd6d13c01caeeed3d5a2f72e65ceeeb1297dcf3e1a39f8bbdfd3ee1b6ca3b0753638176af25f65577fbf645b4f72518c2599e61e6f7fe9166594d44f50367531c2da81ebd977078d7545ac6555581377f7c1c89e58b3658e56951d4531c6bfa52a42b180f39ce9c13915f39a90880d31dd5c3941b49baddf20ba74ef685127643ec3b1370fe5da89d76006bf8399543de7be1cffb45da386ea21319d1d1d5c89e460964d04d30ff97c80044e64678d11d56e1bcbde2652ab0209379489782d90d42945481d7f9a4aebb5055b9f56ca9773bb290e53fc284fa8352a17d7efffcdccc68e7c988f6742b58e9002b80d6283b51938b844518e9150e5be6f1138aa3370ddd2f408cf80626a410db3f21b4479700ea673ad5dea35f8401b292427cf2df6681c0d6c275a2fcf68f2a468b61a8408dbe737e5daf3b3109b858c964b68e91f44880cb31c40505122e2257ab849e804bc208f2349ce5f538f5580ec36ea6339ef5c37811a64264d4bcf302ec5a081023977aaf73a8babf3c471751d94fcbef3d92f7a870536692deb22df73f17b50388d03e9acb23faee3d62cf5aa9af24e309b64ee263594369b923f2a236633c940e76011a783e523ede22633aae91c7be15b675ee0daa1d43952c5a5eb57db6ba523769fd1dd44386cce35eff7f489d8c8ee04d951daa2da3d6d12e1c7db16f025c79fca6cce3ad333ca2f3cacf671e99b674b682623b198def31e0b01bedda4c44bd6cb988ff69960b992cf88bc7b2eb5573799057dd32deee101433be5df8e454e4731fe5e389b42c73e07781b586e090ba7bd0766065b1bfe8ab0611f7bbad5446f913bf74ac7e83d87be19737cdb15d8b75'
     ),
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-KEM_importKey.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-KEM_importKey.js
deleted file mode 100644
index d9257ac..0000000
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-KEM_importKey.js
+++ /dev/null
@@ -1,130 +0,0 @@
-var subtle = crypto.subtle;
-
-function runTests(algorithmName) {
-  var algorithm = { name: algorithmName };
-  var data = keyData[algorithmName];
-  var jwkData = {
-    jwk: { kty: data.jwk.kty, alg: data.jwk.alg, pub: data.jwk.pub },
-  };
-
-  [true, false].forEach(function (extractable) {
-    // Test public keys first
-    allValidUsages(data.publicUsages, true).forEach(function (usages) {
-      ['spki', 'jwk', 'raw-public'].forEach(function (format) {
-        if (format === 'jwk') {
-          // Not all fields used for public keys
-          testFormat(
-            format,
-            algorithm,
-            jwkData,
-            algorithmName,
-            usages,
-            extractable
-          );
-        } else {
-          testFormat(
-            format,
-            algorithm,
-            data,
-            algorithmName,
-            usages,
-            extractable
-          );
-        }
-      });
-    });
-
-    // Next, test private keys
-    allValidUsages(data.privateUsages).forEach(function (usages) {
-      ['pkcs8', 'jwk', 'raw-seed'].forEach(function (format) {
-        testFormat(format, algorithm, data, algorithmName, usages, extractable);
-      });
-    });
-  });
-}
-
-// Test importKey with a given key format and other parameters. If
-// extrable is true, export the key and verify that it matches the input.
-function testFormat(format, algorithm, keyData, keySize, usages, extractable) {
-  [algorithm, algorithm.name].forEach((alg) => {
-    promise_test(function (test) {
-      return subtle
-        .importKey(format, keyData[format], alg, extractable, usages)
-        .then(
-          function (key) {
-            assert_equals(
-              key.constructor,
-              CryptoKey,
-              'Imported a CryptoKey object'
-            );
-            assert_goodCryptoKey(
-              key,
-              algorithm,
-              extractable,
-              usages,
-              format === 'pkcs8' ||
-                format === 'raw-seed' ||
-                (format === 'jwk' && keyData[format].priv)
-                ? 'private'
-                : 'public'
-            );
-            if (!extractable) {
-              return;
-            }
-
-            return subtle.exportKey(format, key).then(
-              function (result) {
-                if (format !== 'jwk') {
-                  assert_true(
-                    equalBuffers(keyData[format], result),
-                    'Round trip works'
-                  );
-                } else {
-                  assert_true(
-                    equalJwk(keyData[format], result),
-                    'Round trip works'
-                  );
-                }
-              },
-              function (err) {
-                assert_unreached(
-                  'Threw an unexpected error: ' + err.toString()
-                );
-              }
-            );
-          },
-          function (err) {
-            assert_unreached('Threw an unexpected error: ' + err.toString());
-          }
-        );
-    }, 'Good parameters: ' +
-      keySize.toString() +
-      ' bits ' +
-      parameterString(format, keyData[format], alg, extractable, usages));
-  });
-}
-
-// Helper methods follow:
-
-// Convert method parameters to a string to uniquely name each test
-function parameterString(format, data, algorithm, extractable, usages) {
-  if ('byteLength' in data) {
-    data = 'buffer(' + data.byteLength.toString() + ')';
-  } else {
-    data = 'object(' + Object.keys(data).join(', ') + ')';
-  }
-  var result =
-    '(' +
-    objectToString(format) +
-    ', ' +
-    objectToString(data) +
-    ', ' +
-    objectToString(algorithm) +
-    ', ' +
-    objectToString(extractable) +
-    ', ' +
-    objectToString(usages) +
-    ')';
-
-  return result;
-}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-KEM_importKey.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-KEM_importKey.tentative.https.any.js
index 8b459c1..11d80c7 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-KEM_importKey.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-KEM_importKey.tentative.https.any.js
@@ -2,7 +2,7 @@
 // META: timeout=long
 // META: script=../util/helpers.js
 // META: script=ML-KEM_importKey_fixtures.js
-// META: script=ML-KEM_importKey.js
+// META: script=ml_importKey.js
 
 runTests("ML-KEM-512");
 runTests("ML-KEM-768");
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey.https.any.js
index 3b78bab..473fab2 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: importKey() for EC keys
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=../util/ec_key_fixtures.js
 
 // Test importKey and exportKey for EC algorithms. Only "happy paths" are
 // currently tested - those where the operation should succeed.
@@ -9,56 +10,7 @@
 
     var curves = ['P-256', 'P-384', 'P-521'];
 
-    var keyData = {
-        "P-521": {
-            spki: new Uint8Array([48, 129, 155, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, 129, 134, 0, 4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56, 251]),
-            spki_compressed: new Uint8Array([48, 88, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, 68, 0, 3, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63]),
-            raw: new Uint8Array([4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56, 251]),
-            raw_compressed: new Uint8Array([3, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63]),
-            pkcs8: new Uint8Array([48, 129, 238, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 4, 129, 214, 48, 129, 211, 2, 1, 1, 4, 66, 0, 244, 8, 117, 131, 104, 186, 147, 15, 48, 247, 106, 224, 84, 254, 92, 210, 206, 127, 218, 44, 159, 118, 166, 212, 54, 207, 117, 214, 108, 68, 11, 254, 99, 49, 199, 193, 114, 161, 36, 120, 25, 60, 130, 81, 72, 123, 201, 18, 99, 250, 80, 33, 127, 133, 255, 99, 111, 89, 205, 84, 110, 58, 180, 131, 180, 161, 129, 137, 3, 129, 134, 0, 4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56, 251]),
-            pkcs8_private_only: new Uint8Array([48, 96, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 4, 73, 48, 71, 2, 1, 1, 4, 66, 0, 244, 8, 117, 131, 104, 186, 147, 15, 48, 247, 106, 224, 84, 254, 92, 210, 206, 127, 218, 44, 159, 118, 166, 212, 54, 207, 117, 214, 108, 68, 11, 254, 99, 49, 199, 193, 114, 161, 36, 120, 25, 60, 130, 81, 72, 123, 201, 18, 99, 250, 80, 33, 127, 133, 255, 99, 111, 89, 205, 84, 110, 58, 180, 131, 180]),
-            jwk: {
-                kty: "EC",
-                crv: "P-521",
-                x: "AVb0efjfHiCn_8BM5CDD4VSuJRmWvuQvA0uE1Bt0PzTkXzEbgTqc3sjNpZu7vTHUYLMpJSHnwbci5WZ8A9svrnU_",
-                y: "AVAXNs_iRzlDINjkr8L9ObWpMxBhuB4iQSgrnheJGCK1t54FL0WXtZZD_Tk3nFG9USXE9IvD8CXOPNNpUyhsyzj7",
-                d: "APQIdYNoupMPMPdq4FT-XNLOf9osn3am1DbPddZsRAv-YzHHwXKhJHgZPIJRSHvJEmP6UCF_hf9jb1nNVG46tIO0"
-            }
-        },
-
-        "P-256": {
-            spki: new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97, 232]),
-            spki_compressed: new Uint8Array([48, 57, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 34, 0, 2, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209]),
-            raw: new Uint8Array([4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97, 232]),
-            raw_compressed: new Uint8Array([2, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209]),
-            pkcs8: new Uint8Array([48, 129, 135, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 4, 109, 48, 107, 2, 1, 1, 4, 32, 19, 211, 58, 45, 90, 191, 156, 249, 235, 178, 31, 248, 96, 212, 174, 254, 110, 86, 231, 119, 144, 244, 222, 233, 180, 8, 132, 235, 211, 53, 68, 234, 161, 68, 3, 66, 0, 4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97, 232]),
-            pkcs8_private_only: new Uint8Array([48, 65, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 4, 39, 48, 37, 2, 1, 1, 4, 32, 19, 211, 58, 45, 90, 191, 156, 249, 235, 178, 31, 248, 96, 212, 174, 254, 110, 86, 231, 119, 144, 244, 222, 233, 180, 8, 132, 235, 211, 53, 68, 234]),
-            jwk: {
-                kty: "EC",
-                crv: "P-256",
-                x: "0hCwpvnZ8BKGgFi0P6T0cQGFQ7ugDJJQ35JXwqyuXdE",
-                y: "zgN1UtSBRQzjm00QlXAbF1v6s0uObAmeGPHBmDWDYeg",
-                d: "E9M6LVq_nPnrsh_4YNSu_m5W53eQ9N7ptAiE69M1ROo"
-            }
-        },
-
-        "P-384": {
-            spki: new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172, 232]),
-            spki_compressed: new Uint8Array([48, 70, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 50, 0, 2, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53]),
-            raw: new Uint8Array([4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172, 232]),
-            raw_compressed: new Uint8Array([2, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53]),
-            pkcs8: new Uint8Array([48, 129, 182, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 4, 129, 158, 48, 129, 155, 2, 1, 1, 4, 48, 69, 55, 181, 153, 7, 132, 211, 194, 210, 46, 150, 168, 249, 47, 161, 170, 73, 46, 232, 115, 229, 118, 164, 21, 130, 225, 68, 24, 60, 152, 136, 209, 14, 107, 158, 180, 206, 212, 178, 204, 64, 18, 228, 172, 94, 168, 64, 115, 161, 100, 3, 98, 0, 4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172, 232]),
-            pkcs8_private_only: new Uint8Array([48, 78, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 4, 55, 48, 53, 2, 1, 1, 4, 48, 69, 55, 181, 153, 7, 132, 211, 194, 210, 46, 150, 168, 249, 47, 161, 170, 73, 46, 232, 115, 229, 118, 164, 21, 130, 225, 68, 24, 60, 152, 136, 209, 14, 107, 158, 180, 206, 212, 178, 204, 64, 18, 228, 172, 94, 168, 64, 115]),
-            jwk: {
-                kty: "EC",
-                crv: "P-384",
-                x: "IZwU1mYXs27G2IVrOFtzp000T9iude8EZDXdpU47RL1fvevR0I3Wni19wdwhjLQ1",
-                y: "vSgTjMd4M3qEL2vWGyQOdCSfJGZ8KlgQp2v8KOAzX4imUB3sAZdtqFr7AIactqzo",
-                d: "RTe1mQeE08LSLpao-S-hqkku6HPldqQVguFEGDyYiNEOa560ztSyzEAS5KxeqEBz"
-            }
-        },
-
-    };
+    var keyData = ecKeyData;
 
     // combinations to test
     var testVectors = [
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey_failures_ECDH.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey_failures_ECDH.https.any.js
index 423d399f19..260e497 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey_failures_ECDH.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey_failures_ECDH.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: importKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=../util/ec_key_fixtures.js
 // META: script=ec_importKey_failures_fixtures.js
 // META: script=importKey_failures.js
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey_failures_ECDSA.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey_failures_ECDSA.https.any.js
index 5279407..96116d5 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey_failures_ECDSA.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey_failures_ECDSA.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: importKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=../util/ec_key_fixtures.js
 // META: script=ec_importKey_failures_fixtures.js
 // META: script=importKey_failures.js
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey_failures_fixtures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey_failures_fixtures.js
index dc0e11d..a37a72c 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey_failures_fixtures.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ec_importKey_failures_fixtures.js
@@ -1,17 +1,41 @@
-// Setup: define the correct behaviors that should be sought, and create
-// helper functions that generate all possible test parameters for
-// different situations.
+function ecPrivateJwk(namedCurve) {
+    return Object.assign({}, ecKeyData[namedCurve].jwk);
+}
+
 function getValidKeyData(algorithm) {
-    return validKeyData[algorithm.namedCurve];
+    var key = ecKeyData[algorithm.namedCurve];
+    return [
+        {format: "spki", data: key.spki},
+        {format: "raw", data: key.raw},
+        {format: "pkcs8", data: key.pkcs8},
+        {format: "jwk", data: ecPrivateJwk(algorithm.namedCurve)}
+    ];
 }
 
 function getBadKeyLengthData(algorithm) {
-    return badKeyLengthData[algorithm.namedCurve];
+    var key = ecKeyData[algorithm.namedCurve];
+    var jwk = ecPrivateJwk(algorithm.namedCurve);
+    jwk.x = jwk.x.slice(0, -1);
+    return [
+        {format: "spki", data: key.spki.slice(0, -1)},
+        {format: "raw", data: key.raw.slice(0, -1)},
+        {format: "pkcs8", data: key.pkcs8.slice(0, -1)},
+        {format: "jwk", data: jwk}
+    ];
 }
 
 function getMissingJWKFieldKeyData(algorithm) {
-    // The curve doesn't affect when testing for missing JWK fields.
-    return missingJWKFieldKeyData["P-521"];
+    var missingX = ecPrivateJwk("P-521");
+    var missingKty = ecPrivateJwk("P-521");
+    var missingCrv = ecPrivateJwk("P-521");
+    delete missingX.x;
+    delete missingKty.kty;
+    delete missingCrv.crv;
+    return [
+        {param: "x", data: missingX},
+        {param: "kty", data: missingKty},
+        {param: "crv", data: missingCrv}
+    ];
 }
 
 function getMismatchedJWKKeyData(algorithm) {
@@ -20,206 +44,15 @@
 }
 
 function getMismatchedKtyField(algorithm) {
-    return mismatchedKtyField[algorithm.namedCurve];
+    return "OKP";
 }
 
 function getMismatchedCrvField(algorithm) {
-    return mismatchedCrvField[algorithm.namedCurve];
+    return mismatchedEcCurves[algorithm.namedCurve];
 }
 
-var validKeyData = {
-    "P-521": [
-        {
-            format: "spki",
-            data: new Uint8Array([48, 129, 155, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, 129, 134, 0, 4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56, 251]),
-        },
-        {
-            format: "raw",
-            data: new Uint8Array([4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56, 251]),
-        },
-        {
-            format:"pkcs8",
-            data: new Uint8Array([48, 129, 238, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 4, 129, 214, 48, 129, 211, 2, 1, 1, 4, 66, 0, 244, 8, 117, 131, 104, 186, 147, 15, 48, 247, 106, 224, 84, 254, 92, 210, 206, 127, 218, 44, 159, 118, 166, 212, 54, 207, 117, 214, 108, 68, 11, 254, 99, 49, 199, 193, 114, 161, 36, 120, 25, 60, 130, 81, 72, 123, 201, 18, 99, 250, 80, 33, 127, 133, 255, 99, 111, 89, 205, 84, 110, 58, 180, 131, 180, 161, 129, 137, 3, 129, 134, 0, 4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56, 251]),
-        },
-        {
-            format: "jwk",
-            data: {
-                kty: "EC",
-                crv: "P-521",
-                x: "AVb0efjfHiCn_8BM5CDD4VSuJRmWvuQvA0uE1Bt0PzTkXzEbgTqc3sjNpZu7vTHUYLMpJSHnwbci5WZ8A9svrnU_",
-                y: "AVAXNs_iRzlDINjkr8L9ObWpMxBhuB4iQSgrnheJGCK1t54FL0WXtZZD_Tk3nFG9USXE9IvD8CXOPNNpUyhsyzj7",
-                d: "APQIdYNoupMPMPdq4FT-XNLOf9osn3am1DbPddZsRAv-YzHHwXKhJHgZPIJRSHvJEmP6UCF_hf9jb1nNVG46tIO0"
-            }
-        }
-    ],
-    "P-256": [
-        {
-            format: "spki",
-            data: new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97, 232]),
-        },
-        {
-            format: "raw",
-            data: new Uint8Array([4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97, 232]),
-        },
-        {
-            format: "pkcs8",
-            data: new Uint8Array([48, 129, 135, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 4, 109, 48, 107, 2, 1, 1, 4, 32, 19, 211, 58, 45, 90, 191, 156, 249, 235, 178, 31, 248, 96, 212, 174, 254, 110, 86, 231, 119, 144, 244, 222, 233, 180, 8, 132, 235, 211, 53, 68, 234, 161, 68, 3, 66, 0, 4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97, 232]),
-        },
-        {
-            format: "jwk",
-            data: {
-                kty: "EC",
-                crv: "P-256",
-                x: "0hCwpvnZ8BKGgFi0P6T0cQGFQ7ugDJJQ35JXwqyuXdE",
-                y: "zgN1UtSBRQzjm00QlXAbF1v6s0uObAmeGPHBmDWDYeg",
-                d: "E9M6LVq_nPnrsh_4YNSu_m5W53eQ9N7ptAiE69M1ROo"
-            }
-        },
-    ],
-    "P-384": [
-        {
-            format: "spki",
-            data: new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172, 232]),
-        },
-        {
-            format: "raw",
-            data: new Uint8Array([4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172, 232]),
-        },
-        {
-            format: "pkcs8",
-            data: new Uint8Array([48, 129, 182, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 4, 129, 158, 48, 129, 155, 2, 1, 1, 4, 48, 69, 55, 181, 153, 7, 132, 211, 194, 210, 46, 150, 168, 249, 47, 161, 170, 73, 46, 232, 115, 229, 118, 164, 21, 130, 225, 68, 24, 60, 152, 136, 209, 14, 107, 158, 180, 206, 212, 178, 204, 64, 18, 228, 172, 94, 168, 64, 115, 161, 100, 3, 98, 0, 4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172, 232]),
-        },
-        {
-            format: "jwk",
-            data: {
-                kty: "EC",
-                crv: "P-384",
-                x: "IZwU1mYXs27G2IVrOFtzp000T9iude8EZDXdpU47RL1fvevR0I3Wni19wdwhjLQ1",
-                y: "vSgTjMd4M3qEL2vWGyQOdCSfJGZ8KlgQp2v8KOAzX4imUB3sAZdtqFr7AIactqzo",
-                d: "RTe1mQeE08LSLpao-S-hqkku6HPldqQVguFEGDyYiNEOa560ztSyzEAS5KxeqEBz"
-            }
-        }
-    ]
-};
-
-// Removed just the last byte.
-var badKeyLengthData = {
-    "P-521": [
-        {
-            format: "spki",
-            data: new Uint8Array([48, 129, 155, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, 129, 134, 0, 4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56]),
-        },
-        {
-            format: "raw",
-            data: new Uint8Array([4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56]),
-        },
-        {
-            format:"pkcs8",
-            data: new Uint8Array([48, 129, 238, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 4, 129, 214, 48, 129, 211, 2, 1, 1, 4, 66, 0, 244, 8, 117, 131, 104, 186, 147, 15, 48, 247, 106, 224, 84, 254, 92, 210, 206, 127, 218, 44, 159, 118, 166, 212, 54, 207, 117, 214, 108, 68, 11, 254, 99, 49, 199, 193, 114, 161, 36, 120, 25, 60, 130, 81, 72, 123, 201, 18, 99, 250, 80, 33, 127, 133, 255, 99, 111, 89, 205, 84, 110, 58, 180, 131, 180, 161, 129, 137, 3, 129, 134, 0, 4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56]),
-        },
-        {
-            format: "jwk",
-            data: {
-                kty: "EC",
-                crv: "P-521",
-                x: "AVb0efjfHiCn_8BM5CDD4VSuJRmWvuQvA0uE1Bt0PzTkXzEbgTqc3sjNpZu7vTHUYLMpJSHnwbci5WZ8A9svrnU",
-                y: "AVAXNs_iRzlDINjkr8L9ObWpMxBhuB4iQSgrnheJGCK1t54FL0WXtZZD_Tk3nFG9USXE9IvD8CXOPNNpUyhsyzj7",
-                d: "APQIdYNoupMPMPdq4FT-XNLOf9osn3am1DbPddZsRAv-YzHHwXKhJHgZPIJRSHvJEmP6UCF_hf9jb1nNVG46tIO0"
-            }
-        }
-    ],
-    "P-256": [
-        {
-            format: "spki",
-            data: new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97]),
-        },
-        {
-            format: "raw",
-            data: new Uint8Array([4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97]),
-        },
-        {
-            format: "pkcs8",
-            data: new Uint8Array([48, 129, 135, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 4, 109, 48, 107, 2, 1, 1, 4, 32, 19, 211, 58, 45, 90, 191, 156, 249, 235, 178, 31, 248, 96, 212, 174, 254, 110, 86, 231, 119, 144, 244, 222, 233, 180, 8, 132, 235, 211, 53, 68, 234, 161, 68, 3, 66, 0, 4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97]),
-        },
-        {
-            format: "jwk",
-            data: {
-                kty: "EC",
-                crv: "P-256",
-                x: "0hCwpvnZ8BKGgFi0P6T0cQGFQ7ugDJJQ35JXwqyuXd",
-                y: "zgN1UtSBRQzjm00QlXAbF1v6s0uObAmeGPHBmDWDYeg",
-                d: "E9M6LVq_nPnrsh_4YNSu_m5W53eQ9N7ptAiE69M1ROo"
-            }
-        },
-    ],
-    "P-384": [
-        {
-            format: "spki",
-            data: new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172]),
-        },
-        {
-            format: "raw",
-            data: new Uint8Array([4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172]),
-        },
-        {
-            format: "pkcs8",
-            data: new Uint8Array([48, 129, 182, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 4, 129, 158, 48, 129, 155, 2, 1, 1, 4, 48, 69, 55, 181, 153, 7, 132, 211, 194, 210, 46, 150, 168, 249, 47, 161, 170, 73, 46, 232, 115, 229, 118, 164, 21, 130, 225, 68, 24, 60, 152, 136, 209, 14, 107, 158, 180, 206, 212, 178, 204, 64, 18, 228, 172, 94, 168, 64, 115, 161, 100, 3, 98, 0, 4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172]),
-        },
-        {
-            format: "jwk",
-            data: {
-                kty: "EC",
-                crv: "P-384",
-                x: "IZwU1mYXs27G2IVrOFtzp000T9iude8EZDXdpU47RL1fvevR0I3Wni19wdwhjLQ",
-                y: "vSgTjMd4M3qEL2vWGyQOdCSfJGZ8KlgQp2v8KOAzX4imUB3sAZdtqFr7AIactqzo",
-                d: "RTe1mQeE08LSLpao-S-hqkku6HPldqQVguFEGDyYiNEOa560ztSyzEAS5KxeqEBz"
-            }
-        }
-    ]
-};
-
-var missingJWKFieldKeyData = {
-    "P-521": [
-        {
-            param: "x",
-            data: {
-                kty: "EC",
-                crv: "P-521",
-                y: "AVAXNs_iRzlDINjkr8L9ObWpMxBhuB4iQSgrnheJGCK1t54FL0WXtZZD_Tk3nFG9USXE9IvD8CXOPNNpUyhsyzj7",
-                d: "APQIdYNoupMPMPdq4FT-XNLOf9osn3am1DbPddZsRAv-YzHHwXKhJHgZPIJRSHvJEmP6UCF_hf9jb1nNVG46tIO0"
-            }
-        },
-        {
-            param: "kty",
-            data: {
-                crv: "P-521",
-                x: "AVb0efjfHiCn_8BM5CDD4VSuJRmWvuQvA0uE1Bt0PzTkXzEbgTqc3sjNpZu7vTHUYLMpJSHnwbci5WZ8A9svrnU_",
-                y: "AVAXNs_iRzlDINjkr8L9ObWpMxBhuB4iQSgrnheJGCK1t54FL0WXtZZD_Tk3nFG9USXE9IvD8CXOPNNpUyhsyzj7",
-                d: "APQIdYNoupMPMPdq4FT-XNLOf9osn3am1DbPddZsRAv-YzHHwXKhJHgZPIJRSHvJEmP6UCF_hf9jb1nNVG46tIO0"
-            }
-        },
-        {
-            param: "crv",
-            data: {
-                kty: "EC",
-                x: "AVb0efjfHiCn_8BM5CDD4VSuJRmWvuQvA0uE1Bt0PzTkXzEbgTqc3sjNpZu7vTHUYLMpJSHnwbci5WZ8A9svrnU_",
-                y: "AVAXNs_iRzlDINjkr8L9ObWpMxBhuB4iQSgrnheJGCK1t54FL0WXtZZD_Tk3nFG9USXE9IvD8CXOPNNpUyhsyzj7",
-                d: "APQIdYNoupMPMPdq4FT-XNLOf9osn3am1DbPddZsRAv-YzHHwXKhJHgZPIJRSHvJEmP6UCF_hf9jb1nNVG46tIO0"
-            }
-        }
-    ]
-};
-
-// The 'kty' field doesn't match the key algorithm.
-var mismatchedKtyField =  {
-    "P-521": "OKP",
-    "P-256": "OKP",
-    "P-384": "OKP",
-}
-
-// The 'kty' field doesn't match the key algorithm.
-var mismatchedCrvField =  {
+var mismatchedEcCurves = {
     "P-521": "P-256",
     "P-256": "P-384",
-    "P-384": "P-521",
-}
+    "P-384": "P-521"
+};
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/importKey_failures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/importKey_failures.js
index f45da96c..7581614 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/importKey_failures.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/importKey_failures.js
@@ -1,8 +1,6 @@
 function run_test(algorithmNames) {
     var subtle = crypto.subtle; // Change to test prefixed implementations
 
-    setup({explicit_timeout: true});
-
 // These tests check that importKey and exportKey throw an error, and that
 // the error is of the right type, for a wide set of incorrect parameters.
 
@@ -79,16 +77,15 @@
         }, testTag + ": importKey" + parameterString(format, algorithm, extractable, usages, keyData));
     }
 
-    // Don't create an exhaustive list of all invalid usages,
-    // because there would usually be nearly 2**8 of them,
-    // way too many to test. Instead, create every singleton
+    // Don't create an exhaustive list of all invalid usages because
+    // there would be too many to test. Instead, create every singleton
     // of an illegal usage, and "poison" every valid usage
     // with an illegal one.
     function invalidUsages(validUsages, mandatoryUsages) {
         var results = [];
 
         var illegalUsages = [];
-        ["encrypt", "decrypt", "sign", "verify", "wrapKey", "unwrapKey", "deriveKey", "deriveBits"].forEach(function(usage) {
+        allKeyUsages.forEach(function(usage) {
             if (!validUsages.includes(usage)) {
                 illegalUsages.push(usage);
             }
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-DSA_importKey.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ml_importKey.js
similarity index 98%
rename from third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-DSA_importKey.js
rename to third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ml_importKey.js
index d9257ac..a33c3a85 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ML-DSA_importKey.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/ml_importKey.js
@@ -1,5 +1,6 @@
 var subtle = crypto.subtle;
 
+// Shared ML-DSA and ML-KEM import/export tests.
 function runTests(algorithmName) {
   var algorithm = { name: algorithmName };
   var data = keyData[algorithmName];
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any.js
index 49656489..b6b3786 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_Ed25519.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: importKey() for OKP keys
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=../util/okp_key_fixtures.js
 // META: script=okp_importKey_fixtures.js
 // META: script=okp_importKey.js
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_Ed448.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_Ed448.tentative.https.any.js
index 5bb7460..8498ce4 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_Ed448.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_Ed448.tentative.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: importKey() for OKP keys
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=../util/okp_key_fixtures.js
 // META: script=okp_importKey_fixtures.js
 // META: script=okp_importKey.js
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_X25519.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_X25519.https.any.js
index de1431d..5d629c0 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_X25519.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_X25519.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: importKey() for OKP keys
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=../util/okp_key_fixtures.js
 // META: script=okp_importKey_fixtures.js
 // META: script=okp_importKey.js
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_X448.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_X448.tentative.https.any.js
index f8552be3..09361ed 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_X448.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_X448.tentative.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: importKey() for OKP keys
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=../util/okp_key_fixtures.js
 // META: script=okp_importKey_fixtures.js
 // META: script=okp_importKey.js
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js
index e07b796..0b541d7 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed25519.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: importKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=../util/okp_key_fixtures.js
 // META: script=okp_importKey_failures_fixtures.js
 // META: script=importKey_failures.js
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.tentative.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.tentative.https.any-expected.txt
index 59e83f5783..a4f7c3d 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.tentative.https.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.tentative.https.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 520 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 760 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: importKey(spki, Ed448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, Ed448, false, [encrypt])
@@ -84,6 +84,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, Ed448, false, [verify, verify, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, Ed448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, Ed448, false, [encrypt])
@@ -168,6 +216,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, sign, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, Ed448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, Ed448, false, [encrypt])
@@ -252,6 +348,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, Ed448, false, [verify, verify, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), Ed448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), Ed448, false, [encrypt])
@@ -336,6 +480,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, sign, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [encrypt])
@@ -420,6 +612,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, verify, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [encrypt])
@@ -504,6 +744,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, verify, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [encrypt])
@@ -588,6 +876,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, sign, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [encrypt])
@@ -672,6 +1008,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [encrypt])
@@ -756,6 +1140,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, sign, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [encrypt])
@@ -840,6 +1272,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, verify, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: importKey(pkcs8, Ed448, true, [])
   assert_equals: Empty usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: importKey(pkcs8, Ed448, false, [])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.tentative.https.any.js
index 8ff3de5..726d5c1 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.tentative.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: importKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=../util/okp_key_fixtures.js
 // META: script=okp_importKey_failures_fixtures.js
 // META: script=importKey_failures.js
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.tentative.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.tentative.https.any.worker-expected.txt
index 59e83f5783..a4f7c3d 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.tentative.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_Ed448.tentative.https.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 520 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 760 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: importKey(spki, Ed448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, Ed448, false, [encrypt])
@@ -84,6 +84,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, Ed448, false, [verify, verify, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, true, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, Ed448, false, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, Ed448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, Ed448, false, [encrypt])
@@ -168,6 +216,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, sign, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, true, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, Ed448, false, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, Ed448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, Ed448, false, [encrypt])
@@ -252,6 +348,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, Ed448, false, [verify, verify, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, true, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, Ed448, false, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), Ed448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), Ed448, false, [encrypt])
@@ -336,6 +480,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, sign, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, true, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), Ed448, false, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [encrypt])
@@ -420,6 +612,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, verify, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, true, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , Ed448, false, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [encrypt])
@@ -504,6 +744,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, verify, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, true, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: Ed448}, false, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [encrypt])
@@ -588,6 +876,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, sign, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, true, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: Ed448}, false, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [encrypt])
@@ -672,6 +1008,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, true, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: Ed448}, false, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [encrypt])
@@ -756,6 +1140,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, sign, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, sign, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, sign, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, sign, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, true, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: Ed448}, false, [sign, sign, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [encrypt])
@@ -840,6 +1272,54 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, verify, deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, verify, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, verify, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, verify, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, true, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: Ed448}, false, [verify, verify, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: importKey(pkcs8, Ed448, true, [])
   assert_equals: Empty usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: importKey(pkcs8, Ed448, false, [])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js
index a0116bee..d4c9c10 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X25519.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: importKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=../util/okp_key_fixtures.js
 // META: script=okp_importKey_failures_fixtures.js
 // META: script=importKey_failures.js
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.tentative.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.tentative.https.any-expected.txt
index 903dd31e..a0b8921 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.tentative.https.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.tentative.https.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 446 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 654 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: importKey(spki, X448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, X448, false, [encrypt])
@@ -32,6 +32,22 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, X448, false, [deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, X448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, X448, false, [encrypt])
@@ -152,6 +168,86 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, X448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, X448, false, [encrypt])
@@ -184,6 +280,22 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, X448, false, [deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), X448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), X448, false, [encrypt])
@@ -304,6 +416,86 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , X448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , X448, false, [encrypt])
@@ -336,6 +528,22 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , X448, false, [deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, {name: X448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, {name: X448}, false, [encrypt])
@@ -368,6 +576,22 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, {name: X448}, false, [deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [encrypt])
@@ -488,6 +712,86 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, {name: X448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, {name: X448}, false, [encrypt])
@@ -520,6 +824,22 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, {name: X448}, false, [deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [encrypt])
@@ -640,6 +960,86 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , {name: X448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , {name: X448}, false, [encrypt])
@@ -672,6 +1072,22 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , {name: X448}, false, [deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: importKey(pkcs8, X448, true, [])
   assert_equals: Empty usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: importKey(pkcs8, X448, false, [])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.tentative.https.any.js
index eccce68..2b575d7 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.tentative.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: importKey() for Failures
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=../util/okp_key_fixtures.js
 // META: script=okp_importKey_failures_fixtures.js
 // META: script=importKey_failures.js
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.tentative.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.tentative.https.any.worker-expected.txt
index 903dd31e..a0b8921 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.tentative.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_X448.tentative.https.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 446 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 654 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Bad usages: importKey(spki, X448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, X448, false, [encrypt])
@@ -32,6 +32,22 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, X448, false, [deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, X448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, X448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, X448, false, [encrypt])
@@ -152,6 +168,86 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, X448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, X448, false, [encrypt])
@@ -184,6 +280,22 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, X448, false, [deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, X448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), X448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), X448, false, [encrypt])
@@ -304,6 +416,86 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), X448, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , X448, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , X448, false, [encrypt])
@@ -336,6 +528,22 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , X448, false, [deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , X448, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, {name: X448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, {name: X448}, false, [encrypt])
@@ -368,6 +576,22 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(spki, {name: X448}, false, [deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(spki, {name: X448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [encrypt])
@@ -488,6 +712,86 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(pkcs8, {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, {name: X448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, {name: X448}, false, [encrypt])
@@ -520,6 +824,22 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(raw, {name: X448}, false, [deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(raw, {name: X448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [encrypt])
@@ -640,6 +960,86 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, deriveKey, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, true, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk(private), {name: X448}, false, [deriveKey, deriveBits, deriveKey, deriveBits, decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , {name: X448}, true, [encrypt])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , {name: X448}, false, [encrypt])
@@ -672,6 +1072,22 @@
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Bad usages: importKey(jwk (public) , {name: X448}, false, [deriveBits])
   assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, true, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, false, [encapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, true, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, false, [encapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, true, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, false, [decapsulateKey])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, true, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
+[FAIL] Bad usages: importKey(jwk (public) , {name: X448}, false, [decapsulateBits])
+  assert_equals: Bad usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: importKey(pkcs8, X448, true, [])
   assert_equals: Empty usages not supported. expected "SyntaxError" but got "NotSupportedError"
 [FAIL] Empty usages: importKey(pkcs8, X448, false, [])
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_fixtures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_fixtures.js
index 6a7f583..01c65d60 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_fixtures.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_failures_fixtures.js
@@ -1,438 +1,85 @@
-// Setup: define the correct behaviors that should be sought, and create
-// helper functions that generate all possible test parameters for
-// different situations.
+function algorithmName(algorithm) {
+    return algorithm.name || algorithm;
+}
+
+function privateJwk(name) {
+    return Object.assign({}, okpKeyData[name].jwk);
+}
+
+function publicJwk(name) {
+    var jwk = privateJwk(name);
+    delete jwk.d;
+    return jwk;
+}
+
 function getValidKeyData(algorithm) {
-    return validKeyData[algorithm.name || algorithm];
+    var name = algorithmName(algorithm);
+    var key = okpKeyData[name];
+    return [
+        {format: "spki", data: key.spki},
+        {format: "pkcs8", data: key.pkcs8},
+        {format: "raw", data: key.raw},
+        {format: "jwk", data: privateJwk(name)},
+        {format: "jwk", data: publicJwk(name)}
+    ];
 }
 
 function getBadKeyLengthData(algorithm) {
-    return badKeyLengthData[algorithm.name || algorithm];
+    var name = algorithmName(algorithm);
+    var key = okpKeyData[name];
+    var badPrivateJwk = privateJwk(name);
+    var badPublicJwk = publicJwk(name);
+    badPrivateJwk.d = badPrivateJwk.d.slice(0, -1);
+    badPublicJwk.x = badPublicJwk.x.slice(0, -1);
+    return [
+        {format: "spki", data: key.spki.slice(0, -1)},
+        {format: "pkcs8", data: key.pkcs8.slice(0, -1)},
+        {format: "raw", data: key.raw.slice(0, -1)},
+        {format: "jwk", data: badPrivateJwk},
+        {format: "jwk", data: badPublicJwk}
+    ];
 }
 
 function getMissingJWKFieldKeyData(algorithm) {
-    return missingJWKFieldKeyData[algorithm.name || algorithm];
+    var name = algorithmName(algorithm);
+    var missingX = privateJwk(name);
+    var missingKty = privateJwk(name);
+    var missingCrv = name === "Ed448" ? privateJwk(name) : publicJwk(name);
+    delete missingX.x;
+    delete missingKty.kty;
+    delete missingCrv.crv;
+    return [
+        {param: "x", data: missingX},
+        {param: "kty", data: missingKty},
+        {param: "crv", data: missingCrv}
+    ];
 }
 
 function getMismatchedJWKKeyData(algorithm) {
-    return mismatchedJWKKeyData[algorithm.name || algorithm];
+    var name = algorithmName(algorithm);
+    var jwk = privateJwk(name);
+    jwk.x = mismatchedPublicKeys[name];
+    return [jwk];
 }
 
 function getMismatchedKtyField(algorithm) {
-    return mismatchedKtyField[algorithm.name || algorithm];
+    return "EC";
 }
 
 function getMismatchedCrvField(algorithm) {
-    return mismatchedCrvField[algorithm.name || algorithm];
+    return mismatchedCurves[algorithmName(algorithm)];
 }
 
-var validKeyData = {
-    "Ed25519": [
-        {
-            format: "spki",
-            data: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61, 204])
-        },
-        {
-            format: "pkcs8",
-            data: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 243, 200, 244, 196, 141, 248, 120, 20, 110, 140, 211, 191, 109, 244, 229, 14, 56, 155, 167, 7, 78, 21, 194, 53, 45, 205, 93, 48, 141, 76, 168, 31])
-        },
-        {
-            format: "raw",
-            data: new Uint8Array([216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61, 204])
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "Ed25519",
-                d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8",
-                x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPcw",
-                kty: "OKP"
-            },
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "Ed25519",
-                x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPcw",
-                kty: "OKP"
-            },
-        }
-    ],
-    "Ed448": [
-        {
-            format: "spki",
-            data: new Uint8Array([48, 67, 48, 5, 6, 3, 43, 101, 113, 3, 58, 0, 171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90, 128]),
-        },
-        {
-            format: "pkcs8",
-            data: new Uint8Array([48, 71, 2, 1, 0, 48, 5, 6, 3, 43, 101, 113, 4, 59, 4, 57, 14, 255, 3, 69, 140, 40, 224, 23, 156, 82, 29, 227, 18, 201, 105, 183, 131, 67, 72, 236, 171, 153, 26, 96, 227, 178, 233, 167, 158, 76, 217, 228, 128, 239, 41, 23, 18, 210, 200, 61, 4, 114, 114, 213, 201, 244, 40, 102, 79, 105, 109, 38, 112, 69, 143, 29, 46]),
-        },
-        {
-            format: "raw",
-            data: new Uint8Array([171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90, 128]),
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "Ed448",
-                d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u",
-                x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA",
-                kty: "OKP"
-            },
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "Ed448",
-                x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA",
-                kty: "OKP"
-            },
-        },
-    ],
-    "X25519": [
-        {
-            format: "spki",
-            data: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0, 28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151, 6]),
-        },
-        {
-            format: "pkcs8",
-            data: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32, 200, 131, 142, 118, 208, 87, 223, 183, 216, 201, 90, 105, 225, 56, 22, 10, 221, 99, 115, 253, 113, 164, 210, 118, 187, 86, 227, 168, 27, 100, 255, 97]),
-        },
-        {
-            format: "raw",
-            data: new Uint8Array([28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151, 6]),
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "X25519",
-                d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E",
-                x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY",
-                kty: "OKP"
-            },
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "X25519",
-                x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY",
-                kty: "OKP"
-            },
-        },
-    ],
-    "X448": [
-        {
-            format: "spki",
-            data: new Uint8Array([48, 66, 48, 5, 6, 3, 43, 101, 111, 3, 57, 0, 182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206, 111]),
-        },
-        {
-            format: "pkcs8",
-            data: new Uint8Array([48, 70, 2, 1, 0, 48, 5, 6, 3, 43, 101, 111, 4, 58, 4, 56, 88, 199, 210, 154, 62, 181, 25, 178, 157, 0, 207, 177, 145, 187, 100, 252, 109, 138, 66, 216, 241, 113, 118, 39, 43, 137, 242, 39, 45, 24, 25, 41, 92, 101, 37, 192, 130, 150, 113, 176, 82, 239, 7, 39, 83, 15, 24, 142, 49, 208, 204, 83, 191, 38, 146, 158]),
-        },
-        {
-            format: "raw",
-            data: new Uint8Array([182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206, 111]),
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "X448",
-                d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4",
-                x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8",
-                kty: "OKP"
-            },
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "X448",
-                x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8",
-                kty: "OKP"
-            },
-        },
-    ],
+var mismatchedPublicKeys = {
+    "Ed25519": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+    "Ed448": "X9dEm1m0Yf0s54fsYWrUah2hNCSFpw4fig6nXYDpZ3jt8SR2m0bHBhvWeD3x5Q9s0foavq_oJWGA",
+    "X25519": "hSDwCYkwp1R0i33ctD73Wg2_Og0mOBr066SpjqqbTmo",
+    "X448": "mwj3zDG34+Z9ItWuoSEHSic70rg94Jxj+qc9LCLF2bvINmRyQdlT1AxbEtqIEg1TF3+A5TLEH6A"
 };
 
-// Removed just the last byte.
-var badKeyLengthData = {
-    "Ed25519": [
-        {
-            format: "spki",
-            data: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61])
-        },
-        {
-            format: "pkcs8",
-            data: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 243, 200, 244, 196, 141, 248, 120, 20, 110, 140, 211, 191, 109, 244, 229, 14, 56, 155, 167, 7, 78, 21, 194, 53, 45, 205, 93, 48, 141, 76, 168])
-        },
-        {
-            format: "raw",
-            data: new Uint8Array([216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61])
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "Ed25519",
-                d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB",
-                x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPcw",
-                kty: "OKP"
-            }
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "Ed25519",
-                x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPc",
-                kty: "OKP"
-            }
-        }
-    ],
-    "Ed448": [
-        {
-            format: "spki",
-            data: new Uint8Array([48, 67, 48, 5, 6, 3, 43, 101, 113, 3, 58, 0, 171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90]),
-        },
-        {
-            format: "pkcs8",
-            data: new Uint8Array([48, 71, 2, 1, 0, 48, 5, 6, 3, 43, 101, 113, 4, 59, 4, 57, 14, 255, 3, 69, 140, 40, 224, 23, 156, 82, 29, 227, 18, 201, 105, 183, 131, 67, 72, 236, 171, 153, 26, 96, 227, 178, 233, 167, 158, 76, 217, 228, 128, 239, 41, 23, 18, 210, 200, 61, 4, 114, 114, 213, 201, 244, 40, 102, 79, 105, 109, 38, 112, 69, 143, 29]),
-        },
-        {
-            format: "raw",
-            data: new Uint8Array([171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90]),
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "Ed448",
-                d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0",
-                x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA",
-                kty: "OKP"
-            },
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "Ed448",
-                x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalq",
-                kty: "OKP"
-            },
-        },
-    ],
-    "X25519": [
-        {
-            format: "spki",
-            data: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0, 28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151]),
-        },
-        {
-            format: "pkcs8",
-            data: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32, 200, 131, 142, 118, 208, 87, 223, 183, 216, 201, 90, 105, 225, 56, 22, 10, 221, 99, 115, 253, 113, 164, 210, 118, 187, 86, 227, 168, 27, 100, 255]),
-        },
-        {
-            format: "raw",
-            data: new Uint8Array([28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151]),
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "X25519",
-                x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lw",
-                kty: "OKP"
-            }
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "X25519",
-                d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2",
-                x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY",
-                kty: "OKP"
-            },
-        },
-    ],
-    "X448": [
-        {
-            format: "spki",
-            data: new Uint8Array([48, 66, 48, 5, 6, 3, 43, 101, 111, 3, 57, 0, 182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206]),
-        },
-        {
-            format: "pkcs8",
-            data: new Uint8Array([48, 70, 2, 1, 0, 48, 5, 6, 3, 43, 101, 111, 4, 58, 4, 56, 88, 199, 210, 154, 62, 181, 25, 178, 157, 0, 207, 177, 145, 187, 100, 252, 109, 138, 66, 216, 241, 113, 118, 39, 43, 137, 242, 39, 45, 24, 25, 41, 92, 101, 37, 192, 130, 150, 113, 176, 82, 239, 7, 39, 83, 15, 24, 142, 49, 208, 204, 83, 191, 38, 146]),
-        },
-        {
-            format: "raw",
-            data: new Uint8Array([182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206]),
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "X448",
-                d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp",
-                x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8",
-                kty: "OKP"
-            },
-        },
-        {
-            format: "jwk",
-            data: {
-                crv: "X448",
-                x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm",
-                kty: "OKP"
-            },
-        },
-    ],
-};
-
-var missingJWKFieldKeyData =  {
-    "Ed25519": [
-        {
-            param: "x",
-            data: {
-                crv: "Ed25519",
-                d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8",
-                kty: "OKP"
-            },
-        },
-        {
-            param: "kty",
-            data: {
-                crv: "Ed25519",
-                x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
-                d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8",
-            },
-        },
-        {
-            param: "crv",
-            data: {
-                x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
-                kty: "OKP"
-            },
-        }
-    ],
-    "Ed448": [
-        {
-            param: "x",
-            data: {
-                crv: "Ed448",
-                d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u",
-                kty: "OKP"
-            }
-        },
-        {
-            param: "kty",
-            data: {
-                crv: "Ed448",
-                d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u",
-                x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA",
-            }
-        },
-        {
-            param: "crv",
-            data: {
-                d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u",
-                x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA",
-                kty: "OKP"
-            }
-        }
-    ],
-    "X25519": [
-        {
-            param: "x",
-            data: {
-                crv: "X25519",
-                d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E",
-                kty: "OKP"
-            },
-        },
-        {
-            param: "kty",
-            data: {
-                crv: "X25519",
-                d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E",
-                x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY",
-            },
-        },
-        {
-            param: "crv",
-            data: {
-                x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY",
-                kty: "OKP"
-            },
-        }
-    ],
-    "X448": [
-        {
-            param: "x",
-            data: {
-                crv: "X448",
-                d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4",
-                kty: "OKP"
-            }
-        },
-        {
-            param: "kty",
-            data: {
-                crv: "X448",
-                d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4",
-                x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8",
-            }
-        },
-        {
-            param: "crv",
-            data: {
-                x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8",
-                kty: "OKP"
-            }
-        }
-    ],
-};
-
-// The public key doesn't match the private key.
-var mismatchedJWKKeyData =  {
-    "Ed25519": [
-        {
-            crv: "Ed25519",
-            d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8",
-            x: "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
-            kty: "OKP"
-        },
-    ],
-    "Ed448": [
-        {
-            crv: "Ed448",
-            d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u",
-            x: "X9dEm1m0Yf0s54fsYWrUah2hNCSFpw4fig6nXYDpZ3jt8SR2m0bHBhvWeD3x5Q9s0foavq_oJWGA",
-            kty: "OKP"
-        },
-    ],
-    "X25519": [
-        {
-            crv: "X25519",
-            d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E",
-            x: "hSDwCYkwp1R0i33ctD73Wg2_Og0mOBr066SpjqqbTmo",
-            kty: "OKP"
-        },
-    ],
-    "X448": [
-        {
-
-            crv: "X448",
-            kty: "OKP",
-            d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4",
-            x: "mwj3zDG34+Z9ItWuoSEHSic70rg94Jxj+qc9LCLF2bvINmRyQdlT1AxbEtqIEg1TF3+A5TLEH6A",
-        },
-    ],
-}
-
-// The 'kty' field doesn't match the key algorithm.
-var mismatchedKtyField =  {
-    "Ed25519": "EC",
-    "X25519": "EC",
-    "Ed448": "EC",
-    "X448": "EC",
-}
-
-// The 'kty' field doesn't match the key algorithm.
-var mismatchedCrvField =  {
+var mismatchedCurves = {
     "Ed25519": "X25519",
     "X25519": "Ed25519",
     "Ed448": "X448",
-    "X448": "Ed448",
-}
+    "X448": "Ed448"
+};
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_fixtures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_fixtures.js
index 58b41d5..5fe0f05 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_fixtures.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/import_export/okp_importKey_fixtures.js
@@ -1,58 +1 @@
-var keyData = {
-    "Ed25519": {
-        privateUsages: ["sign"],
-        publicUsages: ["verify"],
-        spki: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61, 204]),
-        raw: new Uint8Array([216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61, 204]),
-        pkcs8: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 243, 200, 244, 196, 141, 248, 120, 20, 110, 140, 211, 191, 109, 244, 229, 14, 56, 155, 167, 7, 78, 21, 194, 53, 45, 205, 93, 48, 141, 76, 168, 31]),
-        jwk: {
-            crv: "Ed25519",
-            d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8",
-            x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPcw",
-            kty: "OKP"
-        }
-    },
-
-    "Ed448": {
-        privateUsages: ["sign"],
-        publicUsages: ["verify"],
-        spki: new Uint8Array([48, 67, 48, 5, 6, 3, 43, 101, 113, 3, 58, 0, 171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90, 128]),
-        raw: new Uint8Array([171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90, 128]),
-        pkcs8: new Uint8Array([48, 71, 2, 1, 0, 48, 5, 6, 3, 43, 101, 113, 4, 59, 4, 57, 14, 255, 3, 69, 140, 40, 224, 23, 156, 82, 29, 227, 18, 201, 105, 183, 131, 67, 72, 236, 171, 153, 26, 96, 227, 178, 233, 167, 158, 76, 217, 228, 128, 239, 41, 23, 18, 210, 200, 61, 4, 114, 114, 213, 201, 244, 40, 102, 79, 105, 109, 38, 112, 69, 143, 29, 46]),
-        jwk: {
-            crv: "Ed448",
-            d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u",
-            x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA",
-            kty: "OKP"
-        }
-    },
-
-    "X25519": {
-        privateUsages: ["deriveKey", "deriveBits"],
-        publicUsages: [],
-        spki: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0, 28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151, 6]),
-        raw: new Uint8Array([28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151, 6]),
-        pkcs8: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32, 200, 131, 142, 118, 208, 87, 223, 183, 216, 201, 90, 105, 225, 56, 22, 10, 221, 99, 115, 253, 113, 164, 210, 118, 187, 86, 227, 168, 27, 100, 255, 97]),
-        jwk: {
-            crv: "X25519",
-            d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E",
-            x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY",
-            kty: "OKP"
-        }
-    },
-
-    "X448": {
-        privateUsages: ["deriveKey", "deriveBits"],
-        publicUsages: [],
-        spki: new Uint8Array([48, 66, 48, 5, 6, 3, 43, 101, 111, 3, 57, 0, 182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206, 111]),
-        raw: new Uint8Array([182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206, 111]),
-        pkcs8: new Uint8Array([48, 70, 2, 1, 0, 48, 5, 6, 3, 43, 101, 111, 4, 58, 4, 56, 88, 199, 210, 154, 62, 181, 25, 178, 157, 0, 207, 177, 145, 187, 100, 252, 109, 138, 66, 216, 241, 113, 118, 39, 43, 137, 242, 39, 45, 24, 25, 41, 92, 101, 37, 192, 130, 150, 113, 176, 82, 239, 7, 39, 83, 15, 24, 142, 49, 208, 204, 83, 191, 38, 146, 158]),
-        jwk: {
-            crv: "X448",
-            d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4",
-            x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8",
-            kty: "OKP"
-        }
-    },
-
-};
+var keyData = okpKeyData;
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/ecdsa.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/ecdsa.https.any.js
index 3f1e2e5..2af5e05 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/ecdsa.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/ecdsa.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: sign() and verify() Using ECDSA
 // META: script=../util/helpers.js
 // META: script=ecdsa_vectors.js
+// META: script=signature.js
 // META: script=ecdsa.js
 // META: timeout=long
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/ecdsa.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/ecdsa.js
index cc13b1b..a1a744a 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/ecdsa.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/ecdsa.js
@@ -1,658 +1,71 @@
-
 function run_test() {
-    setup({explicit_done: true});
-
-    var subtle = self.crypto.subtle; // Change to test prefixed implementations
-
-    // When are all these tests really done? When all the promises they use have resolved.
-    var all_promises = [];
-
-    // Source file [algorithm_name]_vectors.js provides the getTestVectors method
-    // for the algorithm that drives these tests.
-    var testVectors = getTestVectors();
-    var invalidTestVectors = getInvalidTestVectors();
-
-    // Test verification first, because signing tests rely on that working
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-            promise_test(function(test) {
-                var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification");
-        });
-
-        all_promises.push(promise);
+    const subtle = self.crypto.subtle;
+    const normalize = vector => ({...vector, data: vector.plaintext});
+    const testVectors = getTestVectors().map(normalize);
+    const invalidTestVectors = getInvalidTestVectors().map(normalize);
+    const algorithmIdentifier = vector => ({
+        name: vector.algorithmName,
+        hash: vector.hashName,
+    });
+    const importAlgorithm = vector => ({
+        name: vector.algorithmName,
+        namedCurve: vector.namedCurve,
     });
 
-    // Test verification with an altered buffer during call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                signature[0] = 255 - signature[0];
-                var algorithm = {
-                    hash: vector.hashName,
-                    get name() {
-                        signature[0] = vector.signature[0];
-                        return vector.algorithmName;
-                    }
-                };
-                var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification with altered signature during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with altered signature during call");
-        });
-
-        all_promises.push(promise);
+    runSignatureTests({
+        vectors: testVectors,
+        invalidVectors: invalidTestVectors,
+        algorithmIdentifier,
+        importAlgorithm,
+        dataLabel: "plaintext",
     });
 
-    // Test verification with an altered buffer after call
     testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
+        promise_test(async function() {
+            const key = await subtle.importKey(
+                vector.publicKeyFormat,
+                vector.publicKeyBuffer,
+                importAlgorithm(vector),
+                false,
+                ["verify"]
+            );
+            const hash = vector.hashName === "SHA-1" ? "SHA-256" : "SHA-1";
+            const isVerified = await subtle.verify(
+                {name: vector.algorithmName, hash},
+                key,
+                vector.signature,
+                vector.data
+            );
+            assert_false(isVerified, "Signature NOT verified");
+        }, vector.name + " verification failure due to wrong hash");
 
-                signature[0] = 255 - signature[0];
-                return operation;
-            }, vector.name + " verification with altered signature after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with altered signature after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with a transferred buffer during call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                var algorithm = {
-                    get name() {
-                        signature.buffer.transfer();
-                        return vector.algorithmName;
-                    },
-                    hash: vector.hashName
-                };
-                var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification with transferred signature during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with transferred signature during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with a transferred buffer after call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                signature.buffer.transfer();
-                return operation;
-            }, vector.name + " verification with transferred signature after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with transferred signature after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful verification even if plaintext is altered during call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                plaintext[0] = 255 - plaintext[0];
-                var algorithm = {
-                    hash: vector.hashName,
-                    get name() {
-                        plaintext[0] = vector.plaintext[0];
-                        return vector.algorithmName;
-                    }
-                };
-                var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " with altered plaintext during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with altered plaintext during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful verification even if plaintext is altered after call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                plaintext[0] = 255 - plaintext[0];
-                return operation;
-            }, vector.name + " with altered plaintext after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with altered plaintext after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for failed verification if plaintext is transferred during call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                var algorithm = {
-                    get name() {
-                        plaintext.buffer.transfer();
-                        return vector.algorithmName;
-                    },
-                    hash: vector.hashName
-                };
-                var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " with transferred plaintext during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with transferred plaintext during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful verification even if plaintext is transferred after call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                plaintext.buffer.transfer();
-                return operation;
-            }, vector.name + " with transferred plaintext after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with transferred plaintext after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for failures due to using privateKey to verify.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-            promise_test(function(test) {
-                return subtle.verify(algorithm, vector.privateKey, vector.signature, vector.plaintext)
-                .then(function(plaintext) {
-                    assert_unreached("Should have thrown error for using privateKey to verify in " + vector.name + ": '" + err.message + "'");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
-                });
-            }, vector.name + " using privateKey to verify");
-
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " using privateKey to verify");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for failures due to using publicKey to sign.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-            promise_test(function(test) {
-                return subtle.sign(algorithm, vector.publicKey, vector.plaintext)
-                .then(function(signature) {
-                    assert_unreached("Should have thrown error for using publicKey to sign in " + vector.name + ": '" + err.message + "'");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
-                });
-            }, vector.name + " using publicKey to sign");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " using publicKey to sign");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for failures due to no "verify" usage.
-    testVectors.forEach(function(originalVector) {
-        var vector = Object.assign({}, originalVector);
-
-        var promise = importVectorKeys(vector, [], ["sign"])
-        .then(function(vectors) {
-            var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-            promise_test(function(test) {
-                return subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext)
-                .then(function(plaintext) {
-                    assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": '" + err.message + "'");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
-                });
-            }, vector.name + " no verify usage");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " no verify usage");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful signing and verification.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-            promise_test(function(test) {
-                return subtle.sign(algorithm, vector.privateKey, vector.plaintext)
-                .then(function(signature) {
-                    // Can we verify the signature?
-                    return subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext)
-                    .then(function(is_verified) {
-                        assert_true(is_verified, "Round trip verification works");
-                        return signature;
-                    }, function(err) {
-                        assert_unreached("verify error for test " + vector.name + ": '" + err.message + "'");
-                    });
-                }, function(err) {
-                    assert_unreached("sign error for test " + vector.name + ": '" + err.message + "'");
-                });
-            }, vector.name + " round trip");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested signing or verifying
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " round trip");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test signing with the wrong algorithm
-    testVectors.forEach(function(vector) {
-        // Want to get the key for the wrong algorithm
-        var promise = subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"])
-        .then(function(wrongKey) {
-            var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-            return importVectorKeys(vector, ["verify"], ["sign"])
-            .then(function(vectors) {
-                promise_test(function(test) {
-                    var operation = subtle.sign(algorithm, wrongKey, vector.plaintext)
-                    .then(function(signature) {
-                        assert_unreached("Signing should not have succeeded for " + vector.name);
-                    }, function(err) {
-                        assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
-                    });
-
-                    return operation;
-                }, vector.name + " signing with wrong algorithm name");
-
-            }, function(err) {
-                // We need a failed test if the importVectorKey operation fails, so
-                // we know we never tested verification.
-                promise_test(function(test) {
-                    assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-                }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name");
-            });
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'");
-            }, "generate wrong key step: " + vector.name + " signing with wrong algorithm name");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with the wrong algorithm
-    testVectors.forEach(function(vector) {
-        // Want to get the key for the wrong algorithm
-        var promise = subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"])
-        .then(function(wrongKey) {
-            return importVectorKeys(vector, ["verify"], ["sign"])
-            .then(function(vectors) {
-                var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-                promise_test(function(test) {
-                    var operation = subtle.verify(algorithm, wrongKey, vector.signature, vector.plaintext)
-                    .then(function(signature) {
-                        assert_unreached("Verifying should not have succeeded for " + vector.name);
-                    }, function(err) {
-                        assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
-                    });
-
-                    return operation;
-                }, vector.name + " verifying with wrong algorithm name");
-
-            }, function(err) {
-                // We need a failed test if the importVectorKey operation fails, so
-                // we know we never tested verification.
-                promise_test(function(test) {
-                    assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-                }, "importVectorKeys step: " + vector.name + " verifying with wrong algorithm name");
-            });
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'");
-            }, "generate wrong key step: " + vector.name + " verifying with wrong algorithm name");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification fails with wrong signature
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-            var signature = copyBuffer(vector.signature);
-            signature[0] = 255 - signature[0];
-            promise_test(function(test) {
-                var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification failure due to altered signature");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification failure due to altered signature");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification fails with wrong hash
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            var hashName = "SHA-1";
-            if (vector.hashName === "SHA-1") {
-                hashName = "SHA-256"
+        promise_test(async function() {
+            const key = await subtle.importKey(
+                vector.publicKeyFormat,
+                vector.publicKeyBuffer,
+                importAlgorithm(vector),
+                false,
+                ["verify"]
+            );
+            const hash = vector.hashName.substring(0, 3) +
+                vector.hashName.substring(4);
+            let error;
+            try {
+                await subtle.verify(
+                    {name: vector.algorithmName, hash},
+                    key,
+                    vector.signature,
+                    vector.data
+                );
+            } catch (caught) {
+                error = caught;
             }
-            var algorithm = {name: vector.algorithmName, hash: hashName};
-            promise_test(function(test) {
-                var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification failure due to wrong hash");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification failure due to wrong hash");
-        });
-
-        all_promises.push(promise);
+            assert_not_equals(error, undefined, "Verification should throw");
+            assert_equals(
+                error.name,
+                "NotSupportedError",
+                "Correctly throws NotSupportedError for illegal hash name"
+            );
+        }, vector.name + " verification failure due to bad hash name");
     });
-
-    // Test verification fails with bad hash name
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            // use the wrong name for the hash
-            var hashName = vector.hashName.substring(0, 3) + vector.hashName.substring(4);
-            var algorithm = {name: vector.algorithmName, hash: hashName};
-            promise_test(function(test) {
-                var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_unreached("Verification should throw an error");
-                }, function(err) {
-                    assert_equals(err.name, "NotSupportedError", "Correctly throws NotSupportedError for illegal hash name")
-                });
-
-                return operation;
-            }, vector.name + " verification failure due to bad hash name");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification failure due to bad hash name");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification fails with short (odd length) signature
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-            var signature = vector.signature.slice(1); // Skip the first byte
-            promise_test(function(test) {
-                var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification failure due to shortened signature");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification failure due to shortened signature");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification fails with wrong plaintext
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-            var plaintext = copyBuffer(vector.plaintext);
-            plaintext[0] = 255 - plaintext[0];
-            promise_test(function(test) {
-                var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification failure due to altered plaintext");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification failure due to altered plaintext");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test invalid signatures
-    invalidTestVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            var algorithm = {name: vector.algorithmName, hash: vector.hashName};
-            promise_test(function(test) {
-                var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature unexpectedly verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification");
-        });
-
-        all_promises.push(promise);
-    });
-
-    promise_test(function() {
-        return Promise.all(all_promises)
-            .then(function() {done();})
-            .catch(function() {done();})
-    }, "setup");
-
-    // A test vector has all needed fields for signing and verifying, EXCEPT that the
-    // key field may be null. This function replaces that null with the Correct
-    // CryptoKey object.
-    //
-    // Returns a Promise that yields an updated vector on success.
-    function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) {
-        var publicPromise, privatePromise;
-
-        if (vector.publicKey !== null) {
-            publicPromise = new Promise(function(resolve, reject) {
-                resolve(vector);
-            });
-        } else {
-            publicPromise = subtle.importKey(vector.publicKeyFormat, vector.publicKeyBuffer, {name: vector.algorithmName, namedCurve: vector.namedCurve}, false, publicKeyUsages)
-            .then(function(key) {
-                vector.publicKey = key;
-                return vector;
-            });        // Returns a copy of the sourceBuffer it is sent.
-        }
-
-        if (vector.privateKey !== null) {
-            privatePromise = new Promise(function(resolve, reject) {
-                resolve(vector);
-            });
-        } else {
-            privatePromise = subtle.importKey(vector.privateKeyFormat, vector.privateKeyBuffer, {name: vector.algorithmName, namedCurve: vector.namedCurve}, false, privateKeyUsages)
-            .then(function(key) {
-                vector.privateKey = key;
-                return vector;
-            });
-        }
-
-        return Promise.all([publicPromise, privatePromise]);
-    }
-
-    return;
 }
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa.js
index 7796156..a60ceda 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa.js
@@ -1,337 +1,10 @@
-
 function run_test(algorithmName) {
-  var subtle = self.crypto.subtle; // Change to test prefixed implementations
-
-  // Source file [algorithm_name]_vectors.js provides the getTestVectors method
-  // for the algorithm that drives these tests.
-  var testVectors = getTestVectors(algorithmName);
-
-  testVectors.forEach(function(vector) {
-      var algorithm = {name: vector.algorithmName};
-
-      // Test verification first, because signing tests rely on that working
-      promise_test(async() => {
-          let isVerified = false;
-          let key;
-          try {
-              key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]);
-              isVerified = await subtle.verify(algorithm, key, vector.signature, vector.data)
-          } catch (err) {
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-          };
-          assert_true(isVerified, "Signature verified");
-      }, vector.name + " verification");
-
-      // Test verification with an altered buffer during call
-      promise_test(async() => {
-          let isVerified = false;
-          let key;
-          try {
-              key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]);
-              var signature = copyBuffer(vector.signature);
-              signature[0] = 255 - signature[0];
-              isVerified = await subtle.verify({
-                  get name() {
-                      signature[0] = vector.signature[0];
-                      return vector.algorithmName;
-                  }
-              }, key, signature, vector.data);
-          } catch (err) {
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-          };
-          assert_true(isVerified, "Signature verified");
-      }, vector.name + " verification with altered signature during call");
-
-      // Test verification with an altered buffer after call
-      promise_test(async() => {
-          let isVerified = false;
-          let key;
-          try {
-              key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]);
-              var signature = copyBuffer(vector.signature);
-              [isVerified] = await Promise.all([
-                  subtle.verify(algorithm, key, signature, vector.data),
-                  signature[0] = 255 - signature[0]
-              ]);
-          } catch (err) {
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-          };
-          assert_true(isVerified, "Signature verified");
-      }, vector.name + " verification with altered signature after call");
-
-      // Test verification with a transferred buffer during call
-      promise_test(async() => {
-          let isVerified = false;
-          let key;
-          try {
-              key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]);
-              var signature = copyBuffer(vector.signature);
-              isVerified = await subtle.verify({
-                  get name() {
-                      signature.buffer.transfer();
-                      return vector.algorithmName;
-                  }
-              }, key, signature, vector.data);
-          } catch (err) {
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-          };
-          assert_false(isVerified, "Signature is NOT verified");
-      }, vector.name + " verification with transferred signature during call");
-
-      // Test verification with a transferred buffer after call
-      promise_test(async() => {
-          let isVerified = false;
-          let key;
-          try {
-              key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]);
-              var signature = copyBuffer(vector.signature);
-              var operation = subtle.verify(algorithm, key, signature, vector.data);
-              signature.buffer.transfer();
-              isVerified = await operation;
-          } catch (err) {
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-          };
-          assert_true(isVerified, "Signature verified");
-      }, vector.name + " verification with transferred signature after call");
-
-      // Check for successful verification even if data is altered during call.
-      promise_test(async() => {
-          let isVerified = false;
-          let key;
-          try {
-              key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]);
-              var data = copyBuffer(vector.data);
-              data[0] = 255 - data[0];
-              isVerified = await subtle.verify({
-                  get name() {
-                      data[0] = vector.data[0];
-                      return vector.algorithmName;
-                  }
-              }, key, vector.signature, data);
-          } catch (err) {
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-          };
-          assert_true(isVerified, "Signature verified");
-      }, vector.name + " with altered data during call");
-
-      // Check for successful verification even if data is altered after call.
-      promise_test(async() => {
-          let isVerified = false;
-          let key;
-          try {
-              key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]);
-              var data = copyBuffer(vector.data);
-              [isVerified] = await Promise.all([
-                  subtle.verify(algorithm, key, vector.signature, data),
-                  data[0] = 255 - data[0]
-              ]);
-          } catch (err) {
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-          };
-          assert_true(isVerified, "Signature verified");
-      }, vector.name + " with altered data after call");
-
-      // Check for failed verification if data is transferred during call.
-      promise_test(async() => {
-          let isVerified = false;
-          let key;
-          try {
-              key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]);
-              var data = copyBuffer(vector.data);
-              isVerified = await subtle.verify({
-                  get name() {
-                      data.buffer.transfer();
-                      return vector.algorithmName;
-                  }
-              }, key, vector.signature, data);
-          } catch (err) {
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-          };
-          assert_false(isVerified, "Signature is NOT verified");
-      }, vector.name + " with transferred data during call");
-
-      // Check for successful verification even if data is transferred after call.
-      promise_test(async() => {
-          let isVerified = false;
-          let key;
-          try {
-              key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]);
-              var data = copyBuffer(vector.data);
-              var operation = subtle.verify(algorithm, key, vector.signature, data);
-              data.buffer.transfer();
-              isVerified = await operation;
-          } catch (err) {
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-          };
-          assert_true(isVerified, "Signature verified");
-      }, vector.name + " with transferred data after call");
-
-      // Check for failures due to using privateKey to verify.
-      promise_test(async() => {
-          let isVerified = false;
-          let key;
-          try {
-              key = await subtle.importKey("pkcs8", vector.privateKeyBuffer, algorithm, false, ["sign"]);
-              isVerified = await subtle.verify(algorithm, key, vector.signature, vector.data)
-              assert_unreached("Should have thrown error for using privateKey to verify in " + vector.name);
-          } catch (err) {
-              if (err instanceof AssertionError)
-                  throw err;
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
-          };
-          assert_false(isVerified, "Signature verified");
-      }, vector.name + " using privateKey to verify");
-
-      // Check for failures due to using publicKey to sign.
-      promise_test(async() => {
-          let isVerified = false;
-          let key;
-          try {
-              key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]);
-              let signature = await subtle.sign(algorithm, key, vector.data);
-              assert_unreached("Should have thrown error for using publicKey to sign in " + vector.name);
-          } catch (err) {
-              if (err instanceof AssertionError)
-                  throw err;
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
-          };
-      }, vector.name + " using publicKey to sign");
-
-      // Check for failures due to no "verify" usage.
-      promise_test(async() => {
-          let isVerified = false;
-          let key;
-          try {
-              key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, []);
-              isVerified = await subtle.verify(algorithm, key, vector.signature, vector.data)
-              assert_unreached("Should have thrown error for no verify usage in " + vector.name);
-          } catch (err) {
-              if (err instanceof AssertionError)
-                  throw err;
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
-          };
-          assert_false(isVerified, "Signature verified");
-      }, vector.name + " no verify usage");
-
-      // Check for successful signing and verification.
-      var algorithm = {name: vector.algorithmName};
-      promise_test(async() => {
-          let isVerified = false;
-          let privateKey, publicKey;
-          let signature;
-          try {
-              privateKey = await subtle.importKey("pkcs8", vector.privateKeyBuffer, algorithm, false, ["sign"]);
-              publicKey = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]);
-              signature = await subtle.sign(algorithm, privateKey, vector.data);
-              isVerified = await subtle.verify(algorithm, publicKey, vector.signature, vector.data)
-          } catch (err) {
-              assert_false(publicKey === undefined || privateKey === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_false(signature === undefined, "sign error for test " + vector.name + ": '" + err.message + "'");
-              assert_unreached("verify error for test " + vector.name + ": '" + err.message + "'");
-          };
-          assert_true(isVerified, "Round trip verification works");
-      }, vector.name + " round trip");
-
-      // Test signing with the wrong algorithm
-      var algorithm = {name: vector.algorithmName};
-      promise_test(async() => {
-          let wrongKey;
-          try {
-              wrongKey = await subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"])
-              let signature = await subtle.sign(algorithm, wrongKey, vector.data);
-              assert_unreached("Signing should not have succeeded for " + vector.name);
-          } catch (err) {
-              if (err instanceof AssertionError)
-                  throw err;
-              assert_false(wrongKey === undefined, "Generate wrong key for test " + vector.name + " failed: '" + err.message + "'");
-              assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
-          };
-      }, vector.name + " signing with wrong algorithm name");
-
-      // Test verification with the wrong algorithm
-      var algorithm = {name: vector.algorithmName};
-      promise_test(async() => {
-          let wrongKey;
-          try {
-              wrongKey = await subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"])
-              let isVerified = await subtle.verify(algorithm, wrongKey, vector.signature, vector.data)
-              assert_unreached("Verifying should not have succeeded for " + vector.name);
-          } catch (err) {
-              if (err instanceof AssertionError)
-                  throw err;
-              assert_false(wrongKey === undefined, "Generate wrong key for test " + vector.name + " failed: '" + err.message + "'");
-              assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
-          };
-      }, vector.name + " verifying with wrong algorithm name");
-
-      // Test verification fails with wrong signature
-      var algorithm = {name: vector.algorithmName};
-      promise_test(async() => {
-          let key;
-          let isVerified = true;
-          var signature = copyBuffer(vector.signature);
-          signature[0] = 255 - signature[0];
-          try {
-              key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]);
-              isVerified = await subtle.verify(algorithm, key, signature, vector.data)
-          } catch (err) {
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-          };
-          assert_false(isVerified, "Signature verified");
-      }, vector.name + " verification failure due to altered signature");
-
-      // Test verification fails with short (odd length) signature
-      promise_test(async() => {
-          let key;
-          let isVerified = true;
-          var signature = vector.signature.slice(1); // Skip the first byte
-          try {
-              key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]);
-              isVerified = await subtle.verify(algorithm, key, signature, vector.data)
-          } catch (err) {
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-          };
-          assert_false(isVerified, "Signature verified");
-      }, vector.name + " verification failure due to shortened signature");
-
-      // Test verification fails with wrong data
-      promise_test(async() => {
-          let key;
-          let isVerified = true;
-          var data = copyBuffer(vector.data);
-          data[0] = 255 - data[0];
-          try {
-              key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]);
-              isVerified = await subtle.verify(algorithm, key, vector.signature, data)
-          } catch (err) {
-              assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
-              assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-          };
-          assert_false(isVerified, "Signature verified");
-      }, vector.name + " verification failure due to altered data");
-
-      // Test that generated keys are valid for signing and verifying.
-      promise_test(async() => {
-          let key = await subtle.generateKey(algorithm, false, ["sign", "verify"]);
-          let signature = await subtle.sign(algorithm, key.privateKey, vector.data);
-          let isVerified = await subtle.verify(algorithm, key.publicKey, signature, vector.data);
-          assert_true(isVerified, "Verificaton failed.");
-      }, "Sign and verify using generated " + vector.algorithmName + " keys.");
+  runSignatureTests({
+    vectors: getTestVectors(algorithmName),
+    algorithmIdentifier(vector) {
+      return {name: vector.algorithmName};
+    },
+    katFirst: true,
+    generatedKeys: true,
   });
-
-  return;
 }
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve25519.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve25519.https.any.js
index b012b64..a4f32cb 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve25519.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve25519.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: sign() and verify() Using EdDSA
 // META: script=../util/helpers.js
 // META: script=eddsa_vectors.js
+// META: script=signature.js
 // META: script=eddsa.js
 // META: timeout=long
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve448.tentative.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve448.tentative.https.any-expected.txt
index 377a39e3..28b846e 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve448.tentative.https.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve448.tentative.https.any-expected.txt
@@ -1,40 +1,40 @@
 This is a testharness.js-based test.
 [FAIL] EdDSA Ed448 verification
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 verification with altered signature during call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 verification with altered signature after call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 verification with transferred signature during call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 verification with transferred signature after call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 with altered data during call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 with altered data after call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 with transferred data during call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 with transferred data after call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 using privateKey to verify
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 using publicKey to sign
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 no verify usage
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 round trip
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 signing with wrong algorithm name
   assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'sign' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
 [FAIL] EdDSA Ed448 verifying with wrong algorithm name
   assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'verify' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
 [FAIL] EdDSA Ed448 verification failure due to altered signature
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 verification failure due to shortened signature
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 verification failure due to altered data
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] Sign and verify using generated Ed448 keys.
   promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'generateKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve448.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve448.tentative.https.any.js
index 281bb63..8dd43dfe 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve448.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve448.tentative.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: sign() and verify() Using EdDSA
 // META: script=../util/helpers.js
 // META: script=eddsa_vectors.js
+// META: script=signature.js
 // META: script=eddsa.js
 // META: timeout=long
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve448.tentative.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve448.tentative.https.any.worker-expected.txt
index 377a39e3..28b846e 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve448.tentative.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/eddsa_curve448.tentative.https.any.worker-expected.txt
@@ -1,40 +1,40 @@
 This is a testharness.js-based test.
 [FAIL] EdDSA Ed448 verification
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 verification with altered signature during call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 verification with altered signature after call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 verification with transferred signature during call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 verification with transferred signature after call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 with altered data during call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 with altered data after call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 with transferred data during call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 with transferred data after call
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 using privateKey to verify
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 using publicKey to sign
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 no verify usage
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 round trip
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 signing with wrong algorithm name
   assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'sign' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
 [FAIL] EdDSA Ed448 verifying with wrong algorithm name
   assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'verify' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
 [FAIL] EdDSA Ed448 verification failure due to altered signature
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 verification failure due to shortened signature
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] EdDSA Ed448 verification failure due to altered data
-  assert_false: importKey failed for EdDSA Ed448. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' expected false got true
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 [FAIL] Sign and verify using generated Ed448 keys.
   promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'generateKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/hmac.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/hmac.https.any.js
index a6c88f8..07061118 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/hmac.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/hmac.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: sign() and verify() Using HMAC
 // META: script=../util/helpers.js
 // META: script=hmac_vectors.js
+// META: script=mac.js
 // META: script=hmac.js
 // META: timeout=long
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/hmac.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/hmac.js
index 929d946..05b9ced 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/hmac.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/hmac.js
@@ -1,494 +1,11 @@
-
 function run_test() {
-    setup({explicit_done: true});
-
-    var subtle = self.crypto.subtle; // Change to test prefixed implementations
-
-    // When are all these tests really done? When all the promises they use have resolved.
-    var all_promises = [];
-
-    // Source file hmac_vectors.js provides the getTestVectors method
-    // for the algorithm that drives these tests.
-    var testVectors = getTestVectors();
-
-    // Test verification first, because signing tests rely on that working
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with an altered buffer during call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                signature[0] = 255 - signature[0];
-                var operation = subtle.verify({
-                    get name() {
-                        signature[0] = vector.signature[0];
-                        return "HMAC";
-                    },
-                    hash: vector.hash
-                }, vector.key, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature is not verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification with altered signature during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with altered signature during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with an altered buffer after call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature is not verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                signature[0] = 255 - signature[0];
-                return operation;
-            }, vector.name + " verification with altered signature after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with altered signature after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with a transferred buffer during call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                var operation = subtle.verify({
-                    get name() {
-                        signature.buffer.transfer();
-                        return "HMAC";
-                    },
-                    hash: vector.hash
-                }, vector.key, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification with transferred signature during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with transferred signature during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with a transferred buffer after call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature is not verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                signature.buffer.transfer();
-                return operation;
-            }, vector.name + " verification with transferred signature after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with transferred signature after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful verification even if plaintext is altered during call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                plaintext[0] = 255 - plaintext[0];
-                var operation = subtle.verify({
-                    hash: vector.hash,
-                    get name() {
-                        plaintext[0] = vector.plaintext[0];
-                        return "HMAC";
-                    }
-                }, vector.key, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " with altered plaintext during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with altered plaintext during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful verification even if plaintext is altered after call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                plaintext[0] = 255 - plaintext[0];
-                return operation;
-            }, vector.name + " with altered plaintext after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with altered plaintext after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for failed verification if plaintext is transferred during call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                var operation = subtle.verify({
-                    get name() {
-                        plaintext.buffer.transfer();
-                        return "HMAC";
-                    },
-                    hash: vector.hash
-                }, vector.key, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " with transferred plaintext during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with transferred plaintext during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful verification even if plaintext is transferred after call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                plaintext.buffer.transfer();
-                return operation;
-            }, vector.name + " with transferred plaintext after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with transferred plaintext after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for failures due to no "verify" usage.
-    testVectors.forEach(function(originalVector) {
-        var vector = Object.assign({}, originalVector);
-
-        var promise = importVectorKeys(vector, ["sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                return subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, vector.plaintext)
-                .then(function(plaintext) {
-                    assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": '" + err.message + "'");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
-                });
-            }, vector.name + " no verify usage");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " no verify usage");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful signing and verification.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                return subtle.sign({name: "HMAC", hash: vector.hash}, vector.key, vector.plaintext)
-                .then(function(signature) {
-                    assert_true(equalBuffers(signature, vector.signature), "Signing did not give the expected output");
-                    // Can we get the verify the new signature?
-                    return subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext)
-                    .then(function(is_verified) {
-                        assert_true(is_verified, "Round trip verifies");
-                        return signature;
-                    }, function(err) {
-                        assert_unreached("verify error for test " + vector.name + ": '" + err.message + "'");
-                    });
-                });
-            }, vector.name + " round trip");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested signing or verifying
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " round trip");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test signing with the wrong algorithm
-    testVectors.forEach(function(vector) {
-        // Want to get the key for the wrong algorithm
-        var promise = subtle.generateKey({name: "ECDSA", namedCurve: "P-256", hash: "SHA-256"}, false, ["sign", "verify"])
-        .then(function(wrongKey) {
-            return importVectorKeys(vector, ["verify", "sign"])
-            .then(function(vectors) {
-                promise_test(function(test) {
-                    var operation = subtle.sign({name: "HMAC", hash: vector.hash}, wrongKey.privateKey, vector.plaintext)
-                    .then(function(signature) {
-                        assert_unreached("Signing should not have succeeded for " + vector.name);
-                    }, function(err) {
-                        assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
-                    });
-
-                    return operation;
-                }, vector.name + " signing with wrong algorithm name");
-
-            }, function(err) {
-                // We need a failed test if the importVectorKey operation fails, so
-                // we know we never tested verification.
-                promise_test(function(test) {
-                    assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-                }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name");
-            });
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'");
-            }, "generate wrong key step: " + vector.name + " signing with wrong algorithm name");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with the wrong algorithm
-    testVectors.forEach(function(vector) {
-        // Want to get the key for the wrong algorithm
-        var promise = subtle.generateKey({name: "ECDSA", namedCurve: "P-256", hash: "SHA-256"}, false, ["sign", "verify"])
-        .then(function(wrongKey) {
-            return importVectorKeys(vector, ["verify", "sign"])
-            .then(function(vector) {
-                promise_test(function(test) {
-                    var operation = subtle.verify({name: "HMAC", hash: vector.hash}, wrongKey.publicKey, vector.signature, vector.plaintext)
-                    .then(function(signature) {
-                        assert_unreached("Verifying should not have succeeded for " + vector.name);
-                    }, function(err) {
-                        assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
-                    });
-
-                    return operation;
-                }, vector.name + " verifying with wrong algorithm name");
-
-            }, function(err) {
-                // We need a failed test if the importVectorKey operation fails, so
-                // we know we never tested verification.
-                promise_test(function(test) {
-                    assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-                }, "importVectorKeys step: " + vector.name + " verifying with wrong algorithm name");
-            });
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'");
-            }, "generate wrong key step: " + vector.name + " verifying with wrong algorithm name");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Verification should fail if the plaintext is changed
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            var plaintext = copyBuffer(vector.plaintext);
-            plaintext[0] = 255 - plaintext[0];
-            promise_test(function(test) {
-                var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification failure due to wrong plaintext");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification failure due to wrong plaintext");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Verification should fail if the signature is changed
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            var signature = copyBuffer(vector.signature);
-            signature[0] = 255 - signature[0];
-            promise_test(function(test) {
-                var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification failure due to wrong signature");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification failure due to wrong signature");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Verification should fail if the signature is wrong length
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            var signature = vector.signature.slice(1); // Drop first byte
-            promise_test(function(test) {
-                var operation = subtle.verify({name: "HMAC", hash: vector.hash}, vector.key, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification failure due to short signature");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification failure due to short signature");
-        });
-
-        all_promises.push(promise);
-    });
-
-
-
-    promise_test(function() {
-        return Promise.all(all_promises)
-            .then(function() {done();})
-            .catch(function() {done();})
-    }, "setup");
-
-    // A test vector has all needed fields for signing and verifying, EXCEPT that the
-    // key field may be null. This function replaces that null with the Correct
-    // CryptoKey object.
-    //
-    // Returns a Promise that yields an updated vector on success.
-    function importVectorKeys(vector, keyUsages) {
-        if (vector.key !== null) {
-            return new Promise(function(resolve, reject) {
-                resolve(vector);
-            });
-        } else {
-            return subtle.importKey("raw", vector.keyBuffer, {name: "HMAC", hash: vector.hash}, false, keyUsages)
-            .then(function(key) {
-                vector.key = key;
-                return vector;
-            });
+    runMacTests({
+        importFormat: "raw",
+        importAlgorithm: function(vector) {
+            return {name: "HMAC", hash: vector.hash};
+        },
+        operationAlgorithm: function(vector) {
+            return {name: "HMAC", hash: vector.hash};
         }
-    }
-
-    return;
+    });
 }
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/kmac.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/kmac.js
index d9d82d1..ac30a555 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/kmac.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/kmac.js
@@ -1,586 +1,25 @@
 function run_test() {
-    setup({explicit_done: true});
-
-    var subtle = self.crypto.subtle; // Change to test prefixed implementations
-
-    // When are all these tests really done? When all the promises they use have resolved.
-    var all_promises = [];
-
-    // Source file kmac_vectors.js provides the getTestVectors method
-    // for the algorithm that drives these tests.
-    var testVectors = getTestVectors();
-
-    // Test verification first, because signing tests rely on that working
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var algorithmParams = {name: vector.algorithm, outputLength: vector.outputLength};
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                var operation = subtle.verify(algorithmParams, vector.key, vector.signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with an altered buffer during call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                signature[0] = 255 - signature[0];
-                var algorithmParams = {
-                    outputLength: vector.outputLength,
-                    get name() {
-                        signature[0] = vector.signature[0];
-                        return vector.algorithm;
-                    }
-                };
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                var operation = subtle.verify(algorithmParams, vector.key, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature is not verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification with altered signature during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with altered signature during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with an altered buffer after call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                var algorithmParams = {name: vector.algorithm, outputLength: vector.outputLength};
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                var operation = subtle.verify(algorithmParams, vector.key, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature is not verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                signature[0] = 255 - signature[0];
-                return operation;
-            }, vector.name + " verification with altered signature after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with altered signature after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with a transferred buffer during call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                var algorithmParams = {
-                    get name() {
-                        signature.buffer.transfer();
-                        return vector.algorithm;
-                    },
-                    outputLength: vector.outputLength
-                };
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                var operation = subtle.verify(algorithmParams, vector.key, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification with transferred signature during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with transferred signature during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with a transferred buffer after call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                var algorithmParams = {name: vector.algorithm, outputLength: vector.outputLength};
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                var operation = subtle.verify(algorithmParams, vector.key, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature is not verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                signature.buffer.transfer();
-                return operation;
-            }, vector.name + " verification with transferred signature after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with transferred signature after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful verification even if plaintext is altered during call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                plaintext[0] = 255 - plaintext[0];
-                var algorithmParams = {
-                    outputLength: vector.outputLength,
-                    get name() {
-                        plaintext[0] = vector.plaintext[0];
-                        return vector.algorithm;
-                    }
-                };
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                var operation = subtle.verify(algorithmParams, vector.key, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " with altered plaintext during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with altered plaintext during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful verification even if plaintext is altered after call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                var algorithmParams = {name: vector.algorithm, outputLength: vector.outputLength};
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                var operation = subtle.verify(algorithmParams, vector.key, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                plaintext[0] = 255 - plaintext[0];
-                return operation;
-            }, vector.name + " with altered plaintext after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with altered plaintext after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for failed verification if plaintext is transferred during call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                var algorithmParams = {
-                    get name() {
-                        plaintext.buffer.transfer();
-                        return vector.algorithm;
-                    },
-                    outputLength: vector.outputLength
-                };
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                var operation = subtle.verify(algorithmParams, vector.key, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " with transferred plaintext during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with transferred plaintext during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful verification even if plaintext is transferred after call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                var algorithmParams = {name: vector.algorithm, outputLength: vector.outputLength};
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                var operation = subtle.verify(algorithmParams, vector.key, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                plaintext.buffer.transfer();
-                return operation;
-            }, vector.name + " with transferred plaintext after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with transferred plaintext after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for failures due to no "verify" usage.
-    testVectors.forEach(function(originalVector) {
-        var vector = Object.assign({}, originalVector);
-
-        var promise = importVectorKeys(vector, ["sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var algorithmParams = {name: vector.algorithm, outputLength: vector.outputLength};
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                return subtle.verify(algorithmParams, vector.key, vector.signature, vector.plaintext)
-                .then(function(plaintext) {
-                    assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": '" + err.message + "'");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
-                });
-            }, vector.name + " no verify usage");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " no verify usage");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful signing and verification.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var algorithmParams = {name: vector.algorithm, outputLength: vector.outputLength};
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                return subtle.sign(algorithmParams, vector.key, vector.plaintext)
-                .then(function(signature) {
-                    assert_true(equalBuffers(signature, vector.signature), "Signing did not give the expected output");
-                    // Can we get the verify the new signature?
-                    return subtle.verify(algorithmParams, vector.key, signature, vector.plaintext)
-                    .then(function(is_verified) {
-                        assert_true(is_verified, "Round trip verifies");
-                        return signature;
-                    }, function(err) {
-                        assert_unreached("verify error for test " + vector.name + ": '" + err.message + "'");
-                    });
-                });
-            }, vector.name + " round trip");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested signing or verifying
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " round trip");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test signing with the wrong algorithm
-    testVectors.forEach(function(vector) {
-        // Want to get the key for the wrong algorithm
-        var promise = subtle.generateKey({name: "ECDSA", namedCurve: "P-256", hash: "SHA-256"}, false, ["sign", "verify"])
-        .then(function(wrongKey) {
-            return importVectorKeys(vector, ["verify", "sign"])
-            .then(function(vectors) {
-                promise_test(function(test) {
-                    var algorithmParams = {name: vector.algorithm, outputLength: vector.outputLength};
-                    if (vector.customization !== undefined) {
-                        algorithmParams.customization = vector.customization;
-                    }
-                    var operation = subtle.sign(algorithmParams, wrongKey.privateKey, vector.plaintext)
-                    .then(function(signature) {
-                        assert_unreached("Signing should not have succeeded for " + vector.name);
-                    }, function(err) {
-                        assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
-                    });
-
-                    return operation;
-                }, vector.name + " signing with wrong algorithm name");
-
-            }, function(err) {
-                // We need a failed test if the importVectorKey operation fails, so
-                // we know we never tested verification.
-                promise_test(function(test) {
-                    assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-                }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name");
-            });
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'");
-            }, "generate wrong key step: " + vector.name + " signing with wrong algorithm name");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with the wrong algorithm
-    testVectors.forEach(function(vector) {
-        // Want to get the key for the wrong algorithm
-        var promise = subtle.generateKey({name: "ECDSA", namedCurve: "P-256", hash: "SHA-256"}, false, ["sign", "verify"])
-        .then(function(wrongKey) {
-            return importVectorKeys(vector, ["verify", "sign"])
-            .then(function(vector) {
-                promise_test(function(test) {
-                    var algorithmParams = {name: vector.algorithm, outputLength: vector.outputLength};
-                    if (vector.customization !== undefined) {
-                        algorithmParams.customization = vector.customization;
-                    }
-                    var operation = subtle.verify(algorithmParams, wrongKey.publicKey, vector.signature, vector.plaintext)
-                    .then(function(signature) {
-                        assert_unreached("Verifying should not have succeeded for " + vector.name);
-                    }, function(err) {
-                        assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
-                    });
-
-                    return operation;
-                }, vector.name + " verifying with wrong algorithm name");
-
-            }, function(err) {
-                // We need a failed test if the importVectorKey operation fails, so
-                // we know we never tested verification.
-                promise_test(function(test) {
-                    assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-                }, "importVectorKeys step: " + vector.name + " verifying with wrong algorithm name");
-            });
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'");
-            }, "generate wrong key step: " + vector.name + " verifying with wrong algorithm name");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Verification should fail if the plaintext is changed
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            var plaintext = copyBuffer(vector.plaintext);
-            plaintext[0] = 255 - plaintext[0];
-            promise_test(function(test) {
-                var algorithmParams = {name: vector.algorithm, outputLength: vector.outputLength};
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                var operation = subtle.verify(algorithmParams, vector.key, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification failure due to wrong plaintext");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification failure due to wrong plaintext");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Verification should fail if the signature is changed
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            var signature = copyBuffer(vector.signature);
-            signature[0] = 255 - signature[0];
-            promise_test(function(test) {
-                var algorithmParams = {name: vector.algorithm, outputLength: vector.outputLength};
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                var operation = subtle.verify(algorithmParams, vector.key, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification failure due to wrong signature");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification failure due to wrong signature");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Verification should fail if the signature is wrong length
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            var signature = vector.signature.slice(1); // Drop first byte
-            promise_test(function(test) {
-                var algorithmParams = {name: vector.algorithm, outputLength: vector.outputLength};
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                var operation = subtle.verify(algorithmParams, vector.key, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification failure due to short signature");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification failure due to short signature");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification failure due to wrong length parameter
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify", "sign"])
-        .then(function(vector) {
-            promise_test(function(test) {
-                var differentLength = vector.outputLength === 256 ? 512 : 256;
-                var algorithmParams = {name: vector.algorithm, outputLength: differentLength};
-                if (vector.customization !== undefined) {
-                    algorithmParams.customization = vector.customization;
-                }
-                var operation = subtle.verify(algorithmParams, vector.key, vector.signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified with wrong length");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification failure due to wrong length parameter");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification failure due to wrong length parameter");
-        });
-
-        all_promises.push(promise);
-    });
-
-    promise_test(function() {
-        return Promise.all(all_promises)
-            .then(function() {done();})
-            .catch(function() {done();})
-    }, "setup");
-
-    // A test vector has all needed fields for signing and verifying, EXCEPT that the
-    // key field may be null. This function replaces that null with the Correct
-    // CryptoKey object.
-    //
-    // Returns a Promise that yields an updated vector on success.
-    function importVectorKeys(vector, keyUsages) {
-        if (vector.key !== null) {
-            return new Promise(function(resolve, reject) {
-                resolve(vector);
-            });
-        } else {
-            return subtle.importKey("raw-secret", vector.keyBuffer, {name: vector.algorithm}, false, keyUsages)
-            .then(function(key) {
-                vector.key = key;
-                return vector;
-            });
+    function operationAlgorithm(vector) {
+        var algorithm = {
+            name: vector.algorithm,
+            outputLength: vector.outputLength
+        };
+        if (vector.customization !== undefined) {
+            algorithm.customization = vector.customization;
         }
+        return algorithm;
     }
 
-    return;
+    runMacTests({
+        importFormat: "raw-secret",
+        importAlgorithm: function(vector) {
+            return {name: vector.algorithm};
+        },
+        operationAlgorithm: operationAlgorithm,
+        wrongVerificationAlgorithm: function(vector) {
+            var algorithm = operationAlgorithm(vector);
+            algorithm.outputLength = vector.outputLength === 256 ? 512 : 256;
+            return algorithm;
+        }
+    });
 }
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/kmac.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/kmac.tentative.https.any.js
index 3d34c575..0a57ba7 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/kmac.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/kmac.tentative.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: sign() and verify() Using KMAC
 // META: script=../util/helpers.js
 // META: script=kmac_vectors.js
+// META: script=mac.js
 // META: script=kmac.js
 // META: timeout=long
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mac.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mac.js
new file mode 100644
index 0000000..27787c9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mac.js
@@ -0,0 +1,514 @@
+
+function runMacTests(options) {
+    setup({explicit_done: true});
+
+    var subtle = self.crypto.subtle; // Change to test prefixed implementations
+
+    // When are all these tests really done? When all the promises they use have resolved.
+    var all_promises = [];
+
+    // The algorithm-specific vector source provides getTestVectors().
+    var testVectors = getTestVectors();
+
+    function operationAlgorithm(vector, nameGetter) {
+        var algorithm = options.operationAlgorithm(vector);
+        if (nameGetter !== undefined) {
+            Object.defineProperty(algorithm, "name", {
+                enumerable: true,
+                get: nameGetter
+            });
+        }
+        return algorithm;
+    }
+
+    // Test verification first, because signing tests rely on that working
+    testVectors.forEach(function(vector) {
+        var promise = importVectorKeys(vector, ["verify", "sign"])
+        .then(function(vector) {
+            promise_test(function(test) {
+                var operation = subtle.verify(operationAlgorithm(vector), vector.key, vector.signature, vector.plaintext)
+                .then(function(is_verified) {
+                    assert_true(is_verified, "Signature verified");
+                }, function(err) {
+                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
+                });
+
+                return operation;
+            }, vector.name + " verification");
+
+        }, function(err) {
+            // We need a failed test if the importVectorKey operation fails, so
+            // we know we never tested verification.
+            promise_test(function(test) {
+                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+            }, "importVectorKeys step: " + vector.name + " verification");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Test verification with an altered buffer during call
+    testVectors.forEach(function(vector) {
+        var promise = importVectorKeys(vector, ["verify", "sign"])
+        .then(function(vector) {
+            promise_test(function(test) {
+                var signature = copyBuffer(vector.signature);
+                signature[0] = 255 - signature[0];
+                var operation = subtle.verify(operationAlgorithm(vector, function() {
+                        signature[0] = vector.signature[0];
+                        return options.operationAlgorithm(vector).name;
+                    }), vector.key, signature, vector.plaintext)
+                .then(function(is_verified) {
+                    assert_true(is_verified, "Signature is not verified");
+                }, function(err) {
+                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
+                });
+
+                return operation;
+            }, vector.name + " verification with altered signature during call");
+        }, function(err) {
+            promise_test(function(test) {
+                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+            }, "importVectorKeys step: " + vector.name + " verification with altered signature during call");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Test verification with an altered buffer after call
+    testVectors.forEach(function(vector) {
+        var promise = importVectorKeys(vector, ["verify", "sign"])
+        .then(function(vector) {
+            promise_test(function(test) {
+                var signature = copyBuffer(vector.signature);
+                var operation = subtle.verify(operationAlgorithm(vector), vector.key, signature, vector.plaintext)
+                .then(function(is_verified) {
+                    assert_true(is_verified, "Signature is not verified");
+                }, function(err) {
+                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
+                });
+
+                signature[0] = 255 - signature[0];
+                return operation;
+            }, vector.name + " verification with altered signature after call");
+        }, function(err) {
+            promise_test(function(test) {
+                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+            }, "importVectorKeys step: " + vector.name + " verification with altered signature after call");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Test verification with a transferred buffer during call
+    testVectors.forEach(function(vector) {
+        var promise = importVectorKeys(vector, ["verify", "sign"])
+        .then(function(vector) {
+            promise_test(function(test) {
+                var signature = copyBuffer(vector.signature);
+                var operation = subtle.verify(operationAlgorithm(vector, function() {
+                        signature.buffer.transfer();
+                        return options.operationAlgorithm(vector).name;
+                    }), vector.key, signature, vector.plaintext)
+                .then(function(is_verified) {
+                    assert_false(is_verified, "Signature is NOT verified");
+                }, function(err) {
+                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
+                });
+
+                return operation;
+            }, vector.name + " verification with transferred signature during call");
+        }, function(err) {
+            promise_test(function(test) {
+                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+            }, "importVectorKeys step: " + vector.name + " verification with transferred signature during call");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Test verification with a transferred buffer after call
+    testVectors.forEach(function(vector) {
+        var promise = importVectorKeys(vector, ["verify", "sign"])
+        .then(function(vector) {
+            promise_test(function(test) {
+                var signature = copyBuffer(vector.signature);
+                var operation = subtle.verify(operationAlgorithm(vector), vector.key, signature, vector.plaintext)
+                .then(function(is_verified) {
+                    assert_true(is_verified, "Signature is not verified");
+                }, function(err) {
+                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
+                });
+
+                signature.buffer.transfer();
+                return operation;
+            }, vector.name + " verification with transferred signature after call");
+        }, function(err) {
+            promise_test(function(test) {
+                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+            }, "importVectorKeys step: " + vector.name + " verification with transferred signature after call");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Check for successful verification even if plaintext is altered during call.
+    testVectors.forEach(function(vector) {
+        var promise = importVectorKeys(vector, ["verify", "sign"])
+        .then(function(vector) {
+            promise_test(function(test) {
+                var plaintext = copyBuffer(vector.plaintext);
+                plaintext[0] = 255 - plaintext[0];
+                var operation = subtle.verify(operationAlgorithm(vector, function() {
+                        plaintext[0] = vector.plaintext[0];
+                        return options.operationAlgorithm(vector).name;
+                    }), vector.key, vector.signature, plaintext)
+                .then(function(is_verified) {
+                    assert_true(is_verified, "Signature verified");
+                }, function(err) {
+                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
+                });
+
+                return operation;
+            }, vector.name + " with altered plaintext during call");
+        }, function(err) {
+            promise_test(function(test) {
+                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+            }, "importVectorKeys step: " + vector.name + " with altered plaintext during call");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Check for successful verification even if plaintext is altered after call.
+    testVectors.forEach(function(vector) {
+        var promise = importVectorKeys(vector, ["verify", "sign"])
+        .then(function(vector) {
+            promise_test(function(test) {
+                var plaintext = copyBuffer(vector.plaintext);
+                var operation = subtle.verify(operationAlgorithm(vector), vector.key, vector.signature, plaintext)
+                .then(function(is_verified) {
+                    assert_true(is_verified, "Signature verified");
+                }, function(err) {
+                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
+                });
+
+                plaintext[0] = 255 - plaintext[0];
+                return operation;
+            }, vector.name + " with altered plaintext after call");
+        }, function(err) {
+            promise_test(function(test) {
+                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+            }, "importVectorKeys step: " + vector.name + " with altered plaintext after call");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Check for failed verification if plaintext is transferred during call.
+    testVectors.forEach(function(vector) {
+        var promise = importVectorKeys(vector, ["verify", "sign"])
+        .then(function(vector) {
+            promise_test(function(test) {
+                var plaintext = copyBuffer(vector.plaintext);
+                var operation = subtle.verify(operationAlgorithm(vector, function() {
+                        plaintext.buffer.transfer();
+                        return options.operationAlgorithm(vector).name;
+                    }), vector.key, vector.signature, plaintext)
+                .then(function(is_verified) {
+                    assert_false(is_verified, "Signature is NOT verified");
+                }, function(err) {
+                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
+                });
+
+                return operation;
+            }, vector.name + " with transferred plaintext during call");
+        }, function(err) {
+            promise_test(function(test) {
+                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+            }, "importVectorKeys step: " + vector.name + " with transferred plaintext during call");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Check for successful verification even if plaintext is transferred after call.
+    testVectors.forEach(function(vector) {
+        var promise = importVectorKeys(vector, ["verify", "sign"])
+        .then(function(vector) {
+            promise_test(function(test) {
+                var plaintext = copyBuffer(vector.plaintext);
+                var operation = subtle.verify(operationAlgorithm(vector), vector.key, vector.signature, plaintext)
+                .then(function(is_verified) {
+                    assert_true(is_verified, "Signature verified");
+                }, function(err) {
+                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
+                });
+
+                plaintext.buffer.transfer();
+                return operation;
+            }, vector.name + " with transferred plaintext after call");
+        }, function(err) {
+            promise_test(function(test) {
+                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+            }, "importVectorKeys step: " + vector.name + " with transferred plaintext after call");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Check for failures due to no "verify" usage.
+    testVectors.forEach(function(originalVector) {
+        var vector = Object.assign({}, originalVector);
+
+        var promise = importVectorKeys(vector, ["sign"])
+        .then(function(vector) {
+            promise_test(function(test) {
+                return subtle.verify(operationAlgorithm(vector), vector.key, vector.signature, vector.plaintext)
+                .then(function(plaintext) {
+                    assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": '" + err.message + "'");
+                }, function(err) {
+                    assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
+                });
+            }, vector.name + " no verify usage");
+        }, function(err) {
+            promise_test(function(test) {
+                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+            }, "importVectorKeys step: " + vector.name + " no verify usage");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Check for successful signing and verification.
+    testVectors.forEach(function(vector) {
+        var promise = importVectorKeys(vector, ["verify", "sign"])
+        .then(function(vectors) {
+            promise_test(function(test) {
+                return subtle.sign(operationAlgorithm(vector), vector.key, vector.plaintext)
+                .then(function(signature) {
+                    assert_true(equalBuffers(signature, vector.signature), "Signing did not give the expected output");
+                    // Can we get the verify the new signature?
+                    return subtle.verify(operationAlgorithm(vector), vector.key, signature, vector.plaintext)
+                    .then(function(is_verified) {
+                        assert_true(is_verified, "Round trip verifies");
+                        return signature;
+                    }, function(err) {
+                        assert_unreached("verify error for test " + vector.name + ": '" + err.message + "'");
+                    });
+                });
+            }, vector.name + " round trip");
+
+        }, function(err) {
+            // We need a failed test if the importVectorKey operation fails, so
+            // we know we never tested signing or verifying
+            promise_test(function(test) {
+                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+            }, "importVectorKeys step: " + vector.name + " round trip");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Test signing with the wrong algorithm
+    testVectors.forEach(function(vector) {
+        // Want to get the key for the wrong algorithm
+        var promise = subtle.generateKey({name: "ECDSA", namedCurve: "P-256", hash: "SHA-256"}, false, ["sign", "verify"])
+        .then(function(wrongKey) {
+            return importVectorKeys(vector, ["verify", "sign"])
+            .then(function(vectors) {
+                promise_test(function(test) {
+                    var operation = subtle.sign(operationAlgorithm(vector), wrongKey.privateKey, vector.plaintext)
+                    .then(function(signature) {
+                        assert_unreached("Signing should not have succeeded for " + vector.name);
+                    }, function(err) {
+                        assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
+                    });
+
+                    return operation;
+                }, vector.name + " signing with wrong algorithm name");
+
+            }, function(err) {
+                // We need a failed test if the importVectorKey operation fails, so
+                // we know we never tested verification.
+                promise_test(function(test) {
+                    assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+                }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name");
+            });
+        }, function(err) {
+            promise_test(function(test) {
+                assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'");
+            }, "generate wrong key step: " + vector.name + " signing with wrong algorithm name");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Test verification with the wrong algorithm
+    testVectors.forEach(function(vector) {
+        // Want to get the key for the wrong algorithm
+        var promise = subtle.generateKey({name: "ECDSA", namedCurve: "P-256", hash: "SHA-256"}, false, ["sign", "verify"])
+        .then(function(wrongKey) {
+            return importVectorKeys(vector, ["verify", "sign"])
+            .then(function(vector) {
+                promise_test(function(test) {
+                    var operation = subtle.verify(operationAlgorithm(vector), wrongKey.publicKey, vector.signature, vector.plaintext)
+                    .then(function(signature) {
+                        assert_unreached("Verifying should not have succeeded for " + vector.name);
+                    }, function(err) {
+                        assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
+                    });
+
+                    return operation;
+                }, vector.name + " verifying with wrong algorithm name");
+
+            }, function(err) {
+                // We need a failed test if the importVectorKey operation fails, so
+                // we know we never tested verification.
+                promise_test(function(test) {
+                    assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+                }, "importVectorKeys step: " + vector.name + " verifying with wrong algorithm name");
+            });
+        }, function(err) {
+            promise_test(function(test) {
+                assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'");
+            }, "generate wrong key step: " + vector.name + " verifying with wrong algorithm name");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Verification should fail if the plaintext is changed
+    testVectors.forEach(function(vector) {
+        var promise = importVectorKeys(vector, ["verify", "sign"])
+        .then(function(vector) {
+            var plaintext = copyBuffer(vector.plaintext);
+            plaintext[0] = 255 - plaintext[0];
+            promise_test(function(test) {
+                var operation = subtle.verify(operationAlgorithm(vector), vector.key, vector.signature, plaintext)
+                .then(function(is_verified) {
+                    assert_false(is_verified, "Signature is NOT verified");
+                }, function(err) {
+                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
+                });
+
+                return operation;
+            }, vector.name + " verification failure due to wrong plaintext");
+
+        }, function(err) {
+            // We need a failed test if the importVectorKey operation fails, so
+            // we know we never tested verification.
+            promise_test(function(test) {
+                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+            }, "importVectorKeys step: " + vector.name + " verification failure due to wrong plaintext");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Verification should fail if the signature is changed
+    testVectors.forEach(function(vector) {
+        var promise = importVectorKeys(vector, ["verify", "sign"])
+        .then(function(vector) {
+            var signature = copyBuffer(vector.signature);
+            signature[0] = 255 - signature[0];
+            promise_test(function(test) {
+                var operation = subtle.verify(operationAlgorithm(vector), vector.key, signature, vector.plaintext)
+                .then(function(is_verified) {
+                    assert_false(is_verified, "Signature is NOT verified");
+                }, function(err) {
+                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
+                });
+
+                return operation;
+            }, vector.name + " verification failure due to wrong signature");
+
+        }, function(err) {
+            // We need a failed test if the importVectorKey operation fails, so
+            // we know we never tested verification.
+            promise_test(function(test) {
+                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+            }, "importVectorKeys step: " + vector.name + " verification failure due to wrong signature");
+        });
+
+        all_promises.push(promise);
+    });
+
+    // Verification should fail if the signature is wrong length
+    testVectors.forEach(function(vector) {
+        var promise = importVectorKeys(vector, ["verify", "sign"])
+        .then(function(vector) {
+            var signature = vector.signature.slice(1); // Drop first byte
+            promise_test(function(test) {
+                var operation = subtle.verify(operationAlgorithm(vector), vector.key, signature, vector.plaintext)
+                .then(function(is_verified) {
+                    assert_false(is_verified, "Signature is NOT verified");
+                }, function(err) {
+                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
+                });
+
+                return operation;
+            }, vector.name + " verification failure due to short signature");
+
+        }, function(err) {
+            // We need a failed test if the importVectorKey operation fails, so
+            // we know we never tested verification.
+            promise_test(function(test) {
+                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+            }, "importVectorKeys step: " + vector.name + " verification failure due to short signature");
+        });
+
+        all_promises.push(promise);
+    });
+
+    if (options.wrongVerificationAlgorithm !== undefined) {
+        // Test verification failure due to algorithm-specific parameters.
+        testVectors.forEach(function(vector) {
+            var promise = importVectorKeys(vector, ["verify", "sign"])
+            .then(function(vector) {
+                promise_test(function(test) {
+                    var operation = subtle.verify(options.wrongVerificationAlgorithm(vector), vector.key, vector.signature, vector.plaintext)
+                    .then(function(is_verified) {
+                        assert_false(is_verified, "Signature is NOT verified with wrong length");
+                    }, function(err) {
+                        assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
+                    });
+
+                    return operation;
+                }, vector.name + " verification failure due to wrong length parameter");
+
+            }, function(err) {
+                promise_test(function(test) {
+                    assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
+                }, "importVectorKeys step: " + vector.name + " verification failure due to wrong length parameter");
+            });
+
+            all_promises.push(promise);
+        });
+    }
+
+
+
+    promise_test(function() {
+        return Promise.all(all_promises).finally(done);
+    }, "setup");
+
+    // A test vector has all needed fields for signing and verifying, EXCEPT that the
+    // key field may be null. This function replaces that null with the Correct
+    // CryptoKey object.
+    //
+    // Returns a Promise that yields an updated vector on success.
+    function importVectorKeys(vector, keyUsages) {
+        if (vector.key !== null) {
+            return Promise.resolve(vector);
+        } else {
+            return subtle.importKey(options.importFormat, vector.keyBuffer, options.importAlgorithm(vector), false, keyUsages)
+            .then(function(key) {
+                vector.key = key;
+                return vector;
+            });
+        }
+    }
+
+    return;
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.js
index 1087927..a2a4ef27 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.js
@@ -1,1038 +1,10 @@
 function run_test() {
-  setup({ explicit_done: true });
-
-  var subtle = self.crypto.subtle; // Change to test prefixed implementations
-
-  // When are all these tests really done? When all the promises they use have resolved.
-  var all_promises = [];
-
-  // Source file [algorithm_name]_vectors.js provides the getTestVectors method
-  // for the algorithm that drives these tests.
-  var testVectors = getTestVectors();
-  var invalidTestVectors = getInvalidTestVectors();
-
-  // Test verification first, because signing tests rely on that working
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        var algorithm = vector.algorithmName;
-        promise_test(function (test) {
-          var operation = subtle
-            .verify(algorithm, vector.publicKey, vector.signature, vector.data)
-            .then(
-              function (is_verified) {
-                assert_true(is_verified, 'Signature verified');
-              },
-              function (err) {
-                assert_unreached(
-                  'Verification should not throw error ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-
-          return operation;
-        }, vector.name + ' verification');
-      },
-      function (err) {
-        // We need a failed test if the importVectorKey operation fails, so
-        // we know we never tested verification.
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' + vector.name + ' verification');
-      }
-    );
-
-    all_promises.push(promise);
+  runSignatureTests({
+    vectors: getTestVectors(),
+    invalidVectors: getInvalidTestVectors(),
+    algorithmIdentifier(vector) {
+      return vector.algorithmName;
+    },
+    dataLabel: 'plaintext',
   });
-
-  // Test verification with an altered buffer during call
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        promise_test(function (test) {
-          var signature = copyBuffer(vector.signature);
-          signature[0] = 255 - signature[0];
-          var operation = subtle
-            .verify(
-              {
-                get name() {
-                  signature[0] = vector.signature[0];
-                  return vector.algorithmName;
-                },
-              },
-              vector.publicKey,
-              signature,
-              vector.data
-            )
-            .then(
-              function (is_verified) {
-                assert_true(is_verified, 'Signature verified');
-              },
-              function (err) {
-                assert_unreached(
-                  'Verification should not throw error ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-
-          return operation;
-        }, vector.name + ' verification with altered signature during call');
-      },
-      function (err) {
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' +
-          vector.name +
-          ' verification with altered signature during call');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Test verification with an altered buffer after call
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        var algorithm = vector.algorithmName;
-        promise_test(function (test) {
-          var signature = copyBuffer(vector.signature);
-          var operation = subtle
-            .verify(algorithm, vector.publicKey, signature, vector.data)
-            .then(
-              function (is_verified) {
-                assert_true(is_verified, 'Signature verified');
-              },
-              function (err) {
-                assert_unreached(
-                  'Verification should not throw error ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-
-          signature[0] = 255 - signature[0];
-          return operation;
-        }, vector.name + ' verification with altered signature after call');
-      },
-      function (err) {
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' +
-          vector.name +
-          ' verification with altered signature after call');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Test verification with a transferred buffer during call
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        promise_test(function (test) {
-          var signature = copyBuffer(vector.signature);
-          var operation = subtle
-            .verify(
-              {
-                get name() {
-                  signature.buffer.transfer();
-                  return vector.algorithmName;
-                },
-              },
-              vector.publicKey,
-              signature,
-              vector.data
-            )
-            .then(
-              function (is_verified) {
-                assert_false(is_verified, 'Signature is NOT verified');
-              },
-              function (err) {
-                assert_unreached(
-                  'Verification should not throw error ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-
-          return operation;
-        }, vector.name + ' verification with transferred signature during call');
-      },
-      function (err) {
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' +
-          vector.name +
-          ' verification with transferred signature during call');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Test verification with a transferred buffer after call
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        var algorithm = vector.algorithmName;
-        promise_test(function (test) {
-          var signature = copyBuffer(vector.signature);
-          var operation = subtle
-            .verify(algorithm, vector.publicKey, signature, vector.data)
-            .then(
-              function (is_verified) {
-                assert_true(is_verified, 'Signature verified');
-              },
-              function (err) {
-                assert_unreached(
-                  'Verification should not throw error ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-
-          signature.buffer.transfer();
-          return operation;
-        }, vector.name + ' verification with transferred signature after call');
-      },
-      function (err) {
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' +
-          vector.name +
-          ' verification with transferred signature after call');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Check for successful verification even if plaintext is altered during call.
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        promise_test(function (test) {
-          var plaintext = copyBuffer(vector.data);
-          plaintext[0] = 255 - plaintext[0];
-          var operation = subtle
-            .verify(
-              {
-                get name() {
-                  plaintext[0] = vector.data[0];
-                  return vector.algorithmName;
-                },
-              },
-              vector.publicKey,
-              vector.signature,
-              plaintext
-            )
-            .then(
-              function (is_verified) {
-                assert_true(is_verified, 'Signature verified');
-              },
-              function (err) {
-                assert_unreached(
-                  'Verification should not throw error ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-
-          return operation;
-        }, vector.name + ' with altered plaintext during call');
-      },
-      function (err) {
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' +
-          vector.name +
-          ' with altered plaintext during call');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Check for successful verification even if plaintext is altered after call.
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        var algorithm = vector.algorithmName;
-        promise_test(function (test) {
-          var plaintext = copyBuffer(vector.data);
-          var operation = subtle
-            .verify(algorithm, vector.publicKey, vector.signature, plaintext)
-            .then(
-              function (is_verified) {
-                assert_true(is_verified, 'Signature verified');
-              },
-              function (err) {
-                assert_unreached(
-                  'Verification should not throw error ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-
-          plaintext[0] = 255 - plaintext[0];
-          return operation;
-        }, vector.name + ' with altered plaintext after call');
-      },
-      function (err) {
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' +
-          vector.name +
-          ' with altered plaintext after call');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Check for failed verification if plaintext is transferred during call.
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        promise_test(function (test) {
-          var plaintext = copyBuffer(vector.data);
-          var operation = subtle
-            .verify(
-              {
-                get name() {
-                  plaintext.buffer.transfer();
-                  return vector.algorithmName;
-                },
-              },
-              vector.publicKey,
-              vector.signature,
-              plaintext
-            )
-            .then(
-              function (is_verified) {
-                assert_false(is_verified, 'Signature is NOT verified');
-              },
-              function (err) {
-                assert_unreached(
-                  'Verification should not throw error ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-
-          return operation;
-        }, vector.name + ' with transferred plaintext during call');
-      },
-      function (err) {
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' +
-          vector.name +
-          ' with transferred plaintext during call');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Check for successful verification even if plaintext is transferred after call.
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        var algorithm = vector.algorithmName;
-        promise_test(function (test) {
-          var plaintext = copyBuffer(vector.data);
-          var operation = subtle
-            .verify(algorithm, vector.publicKey, vector.signature, plaintext)
-            .then(
-              function (is_verified) {
-                assert_true(is_verified, 'Signature verified');
-              },
-              function (err) {
-                assert_unreached(
-                  'Verification should not throw error ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-
-          plaintext.buffer.transfer();
-          return operation;
-        }, vector.name + ' with transferred plaintext after call');
-      },
-      function (err) {
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' +
-          vector.name +
-          ' with transferred plaintext after call');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Check for failures due to using privateKey to verify.
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        var algorithm = vector.algorithmName;
-        promise_test(function (test) {
-          return subtle
-            .verify(algorithm, vector.privateKey, vector.signature, vector.data)
-            .then(
-              function (plaintext) {
-                assert_unreached(
-                  'Should have thrown error for using privateKey to verify in ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              },
-              function (err) {
-                assert_equals(
-                  err.name,
-                  'InvalidAccessError',
-                  "Should throw InvalidAccessError instead of '" +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-        }, vector.name + ' using privateKey to verify');
-      },
-      function (err) {
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' +
-          vector.name +
-          ' using privateKey to verify');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Check for failures due to using publicKey to sign.
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        var algorithm = vector.algorithmName;
-        promise_test(function (test) {
-          return subtle.sign(algorithm, vector.publicKey, vector.data).then(
-            function (signature) {
-              assert_unreached(
-                'Should have thrown error for using publicKey to sign in ' +
-                  vector.name +
-                  ': ' +
-                  err.message +
-                  "'"
-              );
-            },
-            function (err) {
-              assert_equals(
-                err.name,
-                'InvalidAccessError',
-                "Should throw InvalidAccessError instead of '" +
-                  err.message +
-                  "'"
-              );
-            }
-          );
-        }, vector.name + ' using publicKey to sign');
-      },
-      function (err) {
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' +
-          vector.name +
-          ' using publicKey to sign');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Check for failures due to no "verify" usage.
-  testVectors.forEach(function (originalVector) {
-    var vector = Object.assign({}, originalVector);
-
-    var promise = importVectorKeys(vector, [], ['sign']).then(
-      function (vectors) {
-        var algorithm = vector.algorithmName;
-        promise_test(function (test) {
-          return subtle
-            .verify(algorithm, vector.publicKey, vector.signature, vector.data)
-            .then(
-              function (plaintext) {
-                assert_unreached(
-                  'Should have thrown error for no verify usage in ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              },
-              function (err) {
-                assert_equals(
-                  err.name,
-                  'InvalidAccessError',
-                  "Should throw InvalidAccessError instead of '" +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-        }, vector.name + ' no verify usage');
-      },
-      function (err) {
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' + vector.name + ' no verify usage');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Check for successful signing and verification.
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        var algorithm = vector.algorithmName;
-        promise_test(function (test) {
-          return subtle.sign(algorithm, vector.privateKey, vector.data).then(
-            function (signature) {
-              // Can we verify the signature?
-              return subtle
-                .verify(algorithm, vector.publicKey, signature, vector.data)
-                .then(
-                  function (is_verified) {
-                    assert_true(is_verified, 'Round trip verification works');
-                    return signature;
-                  },
-                  function (err) {
-                    assert_unreached(
-                      'verify error for test ' +
-                        vector.name +
-                        ': ' +
-                        err.message +
-                        "'"
-                    );
-                  }
-                );
-            },
-            function (err) {
-              assert_unreached(
-                'sign error for test ' + vector.name + ": '" + err.message + "'"
-              );
-            }
-          );
-        }, vector.name + ' round trip');
-      },
-      function (err) {
-        // We need a failed test if the importVectorKey operation fails, so
-        // we know we never tested signing or verifying
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' + vector.name + ' round trip');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Test signing with the wrong algorithm
-  testVectors.forEach(function (vector) {
-    // Want to get the key for the wrong algorithm
-    var promise = subtle
-      .generateKey({ name: 'HMAC', hash: 'SHA-1' }, false, ['sign', 'verify'])
-      .then(
-        function (wrongKey) {
-          var algorithm = vector.algorithmName;
-          return importVectorKeys(vector, ['verify'], ['sign']).then(
-            function (vectors) {
-              promise_test(function (test) {
-                var operation = subtle
-                  .sign(algorithm, wrongKey, vector.data)
-                  .then(
-                    function (signature) {
-                      assert_unreached(
-                        'Signing should not have succeeded for ' + vector.name
-                      );
-                    },
-                    function (err) {
-                      assert_equals(
-                        err.name,
-                        'InvalidAccessError',
-                        "Should have thrown InvalidAccessError instead of '" +
-                          err.message +
-                          "'"
-                      );
-                    }
-                  );
-
-                return operation;
-              }, vector.name + ' signing with wrong algorithm name');
-            },
-            function (err) {
-              // We need a failed test if the importVectorKey operation fails, so
-              // we know we never tested verification.
-              promise_test(function (test) {
-                assert_unreached(
-                  'importVectorKeys failed for ' +
-                    vector.name +
-                    ". Message: ''" +
-                    err.message +
-                    "''"
-                );
-              }, 'importVectorKeys step: ' +
-                vector.name +
-                ' signing with wrong algorithm name');
-            }
-          );
-        },
-        function (err) {
-          promise_test(function (test) {
-            assert_unreached(
-              'Generate wrong key for test ' +
-                vector.name +
-                " failed: '" +
-                err.message +
-                "'"
-            );
-          }, 'generate wrong key step: ' +
-            vector.name +
-            ' signing with wrong algorithm name');
-        }
-      );
-
-    all_promises.push(promise);
-  });
-
-  // Test verification with the wrong algorithm
-  testVectors.forEach(function (vector) {
-    // Want to get the key for the wrong algorithm
-    var promise = subtle
-      .generateKey({ name: 'HMAC', hash: 'SHA-1' }, false, ['sign', 'verify'])
-      .then(
-        function (wrongKey) {
-          return importVectorKeys(vector, ['verify'], ['sign']).then(
-            function (vectors) {
-              var algorithm = vector.algorithmName;
-              promise_test(function (test) {
-                var operation = subtle
-                  .verify(algorithm, wrongKey, vector.signature, vector.data)
-                  .then(
-                    function (signature) {
-                      assert_unreached(
-                        'Verifying should not have succeeded for ' + vector.name
-                      );
-                    },
-                    function (err) {
-                      assert_equals(
-                        err.name,
-                        'InvalidAccessError',
-                        "Should have thrown InvalidAccessError instead of '" +
-                          err.message +
-                          "'"
-                      );
-                    }
-                  );
-
-                return operation;
-              }, vector.name + ' verifying with wrong algorithm name');
-            },
-            function (err) {
-              // We need a failed test if the importVectorKey operation fails, so
-              // we know we never tested verification.
-              promise_test(function (test) {
-                assert_unreached(
-                  'importVectorKeys failed for ' +
-                    vector.name +
-                    ". Message: ''" +
-                    err.message +
-                    "''"
-                );
-              }, 'importVectorKeys step: ' +
-                vector.name +
-                ' verifying with wrong algorithm name');
-            }
-          );
-        },
-        function (err) {
-          promise_test(function (test) {
-            assert_unreached(
-              'Generate wrong key for test ' +
-                vector.name +
-                " failed: '" +
-                err.message +
-                "'"
-            );
-          }, 'generate wrong key step: ' +
-            vector.name +
-            ' verifying with wrong algorithm name');
-        }
-      );
-
-    all_promises.push(promise);
-  });
-
-  // Test verification fails with wrong signature
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        var algorithm = vector.algorithmName;
-        var signature = copyBuffer(vector.signature);
-        signature[0] = 255 - signature[0];
-        promise_test(function (test) {
-          var operation = subtle
-            .verify(algorithm, vector.publicKey, signature, vector.data)
-            .then(
-              function (is_verified) {
-                assert_false(is_verified, 'Signature NOT verified');
-              },
-              function (err) {
-                assert_unreached(
-                  'Verification should not throw error ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-
-          return operation;
-        }, vector.name + ' verification failure due to altered signature');
-      },
-      function (err) {
-        // We need a failed test if the importVectorKey operation fails, so
-        // we know we never tested verification.
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' +
-          vector.name +
-          ' verification failure due to altered signature');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Test verification fails with short (odd length) signature
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        var algorithm = vector.algorithmName;
-        var signature = vector.signature.slice(1); // Skip the first byte
-        promise_test(function (test) {
-          var operation = subtle
-            .verify(algorithm, vector.publicKey, signature, vector.data)
-            .then(
-              function (is_verified) {
-                assert_false(is_verified, 'Signature NOT verified');
-              },
-              function (err) {
-                assert_unreached(
-                  'Verification should not throw error ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-
-          return operation;
-        }, vector.name + ' verification failure due to shortened signature');
-      },
-      function (err) {
-        // We need a failed test if the importVectorKey operation fails, so
-        // we know we never tested verification.
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' +
-          vector.name +
-          ' verification failure due to shortened signature');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Test verification fails with wrong plaintext
-  testVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        var algorithm = vector.algorithmName;
-        var plaintext = copyBuffer(vector.data);
-        plaintext[0] = 255 - plaintext[0];
-        promise_test(function (test) {
-          var operation = subtle
-            .verify(algorithm, vector.publicKey, vector.signature, plaintext)
-            .then(
-              function (is_verified) {
-                assert_false(is_verified, 'Signature NOT verified');
-              },
-              function (err) {
-                assert_unreached(
-                  'Verification should not throw error ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-
-          return operation;
-        }, vector.name + ' verification failure due to altered plaintext');
-      },
-      function (err) {
-        // We need a failed test if the importVectorKey operation fails, so
-        // we know we never tested verification.
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' +
-          vector.name +
-          ' verification failure due to altered plaintext');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  // Test invalid signatures
-  invalidTestVectors.forEach(function (vector) {
-    var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
-      function (vectors) {
-        var algorithm = vector.algorithmName;
-        promise_test(function (test) {
-          var operation = subtle
-            .verify(algorithm, vector.publicKey, vector.signature, vector.data)
-            .then(
-              function (is_verified) {
-                assert_false(is_verified, 'Signature unexpectedly verified');
-              },
-              function (err) {
-                assert_unreached(
-                  'Verification should not throw error ' +
-                    vector.name +
-                    ': ' +
-                    err.message +
-                    "'"
-                );
-              }
-            );
-
-          return operation;
-        }, vector.name + ' verification');
-      },
-      function (err) {
-        // We need a failed test if the importVectorKey operation fails, so
-        // we know we never tested verification.
-        promise_test(function (test) {
-          assert_unreached(
-            'importVectorKeys failed for ' +
-              vector.name +
-              ". Message: ''" +
-              err.message +
-              "''"
-          );
-        }, 'importVectorKeys step: ' + vector.name + ' verification');
-      }
-    );
-
-    all_promises.push(promise);
-  });
-
-  promise_test(function () {
-    return Promise.all(all_promises)
-      .then(function () {
-        done();
-      })
-      .catch(function () {
-        done();
-      });
-  }, 'setup');
-
-  // A test vector has all needed fields for signing and verifying, EXCEPT that the
-  // key field may be null. This function replaces that null with the Correct
-  // CryptoKey object.
-  //
-  // Returns a Promise that yields an updated vector on success.
-  function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) {
-    var publicPromise, privatePromise;
-
-    if (vector.publicKey !== null) {
-      publicPromise = new Promise(function (resolve, reject) {
-        resolve(vector);
-      });
-    } else {
-      publicPromise = subtle
-        .importKey(
-          vector.publicKeyFormat,
-          vector.publicKeyBuffer,
-          { name: vector.algorithmName },
-          false,
-          publicKeyUsages
-        )
-        .then(function (key) {
-          vector.publicKey = key;
-          return vector;
-        }); // Returns a copy of the sourceBuffer it is sent.
-    }
-
-    if (vector.privateKey !== null) {
-      privatePromise = new Promise(function (resolve, reject) {
-        resolve(vector);
-      });
-    } else {
-      privatePromise = subtle
-        .importKey(
-          vector.privateKeyFormat,
-          vector.privateKeyBuffer,
-          { name: vector.algorithmName },
-          false,
-          privateKeyUsages
-        )
-        .then(function (key) {
-          vector.privateKey = key;
-          return vector;
-        });
-    }
-
-    return Promise.all([publicPromise, privatePromise]);
-  }
-
-  return;
 }
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.tentative.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.tentative.https.any-expected.txt
index 08e0658..a421aad6 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.tentative.https.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.tentative.https.any-expected.txt
@@ -1,118 +1,118 @@
 This is a testharness.js-based test.
 Found 57 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification with altered signature during call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification with altered signature during call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification with altered signature during call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification with altered signature after call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification with altered signature after call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification with altered signature after call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification with transferred signature during call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification with transferred signature during call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification with transferred signature during call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification with transferred signature after call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification with transferred signature after call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification with transferred signature after call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test with altered plaintext during call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test with altered plaintext during call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test with altered plaintext during call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test with altered plaintext after call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test with altered plaintext after call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test with altered plaintext after call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test with transferred plaintext during call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test with transferred plaintext during call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test with transferred plaintext during call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test with transferred plaintext after call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test with transferred plaintext after call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test with transferred plaintext after call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test using privateKey to verify
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test using privateKey to verify
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test using privateKey to verify
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test using publicKey to sign
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test using publicKey to sign
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test using publicKey to sign
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test no verify usage
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test no verify usage
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test no verify usage
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test round trip
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test round trip
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test round trip
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification failure due to altered signature
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification failure due to altered signature
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification failure due to altered signature
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification failure due to shortened signature
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification failure due to shortened signature
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification failure due to shortened signature
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification failure due to altered plaintext
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification failure due to altered plaintext
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification failure due to altered plaintext
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 invalid signature verification
-  assert_unreached: importVectorKeys failed for ML-DSA-44 invalid signature. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 invalid signature verification
-  assert_unreached: importVectorKeys failed for ML-DSA-65 invalid signature. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 invalid signature verification
-  assert_unreached: importVectorKeys failed for ML-DSA-87 invalid signature. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test signing with wrong algorithm name
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test signing with wrong algorithm name
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test signing with wrong algorithm name
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verifying with wrong algorithm name
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verifying with wrong algorithm name
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verifying with wrong algorithm name
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
+[FAIL] ML-DSA-44 basic test verification
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test verification with altered signature during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test verification with altered signature after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test verification with transferred signature during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test verification with transferred signature after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test with altered plaintext during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test with altered plaintext after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test with transferred plaintext during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test with transferred plaintext after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test using privateKey to verify
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test using publicKey to sign
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test no verify usage
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test round trip
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test signing with wrong algorithm name
+  assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'sign' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
+[FAIL] ML-DSA-44 basic test verifying with wrong algorithm name
+  assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'verify' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
+[FAIL] ML-DSA-44 basic test verification failure due to altered signature
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test verification failure due to shortened signature
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test verification failure due to altered plaintext
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test verification
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test verification with altered signature during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test verification with altered signature after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test verification with transferred signature during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test verification with transferred signature after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test with altered plaintext during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test with altered plaintext after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test with transferred plaintext during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test with transferred plaintext after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test using privateKey to verify
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test using publicKey to sign
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test no verify usage
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test round trip
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test signing with wrong algorithm name
+  assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'sign' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
+[FAIL] ML-DSA-65 basic test verifying with wrong algorithm name
+  assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'verify' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
+[FAIL] ML-DSA-65 basic test verification failure due to altered signature
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test verification failure due to shortened signature
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test verification failure due to altered plaintext
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test verification
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test verification with altered signature during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test verification with altered signature after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test verification with transferred signature during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test verification with transferred signature after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test with altered plaintext during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test with altered plaintext after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test with transferred plaintext during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test with transferred plaintext after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test using privateKey to verify
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test using publicKey to sign
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test no verify usage
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test round trip
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test signing with wrong algorithm name
+  assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'sign' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
+[FAIL] ML-DSA-87 basic test verifying with wrong algorithm name
+  assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'verify' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
+[FAIL] ML-DSA-87 basic test verification failure due to altered signature
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test verification failure due to shortened signature
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test verification failure due to altered plaintext
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 invalid signature verification
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 invalid signature verification
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 invalid signature verification
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.tentative.https.any.js
index be336b3..253cc85 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.tentative.https.any.js
@@ -1,6 +1,8 @@
 // META: title=WebCryptoAPI: sign() and verify() Using ML-DSA
 // META: script=../util/helpers.js
+// META: script=../util/mldsa_key_fixtures.js
 // META: script=mldsa_vectors.js
+// META: script=signature.js
 // META: script=mldsa.js
 // META: timeout=long
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.tentative.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.tentative.https.any.worker-expected.txt
index 08e0658..a421aad6 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.tentative.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa.tentative.https.any.worker-expected.txt
@@ -1,118 +1,118 @@
 This is a testharness.js-based test.
 Found 57 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification with altered signature during call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification with altered signature during call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification with altered signature during call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification with altered signature after call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification with altered signature after call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification with altered signature after call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification with transferred signature during call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification with transferred signature during call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification with transferred signature during call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification with transferred signature after call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification with transferred signature after call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification with transferred signature after call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test with altered plaintext during call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test with altered plaintext during call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test with altered plaintext during call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test with altered plaintext after call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test with altered plaintext after call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test with altered plaintext after call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test with transferred plaintext during call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test with transferred plaintext during call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test with transferred plaintext during call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test with transferred plaintext after call
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test with transferred plaintext after call
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test with transferred plaintext after call
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test using privateKey to verify
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test using privateKey to verify
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test using privateKey to verify
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test using publicKey to sign
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test using publicKey to sign
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test using publicKey to sign
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test no verify usage
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test no verify usage
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test no verify usage
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test round trip
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test round trip
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test round trip
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification failure due to altered signature
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification failure due to altered signature
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification failure due to altered signature
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification failure due to shortened signature
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification failure due to shortened signature
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification failure due to shortened signature
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verification failure due to altered plaintext
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verification failure due to altered plaintext
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verification failure due to altered plaintext
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 invalid signature verification
-  assert_unreached: importVectorKeys failed for ML-DSA-44 invalid signature. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 invalid signature verification
-  assert_unreached: importVectorKeys failed for ML-DSA-65 invalid signature. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 invalid signature verification
-  assert_unreached: importVectorKeys failed for ML-DSA-87 invalid signature. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test signing with wrong algorithm name
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test signing with wrong algorithm name
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test signing with wrong algorithm name
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-44 basic test verifying with wrong algorithm name
-  assert_unreached: importVectorKeys failed for ML-DSA-44 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-65 basic test verifying with wrong algorithm name
-  assert_unreached: importVectorKeys failed for ML-DSA-65 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
-[FAIL] importVectorKeys step: ML-DSA-87 basic test verifying with wrong algorithm name
-  assert_unreached: importVectorKeys failed for ML-DSA-87 basic test. Message: ''Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name'' Reached unreachable code
+[FAIL] ML-DSA-44 basic test verification
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test verification with altered signature during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test verification with altered signature after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test verification with transferred signature during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test verification with transferred signature after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test with altered plaintext during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test with altered plaintext after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test with transferred plaintext during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test with transferred plaintext after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test using privateKey to verify
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test using publicKey to sign
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test no verify usage
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test round trip
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test signing with wrong algorithm name
+  assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'sign' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
+[FAIL] ML-DSA-44 basic test verifying with wrong algorithm name
+  assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'verify' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
+[FAIL] ML-DSA-44 basic test verification failure due to altered signature
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test verification failure due to shortened signature
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 basic test verification failure due to altered plaintext
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test verification
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test verification with altered signature during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test verification with altered signature after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test verification with transferred signature during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test verification with transferred signature after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test with altered plaintext during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test with altered plaintext after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test with transferred plaintext during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test with transferred plaintext after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test using privateKey to verify
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test using publicKey to sign
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test no verify usage
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test round trip
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test signing with wrong algorithm name
+  assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'sign' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
+[FAIL] ML-DSA-65 basic test verifying with wrong algorithm name
+  assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'verify' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
+[FAIL] ML-DSA-65 basic test verification failure due to altered signature
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test verification failure due to shortened signature
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 basic test verification failure due to altered plaintext
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test verification
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test verification with altered signature during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test verification with altered signature after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test verification with transferred signature during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test verification with transferred signature after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test with altered plaintext during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test with altered plaintext after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test with transferred plaintext during call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test with transferred plaintext after call
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test using privateKey to verify
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test using publicKey to sign
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test no verify usage
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test round trip
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test signing with wrong algorithm name
+  assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'sign' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
+[FAIL] ML-DSA-87 basic test verifying with wrong algorithm name
+  assert_equals: Should have thrown InvalidAccessError instead of 'Failed to execute 'verify' on 'SubtleCrypto': Algorithm: Unrecognized name' expected "InvalidAccessError" but got "NotSupportedError"
+[FAIL] ML-DSA-87 basic test verification failure due to altered signature
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test verification failure due to shortened signature
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 basic test verification failure due to altered plaintext
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-44 invalid signature verification
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-65 invalid signature verification
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
+[FAIL] ML-DSA-87 invalid signature verification
+  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'importKey' on 'SubtleCrypto': Algorithm: Unrecognized name"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa_vectors.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa_vectors.js
index a035821..021060a1 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa_vectors.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/mldsa_vectors.js
@@ -1,393 +1,4 @@
-// PKCS#8 private keys for ML-DSA variants
-var pkcs8 = {
-  'ML-DSA-44': new Uint8Array([
-    48, 52, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 17, 4, 34, 128,
-    32, 153, 21, 95, 99, 48, 150, 218, 124, 190, 8, 122, 137, 72, 184, 79, 118,
-    123, 16, 249, 1, 200, 35, 194, 64, 177, 221, 43, 200, 112, 5, 201, 62,
-  ]),
-  'ML-DSA-65': new Uint8Array([
-    48, 52, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 18, 4, 34, 128,
-    32, 132, 164, 137, 75, 123, 70, 164, 178, 3, 156, 206, 16, 195, 26, 133,
-    186, 176, 195, 102, 48, 254, 35, 29, 66, 103, 17, 67, 152, 38, 7, 130, 139,
-  ]),
-  'ML-DSA-87': new Uint8Array([
-    48, 52, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 19, 4, 34, 128,
-    32, 161, 80, 109, 145, 76, 109, 101, 140, 117, 39, 228, 51, 151, 221, 109,
-    76, 37, 246, 164, 121, 116, 51, 90, 76, 208, 59, 254, 105, 131, 68, 18, 81,
-  ]),
-};
-
-// SPKI public keys for ML-DSA variants
-var spki = {
-  'ML-DSA-44': new Uint8Array([
-    48, 130, 5, 50, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 17, 3, 130, 5,
-    33, 0, 85, 152, 213, 68, 198, 20, 177, 84, 200, 188, 33, 104, 36, 161, 193,
-    127, 226, 151, 48, 107, 122, 191, 89, 86, 188, 3, 245, 199, 18, 79, 168,
-    192, 218, 218, 223, 227, 84, 133, 41, 187, 9, 183, 14, 222, 155, 137, 84,
-    111, 138, 97, 234, 152, 52, 15, 163, 47, 227, 218, 19, 20, 229, 93, 99, 252,
-    155, 213, 30, 241, 158, 211, 213, 191, 155, 73, 211, 145, 59, 194, 75, 140,
-    3, 161, 149, 139, 48, 172, 155, 172, 120, 234, 142, 79, 78, 116, 215, 184,
-    50, 182, 162, 192, 93, 221, 179, 216, 70, 186, 92, 93, 51, 190, 61, 173,
-    105, 206, 9, 208, 52, 54, 208, 252, 115, 176, 146, 118, 23, 0, 5, 41, 151,
-    121, 241, 246, 193, 206, 212, 160, 181, 7, 99, 220, 78, 96, 123, 142, 35,
-    107, 37, 218, 138, 139, 228, 207, 166, 132, 87, 36, 116, 251, 174, 10, 41,
-    196, 171, 26, 212, 90, 213, 223, 141, 87, 232, 59, 46, 173, 16, 247, 173, 4,
-    219, 165, 234, 8, 217, 233, 163, 13, 10, 160, 22, 87, 232, 2, 9, 92, 177,
-    35, 67, 55, 104, 51, 204, 231, 81, 209, 62, 31, 117, 56, 141, 78, 109, 148,
-    118, 191, 93, 193, 59, 30, 88, 237, 19, 169, 14, 11, 150, 102, 215, 144, 69,
-    246, 32, 159, 110, 49, 75, 78, 225, 121, 184, 206, 234, 126, 171, 164, 43,
-    237, 244, 63, 83, 70, 136, 213, 244, 121, 184, 34, 201, 249, 95, 64, 165,
-    99, 14, 195, 231, 18, 121, 20, 156, 80, 231, 133, 197, 0, 94, 195, 22, 251,
-    255, 174, 213, 103, 75, 88, 58, 138, 66, 37, 85, 162, 15, 242, 29, 49, 147,
-    191, 163, 170, 109, 195, 22, 63, 197, 149, 117, 100, 36, 81, 115, 203, 126,
-    62, 86, 103, 193, 182, 69, 238, 233, 162, 62, 248, 132, 158, 212, 208, 187,
-    127, 212, 198, 169, 245, 26, 153, 28, 207, 85, 65, 145, 200, 23, 196, 38,
-    64, 49, 54, 233, 185, 72, 156, 86, 230, 127, 152, 108, 184, 240, 109, 8,
-    103, 228, 206, 142, 73, 74, 214, 52, 182, 203, 82, 201, 175, 188, 111, 42,
-    232, 8, 177, 131, 47, 237, 92, 120, 210, 124, 48, 185, 215, 137, 36, 70,
-    162, 164, 119, 0, 155, 20, 57, 33, 27, 220, 77, 254, 233, 190, 106, 135, 21,
-    194, 57, 238, 95, 162, 78, 164, 33, 210, 215, 43, 100, 96, 232, 87, 19, 43,
-    65, 240, 183, 17, 131, 247, 97, 241, 7, 111, 159, 40, 252, 45, 228, 76, 71,
-    46, 85, 181, 54, 41, 135, 162, 144, 227, 128, 121, 173, 207, 119, 54, 19,
-    197, 23, 231, 176, 125, 190, 32, 76, 60, 34, 24, 160, 42, 73, 190, 124, 99,
-    5, 9, 214, 6, 220, 177, 206, 79, 222, 10, 142, 250, 63, 179, 67, 21, 159,
-    57, 126, 239, 28, 238, 240, 107, 79, 185, 174, 232, 168, 146, 128, 229, 119,
-    19, 21, 33, 239, 193, 42, 178, 152, 124, 24, 203, 131, 193, 93, 162, 2, 208,
-    231, 20, 203, 232, 47, 54, 114, 255, 236, 97, 99, 156, 15, 160, 75, 60, 111,
-    59, 35, 25, 230, 43, 91, 170, 161, 74, 70, 179, 180, 251, 71, 197, 240, 104,
-    42, 39, 202, 206, 12, 115, 249, 138, 55, 252, 216, 61, 185, 121, 76, 137,
-    172, 166, 88, 92, 130, 18, 52, 67, 13, 187, 49, 147, 212, 77, 74, 56, 59,
-    123, 99, 205, 36, 137, 96, 111, 148, 121, 157, 2, 83, 246, 86, 156, 152, 98,
-    172, 77, 244, 214, 225, 184, 240, 121, 144, 56, 213, 161, 43, 67, 4, 161,
-    104, 202, 91, 134, 247, 108, 24, 46, 187, 63, 216, 50, 125, 120, 17, 216,
-    22, 228, 156, 253, 7, 180, 15, 130, 180, 72, 71, 169, 3, 236, 247, 11, 170,
-    32, 76, 236, 225, 133, 250, 20, 235, 200, 143, 89, 25, 158, 49, 158, 164,
-    213, 221, 81, 120, 241, 150, 210, 19, 79, 165, 24, 35, 170, 182, 242, 255,
-    143, 171, 148, 140, 41, 207, 186, 98, 224, 16, 152, 224, 38, 110, 175, 169,
-    111, 132, 201, 178, 114, 25, 196, 50, 149, 158, 193, 180, 101, 183, 92, 109,
-    131, 102, 119, 123, 78, 107, 223, 4, 1, 206, 140, 130, 237, 205, 36, 18,
-    180, 197, 154, 26, 236, 140, 173, 230, 101, 35, 189, 121, 104, 21, 75, 81,
-    224, 186, 212, 81, 107, 66, 244, 235, 64, 90, 206, 39, 44, 43, 162, 187, 63,
-    229, 217, 154, 185, 157, 24, 125, 252, 91, 136, 59, 47, 182, 200, 73, 19,
-    137, 132, 81, 16, 234, 227, 210, 32, 16, 160, 188, 250, 27, 190, 164, 53,
-    244, 219, 199, 177, 146, 117, 50, 99, 72, 235, 37, 154, 72, 51, 203, 61, 39,
-    230, 34, 132, 117, 217, 167, 201, 42, 17, 76, 72, 103, 172, 93, 169, 29, 76,
-    88, 178, 226, 32, 53, 190, 60, 210, 132, 113, 198, 26, 70, 179, 47, 34, 184,
-    88, 178, 208, 1, 196, 89, 136, 167, 33, 38, 9, 255, 89, 202, 27, 93, 229,
-    100, 192, 24, 234, 200, 186, 125, 231, 212, 188, 11, 29, 51, 189, 70, 147,
-    176, 231, 81, 16, 114, 152, 159, 21, 124, 185, 208, 50, 74, 211, 113, 207,
-    35, 54, 173, 205, 133, 52, 167, 199, 87, 158, 120, 33, 204, 163, 146, 233,
-    21, 61, 28, 102, 48, 232, 184, 15, 219, 238, 240, 215, 222, 239, 3, 110,
-    180, 95, 103, 147, 236, 10, 57, 195, 159, 231, 50, 92, 145, 165, 44, 204,
-    121, 187, 9, 210, 80, 86, 251, 169, 132, 236, 248, 23, 207, 222, 227, 13,
-    53, 1, 88, 69, 105, 13, 238, 202, 251, 194, 245, 14, 38, 1, 245, 157, 212,
-    162, 182, 217, 230, 115, 139, 175, 219, 199, 74, 124, 186, 158, 3, 220, 87,
-    220, 177, 85, 13, 58, 168, 223, 6, 238, 156, 80, 121, 44, 166, 176, 0, 48,
-    98, 70, 93, 78, 50, 192, 16, 186, 2, 233, 105, 105, 120, 195, 17, 100, 141,
-    214, 158, 144, 63, 4, 88, 190, 101, 209, 55, 119, 46, 128, 117, 225, 121,
-    204, 195, 19, 61, 170, 116, 251, 225, 230, 28, 27, 249, 84, 195, 224, 190,
-    60, 212, 83, 3, 148, 204, 4, 103, 50, 233, 175, 207, 47, 51, 216, 79, 251,
-    150, 81, 171, 7, 145, 55, 188, 187, 217, 200, 155, 246, 85, 42, 123, 18,
-    112, 192, 116, 163, 40, 187, 132, 192, 210, 188, 106, 117, 217, 185, 183,
-    202, 33, 11, 205, 10, 61, 52, 15, 66, 131, 121, 112, 26, 96, 166, 241, 1,
-    68, 206, 80, 92, 132, 83, 89, 126, 135, 157, 4, 202, 16, 5, 131, 112, 62,
-    56, 234, 176, 213, 119, 205, 203, 17, 106, 156, 117, 251, 135, 21, 73, 219,
-    238, 3, 88, 21, 71, 136, 118, 0, 7, 106, 151, 200, 221, 179, 206, 50, 198,
-    27, 118, 181, 27, 85, 35, 248, 44, 57, 15, 221, 97, 121, 4, 167, 111, 182,
-    207, 195, 52, 134, 195, 128, 173, 111, 98, 152, 29, 138, 197, 175, 171, 247,
-    194, 20, 134, 209, 232, 94, 17, 203, 139, 30, 237, 187, 243, 16, 180, 43,
-    105, 236, 66, 175, 170, 139, 24, 99, 28, 225, 141, 96, 250, 119, 0, 111,
-    212, 34, 217, 42, 134, 88, 78, 76, 126, 169, 168, 59, 154, 93, 54, 43, 161,
-    29, 111, 124, 59, 225, 52, 86, 147, 38, 151, 161, 36, 119, 204, 164, 121,
-    46, 186, 65, 84, 70, 38, 15, 203, 48, 168, 235, 231, 30, 55, 95, 36, 10, 20,
-    166, 109, 8, 18, 109, 251, 213, 82, 142, 240, 49, 249, 180, 78, 69,
-  ]),
-  'ML-DSA-65': new Uint8Array([
-    48, 130, 7, 178, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 18, 3, 130, 7,
-    161, 0, 216, 177, 233, 60, 151, 24, 246, 66, 175, 161, 192, 118, 9, 177, 87,
-    232, 5, 216, 49, 254, 251, 160, 51, 216, 250, 171, 229, 104, 149, 117, 135,
-    107, 5, 239, 244, 167, 83, 57, 49, 153, 29, 135, 108, 138, 117, 236, 120,
-    65, 89, 65, 17, 201, 127, 147, 246, 103, 238, 204, 25, 83, 240, 29, 173,
-    127, 140, 1, 88, 40, 43, 134, 51, 19, 125, 131, 224, 50, 167, 82, 20, 217,
-    215, 162, 41, 95, 215, 56, 130, 226, 177, 216, 20, 169, 133, 85, 140, 9,
-    105, 172, 54, 121, 0, 87, 202, 18, 93, 96, 147, 103, 127, 164, 63, 197, 97,
-    49, 230, 125, 53, 80, 64, 20, 239, 29, 90, 118, 102, 221, 177, 6, 220, 220,
-    170, 107, 107, 18, 138, 125, 138, 70, 66, 60, 185, 232, 209, 4, 100, 17,
-    118, 24, 181, 110, 241, 50, 250, 138, 209, 19, 84, 220, 61, 96, 80, 128, 81,
-    101, 160, 9, 5, 53, 33, 112, 60, 59, 195, 79, 115, 190, 101, 93, 56, 171,
-    146, 51, 82, 233, 66, 225, 226, 211, 72, 58, 9, 74, 222, 185, 175, 211, 156,
-    164, 68, 188, 119, 46, 14, 64, 160, 13, 207, 60, 81, 68, 108, 148, 59, 88,
-    88, 92, 88, 171, 249, 204, 160, 137, 222, 234, 168, 233, 3, 86, 224, 174,
-    105, 224, 1, 177, 184, 230, 236, 30, 85, 249, 56, 105, 211, 50, 83, 168,
-    139, 28, 175, 181, 22, 89, 166, 215, 152, 218, 184, 111, 65, 24, 82, 71,
-    185, 100, 172, 66, 178, 6, 215, 164, 252, 56, 40, 198, 207, 152, 110, 186,
-    207, 39, 235, 179, 38, 215, 88, 105, 109, 177, 130, 91, 5, 223, 164, 78,
-    241, 176, 125, 197, 94, 100, 40, 214, 251, 239, 253, 88, 154, 14, 75, 254,
-    94, 210, 86, 244, 51, 95, 168, 139, 213, 73, 87, 151, 209, 150, 126, 88, 69,
-    251, 116, 157, 122, 96, 212, 206, 42, 218, 38, 85, 184, 50, 246, 188, 35,
-    72, 60, 226, 155, 246, 86, 209, 110, 133, 161, 129, 126, 80, 154, 238, 64,
-    64, 170, 156, 177, 225, 52, 87, 42, 88, 154, 174, 246, 30, 84, 143, 221, 51,
-    27, 65, 60, 40, 22, 120, 42, 145, 34, 82, 235, 79, 20, 215, 211, 231, 151,
-    108, 119, 53, 116, 190, 13, 139, 199, 189, 84, 54, 186, 222, 90, 116, 253,
-    227, 207, 104, 209, 118, 42, 144, 94, 22, 219, 198, 211, 216, 171, 75, 245,
-    61, 240, 157, 108, 81, 19, 238, 39, 120, 210, 242, 63, 126, 6, 15, 18, 97,
-    232, 207, 184, 84, 239, 72, 115, 90, 95, 112, 78, 45, 201, 13, 102, 208,
-    151, 146, 253, 153, 177, 115, 14, 194, 184, 107, 118, 120, 63, 105, 89, 144,
-    60, 242, 80, 106, 32, 11, 204, 170, 165, 221, 182, 31, 187, 159, 216, 118,
-    218, 207, 182, 107, 136, 211, 162, 231, 196, 128, 83, 251, 192, 189, 149, 1,
-    245, 36, 146, 159, 11, 116, 30, 191, 141, 182, 157, 227, 91, 88, 72, 224,
-    55, 195, 96, 245, 135, 249, 142, 16, 229, 237, 27, 60, 2, 91, 138, 82, 197,
-    210, 40, 8, 123, 172, 202, 207, 119, 199, 190, 102, 40, 141, 174, 93, 211,
-    219, 134, 105, 26, 103, 100, 102, 62, 130, 224, 135, 173, 82, 224, 200, 213,
-    76, 211, 38, 181, 16, 97, 99, 75, 14, 42, 162, 223, 237, 141, 1, 29, 140,
-    191, 146, 49, 236, 179, 88, 202, 220, 124, 239, 231, 131, 101, 134, 111, 28,
-    54, 151, 172, 67, 167, 113, 96, 204, 63, 76, 169, 95, 14, 175, 149, 189,
-    173, 39, 177, 102, 23, 158, 102, 236, 224, 70, 203, 190, 132, 230, 136, 3,
-    28, 201, 131, 148, 164, 219, 181, 208, 26, 239, 111, 86, 35, 102, 133, 114,
-    113, 180, 184, 82, 180, 106, 124, 248, 126, 87, 177, 165, 176, 204, 128,
-    141, 179, 141, 34, 119, 160, 57, 206, 168, 95, 197, 110, 79, 60, 48, 170,
-    125, 206, 147, 137, 156, 85, 46, 7, 193, 228, 150, 22, 46, 147, 51, 153,
-    157, 248, 151, 253, 114, 58, 126, 38, 127, 19, 225, 99, 183, 235, 175, 90,
-    166, 8, 233, 141, 10, 85, 232, 147, 7, 20, 137, 240, 84, 231, 86, 134, 50,
-    212, 201, 6, 253, 18, 150, 114, 160, 140, 2, 254, 189, 152, 14, 68, 110, 50,
-    134, 199, 73, 237, 56, 116, 110, 135, 36, 40, 14, 167, 50, 71, 81, 9, 178,
-    54, 182, 247, 64, 32, 204, 116, 92, 131, 40, 30, 246, 188, 236, 182, 187,
-    132, 239, 124, 136, 238, 146, 137, 247, 87, 52, 15, 78, 108, 135, 178, 101,
-    70, 199, 193, 192, 144, 196, 106, 186, 141, 42, 101, 214, 196, 67, 175, 38,
-    8, 189, 148, 166, 253, 221, 144, 119, 81, 1, 232, 175, 81, 237, 13, 188,
-    220, 230, 47, 115, 184, 179, 0, 51, 118, 39, 22, 114, 232, 88, 15, 121, 216,
-    130, 107, 173, 108, 175, 225, 113, 40, 223, 185, 6, 244, 227, 73, 38, 64,
-    84, 10, 200, 53, 81, 179, 217, 106, 172, 59, 161, 70, 180, 70, 48, 56, 46,
-    233, 133, 245, 57, 120, 154, 167, 49, 109, 188, 152, 245, 181, 1, 159, 247,
-    44, 167, 152, 6, 191, 246, 38, 120, 141, 57, 241, 168, 60, 38, 6, 82, 248,
-    87, 85, 0, 10, 22, 19, 44, 178, 63, 38, 78, 1, 210, 166, 140, 3, 199, 112,
-    135, 155, 36, 13, 204, 124, 47, 5, 190, 103, 91, 205, 147, 248, 115, 177,
-    196, 180, 234, 85, 35, 24, 182, 16, 207, 107, 167, 10, 54, 193, 146, 71, 95,
-    45, 109, 184, 43, 101, 155, 184, 55, 171, 196, 136, 89, 227, 84, 183, 31, 0,
-    137, 230, 146, 250, 6, 27, 225, 241, 11, 95, 124, 248, 133, 171, 149, 67,
-    73, 19, 110, 104, 173, 86, 6, 246, 195, 196, 200, 175, 53, 18, 198, 223,
-    140, 85, 208, 253, 204, 220, 255, 232, 88, 238, 20, 97, 24, 52, 13, 3, 179,
-    62, 151, 101, 67, 33, 79, 253, 157, 253, 233, 197, 109, 130, 78, 11, 165,
-    214, 200, 41, 157, 63, 46, 175, 251, 246, 225, 227, 199, 92, 107, 216, 58,
-    124, 226, 202, 153, 92, 31, 250, 182, 92, 99, 0, 110, 18, 78, 228, 166, 190,
-    67, 88, 245, 155, 139, 82, 152, 39, 204, 74, 140, 95, 82, 82, 160, 147, 144,
-    33, 0, 242, 200, 22, 102, 160, 233, 102, 224, 42, 205, 240, 56, 95, 197,
-    226, 175, 235, 240, 125, 10, 51, 12, 246, 150, 234, 173, 95, 132, 173, 174,
-    1, 161, 23, 39, 52, 168, 87, 54, 4, 66, 201, 34, 155, 82, 133, 170, 76, 52,
-    226, 109, 163, 19, 34, 184, 226, 39, 20, 75, 72, 201, 70, 188, 71, 182, 230,
-    6, 19, 255, 8, 145, 193, 63, 81, 150, 24, 72, 89, 168, 74, 98, 173, 133, 67,
-    227, 44, 252, 252, 227, 192, 125, 20, 227, 144, 24, 31, 48, 67, 67, 48, 94,
-    163, 52, 219, 163, 225, 214, 214, 109, 211, 122, 213, 198, 90, 204, 40, 97,
-    211, 121, 17, 28, 132, 246, 110, 230, 51, 197, 42, 162, 143, 199, 158, 215,
-    210, 133, 60, 65, 127, 1, 153, 193, 22, 171, 250, 114, 204, 246, 255, 126,
-    25, 96, 44, 164, 102, 172, 211, 23, 245, 122, 48, 221, 249, 138, 148, 134,
-    206, 135, 246, 42, 235, 198, 89, 189, 45, 125, 204, 69, 193, 48, 29, 144,
-    125, 224, 127, 66, 1, 134, 141, 224, 211, 193, 141, 69, 128, 75, 167, 244,
-    160, 120, 54, 191, 214, 29, 40, 249, 15, 46, 68, 141, 91, 242, 91, 80, 252,
-    109, 122, 154, 64, 153, 56, 65, 254, 106, 18, 4, 172, 171, 136, 80, 98, 79,
-    133, 255, 4, 100, 191, 144, 171, 219, 46, 132, 181, 130, 228, 107, 68, 32,
-    123, 201, 17, 67, 35, 144, 180, 160, 30, 125, 9, 55, 184, 172, 0, 159, 250,
-    232, 83, 168, 162, 102, 158, 121, 208, 177, 116, 163, 160, 80, 241, 46, 156,
-    58, 203, 240, 67, 176, 244, 170, 160, 115, 122, 141, 154, 101, 218, 178,
-    119, 130, 195, 32, 207, 51, 149, 98, 51, 219, 57, 121, 216, 156, 101, 218,
-    184, 220, 204, 41, 181, 149, 63, 80, 194, 11, 143, 164, 219, 23, 123, 141,
-    119, 43, 94, 78, 175, 89, 165, 48, 167, 44, 45, 219, 197, 15, 202, 118, 116,
-    245, 151, 218, 14, 199, 96, 27, 102, 206, 198, 123, 222, 178, 210, 20, 200,
-    38, 25, 124, 58, 102, 92, 197, 107, 51, 5, 236, 125, 173, 198, 113, 144,
-    108, 177, 22, 104, 223, 165, 39, 9, 84, 87, 124, 80, 153, 8, 212, 76, 2, 28,
-    12, 90, 212, 129, 148, 212, 229, 63, 29, 200, 113, 171, 154, 107, 189, 202,
-    22, 147, 7, 32, 253, 70, 37, 224, 98, 199, 129, 21, 54, 49, 52, 124, 84, 40,
-    190, 194, 108, 73, 26, 15, 124, 87, 87, 198, 217, 122, 127, 82, 167, 131,
-    59, 8, 172, 49, 162, 61, 64, 79, 196, 205, 65, 110, 75, 130, 128, 197, 182,
-    251, 110, 141, 197, 184, 166, 244, 246, 217, 20, 105, 85, 42, 80, 251, 77,
-    59, 204, 247, 179, 218, 181, 124, 209, 4, 28, 118, 234, 145, 237, 140, 106,
-    54, 88, 82, 28, 235, 68, 221, 109, 139, 11, 166, 182, 63, 142, 194, 255,
-    213, 219, 116, 158, 31, 224, 119, 126, 232, 160, 144, 1, 177, 92, 219, 162,
-    49, 181, 116, 163, 104, 245, 193, 188, 26, 172, 15, 190, 135, 207, 106, 246,
-    13, 132, 76, 189, 160, 25, 123, 26, 20, 48, 203, 59, 209, 69, 235, 103, 253,
-    160, 108, 83, 206, 70, 98, 0, 2, 57, 162, 202, 63, 45, 89, 173, 201, 254,
-    254, 253, 143, 21, 77, 131, 184, 234, 37, 68, 206, 69, 186, 179, 145, 147,
-    135, 42, 137, 152, 253, 213, 240, 13, 122, 161, 218, 186, 180, 213, 162,
-    150, 231, 63, 112, 182, 233, 86, 12, 225, 195, 133, 37, 28, 22, 147, 201,
-    200, 197, 115, 3, 138, 194, 86, 79, 247, 27, 241, 149, 128, 197, 8, 11, 134,
-    53, 118, 175, 248, 253, 114, 91, 31, 192, 253, 209, 111, 31, 228, 244, 184,
-    179, 146, 145, 167, 137, 155, 184, 218, 38, 62, 187, 22, 181, 193, 93, 16,
-    9, 195, 42, 198, 225, 100, 144, 148, 223, 184, 40, 117, 32, 131, 94, 93, 83,
-    88, 125, 95, 220, 20, 206, 7, 228, 78, 54, 238, 178, 196, 38, 245, 95, 8,
-    235, 106, 17, 175, 142, 193, 60, 179, 53, 244, 92, 147, 244, 218, 95, 127,
-    251, 128, 42, 105, 82, 243, 224, 213, 25, 91, 151, 22, 201, 18, 25, 230,
-    165, 85, 25, 249, 170, 160, 171, 210, 209, 32, 154, 124, 245, 60, 30, 255,
-    138, 154, 29, 85, 156, 232, 177, 78, 14, 137, 52, 215, 247, 26, 211, 115,
-    72, 20, 133, 232, 1, 151, 251, 63, 45, 120, 69, 49, 209, 130, 255, 2, 218,
-    21, 251, 16, 86, 30, 62, 136, 92, 149, 60, 6, 125, 129, 145, 235, 102, 190,
-    144, 248, 1, 53, 135, 21, 158, 44, 158, 230, 246, 172, 249, 161, 105, 204,
-    49, 60, 70, 63, 127, 163, 231, 175, 174, 234, 147, 185, 62, 5, 244, 156, 4,
-    31, 39, 156, 176, 154, 251, 166, 143, 212, 43, 30, 97, 50, 37, 176, 155, 77,
-    149, 102,
-  ]),
-  'ML-DSA-87': new Uint8Array([
-    48, 130, 10, 50, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 19, 3, 130, 10,
-    33, 0, 146, 184, 67, 40, 172, 27, 204, 27, 135, 22, 109, 223, 127, 23, 97,
-    130, 198, 228, 199, 62, 178, 21, 173, 20, 88, 180, 205, 168, 17, 164, 135,
-    92, 85, 146, 76, 194, 152, 80, 239, 157, 33, 21, 239, 182, 176, 254, 124,
-    80, 135, 138, 87, 169, 103, 99, 87, 5, 91, 161, 239, 59, 18, 246, 148, 20,
-    32, 60, 117, 245, 182, 156, 196, 230, 82, 117, 228, 211, 81, 45, 115, 217,
-    23, 95, 138, 95, 103, 124, 32, 89, 187, 48, 251, 103, 81, 234, 76, 4, 57,
-    197, 71, 48, 125, 169, 195, 147, 183, 142, 166, 8, 147, 7, 77, 240, 111, 44,
-    168, 23, 89, 107, 95, 220, 75, 202, 44, 25, 244, 200, 160, 0, 174, 111, 107,
-    204, 22, 36, 122, 191, 101, 33, 52, 252, 14, 47, 25, 109, 135, 48, 131, 253,
-    195, 237, 60, 86, 14, 119, 85, 28, 182, 139, 136, 191, 59, 187, 90, 126,
-    207, 168, 238, 184, 83, 77, 254, 208, 21, 57, 104, 188, 228, 134, 82, 249,
-    190, 249, 163, 120, 111, 68, 225, 210, 131, 29, 151, 153, 146, 71, 163, 104,
-    225, 194, 33, 64, 213, 92, 145, 253, 42, 32, 233, 95, 224, 72, 184, 119,
-    192, 113, 253, 177, 153, 246, 240, 49, 103, 170, 229, 82, 69, 186, 36, 252,
-    29, 124, 255, 25, 148, 5, 160, 159, 105, 157, 185, 25, 158, 122, 145, 30,
-    245, 51, 205, 62, 56, 154, 23, 52, 0, 225, 177, 105, 195, 131, 177, 151,
-    197, 114, 110, 128, 227, 127, 176, 28, 252, 217, 56, 61, 136, 148, 190, 124,
-    108, 129, 62, 243, 235, 37, 70, 228, 233, 10, 67, 124, 120, 58, 164, 83, 57,
-    80, 39, 204, 98, 28, 199, 136, 75, 12, 51, 255, 24, 130, 127, 19, 239, 160,
-    94, 137, 75, 15, 82, 158, 252, 163, 43, 236, 193, 234, 27, 74, 69, 14, 179,
-    171, 10, 169, 27, 138, 172, 213, 189, 222, 167, 110, 24, 11, 118, 151, 223,
-    94, 154, 125, 9, 21, 137, 70, 128, 51, 102, 52, 105, 104, 157, 54, 186, 75,
-    217, 226, 169, 71, 125, 177, 253, 218, 85, 236, 242, 25, 215, 147, 181, 104,
-    242, 82, 75, 50, 165, 49, 102, 128, 82, 237, 170, 134, 162, 196, 56, 247,
-    199, 138, 102, 118, 37, 66, 114, 165, 177, 156, 251, 39, 199, 12, 167, 246,
-    169, 150, 108, 91, 192, 42, 68, 192, 244, 255, 107, 18, 191, 7, 73, 19, 174,
-    28, 116, 66, 131, 96, 173, 60, 131, 190, 249, 219, 246, 71, 182, 124, 183,
-    129, 101, 109, 95, 180, 78, 17, 140, 182, 62, 80, 195, 184, 253, 176, 169,
-    153, 1, 29, 83, 39, 252, 150, 41, 219, 176, 48, 151, 108, 60, 255, 239, 0,
-    85, 101, 193, 110, 204, 12, 254, 22, 136, 15, 154, 217, 88, 13, 28, 252,
-    232, 48, 32, 33, 41, 221, 103, 227, 228, 177, 54, 175, 195, 106, 174, 140,
-    54, 128, 108, 214, 228, 215, 118, 226, 206, 171, 21, 155, 162, 152, 135,
-    203, 16, 170, 130, 100, 173, 155, 243, 80, 40, 98, 157, 248, 7, 101, 88,
-    199, 218, 224, 141, 82, 238, 121, 92, 82, 97, 49, 31, 155, 61, 63, 84, 227,
-    90, 143, 164, 59, 216, 101, 19, 35, 134, 2, 20, 18, 197, 97, 215, 169, 85,
-    220, 75, 254, 91, 125, 144, 43, 65, 128, 29, 66, 77, 184, 172, 89, 123, 203,
-    231, 254, 59, 18, 249, 204, 249, 220, 153, 84, 166, 63, 23, 108, 120, 145,
-    216, 67, 223, 123, 248, 15, 253, 63, 191, 126, 84, 95, 98, 141, 66, 200,
-    129, 194, 174, 30, 80, 48, 75, 113, 14, 102, 101, 218, 249, 14, 203, 24, 6,
-    11, 89, 99, 56, 49, 17, 148, 160, 242, 101, 191, 102, 227, 115, 33, 107, 71,
-    235, 6, 141, 68, 161, 162, 165, 227, 159, 82, 253, 54, 157, 214, 255, 141,
-    151, 154, 134, 150, 93, 141, 97, 158, 244, 180, 135, 42, 10, 3, 148, 9, 152,
-    149, 20, 18, 237, 253, 180, 136, 165, 13, 80, 195, 220, 43, 3, 178, 10, 127,
-    144, 2, 207, 209, 246, 40, 110, 38, 133, 9, 22, 180, 11, 223, 116, 236, 205,
-    186, 25, 183, 155, 165, 77, 206, 101, 255, 106, 223, 70, 10, 22, 176, 119,
-    142, 163, 197, 178, 251, 216, 112, 237, 63, 201, 77, 219, 196, 2, 176, 48,
-    15, 207, 91, 74, 236, 21, 8, 205, 126, 77, 28, 13, 26, 28, 202, 250, 100,
-    79, 134, 209, 193, 78, 202, 58, 248, 124, 215, 64, 143, 244, 17, 23, 163,
-    162, 241, 249, 49, 210, 168, 203, 213, 135, 77, 14, 29, 35, 193, 234, 58,
-    12, 106, 165, 163, 251, 192, 115, 251, 7, 198, 189, 93, 207, 178, 246, 50,
-    189, 185, 51, 45, 84, 140, 194, 34, 46, 92, 90, 136, 81, 81, 53, 52, 253,
-    128, 247, 131, 243, 21, 207, 245, 141, 49, 178, 213, 214, 211, 8, 17, 234,
-    133, 225, 104, 197, 186, 224, 117, 1, 173, 104, 17, 177, 161, 223, 71, 195,
-    159, 194, 45, 177, 116, 26, 187, 193, 161, 213, 179, 158, 8, 122, 186, 122,
-    191, 158, 5, 12, 178, 235, 78, 132, 78, 189, 16, 86, 110, 73, 51, 129, 255,
-    242, 110, 63, 6, 53, 209, 110, 132, 236, 43, 239, 192, 221, 138, 1, 128,
-    176, 0, 113, 85, 119, 201, 36, 188, 202, 9, 223, 98, 196, 42, 130, 158, 149,
-    200, 150, 202, 132, 118, 153, 97, 54, 195, 154, 15, 45, 246, 129, 144, 255,
-    231, 75, 25, 56, 47, 178, 98, 10, 106, 84, 56, 113, 161, 227, 37, 246, 1,
-    245, 129, 173, 70, 186, 11, 28, 125, 198, 243, 225, 113, 130, 5, 138, 52,
-    251, 194, 152, 105, 48, 214, 13, 246, 228, 75, 236, 194, 223, 177, 64, 150,
-    97, 167, 20, 27, 74, 76, 166, 199, 239, 91, 240, 8, 22, 54, 41, 63, 35, 112,
-    255, 251, 88, 252, 147, 173, 185, 43, 98, 83, 155, 230, 176, 55, 161, 8, 83,
-    15, 110, 151, 241, 110, 168, 203, 190, 198, 218, 152, 144, 150, 58, 123, 11,
-    123, 250, 104, 113, 198, 93, 1, 152, 51, 172, 140, 246, 115, 113, 229, 218,
-    166, 200, 35, 123, 16, 133, 169, 191, 59, 103, 12, 114, 15, 60, 37, 4, 92,
-    208, 54, 31, 79, 56, 225, 6, 11, 74, 107, 79, 225, 239, 16, 73, 249, 234,
-    197, 129, 230, 97, 39, 115, 49, 96, 141, 41, 242, 225, 177, 214, 18, 103,
-    34, 229, 37, 176, 241, 99, 82, 227, 195, 77, 32, 64, 4, 120, 49, 199, 209,
-    139, 2, 4, 222, 233, 45, 151, 141, 142, 252, 41, 124, 231, 13, 144, 63, 212,
-    252, 145, 34, 142, 232, 152, 84, 135, 87, 175, 46, 53, 139, 60, 168, 135,
-    167, 101, 253, 127, 152, 138, 154, 31, 231, 198, 77, 89, 182, 9, 54, 103,
-    119, 100, 218, 245, 44, 191, 74, 30, 152, 84, 22, 62, 159, 131, 163, 223, 3,
-    51, 194, 241, 49, 9, 213, 43, 214, 201, 75, 158, 198, 22, 203, 209, 190,
-    199, 189, 75, 4, 6, 72, 60, 241, 113, 171, 30, 42, 143, 73, 51, 72, 206,
-    110, 175, 203, 195, 199, 15, 155, 208, 166, 121, 26, 132, 59, 44, 72, 155,
-    7, 48, 122, 132, 224, 142, 3, 7, 21, 207, 11, 30, 112, 18, 149, 146, 127,
-    41, 104, 197, 169, 86, 213, 108, 253, 111, 160, 5, 11, 202, 172, 233, 32, 5,
-    52, 92, 124, 152, 162, 11, 88, 28, 166, 248, 141, 251, 38, 161, 53, 49, 136,
-    246, 183, 85, 9, 165, 115, 108, 18, 208, 218, 129, 165, 163, 131, 34, 32,
-    94, 226, 121, 93, 24, 87, 226, 105, 25, 242, 128, 198, 78, 26, 237, 21, 92,
-    33, 121, 8, 119, 131, 140, 193, 14, 60, 139, 130, 19, 65, 96, 50, 5, 249,
-    12, 27, 51, 23, 195, 89, 255, 47, 23, 109, 62, 202, 190, 5, 131, 92, 91, 39,
-    27, 209, 132, 146, 95, 98, 66, 26, 230, 186, 236, 186, 162, 149, 81, 143,
-    221, 21, 79, 171, 236, 21, 161, 19, 135, 10, 213, 168, 200, 135, 19, 221,
-    177, 207, 106, 214, 194, 124, 220, 53, 45, 176, 245, 178, 189, 49, 242, 22,
-    230, 154, 241, 146, 230, 187, 180, 244, 158, 145, 197, 225, 51, 150, 140,
-    26, 175, 78, 142, 243, 232, 221, 137, 205, 130, 178, 54, 216, 136, 118, 212,
-    33, 90, 206, 18, 224, 158, 44, 23, 144, 204, 24, 184, 48, 81, 233, 124, 20,
-    222, 77, 119, 144, 22, 145, 241, 19, 206, 135, 252, 164, 114, 84, 37, 175,
-    107, 67, 243, 183, 137, 213, 53, 236, 41, 73, 50, 34, 146, 218, 204, 68,
-    235, 158, 150, 139, 133, 75, 248, 157, 7, 0, 53, 68, 44, 156, 110, 56, 197,
-    209, 201, 0, 155, 225, 85, 35, 169, 193, 231, 36, 31, 142, 101, 168, 29,
-    192, 31, 128, 81, 191, 253, 184, 153, 226, 214, 1, 27, 6, 0, 192, 36, 101,
-    72, 105, 150, 226, 3, 21, 12, 127, 72, 250, 223, 240, 136, 249, 157, 129,
-    247, 91, 178, 157, 208, 86, 146, 23, 176, 150, 35, 211, 155, 22, 207, 119,
-    168, 167, 122, 180, 183, 106, 97, 138, 87, 96, 34, 141, 110, 145, 231, 226,
-    35, 115, 232, 129, 54, 186, 128, 190, 238, 37, 169, 194, 37, 24, 237, 211,
-    164, 111, 54, 88, 245, 125, 67, 78, 194, 11, 194, 235, 217, 87, 203, 14,
-    220, 189, 107, 68, 94, 139, 66, 230, 5, 54, 64, 28, 246, 143, 75, 127, 239,
-    243, 20, 251, 189, 246, 246, 151, 90, 219, 227, 242, 223, 205, 40, 124, 245,
-    4, 189, 72, 39, 145, 110, 96, 120, 164, 27, 117, 146, 177, 30, 76, 173, 106,
-    130, 228, 192, 189, 59, 167, 30, 127, 178, 164, 5, 133, 80, 199, 207, 216,
-    189, 156, 93, 222, 150, 129, 81, 193, 180, 38, 59, 8, 214, 210, 95, 150,
-    182, 175, 148, 43, 153, 169, 66, 62, 135, 140, 159, 89, 195, 253, 3, 132,
-    180, 164, 244, 243, 232, 71, 196, 200, 158, 194, 114, 115, 193, 88, 161,
-    185, 235, 247, 14, 126, 225, 176, 111, 17, 79, 77, 245, 80, 229, 174, 6,
-    180, 156, 217, 85, 174, 201, 255, 237, 85, 83, 53, 188, 153, 98, 91, 193,
-    21, 211, 49, 145, 196, 252, 52, 66, 226, 131, 203, 214, 130, 237, 194, 208,
-    174, 102, 133, 220, 235, 222, 79, 232, 36, 21, 161, 248, 51, 185, 164, 156,
-    95, 121, 65, 57, 233, 183, 29, 199, 105, 104, 12, 90, 195, 186, 81, 199,
-    176, 1, 87, 16, 226, 192, 206, 185, 197, 46, 34, 75, 174, 153, 238, 15, 50,
-    87, 226, 96, 26, 197, 202, 73, 235, 19, 140, 9, 79, 12, 52, 83, 76, 85, 146,
-    234, 24, 151, 179, 178, 118, 58, 162, 223, 103, 69, 244, 231, 135, 167, 204,
-    117, 166, 60, 237, 82, 63, 11, 4, 207, 10, 146, 54, 126, 191, 79, 133, 60,
-    128, 34, 136, 170, 23, 84, 81, 38, 94, 9, 130, 245, 100, 115, 209, 34, 228,
-    158, 101, 216, 135, 208, 207, 191, 169, 115, 252, 144, 139, 226, 252, 6, 44,
-    221, 134, 170, 87, 11, 46, 124, 58, 219, 179, 238, 6, 98, 216, 18, 174, 42,
-    12, 97, 126, 85, 245, 81, 220, 232, 135, 114, 21, 125, 135, 225, 182, 245,
-    228, 223, 242, 62, 158, 35, 76, 6, 110, 25, 184, 206, 124, 237, 54, 252,
-    199, 44, 78, 89, 0, 135, 44, 176, 57, 168, 36, 221, 173, 77, 214, 209, 60,
-    1, 202, 238, 237, 61, 90, 47, 114, 230, 92, 238, 235, 18, 151, 220, 243,
-    225, 163, 159, 139, 189, 253, 62, 225, 182, 202, 59, 7, 83, 99, 129, 118,
-    175, 37, 246, 85, 119, 251, 246, 69, 180, 247, 37, 24, 194, 89, 158, 97,
-    230, 247, 254, 145, 102, 89, 77, 68, 245, 3, 103, 83, 28, 45, 168, 30, 189,
-    151, 112, 120, 215, 84, 90, 198, 85, 85, 129, 55, 127, 124, 28, 137, 229,
-    139, 54, 88, 229, 105, 81, 212, 83, 28, 107, 250, 82, 164, 43, 24, 15, 57,
-    206, 156, 19, 145, 95, 57, 169, 8, 128, 211, 29, 213, 195, 148, 27, 73, 186,
-    221, 242, 11, 167, 78, 246, 133, 18, 118, 67, 236, 59, 19, 112, 254, 93,
-    168, 157, 118, 0, 107, 248, 57, 149, 67, 222, 123, 225, 207, 251, 69, 218,
-    56, 110, 162, 19, 25, 209, 209, 213, 200, 158, 70, 9, 100, 208, 77, 48, 255,
-    151, 200, 0, 68, 230, 70, 120, 209, 29, 86, 225, 188, 189, 226, 101, 42,
-    176, 32, 147, 121, 72, 151, 130, 217, 13, 66, 148, 84, 129, 215, 249, 164,
-    174, 187, 80, 85, 185, 245, 108, 169, 119, 59, 178, 144, 229, 63, 194, 132,
-    250, 131, 82, 161, 125, 126, 255, 252, 220, 204, 104, 231, 201, 136, 246,
-    116, 43, 88, 233, 0, 43, 128, 214, 40, 59, 81, 147, 139, 132, 69, 24, 233,
-    21, 14, 91, 230, 241, 19, 138, 163, 55, 13, 221, 47, 64, 140, 248, 6, 38,
-    164, 16, 219, 63, 33, 180, 71, 151, 0, 234, 103, 58, 213, 222, 163, 95, 132,
-    1, 178, 146, 66, 124, 242, 223, 102, 129, 192, 214, 194, 117, 162, 252, 246,
-    143, 42, 70, 139, 97, 168, 64, 141, 190, 115, 126, 93, 175, 59, 49, 9, 184,
-    88, 201, 100, 182, 142, 145, 244, 72, 128, 203, 49, 196, 5, 5, 18, 46, 34,
-    87, 171, 132, 158, 128, 75, 194, 8, 242, 52, 156, 229, 245, 56, 245, 88, 14,
-    195, 110, 166, 51, 158, 245, 195, 120, 17, 166, 66, 100, 212, 188, 243, 2,
-    236, 90, 8, 16, 35, 151, 122, 175, 115, 168, 186, 191, 60, 71, 23, 81, 217,
-    79, 203, 239, 61, 146, 247, 168, 112, 83, 102, 146, 222, 178, 45, 247, 63,
-    23, 181, 3, 136, 208, 62, 154, 203, 35, 250, 238, 61, 98, 207, 90, 169, 175,
-    36, 227, 9, 182, 78, 226, 99, 89, 67, 105, 185, 35, 242, 162, 54, 99, 60,
-    148, 14, 118, 1, 26, 120, 62, 82, 62, 222, 34, 99, 58, 174, 145, 199, 190,
-    21, 182, 117, 238, 13, 170, 29, 67, 149, 44, 90, 94, 181, 125, 182, 186, 82,
-    55, 105, 253, 29, 212, 67, 134, 204, 227, 94, 255, 127, 72, 157, 140, 142,
-    224, 77, 149, 29, 170, 45, 163, 214, 209, 46, 28, 125, 177, 111, 2, 92, 121,
-    252, 166, 204, 227, 173, 51, 60, 162, 243, 202, 207, 103, 30, 153, 182, 116,
-    182, 130, 98, 59, 25, 141, 239, 49, 224, 176, 27, 237, 218, 76, 68, 189,
-    108, 185, 136, 255, 105, 150, 11, 153, 44, 248, 139, 199, 178, 235, 85, 115,
-    121, 144, 87, 221, 50, 222, 238, 16, 20, 51, 190, 93, 248, 228, 84, 228,
-    115, 31, 229, 227, 137, 180, 44, 115, 224, 119, 129, 181, 134, 224, 144,
-    186, 123, 208, 118, 96, 101, 177, 191, 232, 171, 6, 17, 247, 187, 173, 84,
-    70, 249, 19, 191, 116, 172, 126, 131, 216, 123, 225, 151, 55, 205, 177, 93,
-    139, 117,
-  ]),
-};
+var { pkcs8, spki } = getMldsaKeyFixtures();
 
 // Test message to sign (same for all variants)
 var data = new Uint8Array([
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa.js
index 667f5d2..8a87ba8 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa.js
@@ -1,581 +1,106 @@
-
 function run_test() {
-    setup({explicit_done: true});
-
-    var subtle = self.crypto.subtle; // Change to test prefixed implementations
-
-    // When are all these tests really done? When all the promises they use have resolved.
-    var all_promises = [];
-
-    // Source file [algorithm_name]_vectors.js provides the getTestVectors method
-    // for the algorithm that drives these tests.
-    var testVectors = getTestVectors();
-
-    // Test verification first, because signing tests rely on that working
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var operation = subtle.verify(vector.algorithm, vector.publicKey, vector.signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification");
-        });
-
-        all_promises.push(promise);
+    const subtle = self.crypto.subtle;
+    const testVectors = getTestVectors().map(
+        vector => ({...vector, data: vector.plaintext})
+    );
+    const importAlgorithm = vector => ({
+        name: vector.algorithm.name,
+        hash: vector.hash,
     });
 
-    // Test verification with an altered buffer during call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                signature[0] = 255 - signature[0];
-                var operation = subtle.verify({
-                    ...vector.algorithm,
-                    get name() {
-                        signature[0] = vector.signature[0];
-                        return vector.algorithm.name;
-                    }
-                }, vector.publicKey, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
+    runSignatureTests({
+        vectors: testVectors,
+        algorithmIdentifier(vector) {
+            return vector.algorithm;
+        },
+        importAlgorithm,
+        dataLabel: "plaintext",
+        shortSignature: false,
+        wrongVerifyLabel: " verification with wrong algorithm name",
+        alteredSignatureLabel: " verification failure with altered signature",
+        alteredDataLabel: " verification failure with altered plaintext",
+        async wrongKey(vector, operation) {
+            const name = vector.algorithm.name === "RSA-PSS"
+                ? "RSASSA-PKCS1-v1_5"
+                : "RSA-PSS";
+            const isSign = operation === "sign";
+            return subtle.importKey(
+                isSign ? vector.privateKeyFormat : vector.publicKeyFormat,
+                isSign ? vector.privateKeyBuffer : vector.publicKeyBuffer,
+                {name, hash: vector.hash},
+                false,
+                [operation]
+            );
+        },
+        async roundTrip({
+            vector,
+            algorithm,
+            verificationKey,
+            signingKey,
+        }) {
+            const isDeterministic = !("saltLength" in algorithm) ||
+                algorithm.saltLength === 0;
+            const signature = await subtle.sign(
+                algorithm,
+                signingKey,
+                vector.data
+            );
 
-                return operation;
-            }, vector.name + " verification with altered signature during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with altered signature during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with an altered buffer after call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                var operation = subtle.verify(vector.algorithm, vector.publicKey, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                signature[0] = 255 - signature[0];
-                return operation;
-            }, vector.name + " verification with altered signature after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with altered signature after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with a transferred buffer during call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                var operation = subtle.verify({
-                    ...vector.algorithm,
-                    get name() {
-                        signature.buffer.transfer();
-                        return vector.algorithm.name;
-                    }
-                }, vector.publicKey, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification with transferred signature during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with transferred signature during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with a transferred buffer after call
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                var operation = subtle.verify(vector.algorithm, vector.publicKey, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                signature.buffer.transfer();
-                return operation;
-            }, vector.name + " verification with transferred signature after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with transferred signature after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful verification even if plaintext is altered during call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                plaintext[0] = 255 - plaintext[0];
-                var operation = subtle.verify({
-                    ...vector.algorithm,
-                    get name() {
-                        plaintext[0] = vector.plaintext[0];
-                        return vector.algorithm.name;
-                    }
-                }, vector.publicKey, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " with altered plaintext during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with altered plaintext during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful verification even if plaintext is altered after call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                var operation = subtle.verify(vector.algorithm, vector.publicKey, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                plaintext[0] = 255 - plaintext[0];
-                return operation;
-            }, vector.name + " with altered plaintext after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with altered plaintext after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for failed verification if plaintext is transferred during call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                var operation = subtle.verify({
-                    ...vector.algorithm,
-                    get name() {
-                        plaintext.buffer.transfer();
-                        return vector.algorithm.name;
-                    }
-                }, vector.publicKey, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature is NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " with transferred plaintext during call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with transferred plaintext during call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful verification even if plaintext is transferred after call.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                var operation = subtle.verify(vector.algorithm, vector.publicKey, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_true(is_verified, "Signature verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                plaintext.buffer.transfer();
-                return operation;
-            }, vector.name + " with transferred plaintext after call");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " with transferred plaintext after call");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for failures due to using privateKey to verify.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                return subtle.verify(vector.algorithm, vector.privateKey, vector.signature, vector.plaintext)
-                .then(function(plaintext) {
-                    assert_unreached("Should have thrown error for using privateKey to verify in " + vector.name + ": '" + err.message + "'");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
-                });
-            }, vector.name + " using privateKey to verify");
-
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " using privateKey to verify");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for failures due to using publicKey to sign.
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                return subtle.sign(vector.algorithm, vector.publicKey, vector.plaintext)
-                .then(function(signature) {
-                    assert_unreached("Should have thrown error for using publicKey to sign in " + vector.name + ": '" + err.message + "'");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
-                });
-            }, vector.name + " using publicKey to sign");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " using publicKey to sign");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for failures due to no "verify" usage.
-    testVectors.forEach(function(originalVector) {
-        var vector = Object.assign({}, originalVector);
-
-        var promise = importVectorKeys(vector, [], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                return subtle.verify(vector.algorithm, vector.publicKey, vector.signature, vector.plaintext)
-                .then(function(plaintext) {
-                    assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": '" + err.message + "'");
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'");
-                });
-            }, vector.name + " no verify usage");
-        }, function(err) {
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " no verify usage");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Check for successful signing and verification.
-    testVectors.forEach(function(vector) {
-        // RSA signing is deterministic with PKCS#1 v1.5, or PSS with zero-length salts.
-        const isDeterministic = !("saltLength" in vector.algorithm) || vector.algorithm.saltLength == 0;
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                return subtle.sign(vector.algorithm, vector.privateKey, vector.plaintext)
-                .then(function(signature) {
-                    if (isDeterministic) {
-                        // If deterministic, we can check the output matches. Otherwise, we can only check it verifies.
-                        assert_true(equalBuffers(signature, vector.signature), "Signing did not give the expected output");
-                    }
-                    // Can we verify the new signature?
-                    return subtle.verify(vector.algorithm, vector.publicKey, signature, vector.plaintext)
-                    .then(function(is_verified) {
-                        assert_true(is_verified, "Round trip verifies");
-                        return signature;
-                    }, function(err) {
-                        assert_unreached("verify error for test " + vector.name + ": '" + err.message + "'");
-                    });
-                })
-                .then(function(priorSignature) {
-                    // Will a second signing give us different signature? It should for PSS with non-empty salt
-                    return subtle.sign(vector.algorithm, vector.privateKey, vector.plaintext)
-                    .then(function(signature) {
-                        if (isDeterministic) {
-                            assert_true(equalBuffers(priorSignature, signature), "Two signings with empty salt give same signature")
-                        } else {
-                            assert_false(equalBuffers(priorSignature, signature), "Two signings with a salt give different signatures")
-                        }
-                    }, function(err) {
-                        assert_unreached("second time verify error for test " + vector.name + ": '" + err.message + "'");
-                    });
-                }, function(err) {
-                    assert_unreached("sign error for test " + vector.name + ": '" + err.message + "'");
-                });
-            }, vector.name + " round trip");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested signing or verifying
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " round trip");
-        });
-
-        all_promises.push(promise);
-    });
-
-
-    // Test signing with the wrong algorithm
-    testVectors.forEach(function(vector) {
-        // Want to get the key for the wrong algorithm
-        var alteredVector = Object.assign({}, vector);
-        alteredVector.algorithm = Object.assign({}, vector.algorithm);
-        if (vector.algorithm.name === "RSA-PSS") {
-            alteredVector.algorithm.name = "RSASSA-PKCS1-v1_5";
-        } else {
-            alteredVector.algorithm.name = "RSA-PSS";
-        }
-
-        var promise = importVectorKeys(alteredVector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var operation = subtle.sign(vector.algorithm, alteredVector.privateKey, vector.plaintext)
-                .then(function(signature) {
-                    assert_unreached("Signing should not have succeeded for " + vector.name);
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " signing with wrong algorithm name");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // Test verification with the wrong algorithm
-    testVectors.forEach(function(vector) {
-        // Want to get the key for the wrong algorithm
-        var alteredVector = Object.assign({}, vector);
-        alteredVector.algorithm = Object.assign({}, vector.algorithm);
-        if (vector.algorithm.name === "RSA-PSS") {
-            alteredVector.algorithm.name = "RSASSA-PKCS1-v1_5";
-        } else {
-            alteredVector.algorithm.name = "RSA-PSS";
-        }
-
-        var promise = importVectorKeys(alteredVector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            // Some tests are sign only
-            if (!("signature" in vector)) {
-                return;
+            if (isDeterministic) {
+                assert_true(
+                    equalBuffers(signature, vector.signature),
+                    "Signing did not give the expected output"
+                );
             }
-            promise_test(function(test) {
-                var operation = subtle.verify(vector.algorithm, alteredVector.publicKey, vector.signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_unreached("Verification should not have succeeded for " + vector.name);
-                }, function(err) {
-                    assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'");
-                });
 
-                return operation;
-            }, vector.name + " verification with wrong algorithm name");
+            const isVerified = await subtle.verify(
+                algorithm,
+                verificationKey,
+                signature,
+                vector.data
+            );
+            assert_true(isVerified, "Round trip verifies");
 
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification with wrong algorithm name");
-        });
-
-        all_promises.push(promise);
+            const secondSignature = await subtle.sign(
+                algorithm,
+                signingKey,
+                vector.data
+            );
+            if (isDeterministic) {
+                assert_true(
+                    equalBuffers(signature, secondSignature),
+                    "Two signings with empty salt give same signature"
+                );
+            } else {
+                assert_false(
+                    equalBuffers(signature, secondSignature),
+                    "Two signings with a salt give different signatures"
+                );
+            }
+        },
     });
 
-    // Verification should fail with wrong signature
     testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var signature = copyBuffer(vector.signature);
-                signature[0] = 255 - signature[0];
-                var operation = subtle.verify(vector.algorithm, vector.publicKey, signature, vector.plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification failure with altered signature");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification failure with altered signature");
-        });
-
-        all_promises.push(promise);
-    });
-
-    // [RSA-PSS] Verification should fail with wrong saltLength
-    testVectors.forEach(function(vector) {
-        if (vector.algorithm.name === "RSA-PSS") {
-            var promise = importVectorKeys(vector, ["verify"], ["sign"])
-            .then(function(vectors) {
-                promise_test(function(test) {
-                    const saltLength = vector.algorithm.saltLength === 32 ? 48 : 32;
-                    var operation = subtle.verify({ ...vector.algorithm, saltLength }, vector.publicKey, vector.signature, vector.plaintext)
-                    .then(function(is_verified) {
-                        assert_false(is_verified, "Signature NOT verified");
-                    }, function(err) {
-                        assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                    });
-
-                    return operation;
-                }, vector.name + " verification failure with wrong saltLength");
-
-            }, function(err) {
-                // We need a failed test if the importVectorKey operation fails, so
-                // we know we never tested verification.
-                promise_test(function(test) {
-                    assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-                }, "importVectorKeys step: " + vector.name + " verification failure with wrong saltLength");
-            });
-
-            all_promises.push(promise);
-        }
-    });
-
-    // Verification should fail with wrong plaintext
-    testVectors.forEach(function(vector) {
-        var promise = importVectorKeys(vector, ["verify"], ["sign"])
-        .then(function(vectors) {
-            promise_test(function(test) {
-                var plaintext = copyBuffer(vector.plaintext);
-                plaintext[0] = 255 - plaintext[0];
-                var operation = subtle.verify(vector.algorithm, vector.publicKey, vector.signature, plaintext)
-                .then(function(is_verified) {
-                    assert_false(is_verified, "Signature NOT verified");
-                }, function(err) {
-                    assert_unreached("Verification should not throw error " + vector.name + ": '" + err.message + "'");
-                });
-
-                return operation;
-            }, vector.name + " verification failure with altered plaintext");
-
-        }, function(err) {
-            // We need a failed test if the importVectorKey operation fails, so
-            // we know we never tested verification.
-            promise_test(function(test) {
-                assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''");
-            }, "importVectorKeys step: " + vector.name + " verification failure with altered plaintext");
-        });
-
-        all_promises.push(promise);
-    });
-
-
-    promise_test(function() {
-        return Promise.all(all_promises)
-            .then(function() {done();})
-            .catch(function() {done();})
-    }, "setup");
-
-    // A test vector has all needed fields for signing and verifying, EXCEPT that the
-    // key field may be null. This function replaces that null with the Correct
-    // CryptoKey object.
-    //
-    // Returns a Promise that yields an updated vector on success.
-    function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) {
-        var publicPromise, privatePromise;
-
-        if (vector.publicKey !== null) {
-            publicPromise = new Promise(function(resolve, reject) {
-                resolve(vector);
-            });
-        } else {
-            publicPromise = subtle.importKey(vector.publicKeyFormat, vector.publicKeyBuffer, {name: vector.algorithm.name, hash: vector.hash}, false, publicKeyUsages)
-            .then(function(key) {
-                vector.publicKey = key;
-                return vector;
-            });        // Returns a copy of the sourceBuffer it is sent.
+        if (vector.algorithm.name !== "RSA-PSS") {
+            return;
         }
 
-        if (vector.privateKey !== null) {
-            privatePromise = new Promise(function(resolve, reject) {
-                resolve(vector);
-            });
-        } else {
-            privatePromise = subtle.importKey(vector.privateKeyFormat, vector.privateKeyBuffer, {name: vector.algorithm.name, hash: vector.hash}, false, privateKeyUsages)
-            .then(function(key) {
-                vector.privateKey = key;
-                return vector;
-            });
-        }
-
-        return Promise.all([publicPromise, privatePromise]);
-    }
-
-    return;
+        promise_test(async function() {
+            const key = await subtle.importKey(
+                vector.publicKeyFormat,
+                vector.publicKeyBuffer,
+                importAlgorithm(vector),
+                false,
+                ["verify"]
+            );
+            const saltLength = vector.algorithm.saltLength === 32 ? 48 : 32;
+            const isVerified = await subtle.verify(
+                {...vector.algorithm, saltLength},
+                key,
+                vector.signature,
+                vector.data
+            );
+            assert_false(isVerified, "Signature NOT verified");
+        }, vector.name + " verification failure with wrong saltLength");
+    });
 }
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pkcs.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pkcs.https.any.js
index 3e3a8c23..43b8765 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pkcs.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pkcs.https.any.js
@@ -1,6 +1,8 @@
 // META: title=WebCryptoAPI: sign() and verify() Using RSASSA-PKCS1-v1_5
 // META: script=../util/helpers.js
+// META: script=../util/rsa_key_fixtures.js
 // META: script=rsa_pkcs_vectors.js
+// META: script=signature.js
 // META: script=rsa.js
 // META: timeout=long
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pkcs_vectors.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pkcs_vectors.js
index 71e5d85..f1eca448 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pkcs_vectors.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pkcs_vectors.js
@@ -18,8 +18,9 @@
 //     plaintext - the text to encrypt
 //     signature - the expected signature
 function getTestVectors() {
-    var pkcs8 = new Uint8Array([48, 130, 4, 191, 2, 1, 0, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 4, 130, 4, 169, 48, 130, 4, 165, 2, 1, 0, 2, 130, 1, 1, 0, 211, 87, 96, 146, 230, 41, 87, 54, 69, 68, 231, 228, 35, 59, 123, 219, 41, 61, 178, 8, 81, 34, 196, 121, 50, 133, 70, 249, 240, 247, 18, 246, 87, 196, 177, 120, 104, 201, 48, 144, 140, 197, 148, 247, 237, 0, 192, 20, 66, 193, 175, 4, 194, 246, 120, 164, 139, 162, 200, 15, 209, 113, 62, 48, 181, 172, 80, 120, 122, 195, 81, 101, 137, 241, 113, 150, 127, 99, 134, 173, 163, 73, 0, 166, 187, 4, 238, 206, 164, 43, 240, 67, 206, 217, 160, 249, 77, 12, 192, 158, 145, 155, 157, 113, 102, 192, 138, 182, 206, 32, 70, 64, 174, 164, 196, 146, 13, 182, 216, 110, 185, 22, 208, 220, 192, 244, 52, 26, 16, 56, 4, 41, 231, 225, 3, 33, 68, 234, 148, 157, 232, 246, 192, 204, 191, 149, 250, 142, 146, 141, 112, 216, 163, 140, 225, 104, 219, 69, 246, 241, 52, 102, 61, 111, 101, 111, 92, 234, 188, 114, 93, 168, 192, 42, 171, 234, 170, 19, 172, 54, 167, 92, 192, 186, 225, 53, 223, 49, 20, 182, 101, 137, 199, 237, 60, 182, 21, 89, 174, 90, 56, 79, 22, 43, 250, 128, 219, 228, 97, 127, 134, 195, 241, 208, 16, 201, 79, 226, 201, 191, 1, 154, 110, 99, 179, 239, 192, 40, 212, 60, 238, 97, 28, 133, 236, 38, 60, 144, 108, 70, 55, 114, 198, 145, 27, 25, 238, 192, 150, 202, 118, 236, 94, 49, 225, 227, 2, 3, 1, 0, 1, 2, 130, 1, 1, 0, 139, 55, 92, 203, 135, 200, 37, 197, 255, 61, 83, 208, 9, 145, 110, 150, 65, 5, 126, 24, 82, 114, 39, 160, 122, 178, 38, 190, 16, 136, 129, 58, 59, 56, 187, 123, 72, 243, 119, 5, 81, 101, 250, 42, 147, 57, 210, 77, 198, 103, 213, 197, 186, 52, 39, 230, 164, 129, 23, 110, 172, 21, 255, 212, 144, 104, 49, 30, 28, 40, 59, 159, 58, 142, 12, 184, 9, 180, 99, 12, 80, 170, 143, 62, 69, 166, 11, 53, 158, 25, 191, 140, 187, 94, 202, 214, 78, 118, 31, 16, 149, 116, 63, 243, 106, 175, 92, 240, 236, 185, 127, 237, 173, 221, 166, 11, 91, 243, 93, 129, 26, 117, 184, 34, 35, 12, 250, 160, 25, 47, 173, 64, 84, 126, 39, 84, 72, 170, 51, 22, 191, 142, 43, 76, 224, 133, 79, 199, 112, 139, 83, 123, 162, 45, 19, 33, 11, 9, 174, 195, 122, 39, 89, 239, 192, 130, 161, 83, 27, 35, 169, 23, 48, 3, 125, 222, 78, 242, 107, 95, 150, 239, 220, 195, 159, 211, 76, 52, 90, 213, 28, 187, 228, 79, 229, 139, 138, 59, 78, 201, 151, 134, 108, 8, 109, 255, 27, 136, 49, 239, 10, 31, 234, 38, 60, 247, 218, 205, 3, 192, 76, 188, 194, 178, 121, 229, 127, 165, 185, 83, 153, 107, 251, 29, 214, 136, 23, 175, 127, 180, 44, 222, 247, 165, 41, 74, 87, 250, 194, 184, 173, 115, 159, 27, 2, 153, 2, 129, 129, 0, 251, 248, 51, 194, 198, 49, 201, 112, 36, 12, 142, 116, 133, 240, 106, 62, 162, 168, 72, 34, 81, 26, 134, 39, 221, 70, 78, 248, 175, 175, 113, 72, 209, 164, 37, 182, 184, 101, 125, 221, 82, 70, 131, 43, 142, 83, 48, 32, 197, 187, 181, 104, 133, 90, 106, 236, 62, 66, 33, 215, 147, 241, 220, 91, 47, 37, 132, 226, 65, 94, 72, 233, 162, 189, 41, 43, 19, 64, 49, 249, 156, 142, 180, 47, 192, 188, 208, 68, 155, 242, 44, 230, 222, 201, 112, 20, 239, 229, 172, 147, 235, 232, 53, 135, 118, 86, 37, 44, 187, 177, 108, 65, 91, 103, 177, 132, 210, 40, 69, 104, 162, 119, 213, 147, 53, 88, 92, 253, 2, 129, 129, 0, 214, 184, 206, 39, 199, 41, 93, 93, 22, 252, 53, 112, 237, 100, 200, 218, 147, 3, 250, 210, 148, 136, 193, 166, 94, 154, 215, 17, 249, 3, 112, 24, 125, 187, 253, 129, 49, 109, 105, 100, 139, 200, 140, 197, 200, 53, 81, 175, 255, 69, 222, 186, 207, 182, 17, 5, 247, 9, 228, 195, 8, 9, 185, 0, 49, 235, 214, 134, 36, 68, 150, 198, 246, 158, 105, 46, 189, 200, 20, 246, 66, 57, 244, 173, 21, 117, 110, 203, 120, 197, 165, 176, 153, 49, 219, 24, 48, 119, 197, 70, 163, 140, 76, 116, 56, 137, 173, 61, 62, 208, 121, 181, 98, 46, 208, 18, 15, 160, 225, 249, 59, 89, 61, 183, 216, 82, 224, 95, 2, 129, 128, 56, 135, 75, 157, 131, 247, 129, 120, 206, 45, 158, 252, 23, 92, 131, 137, 127, 214, 127, 48, 107, 191, 166, 159, 100, 238, 52, 35, 104, 206, 212, 124, 128, 195, 241, 206, 23, 122, 117, 141, 100, 186, 251, 12, 151, 134, 164, 66, 133, 250, 1, 205, 236, 53, 7, 205, 238, 125, 201, 183, 226, 178, 29, 60, 187, 204, 16, 14, 238, 153, 103, 132, 59, 5, 115, 41, 253, 204, 166, 41, 152, 237, 15, 17, 179, 140, 232, 176, 171, 199, 222, 57, 1, 124, 113, 207, 208, 174, 87, 84, 108, 85, 145, 68, 205, 208, 175, 208, 100, 95, 126, 168, 255, 7, 185, 116, 209, 237, 68, 253, 31, 142, 0, 245, 96, 191, 109, 69, 2, 129, 129, 0, 133, 41, 239, 144, 115, 207, 143, 123, 95, 249, 226, 26, 186, 223, 58, 65, 115, 211, 144, 6, 112, 223, 175, 89, 66, 106, 188, 223, 4, 147, 193, 61, 47, 29, 27, 70, 184, 36, 166, 172, 24, 148, 179, 217, 37, 37, 12, 24, 30, 52, 114, 193, 96, 120, 5, 110, 177, 154, 141, 40, 247, 31, 48, 128, 146, 117, 52, 129, 212, 148, 68, 253, 247, 140, 158, 166, 194, 68, 7, 220, 1, 142, 119, 211, 175, 239, 56, 91, 47, 247, 67, 158, 150, 35, 121, 65, 51, 45, 212, 70, 206, 190, 255, 219, 68, 4, 254, 79, 113, 89, 81, 97, 208, 22, 64, 44, 51, 77, 15, 87, 198, 26, 190, 79, 249, 244, 203, 249, 2, 129, 129, 0, 135, 216, 119, 8, 212, 103, 99, 228, 204, 190, 178, 209, 233, 113, 46, 91, 240, 33, 109, 112, 222, 148, 32, 165, 178, 6, 155, 116, 89, 185, 159, 93, 159, 127, 47, 173, 124, 215, 154, 174, 230, 122, 127, 154, 52, 67, 126, 60, 121, 168, 74, 240, 205, 141, 233, 223, 242, 104, 235, 12, 71, 147, 245, 1, 249, 136, 213, 64, 246, 211, 71, 92, 32, 121, 184, 34, 122, 35, 217, 104, 222, 196, 227, 198, 101, 3, 24, 113, 147, 69, 150, 48, 71, 43, 253, 182, 186, 29, 231, 134, 199, 151, 250, 111, 78, 166, 90, 42, 132, 25, 38, 47, 41, 103, 136, 86, 203, 115, 201, 189, 75, 200, 155, 94, 4, 27, 34, 119]);
-    var spki = new Uint8Array([48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2, 130, 1, 1, 0, 211, 87, 96, 146, 230, 41, 87, 54, 69, 68, 231, 228, 35, 59, 123, 219, 41, 61, 178, 8, 81, 34, 196, 121, 50, 133, 70, 249, 240, 247, 18, 246, 87, 196, 177, 120, 104, 201, 48, 144, 140, 197, 148, 247, 237, 0, 192, 20, 66, 193, 175, 4, 194, 246, 120, 164, 139, 162, 200, 15, 209, 113, 62, 48, 181, 172, 80, 120, 122, 195, 81, 101, 137, 241, 113, 150, 127, 99, 134, 173, 163, 73, 0, 166, 187, 4, 238, 206, 164, 43, 240, 67, 206, 217, 160, 249, 77, 12, 192, 158, 145, 155, 157, 113, 102, 192, 138, 182, 206, 32, 70, 64, 174, 164, 196, 146, 13, 182, 216, 110, 185, 22, 208, 220, 192, 244, 52, 26, 16, 56, 4, 41, 231, 225, 3, 33, 68, 234, 148, 157, 232, 246, 192, 204, 191, 149, 250, 142, 146, 141, 112, 216, 163, 140, 225, 104, 219, 69, 246, 241, 52, 102, 61, 111, 101, 111, 92, 234, 188, 114, 93, 168, 192, 42, 171, 234, 170, 19, 172, 54, 167, 92, 192, 186, 225, 53, 223, 49, 20, 182, 101, 137, 199, 237, 60, 182, 21, 89, 174, 90, 56, 79, 22, 43, 250, 128, 219, 228, 97, 127, 134, 195, 241, 208, 16, 201, 79, 226, 201, 191, 1, 154, 110, 99, 179, 239, 192, 40, 212, 60, 238, 97, 28, 133, 236, 38, 60, 144, 108, 70, 55, 114, 198, 145, 27, 25, 238, 192, 150, 202, 118, 236, 94, 49, 225, 227, 2, 3, 1, 0, 1]);
+    var keyFixtures = getRsaKeyFixtures();
+    var pkcs8 = keyFixtures.pkcs8;
+    var spki = keyFixtures.spki;
 
     // plaintext
     var plaintext = new Uint8Array([95, 77, 186, 79, 50, 12, 12, 232, 118, 114, 90, 252, 229, 251, 210, 91, 248, 62, 90, 113, 37, 160, 140, 175, 231, 60, 62, 186, 196, 33, 119, 157, 249, 213, 93, 24, 12, 58, 233, 148, 38, 69, 225, 216, 47, 238, 140, 157, 41, 75, 60, 177, 160, 138, 153, 49, 32, 27, 60, 14, 129, 252, 71, 202, 207, 131, 21, 162, 175, 102, 50, 65, 19, 195, 182, 98, 48, 195, 70, 8, 196, 244, 89, 54, 52, 206, 2, 178, 103, 54, 34, 119, 240, 168, 64, 202, 116, 188, 61, 26, 98, 54, 149, 44, 94, 215, 170, 248, 168, 254, 203, 221, 250, 117, 132, 230, 151, 140, 234, 93, 42, 91, 159, 183, 241, 180, 140, 139, 11, 229, 138, 48, 82, 2, 117, 77, 131, 118, 16, 115, 116, 121, 60, 240, 38, 170, 238, 83, 0, 114, 125, 131, 108, 215, 30, 113, 179, 69, 221, 178, 228, 68, 70, 255, 197, 185, 1, 99, 84, 19, 137, 13, 145, 14, 163, 128, 152, 74, 144, 25, 16, 49, 50, 63, 22, 219, 204, 157, 107, 225, 104, 184, 72, 133, 56, 76, 160, 62, 18, 96, 10, 193, 194, 72, 2, 138, 243, 114, 108, 201, 52, 99, 136, 46, 168, 192, 42, 171]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pss.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pss.https.any.js
index 399baba7..c3ed8355 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pss.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pss.https.any.js
@@ -1,6 +1,8 @@
 // META: title=WebCryptoAPI: sign() and verify() Using RSA-PSS
 // META: script=../util/helpers.js
+// META: script=../util/rsa_key_fixtures.js
 // META: script=rsa_pss_vectors.js
+// META: script=signature.js
 // META: script=rsa.js
 // META: timeout=long
 
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pss_vectors.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pss_vectors.js
index c3ce779..224bacc 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pss_vectors.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/rsa_pss_vectors.js
@@ -18,8 +18,9 @@
 //     plaintext - the text to encrypt
 //     signature - the expected signature
 function getTestVectors() {
-    var pkcs8 = new Uint8Array([48, 130, 4, 191, 2, 1, 0, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 4, 130, 4, 169, 48, 130, 4, 165, 2, 1, 0, 2, 130, 1, 1, 0, 211, 87, 96, 146, 230, 41, 87, 54, 69, 68, 231, 228, 35, 59, 123, 219, 41, 61, 178, 8, 81, 34, 196, 121, 50, 133, 70, 249, 240, 247, 18, 246, 87, 196, 177, 120, 104, 201, 48, 144, 140, 197, 148, 247, 237, 0, 192, 20, 66, 193, 175, 4, 194, 246, 120, 164, 139, 162, 200, 15, 209, 113, 62, 48, 181, 172, 80, 120, 122, 195, 81, 101, 137, 241, 113, 150, 127, 99, 134, 173, 163, 73, 0, 166, 187, 4, 238, 206, 164, 43, 240, 67, 206, 217, 160, 249, 77, 12, 192, 158, 145, 155, 157, 113, 102, 192, 138, 182, 206, 32, 70, 64, 174, 164, 196, 146, 13, 182, 216, 110, 185, 22, 208, 220, 192, 244, 52, 26, 16, 56, 4, 41, 231, 225, 3, 33, 68, 234, 148, 157, 232, 246, 192, 204, 191, 149, 250, 142, 146, 141, 112, 216, 163, 140, 225, 104, 219, 69, 246, 241, 52, 102, 61, 111, 101, 111, 92, 234, 188, 114, 93, 168, 192, 42, 171, 234, 170, 19, 172, 54, 167, 92, 192, 186, 225, 53, 223, 49, 20, 182, 101, 137, 199, 237, 60, 182, 21, 89, 174, 90, 56, 79, 22, 43, 250, 128, 219, 228, 97, 127, 134, 195, 241, 208, 16, 201, 79, 226, 201, 191, 1, 154, 110, 99, 179, 239, 192, 40, 212, 60, 238, 97, 28, 133, 236, 38, 60, 144, 108, 70, 55, 114, 198, 145, 27, 25, 238, 192, 150, 202, 118, 236, 94, 49, 225, 227, 2, 3, 1, 0, 1, 2, 130, 1, 1, 0, 139, 55, 92, 203, 135, 200, 37, 197, 255, 61, 83, 208, 9, 145, 110, 150, 65, 5, 126, 24, 82, 114, 39, 160, 122, 178, 38, 190, 16, 136, 129, 58, 59, 56, 187, 123, 72, 243, 119, 5, 81, 101, 250, 42, 147, 57, 210, 77, 198, 103, 213, 197, 186, 52, 39, 230, 164, 129, 23, 110, 172, 21, 255, 212, 144, 104, 49, 30, 28, 40, 59, 159, 58, 142, 12, 184, 9, 180, 99, 12, 80, 170, 143, 62, 69, 166, 11, 53, 158, 25, 191, 140, 187, 94, 202, 214, 78, 118, 31, 16, 149, 116, 63, 243, 106, 175, 92, 240, 236, 185, 127, 237, 173, 221, 166, 11, 91, 243, 93, 129, 26, 117, 184, 34, 35, 12, 250, 160, 25, 47, 173, 64, 84, 126, 39, 84, 72, 170, 51, 22, 191, 142, 43, 76, 224, 133, 79, 199, 112, 139, 83, 123, 162, 45, 19, 33, 11, 9, 174, 195, 122, 39, 89, 239, 192, 130, 161, 83, 27, 35, 169, 23, 48, 3, 125, 222, 78, 242, 107, 95, 150, 239, 220, 195, 159, 211, 76, 52, 90, 213, 28, 187, 228, 79, 229, 139, 138, 59, 78, 201, 151, 134, 108, 8, 109, 255, 27, 136, 49, 239, 10, 31, 234, 38, 60, 247, 218, 205, 3, 192, 76, 188, 194, 178, 121, 229, 127, 165, 185, 83, 153, 107, 251, 29, 214, 136, 23, 175, 127, 180, 44, 222, 247, 165, 41, 74, 87, 250, 194, 184, 173, 115, 159, 27, 2, 153, 2, 129, 129, 0, 251, 248, 51, 194, 198, 49, 201, 112, 36, 12, 142, 116, 133, 240, 106, 62, 162, 168, 72, 34, 81, 26, 134, 39, 221, 70, 78, 248, 175, 175, 113, 72, 209, 164, 37, 182, 184, 101, 125, 221, 82, 70, 131, 43, 142, 83, 48, 32, 197, 187, 181, 104, 133, 90, 106, 236, 62, 66, 33, 215, 147, 241, 220, 91, 47, 37, 132, 226, 65, 94, 72, 233, 162, 189, 41, 43, 19, 64, 49, 249, 156, 142, 180, 47, 192, 188, 208, 68, 155, 242, 44, 230, 222, 201, 112, 20, 239, 229, 172, 147, 235, 232, 53, 135, 118, 86, 37, 44, 187, 177, 108, 65, 91, 103, 177, 132, 210, 40, 69, 104, 162, 119, 213, 147, 53, 88, 92, 253, 2, 129, 129, 0, 214, 184, 206, 39, 199, 41, 93, 93, 22, 252, 53, 112, 237, 100, 200, 218, 147, 3, 250, 210, 148, 136, 193, 166, 94, 154, 215, 17, 249, 3, 112, 24, 125, 187, 253, 129, 49, 109, 105, 100, 139, 200, 140, 197, 200, 53, 81, 175, 255, 69, 222, 186, 207, 182, 17, 5, 247, 9, 228, 195, 8, 9, 185, 0, 49, 235, 214, 134, 36, 68, 150, 198, 246, 158, 105, 46, 189, 200, 20, 246, 66, 57, 244, 173, 21, 117, 110, 203, 120, 197, 165, 176, 153, 49, 219, 24, 48, 119, 197, 70, 163, 140, 76, 116, 56, 137, 173, 61, 62, 208, 121, 181, 98, 46, 208, 18, 15, 160, 225, 249, 59, 89, 61, 183, 216, 82, 224, 95, 2, 129, 128, 56, 135, 75, 157, 131, 247, 129, 120, 206, 45, 158, 252, 23, 92, 131, 137, 127, 214, 127, 48, 107, 191, 166, 159, 100, 238, 52, 35, 104, 206, 212, 124, 128, 195, 241, 206, 23, 122, 117, 141, 100, 186, 251, 12, 151, 134, 164, 66, 133, 250, 1, 205, 236, 53, 7, 205, 238, 125, 201, 183, 226, 178, 29, 60, 187, 204, 16, 14, 238, 153, 103, 132, 59, 5, 115, 41, 253, 204, 166, 41, 152, 237, 15, 17, 179, 140, 232, 176, 171, 199, 222, 57, 1, 124, 113, 207, 208, 174, 87, 84, 108, 85, 145, 68, 205, 208, 175, 208, 100, 95, 126, 168, 255, 7, 185, 116, 209, 237, 68, 253, 31, 142, 0, 245, 96, 191, 109, 69, 2, 129, 129, 0, 133, 41, 239, 144, 115, 207, 143, 123, 95, 249, 226, 26, 186, 223, 58, 65, 115, 211, 144, 6, 112, 223, 175, 89, 66, 106, 188, 223, 4, 147, 193, 61, 47, 29, 27, 70, 184, 36, 166, 172, 24, 148, 179, 217, 37, 37, 12, 24, 30, 52, 114, 193, 96, 120, 5, 110, 177, 154, 141, 40, 247, 31, 48, 128, 146, 117, 52, 129, 212, 148, 68, 253, 247, 140, 158, 166, 194, 68, 7, 220, 1, 142, 119, 211, 175, 239, 56, 91, 47, 247, 67, 158, 150, 35, 121, 65, 51, 45, 212, 70, 206, 190, 255, 219, 68, 4, 254, 79, 113, 89, 81, 97, 208, 22, 64, 44, 51, 77, 15, 87, 198, 26, 190, 79, 249, 244, 203, 249, 2, 129, 129, 0, 135, 216, 119, 8, 212, 103, 99, 228, 204, 190, 178, 209, 233, 113, 46, 91, 240, 33, 109, 112, 222, 148, 32, 165, 178, 6, 155, 116, 89, 185, 159, 93, 159, 127, 47, 173, 124, 215, 154, 174, 230, 122, 127, 154, 52, 67, 126, 60, 121, 168, 74, 240, 205, 141, 233, 223, 242, 104, 235, 12, 71, 147, 245, 1, 249, 136, 213, 64, 246, 211, 71, 92, 32, 121, 184, 34, 122, 35, 217, 104, 222, 196, 227, 198, 101, 3, 24, 113, 147, 69, 150, 48, 71, 43, 253, 182, 186, 29, 231, 134, 199, 151, 250, 111, 78, 166, 90, 42, 132, 25, 38, 47, 41, 103, 136, 86, 203, 115, 201, 189, 75, 200, 155, 94, 4, 27, 34, 119]);
-    var spki = new Uint8Array([48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2, 130, 1, 1, 0, 211, 87, 96, 146, 230, 41, 87, 54, 69, 68, 231, 228, 35, 59, 123, 219, 41, 61, 178, 8, 81, 34, 196, 121, 50, 133, 70, 249, 240, 247, 18, 246, 87, 196, 177, 120, 104, 201, 48, 144, 140, 197, 148, 247, 237, 0, 192, 20, 66, 193, 175, 4, 194, 246, 120, 164, 139, 162, 200, 15, 209, 113, 62, 48, 181, 172, 80, 120, 122, 195, 81, 101, 137, 241, 113, 150, 127, 99, 134, 173, 163, 73, 0, 166, 187, 4, 238, 206, 164, 43, 240, 67, 206, 217, 160, 249, 77, 12, 192, 158, 145, 155, 157, 113, 102, 192, 138, 182, 206, 32, 70, 64, 174, 164, 196, 146, 13, 182, 216, 110, 185, 22, 208, 220, 192, 244, 52, 26, 16, 56, 4, 41, 231, 225, 3, 33, 68, 234, 148, 157, 232, 246, 192, 204, 191, 149, 250, 142, 146, 141, 112, 216, 163, 140, 225, 104, 219, 69, 246, 241, 52, 102, 61, 111, 101, 111, 92, 234, 188, 114, 93, 168, 192, 42, 171, 234, 170, 19, 172, 54, 167, 92, 192, 186, 225, 53, 223, 49, 20, 182, 101, 137, 199, 237, 60, 182, 21, 89, 174, 90, 56, 79, 22, 43, 250, 128, 219, 228, 97, 127, 134, 195, 241, 208, 16, 201, 79, 226, 201, 191, 1, 154, 110, 99, 179, 239, 192, 40, 212, 60, 238, 97, 28, 133, 236, 38, 60, 144, 108, 70, 55, 114, 198, 145, 27, 25, 238, 192, 150, 202, 118, 236, 94, 49, 225, 227, 2, 3, 1, 0, 1]);
+    var keyFixtures = getRsaKeyFixtures();
+    var pkcs8 = keyFixtures.pkcs8;
+    var spki = keyFixtures.spki;
 
     // plaintext for RSA-PSS
     var plaintext = new Uint8Array([95, 77, 186, 79, 50, 12, 12, 232, 118, 114, 90, 252, 229, 251, 210, 91, 248, 62, 90, 113, 37, 160, 140, 175, 231, 60, 62, 186, 196, 33, 119, 157, 249, 213, 93, 24, 12, 58, 233, 148, 38, 69, 225, 216, 47, 238, 140, 157, 41, 75, 60, 177, 160, 138, 153, 49, 32, 27, 60, 14, 129, 252, 71, 202, 207, 131, 21, 162, 175, 102, 50, 65, 19, 195, 182, 98, 48, 195, 70, 8, 196, 244, 89, 54, 52, 206, 2, 178, 103, 54, 34, 119, 240, 168, 64, 202, 116, 188, 61, 26, 98, 54, 149, 44, 94, 215, 170, 248, 168, 254, 203, 221, 250, 117, 132, 230, 151, 140, 234, 93, 42, 91, 159, 183, 241, 180, 140, 139, 11, 229, 138, 48, 82, 2, 117, 77, 131, 118, 16, 115, 116, 121, 60, 240, 38, 170, 238, 83, 0, 114, 125, 131, 108, 215, 30, 113, 179, 69, 221, 178, 228, 68, 70, 255, 197, 185, 1, 99, 84, 19, 137, 13, 145, 14, 163, 128, 152, 74, 144, 25, 16, 49, 50, 63, 22, 219, 204, 157, 107, 225, 104, 184, 72, 133, 56, 76, 160, 62, 18, 96, 10, 193, 194, 72, 2, 138, 243, 114, 108, 201, 52, 99, 136, 46, 168, 192, 42, 171]);
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/signature.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/signature.js
new file mode 100644
index 0000000..2d734e1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/sign_verify/signature.js
@@ -0,0 +1,368 @@
+function runSignatureTests(options) {
+  const subtle = self.crypto.subtle;
+  const publicKeyCache = new WeakMap();
+  const privateKeyCache = new WeakMap();
+  const dataLabel = options.dataLabel || 'data';
+
+  function algorithmIdentifier(vector) {
+    return options.algorithmIdentifier(vector);
+  }
+
+  function algorithmName(vector) {
+    const algorithm = algorithmIdentifier(vector);
+    return typeof algorithm === 'string' ? algorithm : algorithm.name;
+  }
+
+  function algorithmWithNameGetter(vector, getter) {
+    const algorithm = algorithmIdentifier(vector);
+    const result = typeof algorithm === 'string' ? {} : { ...algorithm };
+    Object.defineProperty(result, 'name', {
+      enumerable: true,
+      get: getter,
+    });
+    return result;
+  }
+
+  function importAlgorithm(vector) {
+    return options.importAlgorithm
+      ? options.importAlgorithm(vector)
+      : {name: algorithmName(vector)};
+  }
+
+  function cachedKey(cache, vector, usages, importer) {
+    let keys = cache.get(vector);
+    if (keys === undefined) {
+      keys = new Map();
+      cache.set(vector, keys);
+    }
+
+    const cacheKey = usages.join(',');
+    if (!keys.has(cacheKey)) {
+      keys.set(cacheKey, importer());
+    }
+    return keys.get(cacheKey);
+  }
+
+  function publicKey(vector, usages = ['verify']) {
+    return cachedKey(publicKeyCache, vector, usages, function () {
+      return subtle.importKey(
+        vector.publicKeyFormat || 'spki',
+        vector.publicKeyBuffer,
+        importAlgorithm(vector),
+        false,
+        usages
+      );
+    });
+  }
+
+  function privateKey(vector, usages = ['sign']) {
+    return cachedKey(privateKeyCache, vector, usages, function () {
+      return subtle.importKey(
+        vector.privateKeyFormat || 'pkcs8',
+        vector.privateKeyBuffer,
+        importAlgorithm(vector),
+        false,
+        usages
+      );
+    });
+  }
+
+  async function assertInvalidAccess(operation, message) {
+    let error;
+    try {
+      await operation();
+    } catch (caught) {
+      error = caught;
+    }
+    assert_not_equals(error, undefined, message);
+    assert_equals(
+      error.name,
+      'InvalidAccessError',
+      "Should have thrown InvalidAccessError instead of '" + error.message + "'"
+    );
+  }
+
+  options.vectors.forEach(function (vector) {
+    const algorithm = algorithmIdentifier(vector);
+
+    promise_test(async function () {
+      const key = await publicKey(vector);
+      const isVerified = await subtle.verify(
+        algorithm,
+        key,
+        vector.signature,
+        vector.data
+      );
+      assert_true(isVerified, 'Signature verified');
+    }, vector.name + ' verification');
+
+    promise_test(async function () {
+      const key = await publicKey(vector);
+      const signature = copyBuffer(vector.signature);
+      signature[0] = 255 - signature[0];
+      const duringCallAlgorithm = algorithmWithNameGetter(vector, function () {
+        signature[0] = vector.signature[0];
+        return algorithmName(vector);
+      });
+      const isVerified = await subtle.verify(
+        duringCallAlgorithm,
+        key,
+        signature,
+        vector.data
+      );
+      assert_true(isVerified, 'Signature verified');
+    }, vector.name + ' verification with altered signature during call');
+
+    promise_test(async function () {
+      const key = await publicKey(vector);
+      const signature = copyBuffer(vector.signature);
+      const operation = subtle.verify(algorithm, key, signature, vector.data);
+      signature[0] = 255 - signature[0];
+      assert_true(await operation, 'Signature verified');
+    }, vector.name + ' verification with altered signature after call');
+
+    promise_test(async function () {
+      const key = await publicKey(vector);
+      const signature = copyBuffer(vector.signature);
+      const duringCallAlgorithm = algorithmWithNameGetter(vector, function () {
+        signature.buffer.transfer();
+        return algorithmName(vector);
+      });
+      const isVerified = await subtle.verify(
+        duringCallAlgorithm,
+        key,
+        signature,
+        vector.data
+      );
+      assert_false(isVerified, 'Signature is NOT verified');
+    }, vector.name + ' verification with transferred signature during call');
+
+    promise_test(async function () {
+      const key = await publicKey(vector);
+      const signature = copyBuffer(vector.signature);
+      const operation = subtle.verify(algorithm, key, signature, vector.data);
+      signature.buffer.transfer();
+      assert_true(await operation, 'Signature verified');
+    }, vector.name + ' verification with transferred signature after call');
+
+    promise_test(async function () {
+      const key = await publicKey(vector);
+      const data = copyBuffer(vector.data);
+      data[0] = 255 - data[0];
+      const duringCallAlgorithm = algorithmWithNameGetter(vector, function () {
+        data[0] = vector.data[0];
+        return algorithmName(vector);
+      });
+      const isVerified = await subtle.verify(
+        duringCallAlgorithm,
+        key,
+        vector.signature,
+        data
+      );
+      assert_true(isVerified, 'Signature verified');
+    }, vector.name + ' with altered ' + dataLabel + ' during call');
+
+    promise_test(async function () {
+      const key = await publicKey(vector);
+      const data = copyBuffer(vector.data);
+      const operation = subtle.verify(algorithm, key, vector.signature, data);
+      data[0] = 255 - data[0];
+      assert_true(await operation, 'Signature verified');
+    }, vector.name + ' with altered ' + dataLabel + ' after call');
+
+    promise_test(async function () {
+      const key = await publicKey(vector);
+      const data = copyBuffer(vector.data);
+      const duringCallAlgorithm = algorithmWithNameGetter(vector, function () {
+        data.buffer.transfer();
+        return algorithmName(vector);
+      });
+      const isVerified = await subtle.verify(
+        duringCallAlgorithm,
+        key,
+        vector.signature,
+        data
+      );
+      assert_false(isVerified, 'Signature is NOT verified');
+    }, vector.name + ' with transferred ' + dataLabel + ' during call');
+
+    promise_test(async function () {
+      const key = await publicKey(vector);
+      const data = copyBuffer(vector.data);
+      const operation = subtle.verify(algorithm, key, vector.signature, data);
+      data.buffer.transfer();
+      assert_true(await operation, 'Signature verified');
+    }, vector.name + ' with transferred ' + dataLabel + ' after call');
+
+    promise_test(async function () {
+      const key = await privateKey(vector);
+      await assertInvalidAccess(
+        () => subtle.verify(algorithm, key, vector.signature, vector.data),
+        'Using a private key to verify should fail'
+      );
+    }, vector.name + ' using privateKey to verify');
+
+    promise_test(async function () {
+      const key = await publicKey(vector);
+      await assertInvalidAccess(
+        () => subtle.sign(algorithm, key, vector.data),
+        'Using a public key to sign should fail'
+      );
+    }, vector.name + ' using publicKey to sign');
+
+    promise_test(async function () {
+      const key = await publicKey(vector, []);
+      await assertInvalidAccess(
+        () => subtle.verify(algorithm, key, vector.signature, vector.data),
+        'Verifying without the verify usage should fail'
+      );
+    }, vector.name + ' no verify usage');
+
+    promise_test(async function () {
+      const verificationKey = await publicKey(vector);
+      const signingKey = await privateKey(vector);
+
+      if (options.roundTrip) {
+        await options.roundTrip({
+          subtle,
+          vector,
+          algorithm,
+          verificationKey,
+          signingKey,
+        });
+        return;
+      }
+
+      if (options.katFirst) {
+        const vectorSignatureIsVerified = await subtle.verify(
+          algorithm,
+          verificationKey,
+          vector.signature,
+          vector.data
+        );
+        assert_true(
+          vectorSignatureIsVerified,
+          'Known-answer signature verified'
+        );
+      }
+
+      const signature = await subtle.sign(algorithm, signingKey, vector.data);
+
+      if (!options.katFirst || !equalBuffers(signature, vector.signature)) {
+        const generatedSignatureIsVerified = await subtle.verify(
+          algorithm,
+          verificationKey,
+          signature,
+          vector.data
+        );
+        assert_true(
+          generatedSignatureIsVerified,
+          'Generated signature verified'
+        );
+      }
+    }, vector.name + ' round trip');
+
+    promise_test(async function () {
+      const wrongKey = options.wrongKey
+        ? await options.wrongKey(vector, 'sign')
+        : await subtle.generateKey(
+            {name: 'HMAC', hash: 'SHA-1'},
+            false,
+            ['sign', 'verify']
+          );
+      await assertInvalidAccess(
+        () => subtle.sign(algorithm, wrongKey, vector.data),
+        'Signing with a key for another algorithm should fail'
+      );
+    }, vector.name + ' signing with wrong algorithm name');
+
+    promise_test(async function () {
+      const wrongKey = options.wrongKey
+        ? await options.wrongKey(vector, 'verify')
+        : await subtle.generateKey(
+            {name: 'HMAC', hash: 'SHA-1'},
+            false,
+            ['sign', 'verify']
+          );
+      await assertInvalidAccess(
+        () => subtle.verify(algorithm, wrongKey, vector.signature, vector.data),
+        'Verifying with a key for another algorithm should fail'
+      );
+    }, vector.name +
+      (options.wrongVerifyLabel || ' verifying with wrong algorithm name'));
+
+    promise_test(async function () {
+      const key = await publicKey(vector);
+      const signature = copyBuffer(vector.signature);
+      signature[0] = 255 - signature[0];
+      const isVerified = await subtle.verify(
+        algorithm,
+        key,
+        signature,
+        vector.data
+      );
+      assert_false(isVerified, 'Signature NOT verified');
+    }, vector.name +
+      (options.alteredSignatureLabel ||
+        ' verification failure due to altered signature'));
+
+    if (options.shortSignature !== false) {
+      promise_test(async function () {
+        const key = await publicKey(vector);
+        const signature = vector.signature.slice(1);
+        const isVerified = await subtle.verify(
+          algorithm,
+          key,
+          signature,
+          vector.data
+        );
+        assert_false(isVerified, 'Signature NOT verified');
+      }, vector.name + ' verification failure due to shortened signature');
+    }
+
+    promise_test(async function () {
+      const key = await publicKey(vector);
+      const data = copyBuffer(vector.data);
+      data[0] = 255 - data[0];
+      const isVerified = await subtle.verify(
+        algorithm,
+        key,
+        vector.signature,
+        data
+      );
+      assert_false(isVerified, 'Signature NOT verified');
+    }, vector.name +
+      (options.alteredDataLabel ||
+        ' verification failure due to altered ' + dataLabel));
+
+    if (options.generatedKeys) {
+      promise_test(async function () {
+        const key = await subtle.generateKey(algorithm, false, ['sign', 'verify']);
+        const signature = await subtle.sign(
+          algorithm,
+          key.privateKey,
+          vector.data
+        );
+        const isVerified = await subtle.verify(
+          algorithm,
+          key.publicKey,
+          signature,
+          vector.data
+        );
+        assert_true(isVerified, 'Verification failed.');
+      }, 'Sign and verify using generated ' + algorithmName(vector) + ' keys.');
+    }
+  });
+
+  (options.invalidVectors || []).forEach(function (vector) {
+    promise_test(async function () {
+      const isVerified = await subtle.verify(
+        algorithmIdentifier(vector),
+        await publicKey(vector),
+        vector.signature,
+        vector.data
+      );
+      assert_false(isVerified, 'Signature unexpectedly verified');
+    }, vector.name + ' verification');
+  });
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/supports-modern.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/supports-modern.tentative.https.any.js
index ca23b78..97cf9df 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/supports-modern.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/supports-modern.tentative.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCrypto API: supports method tests for algorithms in https://wicg.github.io/webcrypto-modern-algos/
 // META: script=util/helpers.js
+// META: script=util/supports.js
 
 'use strict';
 
@@ -58,73 +59,11 @@
 ];
 
 // Test that supports method exists and is a static method
-test(() => {
-  assert_true(
-      typeof SubtleCrypto.supports === 'function',
-      'SubtleCrypto.supports should be a function');
-}, 'SubtleCrypto.supports method exists');
+testSupportsMethod();
 
 
 // Test standard WebCrypto algorithms for requested operations
-for (const [algorithmName, algorithmInfo] of Object.entries(modernAlgorithms)) {
-  for (const operation of operations) {
-    promise_test(async (t) => {
-      const isSupported = algorithmInfo.operations.includes(operation);
-
-      // Use appropriate algorithm parameters for each operation
-      let algorithm;
-      let lengthOrAdditionalAlgorithm;
-      switch (operation) {
-        case 'generateKey':
-          algorithm = algorithmInfo.keyGenParams || algorithmName;
-          break;
-        case 'importKey':
-          algorithm = algorithmInfo.importParams || algorithmName;
-          break;
-        case 'sign':
-        case 'verify':
-          algorithm = algorithmInfo.signParams || algorithmName;
-          break;
-        case 'encrypt':
-        case 'decrypt':
-          algorithm = algorithmInfo.encryptParams || algorithmName;
-          break;
-        case 'deriveBits':
-          algorithm = algorithmInfo.deriveBitsParams || algorithmName;
-          if (algorithm?.public instanceof Promise) {
-            algorithm.public = (await algorithm.public).publicKey;
-          }
-          if (algorithmName === 'PBKDF2' || algorithmName === 'HKDF') {
-            lengthOrAdditionalAlgorithm = 256;
-          }
-          break;
-        case 'digest':
-          algorithm = algorithmName;
-          break;
-        case 'encapsulateKey':
-        case 'encapsulateBits':
-        case 'decapsulateKey':
-        case 'decapsulateBits':
-          algorithm = algorithmName;
-          if (operation === 'encapsulateKey' || operation === 'decapsulateKey') {
-            lengthOrAdditionalAlgorithm = { name: 'AES-GCM', length: 256 };
-          }
-          break;
-        default:
-          algorithm = algorithmName;
-      }
-
-      const result = SubtleCrypto.supports(operation, algorithm, lengthOrAdditionalAlgorithm);
-
-      if (isSupported) {
-        assert_true(result, `${algorithmName} should support ${operation}`);
-      } else {
-        assert_false(
-            result, `${algorithmName} should not support ${operation}`);
-      }
-    }, `supports(${operation}, ${algorithmName})`);
-  }
-}
+runSupportsTests(modernAlgorithms, operations);
 
 // Test some algorithm objects with valid parameters
 test(() => {
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/supports.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/supports.tentative.https.any.js
index ac3a32c..fcccbec 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/supports.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/supports.tentative.https.any.js
@@ -1,5 +1,6 @@
 // META: title=WebCrypto API: supports method tests
 // META: script=util/helpers.js
+// META: script=util/supports.js
 
 'use strict';
 
@@ -156,12 +157,7 @@
 ];
 
 // Test that supports method exists and is a static method
-test(() => {
-  assert_true(
-    typeof SubtleCrypto.supports === 'function',
-    'SubtleCrypto.supports should be a function'
-  );
-}, 'SubtleCrypto.supports method exists');
+testSupportsMethod();
 
 // Test invalid operation names
 test(() => {
@@ -192,57 +188,7 @@
 }, 'supports returns false for invalid algorithms');
 
 // Test standard WebCrypto algorithms for requested operations
-for (const [algorithmName, algorithmInfo] of Object.entries(
-  standardAlgorithms
-)) {
-  for (const operation of operations) {
-    promise_test(async (t) => {
-      const isSupported = algorithmInfo.operations.includes(operation);
-
-      // Use appropriate algorithm parameters for each operation
-      let algorithm;
-      let length;
-      switch (operation) {
-        case 'generateKey':
-          algorithm = algorithmInfo.keyGenParams || algorithmName;
-          break;
-        case 'importKey':
-          algorithm = algorithmInfo.importParams || algorithmName;
-          break;
-        case 'sign':
-        case 'verify':
-          algorithm = algorithmInfo.signParams || algorithmName;
-          break;
-        case 'encrypt':
-        case 'decrypt':
-          algorithm = algorithmInfo.encryptParams || algorithmName;
-          break;
-        case 'deriveBits':
-          algorithm = algorithmInfo.deriveBitsParams || algorithmName;
-          if (algorithm?.public instanceof Promise) {
-            algorithm.public = (await algorithm.public).publicKey;
-          }
-          if (algorithmName === 'PBKDF2' || algorithmName === 'HKDF') {
-            length = 256;
-          }
-          break;
-        case 'digest':
-          algorithm = algorithmName;
-          break;
-        default:
-          algorithm = algorithmName;
-      }
-
-      const result = SubtleCrypto.supports(operation, algorithm, length);
-
-      if (isSupported) {
-        assert_true(result, `${algorithmName} should support ${operation}`);
-      } else {
-        assert_false(result, `${algorithmName} should not support ${operation}`);
-      }
-    }, `supports(${operation}, ${algorithmName})`);
-  }
-}
+runSupportsTests(standardAlgorithms, operations);
 
 // Test algorithm objects (not just strings)
 test(() => {
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/tools/generate.py b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/tools/generate.py
index 35ba0f0..cc6c7ae 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/tools/generate.py
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/tools/generate.py
@@ -1,77 +1,262 @@
-# script to generate the generateKey tests
+"""Generate the WebCryptoAPI generateKey registry and entrypoints."""
 
-import os
+import argparse
+import difflib
+import json
+from pathlib import Path
+import sys
 
-here = os.path.dirname(__file__)
 
-successes_html = """<!DOCTYPE html>
-<meta charset=utf-8>
-<meta name="timeout" content="long">
-<title>WebCryptoAPI: generateKey() Successful Calls</title>
-<link rel="author" title="Charles Engelke" href="mailto:w3c@engelke.com">
-<link rel="help" href="https://www.w3.org/TR/WebCryptoAPI/#dfn-SubtleCrypto-method-generateKey">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
+HERE = Path(__file__).resolve().parent
+WEBCRYPTO_ROOT = HERE.parent
+GENERATE_KEY_ROOT = WEBCRYPTO_ROOT / "generateKey"
 
-<script src="/WebCryptoAPI/util/helpers.js"></script>
-<script src="successes.js"></script>
+ALGORITHMS = (
+    ("AES-CTR", "CryptoKey", ("encrypt", "decrypt", "wrapKey", "unwrapKey"), ()),
+    ("AES-CBC", "CryptoKey", ("encrypt", "decrypt", "wrapKey", "unwrapKey"), ()),
+    ("AES-GCM", "CryptoKey", ("encrypt", "decrypt", "wrapKey", "unwrapKey"), ()),
+    ("AES-OCB", "CryptoKey", ("encrypt", "decrypt", "wrapKey", "unwrapKey"), ()),
+    (
+        "ChaCha20-Poly1305",
+        "CryptoKey",
+        ("encrypt", "decrypt", "wrapKey", "unwrapKey"),
+        (),
+    ),
+    ("AES-KW", "CryptoKey", ("wrapKey", "unwrapKey"), ()),
+    ("HMAC", "CryptoKey", ("sign", "verify"), ()),
+    ("RSASSA-PKCS1-v1_5", '"CryptoKeyPair"', ("sign", "verify"), ("sign",)),
+    ("RSA-PSS", '"CryptoKeyPair"', ("sign", "verify"), ("sign",)),
+    (
+        "RSA-OAEP",
+        '"CryptoKeyPair"',
+        ("encrypt", "decrypt", "wrapKey", "unwrapKey"),
+        ("decrypt", "unwrapKey"),
+    ),
+    ("ECDSA", '"CryptoKeyPair"', ("sign", "verify"), ("sign",)),
+    (
+        "ECDH",
+        '"CryptoKeyPair"',
+        ("deriveKey", "deriveBits"),
+        ("deriveKey", "deriveBits"),
+    ),
+    ("Ed25519", '"CryptoKeyPair"', ("sign", "verify"), ("sign",)),
+    ("Ed448", '"CryptoKeyPair"', ("sign", "verify"), ("sign",)),
+    ("ML-DSA-44", '"CryptoKeyPair"', ("sign", "verify"), ("sign",)),
+    ("ML-DSA-65", '"CryptoKeyPair"', ("sign", "verify"), ("sign",)),
+    ("ML-DSA-87", '"CryptoKeyPair"', ("sign", "verify"), ("sign",)),
+    (
+        "ML-KEM-512",
+        '"CryptoKeyPair"',
+        ("decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"),
+        ("decapsulateBits", "decapsulateKey"),
+    ),
+    (
+        "ML-KEM-768",
+        '"CryptoKeyPair"',
+        ("decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"),
+        ("decapsulateBits", "decapsulateKey"),
+    ),
+    (
+        "ML-KEM-1024",
+        '"CryptoKeyPair"',
+        ("decapsulateBits", "decapsulateKey", "encapsulateBits", "encapsulateKey"),
+        ("decapsulateBits", "decapsulateKey"),
+    ),
+    (
+        "X25519",
+        '"CryptoKeyPair"',
+        ("deriveKey", "deriveBits"),
+        ("deriveKey", "deriveBits"),
+    ),
+    (
+        "X448",
+        '"CryptoKeyPair"',
+        ("deriveKey", "deriveBits"),
+        ("deriveKey", "deriveBits"),
+    ),
+    ("KMAC128", "CryptoKey", ("sign", "verify"), ()),
+    ("KMAC256", "CryptoKey", ("sign", "verify"), ()),
+)
 
-<h1>generateKey Tests for Good Parameters</h1>
-<p>
-    <strong>Warning!</strong> RSA key generation is intrinsically
-    very slow, so the related tests can take up to
-    several minutes to complete, depending on browser!
-</p>
+GROUPS = (
+    ("AES-CTR", ("AES-CTR",), False),
+    ("AES-CBC", ("AES-CBC",), False),
+    ("AES-GCM", ("AES-GCM",), False),
+    ("AES-OCB", ("AES-OCB",), True),
+    ("chacha20_poly1305", ("ChaCha20-Poly1305",), True),
+    ("AES-KW", ("AES-KW",), False),
+    ("HMAC", ("HMAC",), False),
+    ("RSASSA-PKCS1-v1_5", ("RSASSA-PKCS1-v1_5",), False),
+    ("RSA-PSS", ("RSA-PSS",), False),
+    ("RSA-OAEP", ("RSA-OAEP",), False),
+    ("ECDSA", ("ECDSA",), False),
+    ("ECDH", ("ECDH",), False),
+    ("Ed25519", ("Ed25519",), False),
+    ("Ed448", ("Ed448",), True),
+    ("ML-DSA", ("ML-DSA-44", "ML-DSA-65", "ML-DSA-87"), True),
+    ("ML-KEM", ("ML-KEM-512", "ML-KEM-768", "ML-KEM-1024"), True),
+    ("X25519", ("X25519",), False),
+    ("X448", ("X448",), True),
+    ("kmac", ("KMAC128", "KMAC256"), True),
+)
 
-<div id="log"></div>
-<script>
-run_test([%s]);
-</script>"""
 
-failures_html = """<!DOCTYPE html>
-<meta charset=utf-8>
-<meta name="timeout" content="long">
-<title>WebCryptoAPI: generateKey() for Failures</title>
-<link rel="author" title="Charles Engelke" href="mailto:w3c@engelke.com">
-<link rel="help" href="https://www.w3.org/TR/WebCryptoAPI/#dfn-SubtleCrypto-method-generateKey">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
+def js_array(values):
+    return json.dumps(list(values))
 
-<script src="/WebCryptoAPI/util/helpers.js"></script>
-<script src="failures.js"></script>
 
-<h1>generateKey Tests for Bad Parameters</h1>
+# Algorithm normalization failures are independent of the algorithm under test,
+# so they get a single wrapper instead of one per group.
+BAD_ALGORITHM_WRAPPER = "\n".join(
+    [
+        "// META: title=WebCryptoAPI: generateKey() for Failures",
+        "// META: timeout=long",
+        "// META: script=../util/helpers.js",
+        "// META: script=failures.js",
+        "run_bad_algorithm_test();",
+        "",
+    ]
+)
 
-<div id="log"></div>
-<script>
-run_test([%s]);
-</script>
-"""
 
-successes_worker = """// META: timeout=long
-importScripts("/resources/testharness.js");
-importScripts("../util/helpers.js");
-importScripts("successes.js");
+def generate_registry():
+    lines = [
+        "// Generated by WebCryptoAPI/tools/generate.py. Do not edit directly.",
+        "const generateKeyTestVectors = [",
+    ]
+    for name, result_type, usages, mandatory_usages in ALGORITHMS:
+        lines.append(
+            f"    {{name: {json.dumps(name)}, resultType: {result_type}, "
+            f"usages: {js_array(usages)}, "
+            f"mandatoryUsages: {js_array(mandatory_usages)}}},"
+        )
+    lines.extend(
+        [
+            "];",
+            "",
+            "function getGenerateKeyTestVectors(algorithmNames) {",
+            "    if (algorithmNames && !Array.isArray(algorithmNames)) {",
+            "        algorithmNames = [algorithmNames];",
+            "    }",
+            "",
+            "    return generateKeyTestVectors.filter(",
+            "        vector => !algorithmNames || algorithmNames.includes(vector.name));",
+            "}",
+            "",
+        ]
+    )
+    return "\n".join(lines)
 
-run_test([%s]);
-done();"""
 
-failures_worker = """// META: timeout=long
-importScripts("/resources/testharness.js");
-importScripts("../util/helpers.js");
-importScripts("failures.js");
-run_test([%s]);
-done();"""
+def success_variants(file_id):
+    if file_id == "RSA-OAEP":
+        return [f"?{start}-{start + 9}" for start in range(1, 151, 10)] + [
+            "?151-last"
+        ]
+    if file_id in {"RSA-PSS", "RSASSA-PKCS1-v1_5"}:
+        return ["?1-10", "?11-20", "?21-30", "?31-last"]
+    return []
 
-names = ["AES-CTR", "AES-CBC", "AES-GCM", "AES-KW", "HMAC", "RSASSA-PKCS1-v1_5",
-         "RSA-PSS", "RSA-OAEP", "ECDSA", "ECDH", "Ed25519", "Ed448", "X25519",
-         "X448"]
 
-for filename_pattern, template in [("test_successes_%s.https.html", successes_html),
-                                   ("test_failures_%s.https.html", failures_html),
-                                   ("successes_%s.https.worker.js", successes_worker),
-                                   ("failures_%s.https.worker.js", failures_worker)]:
-    for name in names:
-        path = os.path.join(here, os.pardir, "generateKey", filename_pattern % name)
-        with open(path, "w") as f:
-            f.write(template % '"%s"' % name)
+def generate_wrapper(kind, file_id, algorithms):
+    title = (
+        "WebCryptoAPI: generateKey() Successful Calls"
+        if kind == "successes"
+        else "WebCryptoAPI: generateKey() for Failures"
+    )
+    lines = [
+        f"// META: title={title}",
+        "// META: timeout=long",
+    ]
+    if kind == "successes":
+        lines.extend(
+            f"// META: variant={variant}" for variant in success_variants(file_id)
+        )
+    lines.append("// META: script=../util/helpers.js")
+    if kind == "successes":
+        lines.append("// META: script=/common/subset-tests.js")
+    lines.extend(
+        [
+            "// META: script=algorithm_registry.js",
+            f"// META: script={kind}.js",
+            f"run_test({js_array(algorithms)});",
+            "",
+        ]
+    )
+    return "\n".join(lines)
+
+
+def expected_outputs():
+    outputs = {
+        GENERATE_KEY_ROOT / "algorithm_registry.js": generate_registry(),
+        GENERATE_KEY_ROOT
+        / "failures_bad_algorithm.https.any.js": BAD_ALGORITHM_WRAPPER,
+    }
+    for file_id, algorithms, tentative in GROUPS:
+        marker = ".tentative" if tentative else ""
+        for kind in ("successes", "failures"):
+            path = GENERATE_KEY_ROOT / f"{kind}_{file_id}{marker}.https.any.js"
+            outputs[path] = generate_wrapper(kind, file_id, algorithms)
+    return outputs
+
+
+def wrapper_paths():
+    return set(GENERATE_KEY_ROOT.glob("successes_*.https.any.js")) | set(
+        GENERATE_KEY_ROOT.glob("failures_*.https.any.js")
+    )
+
+
+def show_diff(path, expected):
+    current = path.read_text(encoding="utf-8") if path.exists() else ""
+    relative = path.relative_to(WEBCRYPTO_ROOT)
+    sys.stdout.writelines(
+        difflib.unified_diff(
+            current.splitlines(keepends=True),
+            expected.splitlines(keepends=True),
+            fromfile=str(relative),
+            tofile=f"{relative} (generated)",
+        )
+    )
+
+
+def check(outputs):
+    failed = False
+    for path, expected in outputs.items():
+        if not path.exists() or path.read_text(encoding="utf-8") != expected:
+            show_diff(path, expected)
+            failed = True
+
+    unexpected = wrapper_paths() - set(outputs)
+    for path in sorted(unexpected):
+        print(f"Unexpected generated wrapper: {path.relative_to(WEBCRYPTO_ROOT)}")
+        failed = True
+
+    return 1 if failed else 0
+
+
+def write(outputs):
+    for path, expected in outputs.items():
+        if not path.exists() or path.read_text(encoding="utf-8") != expected:
+            path.write_text(expected, encoding="utf-8")
+
+    unexpected = wrapper_paths() - set(outputs)
+    for path in sorted(unexpected):
+        print(f"Unexpected generated wrapper: {path.relative_to(WEBCRYPTO_ROOT)}")
+
+    return 1 if unexpected else 0
+
+
+def main():
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument(
+        "--check",
+        action="store_true",
+        help="check generated files without writing them",
+    )
+    args = parser.parse_args()
+    outputs = expected_outputs()
+    return check(outputs) if args.check else write(outputs)
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/ec_key_fixtures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/ec_key_fixtures.js
new file mode 100644
index 0000000..e298fa8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/ec_key_fixtures.js
@@ -0,0 +1,50 @@
+var ecKeyData = {
+    "P-521": {
+        spki: new Uint8Array([48, 129, 155, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, 129, 134, 0, 4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56, 251]),
+        spki_compressed: new Uint8Array([48, 88, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 3, 68, 0, 3, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63]),
+        raw: new Uint8Array([4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56, 251]),
+        raw_compressed: new Uint8Array([3, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63]),
+        pkcs8: new Uint8Array([48, 129, 238, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 4, 129, 214, 48, 129, 211, 2, 1, 1, 4, 66, 0, 244, 8, 117, 131, 104, 186, 147, 15, 48, 247, 106, 224, 84, 254, 92, 210, 206, 127, 218, 44, 159, 118, 166, 212, 54, 207, 117, 214, 108, 68, 11, 254, 99, 49, 199, 193, 114, 161, 36, 120, 25, 60, 130, 81, 72, 123, 201, 18, 99, 250, 80, 33, 127, 133, 255, 99, 111, 89, 205, 84, 110, 58, 180, 131, 180, 161, 129, 137, 3, 129, 134, 0, 4, 1, 86, 244, 121, 248, 223, 30, 32, 167, 255, 192, 76, 228, 32, 195, 225, 84, 174, 37, 25, 150, 190, 228, 47, 3, 75, 132, 212, 27, 116, 63, 52, 228, 95, 49, 27, 129, 58, 156, 222, 200, 205, 165, 155, 187, 189, 49, 212, 96, 179, 41, 37, 33, 231, 193, 183, 34, 229, 102, 124, 3, 219, 47, 174, 117, 63, 1, 80, 23, 54, 207, 226, 71, 57, 67, 32, 216, 228, 175, 194, 253, 57, 181, 169, 51, 16, 97, 184, 30, 34, 65, 40, 43, 158, 23, 137, 24, 34, 181, 183, 158, 5, 47, 69, 151, 181, 150, 67, 253, 57, 55, 156, 81, 189, 81, 37, 196, 244, 139, 195, 240, 37, 206, 60, 211, 105, 83, 40, 108, 203, 56, 251]),
+        pkcs8_private_only: new Uint8Array([48, 96, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 35, 4, 73, 48, 71, 2, 1, 1, 4, 66, 0, 244, 8, 117, 131, 104, 186, 147, 15, 48, 247, 106, 224, 84, 254, 92, 210, 206, 127, 218, 44, 159, 118, 166, 212, 54, 207, 117, 214, 108, 68, 11, 254, 99, 49, 199, 193, 114, 161, 36, 120, 25, 60, 130, 81, 72, 123, 201, 18, 99, 250, 80, 33, 127, 133, 255, 99, 111, 89, 205, 84, 110, 58, 180, 131, 180]),
+        jwk: {
+            kty: "EC",
+            crv: "P-521",
+            x: "AVb0efjfHiCn_8BM5CDD4VSuJRmWvuQvA0uE1Bt0PzTkXzEbgTqc3sjNpZu7vTHUYLMpJSHnwbci5WZ8A9svrnU_",
+            y: "AVAXNs_iRzlDINjkr8L9ObWpMxBhuB4iQSgrnheJGCK1t54FL0WXtZZD_Tk3nFG9USXE9IvD8CXOPNNpUyhsyzj7",
+            d: "APQIdYNoupMPMPdq4FT-XNLOf9osn3am1DbPddZsRAv-YzHHwXKhJHgZPIJRSHvJEmP6UCF_hf9jb1nNVG46tIO0"
+        }
+    },
+
+    "P-256": {
+        spki: new Uint8Array([48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97, 232]),
+        spki_compressed: new Uint8Array([48, 57, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 34, 0, 2, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209]),
+        raw: new Uint8Array([4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97, 232]),
+        raw_compressed: new Uint8Array([2, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209]),
+        pkcs8: new Uint8Array([48, 129, 135, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 4, 109, 48, 107, 2, 1, 1, 4, 32, 19, 211, 58, 45, 90, 191, 156, 249, 235, 178, 31, 248, 96, 212, 174, 254, 110, 86, 231, 119, 144, 244, 222, 233, 180, 8, 132, 235, 211, 53, 68, 234, 161, 68, 3, 66, 0, 4, 210, 16, 176, 166, 249, 217, 240, 18, 134, 128, 88, 180, 63, 164, 244, 113, 1, 133, 67, 187, 160, 12, 146, 80, 223, 146, 87, 194, 172, 174, 93, 209, 206, 3, 117, 82, 212, 129, 69, 12, 227, 155, 77, 16, 149, 112, 27, 23, 91, 250, 179, 75, 142, 108, 9, 158, 24, 241, 193, 152, 53, 131, 97, 232]),
+        pkcs8_private_only: new Uint8Array([48, 65, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 4, 39, 48, 37, 2, 1, 1, 4, 32, 19, 211, 58, 45, 90, 191, 156, 249, 235, 178, 31, 248, 96, 212, 174, 254, 110, 86, 231, 119, 144, 244, 222, 233, 180, 8, 132, 235, 211, 53, 68, 234]),
+        jwk: {
+            kty: "EC",
+            crv: "P-256",
+            x: "0hCwpvnZ8BKGgFi0P6T0cQGFQ7ugDJJQ35JXwqyuXdE",
+            y: "zgN1UtSBRQzjm00QlXAbF1v6s0uObAmeGPHBmDWDYeg",
+            d: "E9M6LVq_nPnrsh_4YNSu_m5W53eQ9N7ptAiE69M1ROo"
+        }
+    },
+
+    "P-384": {
+        spki: new Uint8Array([48, 118, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172, 232]),
+        spki_compressed: new Uint8Array([48, 70, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 3, 50, 0, 2, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53]),
+        raw: new Uint8Array([4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172, 232]),
+        raw_compressed: new Uint8Array([2, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53]),
+        pkcs8: new Uint8Array([48, 129, 182, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 4, 129, 158, 48, 129, 155, 2, 1, 1, 4, 48, 69, 55, 181, 153, 7, 132, 211, 194, 210, 46, 150, 168, 249, 47, 161, 170, 73, 46, 232, 115, 229, 118, 164, 21, 130, 225, 68, 24, 60, 152, 136, 209, 14, 107, 158, 180, 206, 212, 178, 204, 64, 18, 228, 172, 94, 168, 64, 115, 161, 100, 3, 98, 0, 4, 33, 156, 20, 214, 102, 23, 179, 110, 198, 216, 133, 107, 56, 91, 115, 167, 77, 52, 79, 216, 174, 117, 239, 4, 100, 53, 221, 165, 78, 59, 68, 189, 95, 189, 235, 209, 208, 141, 214, 158, 45, 125, 193, 220, 33, 140, 180, 53, 189, 40, 19, 140, 199, 120, 51, 122, 132, 47, 107, 214, 27, 36, 14, 116, 36, 159, 36, 102, 124, 42, 88, 16, 167, 107, 252, 40, 224, 51, 95, 136, 166, 80, 29, 236, 1, 151, 109, 168, 90, 251, 0, 134, 156, 182, 172, 232]),
+        pkcs8_private_only: new Uint8Array([48, 78, 2, 1, 0, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 34, 4, 55, 48, 53, 2, 1, 1, 4, 48, 69, 55, 181, 153, 7, 132, 211, 194, 210, 46, 150, 168, 249, 47, 161, 170, 73, 46, 232, 115, 229, 118, 164, 21, 130, 225, 68, 24, 60, 152, 136, 209, 14, 107, 158, 180, 206, 212, 178, 204, 64, 18, 228, 172, 94, 168, 64, 115]),
+        jwk: {
+            kty: "EC",
+            crv: "P-384",
+            x: "IZwU1mYXs27G2IVrOFtzp000T9iude8EZDXdpU47RL1fvevR0I3Wni19wdwhjLQ1",
+            y: "vSgTjMd4M3qEL2vWGyQOdCSfJGZ8KlgQp2v8KOAzX4imUB3sAZdtqFr7AIactqzo",
+            d: "RTe1mQeE08LSLpao-S-hqkku6HPldqQVguFEGDyYiNEOa560ztSyzEAS5KxeqEBz"
+        }
+    },
+
+};
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/helpers.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/helpers.js
index 3d44b7a..cc44dd1 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/helpers.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/helpers.js
@@ -40,6 +40,21 @@
     "KMAC256",
 ];
 
+var allKeyUsages = [
+    "encrypt",
+    "decrypt",
+    "sign",
+    "verify",
+    "wrapKey",
+    "unwrapKey",
+    "deriveKey",
+    "deriveBits",
+    "encapsulateKey",
+    "encapsulateBits",
+    "decapsulateKey",
+    "decapsulateBits",
+];
+
 
 // Treats an array as a set, and generates an array of all non-empty
 // subsets (which are themselves arrays).
@@ -86,6 +101,35 @@
     }
 }
 
+function mismatchedCryptoKeyAlgorithmMembers(keyAlgorithm, algorithm, registeredAlgorithmName) {
+    const mismatches = [];
+
+    if (["HMAC", "RSASSA-PKCS1-v1_5", "RSA-PSS", "RSA-OAEP"].includes(registeredAlgorithmName)) {
+        const expectedHash = typeof algorithm.hash === "string" ?
+            algorithm.hash : algorithm.hash.name;
+        if (keyAlgorithm.hash.name.toUpperCase() !== expectedHash.toUpperCase()) {
+            mismatches.push("hash");
+        }
+    }
+
+    if (algorithm.namedCurve !== undefined &&
+        keyAlgorithm.namedCurve !== algorithm.namedCurve) {
+        mismatches.push("namedCurve");
+    }
+
+    if (algorithm.modulusLength !== undefined &&
+        keyAlgorithm.modulusLength !== algorithm.modulusLength) {
+        mismatches.push("modulusLength");
+    }
+
+    if (algorithm.publicExponent !== undefined &&
+        !equalBuffers(keyAlgorithm.publicExponent, algorithm.publicExponent)) {
+        mismatches.push("publicExponent");
+    }
+
+    return mismatches;
+}
+
 // Is key a CryptoKey object with correct algorithm, extractable, and usages?
 // Is it a secret, private, or public kind of key?
 function assert_goodCryptoKey(key, algorithm, extractable, usages, kind) {
@@ -132,9 +176,13 @@
     } else {
         assert_equals(key.algorithm.length, algorithm.length, "Correct length");
     }
-    if (["HMAC", "RSASSA-PKCS1-v1_5", "RSA-PSS"].includes(registeredAlgorithmName)) {
-        assert_equals(key.algorithm.hash.name.toUpperCase(), algorithm.hash.toUpperCase(), "Correct hash function");
-    }
+    assert_array_equals(
+        mismatchedCryptoKeyAlgorithmMembers(
+            key.algorithm,
+            algorithm,
+            registeredAlgorithmName),
+        [],
+        "Algorithm members are correct");
 
     if (/^(?:Ed|X)(?:25519|448)$/.test(key.algorithm.name)) {
         assert_false('namedCurve' in key.algorithm, "Does not have a namedCurve property");
@@ -164,12 +212,9 @@
 
     // The usages parameter could have repeats, but the usages
     // property of the result should not.
-    var usageCount = 0;
-    key.usages.forEach(function(usage) {
-        usageCount += 1;
-        assert_in_array(usage, correctUsages, "Has " + usage + " usage");
-    });
-    assert_equals(key.usages.length, usageCount, "usages property is correct");
+    const expectedUsages = unique(correctUsages).sort();
+    const actualUsages = [...key.usages].sort();
+    assert_array_equals(actualUsages, expectedUsages, "usages property is correct");
     assert_equals(key[Symbol.toStringTag], 'CryptoKey', "has the expected Symbol.toStringTag");
 }
 
@@ -287,7 +332,7 @@
     if (!bytes)
         return null;
 
-    bytes = new Uint8Array(bytes);
+    bytes = byteView(bytes);
     var hexBytes = [];
 
     for (var i = 0; i < bytes.length; ++i) {
@@ -302,18 +347,25 @@
 
 function hexStringToUint8Array(hexString)
 {
-    if (hexString.length % 2 != 0)
-        throw "Invalid hexString";
-    var arrayBuffer = new Uint8Array(hexString.length / 2);
-
-    for (var i = 0; i < hexString.length; i += 2) {
-        var byteValue = parseInt(hexString.substr(i, 2), 16);
-        if (byteValue == NaN)
-            throw "Invalid hexString";
-        arrayBuffer[i/2] = byteValue;
+    if (hexString.length % 2 !== 0 || !/^[0-9a-f]*$/i.test(hexString)) {
+        throw new TypeError("Invalid hexadecimal string");
     }
 
-    return arrayBuffer;
+    const result = new Uint8Array(hexString.length / 2);
+
+    for (let i = 0; i < hexString.length; i += 2) {
+        result[i / 2] = parseInt(hexString.slice(i, i + 2), 16);
+    }
+
+    return result;
+}
+
+function byteView(source) {
+    if (ArrayBuffer.isView(source)) {
+        return new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
+    }
+
+    return new Uint8Array(source);
 }
 
 // Compares two ArrayBuffer or ArrayBufferView objects. If bitCount is
@@ -321,44 +373,39 @@
 // in every byte. If bitCount is included, only that leading number of bits
 // have to match.
 function equalBuffers(a, b, bitCount) {
-    var remainder;
+    const aBytes = byteView(a);
+    const bBytes = byteView(b);
 
-    if (typeof bitCount === "undefined" && a.byteLength !== b.byteLength) {
+    if (typeof bitCount === "undefined") {
+        if (aBytes.byteLength !== bBytes.byteLength) {
+            return false;
+        }
+        bitCount = aBytes.byteLength * 8;
+    } else if (!Number.isInteger(bitCount) || bitCount < 0 ||
+               bitCount > aBytes.byteLength * 8 ||
+               bitCount > bBytes.byteLength * 8) {
         return false;
     }
 
-    var aBytes = new Uint8Array(a);
-    var bBytes = new Uint8Array(b);
-
-    var length = a.byteLength;
-    if (typeof bitCount !== "undefined") {
-        length = Math.floor(bitCount / 8);
-    }
-
-    for (var i=0; i<length; i++) {
+    const length = Math.floor(bitCount / 8);
+    for (let i = 0; i < length; i++) {
         if (aBytes[i] !== bBytes[i]) {
             return false;
         }
     }
 
-    if (typeof bitCount !== "undefined") {
-        remainder = bitCount % 8;
-        return aBytes[length] >> (8 - remainder) === bBytes[length] >> (8 - remainder);
+    const remainder = bitCount % 8;
+    if (remainder === 0) {
+        return true;
     }
 
-    return true;
+    const mask = 0xff << (8 - remainder);
+    return (aBytes[length] & mask) === (bBytes[length] & mask);
 }
 
 // Returns a copy of the sourceBuffer it is sent.
 function copyBuffer(sourceBuffer) {
-    var source = new Uint8Array(sourceBuffer);
-    var copy = new Uint8Array(sourceBuffer.byteLength)
-
-    for (var i=0; i<source.byteLength; i++) {
-        copy[i] = source[i];
-    }
-
-    return copy;
+    return new Uint8Array(byteView(sourceBuffer));
 }
 
 // Are two Jwk objects "the same"? That is, does the object returned include
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/mldsa_key_fixtures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/mldsa_key_fixtures.js
new file mode 100644
index 0000000..7b5170c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/mldsa_key_fixtures.js
@@ -0,0 +1,394 @@
+function getMldsaKeyFixtures() {
+  // PKCS#8 private keys for ML-DSA variants
+  const pkcs8 = {
+    'ML-DSA-44': new Uint8Array([
+      48, 52, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 17, 4, 34, 128,
+      32, 153, 21, 95, 99, 48, 150, 218, 124, 190, 8, 122, 137, 72, 184, 79, 118,
+      123, 16, 249, 1, 200, 35, 194, 64, 177, 221, 43, 200, 112, 5, 201, 62,
+    ]),
+    'ML-DSA-65': new Uint8Array([
+      48, 52, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 18, 4, 34, 128,
+      32, 132, 164, 137, 75, 123, 70, 164, 178, 3, 156, 206, 16, 195, 26, 133,
+      186, 176, 195, 102, 48, 254, 35, 29, 66, 103, 17, 67, 152, 38, 7, 130, 139,
+    ]),
+    'ML-DSA-87': new Uint8Array([
+      48, 52, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 19, 4, 34, 128,
+      32, 161, 80, 109, 145, 76, 109, 101, 140, 117, 39, 228, 51, 151, 221, 109,
+      76, 37, 246, 164, 121, 116, 51, 90, 76, 208, 59, 254, 105, 131, 68, 18, 81,
+    ]),
+  };
+
+  // SPKI public keys for ML-DSA variants
+  const spki = {
+    'ML-DSA-44': new Uint8Array([
+      48, 130, 5, 50, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 17, 3, 130, 5,
+      33, 0, 85, 152, 213, 68, 198, 20, 177, 84, 200, 188, 33, 104, 36, 161, 193,
+      127, 226, 151, 48, 107, 122, 191, 89, 86, 188, 3, 245, 199, 18, 79, 168,
+      192, 218, 218, 223, 227, 84, 133, 41, 187, 9, 183, 14, 222, 155, 137, 84,
+      111, 138, 97, 234, 152, 52, 15, 163, 47, 227, 218, 19, 20, 229, 93, 99, 252,
+      155, 213, 30, 241, 158, 211, 213, 191, 155, 73, 211, 145, 59, 194, 75, 140,
+      3, 161, 149, 139, 48, 172, 155, 172, 120, 234, 142, 79, 78, 116, 215, 184,
+      50, 182, 162, 192, 93, 221, 179, 216, 70, 186, 92, 93, 51, 190, 61, 173,
+      105, 206, 9, 208, 52, 54, 208, 252, 115, 176, 146, 118, 23, 0, 5, 41, 151,
+      121, 241, 246, 193, 206, 212, 160, 181, 7, 99, 220, 78, 96, 123, 142, 35,
+      107, 37, 218, 138, 139, 228, 207, 166, 132, 87, 36, 116, 251, 174, 10, 41,
+      196, 171, 26, 212, 90, 213, 223, 141, 87, 232, 59, 46, 173, 16, 247, 173, 4,
+      219, 165, 234, 8, 217, 233, 163, 13, 10, 160, 22, 87, 232, 2, 9, 92, 177,
+      35, 67, 55, 104, 51, 204, 231, 81, 209, 62, 31, 117, 56, 141, 78, 109, 148,
+      118, 191, 93, 193, 59, 30, 88, 237, 19, 169, 14, 11, 150, 102, 215, 144, 69,
+      246, 32, 159, 110, 49, 75, 78, 225, 121, 184, 206, 234, 126, 171, 164, 43,
+      237, 244, 63, 83, 70, 136, 213, 244, 121, 184, 34, 201, 249, 95, 64, 165,
+      99, 14, 195, 231, 18, 121, 20, 156, 80, 231, 133, 197, 0, 94, 195, 22, 251,
+      255, 174, 213, 103, 75, 88, 58, 138, 66, 37, 85, 162, 15, 242, 29, 49, 147,
+      191, 163, 170, 109, 195, 22, 63, 197, 149, 117, 100, 36, 81, 115, 203, 126,
+      62, 86, 103, 193, 182, 69, 238, 233, 162, 62, 248, 132, 158, 212, 208, 187,
+      127, 212, 198, 169, 245, 26, 153, 28, 207, 85, 65, 145, 200, 23, 196, 38,
+      64, 49, 54, 233, 185, 72, 156, 86, 230, 127, 152, 108, 184, 240, 109, 8,
+      103, 228, 206, 142, 73, 74, 214, 52, 182, 203, 82, 201, 175, 188, 111, 42,
+      232, 8, 177, 131, 47, 237, 92, 120, 210, 124, 48, 185, 215, 137, 36, 70,
+      162, 164, 119, 0, 155, 20, 57, 33, 27, 220, 77, 254, 233, 190, 106, 135, 21,
+      194, 57, 238, 95, 162, 78, 164, 33, 210, 215, 43, 100, 96, 232, 87, 19, 43,
+      65, 240, 183, 17, 131, 247, 97, 241, 7, 111, 159, 40, 252, 45, 228, 76, 71,
+      46, 85, 181, 54, 41, 135, 162, 144, 227, 128, 121, 173, 207, 119, 54, 19,
+      197, 23, 231, 176, 125, 190, 32, 76, 60, 34, 24, 160, 42, 73, 190, 124, 99,
+      5, 9, 214, 6, 220, 177, 206, 79, 222, 10, 142, 250, 63, 179, 67, 21, 159,
+      57, 126, 239, 28, 238, 240, 107, 79, 185, 174, 232, 168, 146, 128, 229, 119,
+      19, 21, 33, 239, 193, 42, 178, 152, 124, 24, 203, 131, 193, 93, 162, 2, 208,
+      231, 20, 203, 232, 47, 54, 114, 255, 236, 97, 99, 156, 15, 160, 75, 60, 111,
+      59, 35, 25, 230, 43, 91, 170, 161, 74, 70, 179, 180, 251, 71, 197, 240, 104,
+      42, 39, 202, 206, 12, 115, 249, 138, 55, 252, 216, 61, 185, 121, 76, 137,
+      172, 166, 88, 92, 130, 18, 52, 67, 13, 187, 49, 147, 212, 77, 74, 56, 59,
+      123, 99, 205, 36, 137, 96, 111, 148, 121, 157, 2, 83, 246, 86, 156, 152, 98,
+      172, 77, 244, 214, 225, 184, 240, 121, 144, 56, 213, 161, 43, 67, 4, 161,
+      104, 202, 91, 134, 247, 108, 24, 46, 187, 63, 216, 50, 125, 120, 17, 216,
+      22, 228, 156, 253, 7, 180, 15, 130, 180, 72, 71, 169, 3, 236, 247, 11, 170,
+      32, 76, 236, 225, 133, 250, 20, 235, 200, 143, 89, 25, 158, 49, 158, 164,
+      213, 221, 81, 120, 241, 150, 210, 19, 79, 165, 24, 35, 170, 182, 242, 255,
+      143, 171, 148, 140, 41, 207, 186, 98, 224, 16, 152, 224, 38, 110, 175, 169,
+      111, 132, 201, 178, 114, 25, 196, 50, 149, 158, 193, 180, 101, 183, 92, 109,
+      131, 102, 119, 123, 78, 107, 223, 4, 1, 206, 140, 130, 237, 205, 36, 18,
+      180, 197, 154, 26, 236, 140, 173, 230, 101, 35, 189, 121, 104, 21, 75, 81,
+      224, 186, 212, 81, 107, 66, 244, 235, 64, 90, 206, 39, 44, 43, 162, 187, 63,
+      229, 217, 154, 185, 157, 24, 125, 252, 91, 136, 59, 47, 182, 200, 73, 19,
+      137, 132, 81, 16, 234, 227, 210, 32, 16, 160, 188, 250, 27, 190, 164, 53,
+      244, 219, 199, 177, 146, 117, 50, 99, 72, 235, 37, 154, 72, 51, 203, 61, 39,
+      230, 34, 132, 117, 217, 167, 201, 42, 17, 76, 72, 103, 172, 93, 169, 29, 76,
+      88, 178, 226, 32, 53, 190, 60, 210, 132, 113, 198, 26, 70, 179, 47, 34, 184,
+      88, 178, 208, 1, 196, 89, 136, 167, 33, 38, 9, 255, 89, 202, 27, 93, 229,
+      100, 192, 24, 234, 200, 186, 125, 231, 212, 188, 11, 29, 51, 189, 70, 147,
+      176, 231, 81, 16, 114, 152, 159, 21, 124, 185, 208, 50, 74, 211, 113, 207,
+      35, 54, 173, 205, 133, 52, 167, 199, 87, 158, 120, 33, 204, 163, 146, 233,
+      21, 61, 28, 102, 48, 232, 184, 15, 219, 238, 240, 215, 222, 239, 3, 110,
+      180, 95, 103, 147, 236, 10, 57, 195, 159, 231, 50, 92, 145, 165, 44, 204,
+      121, 187, 9, 210, 80, 86, 251, 169, 132, 236, 248, 23, 207, 222, 227, 13,
+      53, 1, 88, 69, 105, 13, 238, 202, 251, 194, 245, 14, 38, 1, 245, 157, 212,
+      162, 182, 217, 230, 115, 139, 175, 219, 199, 74, 124, 186, 158, 3, 220, 87,
+      220, 177, 85, 13, 58, 168, 223, 6, 238, 156, 80, 121, 44, 166, 176, 0, 48,
+      98, 70, 93, 78, 50, 192, 16, 186, 2, 233, 105, 105, 120, 195, 17, 100, 141,
+      214, 158, 144, 63, 4, 88, 190, 101, 209, 55, 119, 46, 128, 117, 225, 121,
+      204, 195, 19, 61, 170, 116, 251, 225, 230, 28, 27, 249, 84, 195, 224, 190,
+      60, 212, 83, 3, 148, 204, 4, 103, 50, 233, 175, 207, 47, 51, 216, 79, 251,
+      150, 81, 171, 7, 145, 55, 188, 187, 217, 200, 155, 246, 85, 42, 123, 18,
+      112, 192, 116, 163, 40, 187, 132, 192, 210, 188, 106, 117, 217, 185, 183,
+      202, 33, 11, 205, 10, 61, 52, 15, 66, 131, 121, 112, 26, 96, 166, 241, 1,
+      68, 206, 80, 92, 132, 83, 89, 126, 135, 157, 4, 202, 16, 5, 131, 112, 62,
+      56, 234, 176, 213, 119, 205, 203, 17, 106, 156, 117, 251, 135, 21, 73, 219,
+      238, 3, 88, 21, 71, 136, 118, 0, 7, 106, 151, 200, 221, 179, 206, 50, 198,
+      27, 118, 181, 27, 85, 35, 248, 44, 57, 15, 221, 97, 121, 4, 167, 111, 182,
+      207, 195, 52, 134, 195, 128, 173, 111, 98, 152, 29, 138, 197, 175, 171, 247,
+      194, 20, 134, 209, 232, 94, 17, 203, 139, 30, 237, 187, 243, 16, 180, 43,
+      105, 236, 66, 175, 170, 139, 24, 99, 28, 225, 141, 96, 250, 119, 0, 111,
+      212, 34, 217, 42, 134, 88, 78, 76, 126, 169, 168, 59, 154, 93, 54, 43, 161,
+      29, 111, 124, 59, 225, 52, 86, 147, 38, 151, 161, 36, 119, 204, 164, 121,
+      46, 186, 65, 84, 70, 38, 15, 203, 48, 168, 235, 231, 30, 55, 95, 36, 10, 20,
+      166, 109, 8, 18, 109, 251, 213, 82, 142, 240, 49, 249, 180, 78, 69,
+    ]),
+    'ML-DSA-65': new Uint8Array([
+      48, 130, 7, 178, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 18, 3, 130, 7,
+      161, 0, 216, 177, 233, 60, 151, 24, 246, 66, 175, 161, 192, 118, 9, 177, 87,
+      232, 5, 216, 49, 254, 251, 160, 51, 216, 250, 171, 229, 104, 149, 117, 135,
+      107, 5, 239, 244, 167, 83, 57, 49, 153, 29, 135, 108, 138, 117, 236, 120,
+      65, 89, 65, 17, 201, 127, 147, 246, 103, 238, 204, 25, 83, 240, 29, 173,
+      127, 140, 1, 88, 40, 43, 134, 51, 19, 125, 131, 224, 50, 167, 82, 20, 217,
+      215, 162, 41, 95, 215, 56, 130, 226, 177, 216, 20, 169, 133, 85, 140, 9,
+      105, 172, 54, 121, 0, 87, 202, 18, 93, 96, 147, 103, 127, 164, 63, 197, 97,
+      49, 230, 125, 53, 80, 64, 20, 239, 29, 90, 118, 102, 221, 177, 6, 220, 220,
+      170, 107, 107, 18, 138, 125, 138, 70, 66, 60, 185, 232, 209, 4, 100, 17,
+      118, 24, 181, 110, 241, 50, 250, 138, 209, 19, 84, 220, 61, 96, 80, 128, 81,
+      101, 160, 9, 5, 53, 33, 112, 60, 59, 195, 79, 115, 190, 101, 93, 56, 171,
+      146, 51, 82, 233, 66, 225, 226, 211, 72, 58, 9, 74, 222, 185, 175, 211, 156,
+      164, 68, 188, 119, 46, 14, 64, 160, 13, 207, 60, 81, 68, 108, 148, 59, 88,
+      88, 92, 88, 171, 249, 204, 160, 137, 222, 234, 168, 233, 3, 86, 224, 174,
+      105, 224, 1, 177, 184, 230, 236, 30, 85, 249, 56, 105, 211, 50, 83, 168,
+      139, 28, 175, 181, 22, 89, 166, 215, 152, 218, 184, 111, 65, 24, 82, 71,
+      185, 100, 172, 66, 178, 6, 215, 164, 252, 56, 40, 198, 207, 152, 110, 186,
+      207, 39, 235, 179, 38, 215, 88, 105, 109, 177, 130, 91, 5, 223, 164, 78,
+      241, 176, 125, 197, 94, 100, 40, 214, 251, 239, 253, 88, 154, 14, 75, 254,
+      94, 210, 86, 244, 51, 95, 168, 139, 213, 73, 87, 151, 209, 150, 126, 88, 69,
+      251, 116, 157, 122, 96, 212, 206, 42, 218, 38, 85, 184, 50, 246, 188, 35,
+      72, 60, 226, 155, 246, 86, 209, 110, 133, 161, 129, 126, 80, 154, 238, 64,
+      64, 170, 156, 177, 225, 52, 87, 42, 88, 154, 174, 246, 30, 84, 143, 221, 51,
+      27, 65, 60, 40, 22, 120, 42, 145, 34, 82, 235, 79, 20, 215, 211, 231, 151,
+      108, 119, 53, 116, 190, 13, 139, 199, 189, 84, 54, 186, 222, 90, 116, 253,
+      227, 207, 104, 209, 118, 42, 144, 94, 22, 219, 198, 211, 216, 171, 75, 245,
+      61, 240, 157, 108, 81, 19, 238, 39, 120, 210, 242, 63, 126, 6, 15, 18, 97,
+      232, 207, 184, 84, 239, 72, 115, 90, 95, 112, 78, 45, 201, 13, 102, 208,
+      151, 146, 253, 153, 177, 115, 14, 194, 184, 107, 118, 120, 63, 105, 89, 144,
+      60, 242, 80, 106, 32, 11, 204, 170, 165, 221, 182, 31, 187, 159, 216, 118,
+      218, 207, 182, 107, 136, 211, 162, 231, 196, 128, 83, 251, 192, 189, 149, 1,
+      245, 36, 146, 159, 11, 116, 30, 191, 141, 182, 157, 227, 91, 88, 72, 224,
+      55, 195, 96, 245, 135, 249, 142, 16, 229, 237, 27, 60, 2, 91, 138, 82, 197,
+      210, 40, 8, 123, 172, 202, 207, 119, 199, 190, 102, 40, 141, 174, 93, 211,
+      219, 134, 105, 26, 103, 100, 102, 62, 130, 224, 135, 173, 82, 224, 200, 213,
+      76, 211, 38, 181, 16, 97, 99, 75, 14, 42, 162, 223, 237, 141, 1, 29, 140,
+      191, 146, 49, 236, 179, 88, 202, 220, 124, 239, 231, 131, 101, 134, 111, 28,
+      54, 151, 172, 67, 167, 113, 96, 204, 63, 76, 169, 95, 14, 175, 149, 189,
+      173, 39, 177, 102, 23, 158, 102, 236, 224, 70, 203, 190, 132, 230, 136, 3,
+      28, 201, 131, 148, 164, 219, 181, 208, 26, 239, 111, 86, 35, 102, 133, 114,
+      113, 180, 184, 82, 180, 106, 124, 248, 126, 87, 177, 165, 176, 204, 128,
+      141, 179, 141, 34, 119, 160, 57, 206, 168, 95, 197, 110, 79, 60, 48, 170,
+      125, 206, 147, 137, 156, 85, 46, 7, 193, 228, 150, 22, 46, 147, 51, 153,
+      157, 248, 151, 253, 114, 58, 126, 38, 127, 19, 225, 99, 183, 235, 175, 90,
+      166, 8, 233, 141, 10, 85, 232, 147, 7, 20, 137, 240, 84, 231, 86, 134, 50,
+      212, 201, 6, 253, 18, 150, 114, 160, 140, 2, 254, 189, 152, 14, 68, 110, 50,
+      134, 199, 73, 237, 56, 116, 110, 135, 36, 40, 14, 167, 50, 71, 81, 9, 178,
+      54, 182, 247, 64, 32, 204, 116, 92, 131, 40, 30, 246, 188, 236, 182, 187,
+      132, 239, 124, 136, 238, 146, 137, 247, 87, 52, 15, 78, 108, 135, 178, 101,
+      70, 199, 193, 192, 144, 196, 106, 186, 141, 42, 101, 214, 196, 67, 175, 38,
+      8, 189, 148, 166, 253, 221, 144, 119, 81, 1, 232, 175, 81, 237, 13, 188,
+      220, 230, 47, 115, 184, 179, 0, 51, 118, 39, 22, 114, 232, 88, 15, 121, 216,
+      130, 107, 173, 108, 175, 225, 113, 40, 223, 185, 6, 244, 227, 73, 38, 64,
+      84, 10, 200, 53, 81, 179, 217, 106, 172, 59, 161, 70, 180, 70, 48, 56, 46,
+      233, 133, 245, 57, 120, 154, 167, 49, 109, 188, 152, 245, 181, 1, 159, 247,
+      44, 167, 152, 6, 191, 246, 38, 120, 141, 57, 241, 168, 60, 38, 6, 82, 248,
+      87, 85, 0, 10, 22, 19, 44, 178, 63, 38, 78, 1, 210, 166, 140, 3, 199, 112,
+      135, 155, 36, 13, 204, 124, 47, 5, 190, 103, 91, 205, 147, 248, 115, 177,
+      196, 180, 234, 85, 35, 24, 182, 16, 207, 107, 167, 10, 54, 193, 146, 71, 95,
+      45, 109, 184, 43, 101, 155, 184, 55, 171, 196, 136, 89, 227, 84, 183, 31, 0,
+      137, 230, 146, 250, 6, 27, 225, 241, 11, 95, 124, 248, 133, 171, 149, 67,
+      73, 19, 110, 104, 173, 86, 6, 246, 195, 196, 200, 175, 53, 18, 198, 223,
+      140, 85, 208, 253, 204, 220, 255, 232, 88, 238, 20, 97, 24, 52, 13, 3, 179,
+      62, 151, 101, 67, 33, 79, 253, 157, 253, 233, 197, 109, 130, 78, 11, 165,
+      214, 200, 41, 157, 63, 46, 175, 251, 246, 225, 227, 199, 92, 107, 216, 58,
+      124, 226, 202, 153, 92, 31, 250, 182, 92, 99, 0, 110, 18, 78, 228, 166, 190,
+      67, 88, 245, 155, 139, 82, 152, 39, 204, 74, 140, 95, 82, 82, 160, 147, 144,
+      33, 0, 242, 200, 22, 102, 160, 233, 102, 224, 42, 205, 240, 56, 95, 197,
+      226, 175, 235, 240, 125, 10, 51, 12, 246, 150, 234, 173, 95, 132, 173, 174,
+      1, 161, 23, 39, 52, 168, 87, 54, 4, 66, 201, 34, 155, 82, 133, 170, 76, 52,
+      226, 109, 163, 19, 34, 184, 226, 39, 20, 75, 72, 201, 70, 188, 71, 182, 230,
+      6, 19, 255, 8, 145, 193, 63, 81, 150, 24, 72, 89, 168, 74, 98, 173, 133, 67,
+      227, 44, 252, 252, 227, 192, 125, 20, 227, 144, 24, 31, 48, 67, 67, 48, 94,
+      163, 52, 219, 163, 225, 214, 214, 109, 211, 122, 213, 198, 90, 204, 40, 97,
+      211, 121, 17, 28, 132, 246, 110, 230, 51, 197, 42, 162, 143, 199, 158, 215,
+      210, 133, 60, 65, 127, 1, 153, 193, 22, 171, 250, 114, 204, 246, 255, 126,
+      25, 96, 44, 164, 102, 172, 211, 23, 245, 122, 48, 221, 249, 138, 148, 134,
+      206, 135, 246, 42, 235, 198, 89, 189, 45, 125, 204, 69, 193, 48, 29, 144,
+      125, 224, 127, 66, 1, 134, 141, 224, 211, 193, 141, 69, 128, 75, 167, 244,
+      160, 120, 54, 191, 214, 29, 40, 249, 15, 46, 68, 141, 91, 242, 91, 80, 252,
+      109, 122, 154, 64, 153, 56, 65, 254, 106, 18, 4, 172, 171, 136, 80, 98, 79,
+      133, 255, 4, 100, 191, 144, 171, 219, 46, 132, 181, 130, 228, 107, 68, 32,
+      123, 201, 17, 67, 35, 144, 180, 160, 30, 125, 9, 55, 184, 172, 0, 159, 250,
+      232, 83, 168, 162, 102, 158, 121, 208, 177, 116, 163, 160, 80, 241, 46, 156,
+      58, 203, 240, 67, 176, 244, 170, 160, 115, 122, 141, 154, 101, 218, 178,
+      119, 130, 195, 32, 207, 51, 149, 98, 51, 219, 57, 121, 216, 156, 101, 218,
+      184, 220, 204, 41, 181, 149, 63, 80, 194, 11, 143, 164, 219, 23, 123, 141,
+      119, 43, 94, 78, 175, 89, 165, 48, 167, 44, 45, 219, 197, 15, 202, 118, 116,
+      245, 151, 218, 14, 199, 96, 27, 102, 206, 198, 123, 222, 178, 210, 20, 200,
+      38, 25, 124, 58, 102, 92, 197, 107, 51, 5, 236, 125, 173, 198, 113, 144,
+      108, 177, 22, 104, 223, 165, 39, 9, 84, 87, 124, 80, 153, 8, 212, 76, 2, 28,
+      12, 90, 212, 129, 148, 212, 229, 63, 29, 200, 113, 171, 154, 107, 189, 202,
+      22, 147, 7, 32, 253, 70, 37, 224, 98, 199, 129, 21, 54, 49, 52, 124, 84, 40,
+      190, 194, 108, 73, 26, 15, 124, 87, 87, 198, 217, 122, 127, 82, 167, 131,
+      59, 8, 172, 49, 162, 61, 64, 79, 196, 205, 65, 110, 75, 130, 128, 197, 182,
+      251, 110, 141, 197, 184, 166, 244, 246, 217, 20, 105, 85, 42, 80, 251, 77,
+      59, 204, 247, 179, 218, 181, 124, 209, 4, 28, 118, 234, 145, 237, 140, 106,
+      54, 88, 82, 28, 235, 68, 221, 109, 139, 11, 166, 182, 63, 142, 194, 255,
+      213, 219, 116, 158, 31, 224, 119, 126, 232, 160, 144, 1, 177, 92, 219, 162,
+      49, 181, 116, 163, 104, 245, 193, 188, 26, 172, 15, 190, 135, 207, 106, 246,
+      13, 132, 76, 189, 160, 25, 123, 26, 20, 48, 203, 59, 209, 69, 235, 103, 253,
+      160, 108, 83, 206, 70, 98, 0, 2, 57, 162, 202, 63, 45, 89, 173, 201, 254,
+      254, 253, 143, 21, 77, 131, 184, 234, 37, 68, 206, 69, 186, 179, 145, 147,
+      135, 42, 137, 152, 253, 213, 240, 13, 122, 161, 218, 186, 180, 213, 162,
+      150, 231, 63, 112, 182, 233, 86, 12, 225, 195, 133, 37, 28, 22, 147, 201,
+      200, 197, 115, 3, 138, 194, 86, 79, 247, 27, 241, 149, 128, 197, 8, 11, 134,
+      53, 118, 175, 248, 253, 114, 91, 31, 192, 253, 209, 111, 31, 228, 244, 184,
+      179, 146, 145, 167, 137, 155, 184, 218, 38, 62, 187, 22, 181, 193, 93, 16,
+      9, 195, 42, 198, 225, 100, 144, 148, 223, 184, 40, 117, 32, 131, 94, 93, 83,
+      88, 125, 95, 220, 20, 206, 7, 228, 78, 54, 238, 178, 196, 38, 245, 95, 8,
+      235, 106, 17, 175, 142, 193, 60, 179, 53, 244, 92, 147, 244, 218, 95, 127,
+      251, 128, 42, 105, 82, 243, 224, 213, 25, 91, 151, 22, 201, 18, 25, 230,
+      165, 85, 25, 249, 170, 160, 171, 210, 209, 32, 154, 124, 245, 60, 30, 255,
+      138, 154, 29, 85, 156, 232, 177, 78, 14, 137, 52, 215, 247, 26, 211, 115,
+      72, 20, 133, 232, 1, 151, 251, 63, 45, 120, 69, 49, 209, 130, 255, 2, 218,
+      21, 251, 16, 86, 30, 62, 136, 92, 149, 60, 6, 125, 129, 145, 235, 102, 190,
+      144, 248, 1, 53, 135, 21, 158, 44, 158, 230, 246, 172, 249, 161, 105, 204,
+      49, 60, 70, 63, 127, 163, 231, 175, 174, 234, 147, 185, 62, 5, 244, 156, 4,
+      31, 39, 156, 176, 154, 251, 166, 143, 212, 43, 30, 97, 50, 37, 176, 155, 77,
+      149, 102,
+    ]),
+    'ML-DSA-87': new Uint8Array([
+      48, 130, 10, 50, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 3, 19, 3, 130, 10,
+      33, 0, 146, 184, 67, 40, 172, 27, 204, 27, 135, 22, 109, 223, 127, 23, 97,
+      130, 198, 228, 199, 62, 178, 21, 173, 20, 88, 180, 205, 168, 17, 164, 135,
+      92, 85, 146, 76, 194, 152, 80, 239, 157, 33, 21, 239, 182, 176, 254, 124,
+      80, 135, 138, 87, 169, 103, 99, 87, 5, 91, 161, 239, 59, 18, 246, 148, 20,
+      32, 60, 117, 245, 182, 156, 196, 230, 82, 117, 228, 211, 81, 45, 115, 217,
+      23, 95, 138, 95, 103, 124, 32, 89, 187, 48, 251, 103, 81, 234, 76, 4, 57,
+      197, 71, 48, 125, 169, 195, 147, 183, 142, 166, 8, 147, 7, 77, 240, 111, 44,
+      168, 23, 89, 107, 95, 220, 75, 202, 44, 25, 244, 200, 160, 0, 174, 111, 107,
+      204, 22, 36, 122, 191, 101, 33, 52, 252, 14, 47, 25, 109, 135, 48, 131, 253,
+      195, 237, 60, 86, 14, 119, 85, 28, 182, 139, 136, 191, 59, 187, 90, 126,
+      207, 168, 238, 184, 83, 77, 254, 208, 21, 57, 104, 188, 228, 134, 82, 249,
+      190, 249, 163, 120, 111, 68, 225, 210, 131, 29, 151, 153, 146, 71, 163, 104,
+      225, 194, 33, 64, 213, 92, 145, 253, 42, 32, 233, 95, 224, 72, 184, 119,
+      192, 113, 253, 177, 153, 246, 240, 49, 103, 170, 229, 82, 69, 186, 36, 252,
+      29, 124, 255, 25, 148, 5, 160, 159, 105, 157, 185, 25, 158, 122, 145, 30,
+      245, 51, 205, 62, 56, 154, 23, 52, 0, 225, 177, 105, 195, 131, 177, 151,
+      197, 114, 110, 128, 227, 127, 176, 28, 252, 217, 56, 61, 136, 148, 190, 124,
+      108, 129, 62, 243, 235, 37, 70, 228, 233, 10, 67, 124, 120, 58, 164, 83, 57,
+      80, 39, 204, 98, 28, 199, 136, 75, 12, 51, 255, 24, 130, 127, 19, 239, 160,
+      94, 137, 75, 15, 82, 158, 252, 163, 43, 236, 193, 234, 27, 74, 69, 14, 179,
+      171, 10, 169, 27, 138, 172, 213, 189, 222, 167, 110, 24, 11, 118, 151, 223,
+      94, 154, 125, 9, 21, 137, 70, 128, 51, 102, 52, 105, 104, 157, 54, 186, 75,
+      217, 226, 169, 71, 125, 177, 253, 218, 85, 236, 242, 25, 215, 147, 181, 104,
+      242, 82, 75, 50, 165, 49, 102, 128, 82, 237, 170, 134, 162, 196, 56, 247,
+      199, 138, 102, 118, 37, 66, 114, 165, 177, 156, 251, 39, 199, 12, 167, 246,
+      169, 150, 108, 91, 192, 42, 68, 192, 244, 255, 107, 18, 191, 7, 73, 19, 174,
+      28, 116, 66, 131, 96, 173, 60, 131, 190, 249, 219, 246, 71, 182, 124, 183,
+      129, 101, 109, 95, 180, 78, 17, 140, 182, 62, 80, 195, 184, 253, 176, 169,
+      153, 1, 29, 83, 39, 252, 150, 41, 219, 176, 48, 151, 108, 60, 255, 239, 0,
+      85, 101, 193, 110, 204, 12, 254, 22, 136, 15, 154, 217, 88, 13, 28, 252,
+      232, 48, 32, 33, 41, 221, 103, 227, 228, 177, 54, 175, 195, 106, 174, 140,
+      54, 128, 108, 214, 228, 215, 118, 226, 206, 171, 21, 155, 162, 152, 135,
+      203, 16, 170, 130, 100, 173, 155, 243, 80, 40, 98, 157, 248, 7, 101, 88,
+      199, 218, 224, 141, 82, 238, 121, 92, 82, 97, 49, 31, 155, 61, 63, 84, 227,
+      90, 143, 164, 59, 216, 101, 19, 35, 134, 2, 20, 18, 197, 97, 215, 169, 85,
+      220, 75, 254, 91, 125, 144, 43, 65, 128, 29, 66, 77, 184, 172, 89, 123, 203,
+      231, 254, 59, 18, 249, 204, 249, 220, 153, 84, 166, 63, 23, 108, 120, 145,
+      216, 67, 223, 123, 248, 15, 253, 63, 191, 126, 84, 95, 98, 141, 66, 200,
+      129, 194, 174, 30, 80, 48, 75, 113, 14, 102, 101, 218, 249, 14, 203, 24, 6,
+      11, 89, 99, 56, 49, 17, 148, 160, 242, 101, 191, 102, 227, 115, 33, 107, 71,
+      235, 6, 141, 68, 161, 162, 165, 227, 159, 82, 253, 54, 157, 214, 255, 141,
+      151, 154, 134, 150, 93, 141, 97, 158, 244, 180, 135, 42, 10, 3, 148, 9, 152,
+      149, 20, 18, 237, 253, 180, 136, 165, 13, 80, 195, 220, 43, 3, 178, 10, 127,
+      144, 2, 207, 209, 246, 40, 110, 38, 133, 9, 22, 180, 11, 223, 116, 236, 205,
+      186, 25, 183, 155, 165, 77, 206, 101, 255, 106, 223, 70, 10, 22, 176, 119,
+      142, 163, 197, 178, 251, 216, 112, 237, 63, 201, 77, 219, 196, 2, 176, 48,
+      15, 207, 91, 74, 236, 21, 8, 205, 126, 77, 28, 13, 26, 28, 202, 250, 100,
+      79, 134, 209, 193, 78, 202, 58, 248, 124, 215, 64, 143, 244, 17, 23, 163,
+      162, 241, 249, 49, 210, 168, 203, 213, 135, 77, 14, 29, 35, 193, 234, 58,
+      12, 106, 165, 163, 251, 192, 115, 251, 7, 198, 189, 93, 207, 178, 246, 50,
+      189, 185, 51, 45, 84, 140, 194, 34, 46, 92, 90, 136, 81, 81, 53, 52, 253,
+      128, 247, 131, 243, 21, 207, 245, 141, 49, 178, 213, 214, 211, 8, 17, 234,
+      133, 225, 104, 197, 186, 224, 117, 1, 173, 104, 17, 177, 161, 223, 71, 195,
+      159, 194, 45, 177, 116, 26, 187, 193, 161, 213, 179, 158, 8, 122, 186, 122,
+      191, 158, 5, 12, 178, 235, 78, 132, 78, 189, 16, 86, 110, 73, 51, 129, 255,
+      242, 110, 63, 6, 53, 209, 110, 132, 236, 43, 239, 192, 221, 138, 1, 128,
+      176, 0, 113, 85, 119, 201, 36, 188, 202, 9, 223, 98, 196, 42, 130, 158, 149,
+      200, 150, 202, 132, 118, 153, 97, 54, 195, 154, 15, 45, 246, 129, 144, 255,
+      231, 75, 25, 56, 47, 178, 98, 10, 106, 84, 56, 113, 161, 227, 37, 246, 1,
+      245, 129, 173, 70, 186, 11, 28, 125, 198, 243, 225, 113, 130, 5, 138, 52,
+      251, 194, 152, 105, 48, 214, 13, 246, 228, 75, 236, 194, 223, 177, 64, 150,
+      97, 167, 20, 27, 74, 76, 166, 199, 239, 91, 240, 8, 22, 54, 41, 63, 35, 112,
+      255, 251, 88, 252, 147, 173, 185, 43, 98, 83, 155, 230, 176, 55, 161, 8, 83,
+      15, 110, 151, 241, 110, 168, 203, 190, 198, 218, 152, 144, 150, 58, 123, 11,
+      123, 250, 104, 113, 198, 93, 1, 152, 51, 172, 140, 246, 115, 113, 229, 218,
+      166, 200, 35, 123, 16, 133, 169, 191, 59, 103, 12, 114, 15, 60, 37, 4, 92,
+      208, 54, 31, 79, 56, 225, 6, 11, 74, 107, 79, 225, 239, 16, 73, 249, 234,
+      197, 129, 230, 97, 39, 115, 49, 96, 141, 41, 242, 225, 177, 214, 18, 103,
+      34, 229, 37, 176, 241, 99, 82, 227, 195, 77, 32, 64, 4, 120, 49, 199, 209,
+      139, 2, 4, 222, 233, 45, 151, 141, 142, 252, 41, 124, 231, 13, 144, 63, 212,
+      252, 145, 34, 142, 232, 152, 84, 135, 87, 175, 46, 53, 139, 60, 168, 135,
+      167, 101, 253, 127, 152, 138, 154, 31, 231, 198, 77, 89, 182, 9, 54, 103,
+      119, 100, 218, 245, 44, 191, 74, 30, 152, 84, 22, 62, 159, 131, 163, 223, 3,
+      51, 194, 241, 49, 9, 213, 43, 214, 201, 75, 158, 198, 22, 203, 209, 190,
+      199, 189, 75, 4, 6, 72, 60, 241, 113, 171, 30, 42, 143, 73, 51, 72, 206,
+      110, 175, 203, 195, 199, 15, 155, 208, 166, 121, 26, 132, 59, 44, 72, 155,
+      7, 48, 122, 132, 224, 142, 3, 7, 21, 207, 11, 30, 112, 18, 149, 146, 127,
+      41, 104, 197, 169, 86, 213, 108, 253, 111, 160, 5, 11, 202, 172, 233, 32, 5,
+      52, 92, 124, 152, 162, 11, 88, 28, 166, 248, 141, 251, 38, 161, 53, 49, 136,
+      246, 183, 85, 9, 165, 115, 108, 18, 208, 218, 129, 165, 163, 131, 34, 32,
+      94, 226, 121, 93, 24, 87, 226, 105, 25, 242, 128, 198, 78, 26, 237, 21, 92,
+      33, 121, 8, 119, 131, 140, 193, 14, 60, 139, 130, 19, 65, 96, 50, 5, 249,
+      12, 27, 51, 23, 195, 89, 255, 47, 23, 109, 62, 202, 190, 5, 131, 92, 91, 39,
+      27, 209, 132, 146, 95, 98, 66, 26, 230, 186, 236, 186, 162, 149, 81, 143,
+      221, 21, 79, 171, 236, 21, 161, 19, 135, 10, 213, 168, 200, 135, 19, 221,
+      177, 207, 106, 214, 194, 124, 220, 53, 45, 176, 245, 178, 189, 49, 242, 22,
+      230, 154, 241, 146, 230, 187, 180, 244, 158, 145, 197, 225, 51, 150, 140,
+      26, 175, 78, 142, 243, 232, 221, 137, 205, 130, 178, 54, 216, 136, 118, 212,
+      33, 90, 206, 18, 224, 158, 44, 23, 144, 204, 24, 184, 48, 81, 233, 124, 20,
+      222, 77, 119, 144, 22, 145, 241, 19, 206, 135, 252, 164, 114, 84, 37, 175,
+      107, 67, 243, 183, 137, 213, 53, 236, 41, 73, 50, 34, 146, 218, 204, 68,
+      235, 158, 150, 139, 133, 75, 248, 157, 7, 0, 53, 68, 44, 156, 110, 56, 197,
+      209, 201, 0, 155, 225, 85, 35, 169, 193, 231, 36, 31, 142, 101, 168, 29,
+      192, 31, 128, 81, 191, 253, 184, 153, 226, 214, 1, 27, 6, 0, 192, 36, 101,
+      72, 105, 150, 226, 3, 21, 12, 127, 72, 250, 223, 240, 136, 249, 157, 129,
+      247, 91, 178, 157, 208, 86, 146, 23, 176, 150, 35, 211, 155, 22, 207, 119,
+      168, 167, 122, 180, 183, 106, 97, 138, 87, 96, 34, 141, 110, 145, 231, 226,
+      35, 115, 232, 129, 54, 186, 128, 190, 238, 37, 169, 194, 37, 24, 237, 211,
+      164, 111, 54, 88, 245, 125, 67, 78, 194, 11, 194, 235, 217, 87, 203, 14,
+      220, 189, 107, 68, 94, 139, 66, 230, 5, 54, 64, 28, 246, 143, 75, 127, 239,
+      243, 20, 251, 189, 246, 246, 151, 90, 219, 227, 242, 223, 205, 40, 124, 245,
+      4, 189, 72, 39, 145, 110, 96, 120, 164, 27, 117, 146, 177, 30, 76, 173, 106,
+      130, 228, 192, 189, 59, 167, 30, 127, 178, 164, 5, 133, 80, 199, 207, 216,
+      189, 156, 93, 222, 150, 129, 81, 193, 180, 38, 59, 8, 214, 210, 95, 150,
+      182, 175, 148, 43, 153, 169, 66, 62, 135, 140, 159, 89, 195, 253, 3, 132,
+      180, 164, 244, 243, 232, 71, 196, 200, 158, 194, 114, 115, 193, 88, 161,
+      185, 235, 247, 14, 126, 225, 176, 111, 17, 79, 77, 245, 80, 229, 174, 6,
+      180, 156, 217, 85, 174, 201, 255, 237, 85, 83, 53, 188, 153, 98, 91, 193,
+      21, 211, 49, 145, 196, 252, 52, 66, 226, 131, 203, 214, 130, 237, 194, 208,
+      174, 102, 133, 220, 235, 222, 79, 232, 36, 21, 161, 248, 51, 185, 164, 156,
+      95, 121, 65, 57, 233, 183, 29, 199, 105, 104, 12, 90, 195, 186, 81, 199,
+      176, 1, 87, 16, 226, 192, 206, 185, 197, 46, 34, 75, 174, 153, 238, 15, 50,
+      87, 226, 96, 26, 197, 202, 73, 235, 19, 140, 9, 79, 12, 52, 83, 76, 85, 146,
+      234, 24, 151, 179, 178, 118, 58, 162, 223, 103, 69, 244, 231, 135, 167, 204,
+      117, 166, 60, 237, 82, 63, 11, 4, 207, 10, 146, 54, 126, 191, 79, 133, 60,
+      128, 34, 136, 170, 23, 84, 81, 38, 94, 9, 130, 245, 100, 115, 209, 34, 228,
+      158, 101, 216, 135, 208, 207, 191, 169, 115, 252, 144, 139, 226, 252, 6, 44,
+      221, 134, 170, 87, 11, 46, 124, 58, 219, 179, 238, 6, 98, 216, 18, 174, 42,
+      12, 97, 126, 85, 245, 81, 220, 232, 135, 114, 21, 125, 135, 225, 182, 245,
+      228, 223, 242, 62, 158, 35, 76, 6, 110, 25, 184, 206, 124, 237, 54, 252,
+      199, 44, 78, 89, 0, 135, 44, 176, 57, 168, 36, 221, 173, 77, 214, 209, 60,
+      1, 202, 238, 237, 61, 90, 47, 114, 230, 92, 238, 235, 18, 151, 220, 243,
+      225, 163, 159, 139, 189, 253, 62, 225, 182, 202, 59, 7, 83, 99, 129, 118,
+      175, 37, 246, 85, 119, 251, 246, 69, 180, 247, 37, 24, 194, 89, 158, 97,
+      230, 247, 254, 145, 102, 89, 77, 68, 245, 3, 103, 83, 28, 45, 168, 30, 189,
+      151, 112, 120, 215, 84, 90, 198, 85, 85, 129, 55, 127, 124, 28, 137, 229,
+      139, 54, 88, 229, 105, 81, 212, 83, 28, 107, 250, 82, 164, 43, 24, 15, 57,
+      206, 156, 19, 145, 95, 57, 169, 8, 128, 211, 29, 213, 195, 148, 27, 73, 186,
+      221, 242, 11, 167, 78, 246, 133, 18, 118, 67, 236, 59, 19, 112, 254, 93,
+      168, 157, 118, 0, 107, 248, 57, 149, 67, 222, 123, 225, 207, 251, 69, 218,
+      56, 110, 162, 19, 25, 209, 209, 213, 200, 158, 70, 9, 100, 208, 77, 48, 255,
+      151, 200, 0, 68, 230, 70, 120, 209, 29, 86, 225, 188, 189, 226, 101, 42,
+      176, 32, 147, 121, 72, 151, 130, 217, 13, 66, 148, 84, 129, 215, 249, 164,
+      174, 187, 80, 85, 185, 245, 108, 169, 119, 59, 178, 144, 229, 63, 194, 132,
+      250, 131, 82, 161, 125, 126, 255, 252, 220, 204, 104, 231, 201, 136, 246,
+      116, 43, 88, 233, 0, 43, 128, 214, 40, 59, 81, 147, 139, 132, 69, 24, 233,
+      21, 14, 91, 230, 241, 19, 138, 163, 55, 13, 221, 47, 64, 140, 248, 6, 38,
+      164, 16, 219, 63, 33, 180, 71, 151, 0, 234, 103, 58, 213, 222, 163, 95, 132,
+      1, 178, 146, 66, 124, 242, 223, 102, 129, 192, 214, 194, 117, 162, 252, 246,
+      143, 42, 70, 139, 97, 168, 64, 141, 190, 115, 126, 93, 175, 59, 49, 9, 184,
+      88, 201, 100, 182, 142, 145, 244, 72, 128, 203, 49, 196, 5, 5, 18, 46, 34,
+      87, 171, 132, 158, 128, 75, 194, 8, 242, 52, 156, 229, 245, 56, 245, 88, 14,
+      195, 110, 166, 51, 158, 245, 195, 120, 17, 166, 66, 100, 212, 188, 243, 2,
+      236, 90, 8, 16, 35, 151, 122, 175, 115, 168, 186, 191, 60, 71, 23, 81, 217,
+      79, 203, 239, 61, 146, 247, 168, 112, 83, 102, 146, 222, 178, 45, 247, 63,
+      23, 181, 3, 136, 208, 62, 154, 203, 35, 250, 238, 61, 98, 207, 90, 169, 175,
+      36, 227, 9, 182, 78, 226, 99, 89, 67, 105, 185, 35, 242, 162, 54, 99, 60,
+      148, 14, 118, 1, 26, 120, 62, 82, 62, 222, 34, 99, 58, 174, 145, 199, 190,
+      21, 182, 117, 238, 13, 170, 29, 67, 149, 44, 90, 94, 181, 125, 182, 186, 82,
+      55, 105, 253, 29, 212, 67, 134, 204, 227, 94, 255, 127, 72, 157, 140, 142,
+      224, 77, 149, 29, 170, 45, 163, 214, 209, 46, 28, 125, 177, 111, 2, 92, 121,
+      252, 166, 204, 227, 173, 51, 60, 162, 243, 202, 207, 103, 30, 153, 182, 116,
+      182, 130, 98, 59, 25, 141, 239, 49, 224, 176, 27, 237, 218, 76, 68, 189,
+      108, 185, 136, 255, 105, 150, 11, 153, 44, 248, 139, 199, 178, 235, 85, 115,
+      121, 144, 87, 221, 50, 222, 238, 16, 20, 51, 190, 93, 248, 228, 84, 228,
+      115, 31, 229, 227, 137, 180, 44, 115, 224, 119, 129, 181, 134, 224, 144,
+      186, 123, 208, 118, 96, 101, 177, 191, 232, 171, 6, 17, 247, 187, 173, 84,
+      70, 249, 19, 191, 116, 172, 126, 131, 216, 123, 225, 151, 55, 205, 177, 93,
+      139, 117,
+    ]),
+  };
+
+  return { pkcs8, spki };
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/okp_key_fixtures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/okp_key_fixtures.js
new file mode 100644
index 0000000..569e7c72
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/okp_key_fixtures.js
@@ -0,0 +1,58 @@
+var okpKeyData = {
+    "Ed25519": {
+        privateUsages: ["sign"],
+        publicUsages: ["verify"],
+        spki: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61, 204]),
+        raw: new Uint8Array([216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61, 204]),
+        pkcs8: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 243, 200, 244, 196, 141, 248, 120, 20, 110, 140, 211, 191, 109, 244, 229, 14, 56, 155, 167, 7, 78, 21, 194, 53, 45, 205, 93, 48, 141, 76, 168, 31]),
+        jwk: {
+            crv: "Ed25519",
+            d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8",
+            x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPcw",
+            kty: "OKP"
+        }
+    },
+
+    "Ed448": {
+        privateUsages: ["sign"],
+        publicUsages: ["verify"],
+        spki: new Uint8Array([48, 67, 48, 5, 6, 3, 43, 101, 113, 3, 58, 0, 171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90, 128]),
+        raw: new Uint8Array([171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90, 128]),
+        pkcs8: new Uint8Array([48, 71, 2, 1, 0, 48, 5, 6, 3, 43, 101, 113, 4, 59, 4, 57, 14, 255, 3, 69, 140, 40, 224, 23, 156, 82, 29, 227, 18, 201, 105, 183, 131, 67, 72, 236, 171, 153, 26, 96, 227, 178, 233, 167, 158, 76, 217, 228, 128, 239, 41, 23, 18, 210, 200, 61, 4, 114, 114, 213, 201, 244, 40, 102, 79, 105, 109, 38, 112, 69, 143, 29, 46]),
+        jwk: {
+            crv: "Ed448",
+            d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u",
+            x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA",
+            kty: "OKP"
+        }
+    },
+
+    "X25519": {
+        privateUsages: ["deriveKey", "deriveBits"],
+        publicUsages: [],
+        spki: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0, 28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151, 6]),
+        raw: new Uint8Array([28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151, 6]),
+        pkcs8: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32, 200, 131, 142, 118, 208, 87, 223, 183, 216, 201, 90, 105, 225, 56, 22, 10, 221, 99, 115, 253, 113, 164, 210, 118, 187, 86, 227, 168, 27, 100, 255, 97]),
+        jwk: {
+            crv: "X25519",
+            d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E",
+            x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY",
+            kty: "OKP"
+        }
+    },
+
+    "X448": {
+        privateUsages: ["deriveKey", "deriveBits"],
+        publicUsages: [],
+        spki: new Uint8Array([48, 66, 48, 5, 6, 3, 43, 101, 111, 3, 57, 0, 182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206, 111]),
+        raw: new Uint8Array([182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206, 111]),
+        pkcs8: new Uint8Array([48, 70, 2, 1, 0, 48, 5, 6, 3, 43, 101, 111, 4, 58, 4, 56, 88, 199, 210, 154, 62, 181, 25, 178, 157, 0, 207, 177, 145, 187, 100, 252, 109, 138, 66, 216, 241, 113, 118, 39, 43, 137, 242, 39, 45, 24, 25, 41, 92, 101, 37, 192, 130, 150, 113, 176, 82, 239, 7, 39, 83, 15, 24, 142, 49, 208, 204, 83, 191, 38, 146, 158]),
+        jwk: {
+            crv: "X448",
+            d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4",
+            x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8",
+            kty: "OKP"
+        }
+    },
+
+};
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/rsa_key_fixtures.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/rsa_key_fixtures.js
new file mode 100644
index 0000000..13adb1a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/rsa_key_fixtures.js
@@ -0,0 +1,6 @@
+function getRsaKeyFixtures() {
+    return {
+        pkcs8: new Uint8Array([48, 130, 4, 191, 2, 1, 0, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 4, 130, 4, 169, 48, 130, 4, 165, 2, 1, 0, 2, 130, 1, 1, 0, 211, 87, 96, 146, 230, 41, 87, 54, 69, 68, 231, 228, 35, 59, 123, 219, 41, 61, 178, 8, 81, 34, 196, 121, 50, 133, 70, 249, 240, 247, 18, 246, 87, 196, 177, 120, 104, 201, 48, 144, 140, 197, 148, 247, 237, 0, 192, 20, 66, 193, 175, 4, 194, 246, 120, 164, 139, 162, 200, 15, 209, 113, 62, 48, 181, 172, 80, 120, 122, 195, 81, 101, 137, 241, 113, 150, 127, 99, 134, 173, 163, 73, 0, 166, 187, 4, 238, 206, 164, 43, 240, 67, 206, 217, 160, 249, 77, 12, 192, 158, 145, 155, 157, 113, 102, 192, 138, 182, 206, 32, 70, 64, 174, 164, 196, 146, 13, 182, 216, 110, 185, 22, 208, 220, 192, 244, 52, 26, 16, 56, 4, 41, 231, 225, 3, 33, 68, 234, 148, 157, 232, 246, 192, 204, 191, 149, 250, 142, 146, 141, 112, 216, 163, 140, 225, 104, 219, 69, 246, 241, 52, 102, 61, 111, 101, 111, 92, 234, 188, 114, 93, 168, 192, 42, 171, 234, 170, 19, 172, 54, 167, 92, 192, 186, 225, 53, 223, 49, 20, 182, 101, 137, 199, 237, 60, 182, 21, 89, 174, 90, 56, 79, 22, 43, 250, 128, 219, 228, 97, 127, 134, 195, 241, 208, 16, 201, 79, 226, 201, 191, 1, 154, 110, 99, 179, 239, 192, 40, 212, 60, 238, 97, 28, 133, 236, 38, 60, 144, 108, 70, 55, 114, 198, 145, 27, 25, 238, 192, 150, 202, 118, 236, 94, 49, 225, 227, 2, 3, 1, 0, 1, 2, 130, 1, 1, 0, 139, 55, 92, 203, 135, 200, 37, 197, 255, 61, 83, 208, 9, 145, 110, 150, 65, 5, 126, 24, 82, 114, 39, 160, 122, 178, 38, 190, 16, 136, 129, 58, 59, 56, 187, 123, 72, 243, 119, 5, 81, 101, 250, 42, 147, 57, 210, 77, 198, 103, 213, 197, 186, 52, 39, 230, 164, 129, 23, 110, 172, 21, 255, 212, 144, 104, 49, 30, 28, 40, 59, 159, 58, 142, 12, 184, 9, 180, 99, 12, 80, 170, 143, 62, 69, 166, 11, 53, 158, 25, 191, 140, 187, 94, 202, 214, 78, 118, 31, 16, 149, 116, 63, 243, 106, 175, 92, 240, 236, 185, 127, 237, 173, 221, 166, 11, 91, 243, 93, 129, 26, 117, 184, 34, 35, 12, 250, 160, 25, 47, 173, 64, 84, 126, 39, 84, 72, 170, 51, 22, 191, 142, 43, 76, 224, 133, 79, 199, 112, 139, 83, 123, 162, 45, 19, 33, 11, 9, 174, 195, 122, 39, 89, 239, 192, 130, 161, 83, 27, 35, 169, 23, 48, 3, 125, 222, 78, 242, 107, 95, 150, 239, 220, 195, 159, 211, 76, 52, 90, 213, 28, 187, 228, 79, 229, 139, 138, 59, 78, 201, 151, 134, 108, 8, 109, 255, 27, 136, 49, 239, 10, 31, 234, 38, 60, 247, 218, 205, 3, 192, 76, 188, 194, 178, 121, 229, 127, 165, 185, 83, 153, 107, 251, 29, 214, 136, 23, 175, 127, 180, 44, 222, 247, 165, 41, 74, 87, 250, 194, 184, 173, 115, 159, 27, 2, 153, 2, 129, 129, 0, 251, 248, 51, 194, 198, 49, 201, 112, 36, 12, 142, 116, 133, 240, 106, 62, 162, 168, 72, 34, 81, 26, 134, 39, 221, 70, 78, 248, 175, 175, 113, 72, 209, 164, 37, 182, 184, 101, 125, 221, 82, 70, 131, 43, 142, 83, 48, 32, 197, 187, 181, 104, 133, 90, 106, 236, 62, 66, 33, 215, 147, 241, 220, 91, 47, 37, 132, 226, 65, 94, 72, 233, 162, 189, 41, 43, 19, 64, 49, 249, 156, 142, 180, 47, 192, 188, 208, 68, 155, 242, 44, 230, 222, 201, 112, 20, 239, 229, 172, 147, 235, 232, 53, 135, 118, 86, 37, 44, 187, 177, 108, 65, 91, 103, 177, 132, 210, 40, 69, 104, 162, 119, 213, 147, 53, 88, 92, 253, 2, 129, 129, 0, 214, 184, 206, 39, 199, 41, 93, 93, 22, 252, 53, 112, 237, 100, 200, 218, 147, 3, 250, 210, 148, 136, 193, 166, 94, 154, 215, 17, 249, 3, 112, 24, 125, 187, 253, 129, 49, 109, 105, 100, 139, 200, 140, 197, 200, 53, 81, 175, 255, 69, 222, 186, 207, 182, 17, 5, 247, 9, 228, 195, 8, 9, 185, 0, 49, 235, 214, 134, 36, 68, 150, 198, 246, 158, 105, 46, 189, 200, 20, 246, 66, 57, 244, 173, 21, 117, 110, 203, 120, 197, 165, 176, 153, 49, 219, 24, 48, 119, 197, 70, 163, 140, 76, 116, 56, 137, 173, 61, 62, 208, 121, 181, 98, 46, 208, 18, 15, 160, 225, 249, 59, 89, 61, 183, 216, 82, 224, 95, 2, 129, 128, 56, 135, 75, 157, 131, 247, 129, 120, 206, 45, 158, 252, 23, 92, 131, 137, 127, 214, 127, 48, 107, 191, 166, 159, 100, 238, 52, 35, 104, 206, 212, 124, 128, 195, 241, 206, 23, 122, 117, 141, 100, 186, 251, 12, 151, 134, 164, 66, 133, 250, 1, 205, 236, 53, 7, 205, 238, 125, 201, 183, 226, 178, 29, 60, 187, 204, 16, 14, 238, 153, 103, 132, 59, 5, 115, 41, 253, 204, 166, 41, 152, 237, 15, 17, 179, 140, 232, 176, 171, 199, 222, 57, 1, 124, 113, 207, 208, 174, 87, 84, 108, 85, 145, 68, 205, 208, 175, 208, 100, 95, 126, 168, 255, 7, 185, 116, 209, 237, 68, 253, 31, 142, 0, 245, 96, 191, 109, 69, 2, 129, 129, 0, 133, 41, 239, 144, 115, 207, 143, 123, 95, 249, 226, 26, 186, 223, 58, 65, 115, 211, 144, 6, 112, 223, 175, 89, 66, 106, 188, 223, 4, 147, 193, 61, 47, 29, 27, 70, 184, 36, 166, 172, 24, 148, 179, 217, 37, 37, 12, 24, 30, 52, 114, 193, 96, 120, 5, 110, 177, 154, 141, 40, 247, 31, 48, 128, 146, 117, 52, 129, 212, 148, 68, 253, 247, 140, 158, 166, 194, 68, 7, 220, 1, 142, 119, 211, 175, 239, 56, 91, 47, 247, 67, 158, 150, 35, 121, 65, 51, 45, 212, 70, 206, 190, 255, 219, 68, 4, 254, 79, 113, 89, 81, 97, 208, 22, 64, 44, 51, 77, 15, 87, 198, 26, 190, 79, 249, 244, 203, 249, 2, 129, 129, 0, 135, 216, 119, 8, 212, 103, 99, 228, 204, 190, 178, 209, 233, 113, 46, 91, 240, 33, 109, 112, 222, 148, 32, 165, 178, 6, 155, 116, 89, 185, 159, 93, 159, 127, 47, 173, 124, 215, 154, 174, 230, 122, 127, 154, 52, 67, 126, 60, 121, 168, 74, 240, 205, 141, 233, 223, 242, 104, 235, 12, 71, 147, 245, 1, 249, 136, 213, 64, 246, 211, 71, 92, 32, 121, 184, 34, 122, 35, 217, 104, 222, 196, 227, 198, 101, 3, 24, 113, 147, 69, 150, 48, 71, 43, 253, 182, 186, 29, 231, 134, 199, 151, 250, 111, 78, 166, 90, 42, 132, 25, 38, 47, 41, 103, 136, 86, 203, 115, 201, 189, 75, 200, 155, 94, 4, 27, 34, 119]),
+        spki: new Uint8Array([48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2, 130, 1, 1, 0, 211, 87, 96, 146, 230, 41, 87, 54, 69, 68, 231, 228, 35, 59, 123, 219, 41, 61, 178, 8, 81, 34, 196, 121, 50, 133, 70, 249, 240, 247, 18, 246, 87, 196, 177, 120, 104, 201, 48, 144, 140, 197, 148, 247, 237, 0, 192, 20, 66, 193, 175, 4, 194, 246, 120, 164, 139, 162, 200, 15, 209, 113, 62, 48, 181, 172, 80, 120, 122, 195, 81, 101, 137, 241, 113, 150, 127, 99, 134, 173, 163, 73, 0, 166, 187, 4, 238, 206, 164, 43, 240, 67, 206, 217, 160, 249, 77, 12, 192, 158, 145, 155, 157, 113, 102, 192, 138, 182, 206, 32, 70, 64, 174, 164, 196, 146, 13, 182, 216, 110, 185, 22, 208, 220, 192, 244, 52, 26, 16, 56, 4, 41, 231, 225, 3, 33, 68, 234, 148, 157, 232, 246, 192, 204, 191, 149, 250, 142, 146, 141, 112, 216, 163, 140, 225, 104, 219, 69, 246, 241, 52, 102, 61, 111, 101, 111, 92, 234, 188, 114, 93, 168, 192, 42, 171, 234, 170, 19, 172, 54, 167, 92, 192, 186, 225, 53, 223, 49, 20, 182, 101, 137, 199, 237, 60, 182, 21, 89, 174, 90, 56, 79, 22, 43, 250, 128, 219, 228, 97, 127, 134, 195, 241, 208, 16, 201, 79, 226, 201, 191, 1, 154, 110, 99, 179, 239, 192, 40, 212, 60, 238, 97, 28, 133, 236, 38, 60, 144, 108, 70, 55, 114, 198, 145, 27, 25, 238, 192, 150, 202, 118, 236, 94, 49, 225, 227, 2, 3, 1, 0, 1])
+    };
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/supports.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/supports.js
new file mode 100644
index 0000000..18442424
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/util/supports.js
@@ -0,0 +1,74 @@
+'use strict';
+
+function testSupportsMethod() {
+  test(() => {
+    assert_true(
+      typeof SubtleCrypto.supports === 'function',
+      'SubtleCrypto.supports should be a function'
+    );
+  }, 'SubtleCrypto.supports method exists');
+}
+
+function runSupportsTests(algorithms, operations) {
+  for (const [algorithmName, algorithmInfo] of Object.entries(algorithms)) {
+    for (const operation of operations) {
+      promise_test(async (t) => {
+        const isSupported = algorithmInfo.operations.includes(operation);
+
+        let algorithm;
+        let lengthOrAdditionalAlgorithm;
+        switch (operation) {
+          case 'generateKey':
+            algorithm = algorithmInfo.keyGenParams || algorithmName;
+            break;
+          case 'importKey':
+            algorithm = algorithmInfo.importParams || algorithmName;
+            break;
+          case 'sign':
+          case 'verify':
+            algorithm = algorithmInfo.signParams || algorithmName;
+            break;
+          case 'encrypt':
+          case 'decrypt':
+            algorithm = algorithmInfo.encryptParams || algorithmName;
+            break;
+          case 'deriveBits':
+            algorithm = algorithmInfo.deriveBitsParams || algorithmName;
+            if (algorithm?.public instanceof Promise) {
+              algorithm.public = (await algorithm.public).publicKey;
+            }
+            if (algorithmName === 'PBKDF2' || algorithmName === 'HKDF') {
+              lengthOrAdditionalAlgorithm = 256;
+            }
+            break;
+          case 'digest':
+            algorithm = algorithmName;
+            break;
+          case 'encapsulateKey':
+          case 'encapsulateBits':
+          case 'decapsulateKey':
+          case 'decapsulateBits':
+            algorithm = algorithmName;
+            if (operation === 'encapsulateKey' || operation === 'decapsulateKey') {
+              lengthOrAdditionalAlgorithm = { name: 'AES-GCM', length: 256 };
+            }
+            break;
+          default:
+            algorithm = algorithmName;
+        }
+
+        const result = SubtleCrypto.supports(
+          operation,
+          algorithm,
+          lengthOrAdditionalAlgorithm
+        );
+
+        if (isSupported) {
+          assert_true(result, `${algorithmName} should support ${operation}`);
+        } else {
+          assert_false(result, `${algorithmName} should not support ${operation}`);
+        }
+      }, `supports(${operation}, ${algorithmName})`);
+    }
+  }
+}
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js
index 9fa3bb775..d7a0440 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey.https.any.js
@@ -1,6 +1,7 @@
 // META: title=WebCryptoAPI: wrapKey() and unwrapKey()
 // META: timeout=long
 // META: script=../util/helpers.js
+// META: script=../util/okp_key_fixtures.js
 // META: script=wrapKey_unwrapKey_vectors.js
 
 // Tests for wrapKey and unwrapKey round tripping
@@ -285,7 +286,7 @@
         }
 
         if ("kty" in exportedKey && algorithmName === "AES-KW") {
-            return JSON.stringify(exportedKey).length % 8 == 0;
+            return JSON.stringify(exportedKey).length % 8 === 0;
         }
 
         if ("kty" in exportedKey && algorithmName === "RSA-OAEP") {
@@ -478,4 +479,3 @@
 
     function str2ab(str)        { return Uint8Array.from( str.split(''), function(s){return s.charCodeAt(0)} ); }
     function ab2str(ab)         { return String.fromCharCode.apply(null, new Uint8Array(ab)); }
-
diff --git a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey_vectors.js b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey_vectors.js
index cf799a8..36500e4 100644
--- a/third_party/blink/web_tests/external/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey_vectors.js
+++ b/third_party/blink/web_tests/external/wpt/WebCryptoAPI/wrapKey_unwrapKey/wrapKey_unwrapKey_vectors.js
@@ -49,46 +49,10 @@
             d: "E9M6LVq_nPnrsh_4YNSu_m5W53eQ9N7ptAiE69M1ROo"
         }
     },
-    "ED25519": {
-        spki: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 216, 225, 137, 99, 216, 9, 212, 135, 217, 84, 154, 204, 174, 198, 116, 46, 126, 235, 162, 77, 138, 13, 59, 20, 183, 227, 202, 234, 6, 137, 61, 204]),
-        pkcs8: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, 243, 200, 244, 196, 141, 248, 120, 20, 110, 140, 211, 191, 109, 244, 229, 14, 56, 155, 167, 7, 78, 21, 194, 53, 45, 205, 93, 48, 141, 76, 168, 31]),
-        jwk: {
-            crv: "Ed25519",
-            d: "88j0xI34eBRujNO_bfTlDjibpwdOFcI1Lc1dMI1MqB8",
-            x: "2OGJY9gJ1IfZVJrMrsZ0Ln7rok2KDTsUt-PK6gaJPcw",
-            kty: "OKP"
-        }
-    },
-    "ED448": {
-        spki: new Uint8Array([48, 67, 48, 5, 6, 3, 43, 101, 113, 3, 58, 0, 171, 75, 184, 133, 253, 125, 44, 90, 242, 78, 131, 113, 12, 255, 160, 199, 74, 87, 226, 116, 128, 29, 178, 5, 123, 11, 220, 94, 160, 50, 182, 254, 107, 199, 139, 128, 69, 54, 90, 235, 38, 232, 110, 31, 20, 253, 52, 157, 7, 196, 132, 149, 245, 164, 106, 90, 128]),
-        pkcs8: new Uint8Array([48, 71, 2, 1, 0, 48, 5, 6, 3, 43, 101, 113, 4, 59, 4, 57, 14, 255, 3, 69, 140, 40, 224, 23, 156, 82, 29, 227, 18, 201, 105, 183, 131, 67, 72, 236, 171, 153, 26, 96, 227, 178, 233, 167, 158, 76, 217, 228, 128, 239, 41, 23, 18, 210, 200, 61, 4, 114, 114, 213, 201, 244, 40, 102, 79, 105, 109, 38, 112, 69, 143, 29, 46]),
-        jwk: {
-            crv: "Ed448",
-            d: "Dv8DRYwo4BecUh3jEslpt4NDSOyrmRpg47Lpp55M2eSA7ykXEtLIPQRyctXJ9ChmT2ltJnBFjx0u",
-            x: "q0u4hf19LFryToNxDP-gx0pX4nSAHbIFewvcXqAytv5rx4uARTZa6ybobh8U_TSdB8SElfWkalqA",
-            kty: "OKP"
-        }
-    },
-    "X25519": {
-        spki: new Uint8Array([48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0, 28, 242, 177, 230, 2, 46, 197, 55, 55, 30, 215, 245, 62, 84, 250, 17, 84, 216, 62, 152, 235, 100, 234, 81, 250, 229, 179, 48, 124, 254, 151, 6]),
-        pkcs8: new Uint8Array([48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32, 200, 131, 142, 118, 208, 87, 223, 183, 216, 201, 90, 105, 225, 56, 22, 10, 221, 99, 115, 253, 113, 164, 210, 118, 187, 86, 227, 168, 27, 100, 255, 97]),
-        jwk: {
-            crv: "X25519",
-            d: "yIOOdtBX37fYyVpp4TgWCt1jc_1xpNJ2u1bjqBtk_2E",
-            x: "HPKx5gIuxTc3Htf1PlT6EVTYPpjrZOpR-uWzMHz-lwY",
-            kty: "OKP"
-        }
-    },
-    "X448": {
-        spki: new Uint8Array([48, 66, 48, 5, 6, 3, 43, 101, 111, 3, 57, 0, 182, 4, 161, 209, 165, 205, 29, 148, 38, 213, 97, 239, 99, 10, 158, 177, 108, 190, 105, 213, 185, 202, 97, 94, 220, 83, 99, 62, 251, 82, 234, 49, 230, 230, 160, 161, 219, 172, 198, 231, 108, 188, 230, 72, 45, 126, 75, 163, 213, 93, 158, 128, 39, 101, 206, 111]),
-        pkcs8: new Uint8Array([48, 70, 2, 1, 0, 48, 5, 6, 3, 43, 101, 111, 4, 58, 4, 56, 88, 199, 210, 154, 62, 181, 25, 178, 157, 0, 207, 177, 145, 187, 100, 252, 109, 138, 66, 216, 241, 113, 118, 39, 43, 137, 242, 39, 45, 24, 25, 41, 92, 101, 37, 192, 130, 150, 113, 176, 82, 239, 7, 39, 83, 15, 24, 142, 49, 208, 204, 83, 191, 38, 146, 158]),
-        jwk: {
-            crv: "X448",
-            d: "WMfSmj61GbKdAM-xkbtk_G2KQtjxcXYnK4nyJy0YGSlcZSXAgpZxsFLvBydTDxiOMdDMU78mkp4",
-            x: "tgSh0aXNHZQm1WHvYwqesWy-adW5ymFe3FNjPvtS6jHm5qCh26zG52y85kgtfkuj1V2egCdlzm8",
-            kty: "OKP"
-        }
-    },
+    "ED25519": okpKeyData.Ed25519,
+    "ED448": okpKeyData.Ed448,
+    "X25519": okpKeyData.X25519,
+    "X448": okpKeyData.X448,
     "SYMMETRIC128": {
         raw: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
     },
diff --git a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-clipboard-cached-getType-reject.tentative.https-expected.txt b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-clipboard-cached-getType-reject.tentative.https-expected.txt
deleted file mode 100644
index 6d73ed0eb9..0000000
--- a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-clipboard-cached-getType-reject.tentative.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] getType() rejects with DataError on cached ClipboardItem after clipboard.write()
-  promise_rejects_dom: function "function() { throw e; }" threw object "InvalidStateError: Failed to execute 'getType' on 'ClipboardItem': Clipboard data has changed" that is not a DOMException DataError: property "code" is equal to 11, expected 0
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-clipboard-cached-getType-reject.tentative.https.html b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-clipboard-cached-getType-reject.tentative.https.html
index ca2d5e3..32e3fb09 100644
--- a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-clipboard-cached-getType-reject.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-clipboard-cached-getType-reject.tentative.https.html
@@ -43,8 +43,8 @@
   await waitForUserActivation();
   await navigator.clipboard.write([newItem]);
 
-  // Step 5: getType() on the now-stale item should reject with DataError,
+  // Step 5: getType() on the now-stale item should reject with InvalidStateError,
   // even though the data was previously read and cached in step 3.
-  await promise_rejects_dom(t, 'DataError', item.getType('text/plain'));
-}, 'getType() rejects with DataError on cached ClipboardItem after clipboard.write()');
+  await promise_rejects_dom(t, 'InvalidStateError', item.getType('text/plain'));
+}, 'getType() rejects with InvalidStateError on cached ClipboardItem after clipboard.write()');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-clipboard-stale-getType-reject.tentative.https-expected.txt b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-clipboard-stale-getType-reject.tentative.https-expected.txt
deleted file mode 100644
index c1fc022..0000000
--- a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-clipboard-stale-getType-reject.tentative.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] getType() rejects with DataError on stale ClipboardItem after clipboard.write()
-  promise_rejects_dom: function "function() { throw e; }" threw object "InvalidStateError: Failed to execute 'getType' on 'ClipboardItem': Clipboard data has changed" that is not a DOMException DataError: property "code" is equal to 11, expected 0
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-clipboard-stale-getType-reject.tentative.https.html b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-clipboard-stale-getType-reject.tentative.https.html
index 386b201..f07de00 100644
--- a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-clipboard-stale-getType-reject.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-clipboard-stale-getType-reject.tentative.https.html
@@ -41,7 +41,7 @@
   await waitForUserActivation();
   await navigator.clipboard.write([newItem]);
 
-  // Step 4: getType() on the stale item should reject with DataError.
-  await promise_rejects_dom(t, 'DataError', staleItem.getType('text/plain'));
-}, 'getType() rejects with DataError on stale ClipboardItem after clipboard.write()');
+  // Step 4: getType() on the stale item should reject with InvalidStateError.
+  await promise_rejects_dom(t, 'InvalidStateError', staleItem.getType('text/plain'));
+}, 'getType() rejects with InvalidStateError on stale ClipboardItem after clipboard.write()');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/generic/source-list-wildcard-host-and-port.https.sub.html b/third_party/blink/web_tests/external/wpt/content-security-policy/generic/source-list-wildcard-host-and-port.https.sub.html
new file mode 100644
index 0000000..44fe7f7b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/generic/source-list-wildcard-host-and-port.https.sub.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<title>A host-source of "*:*" with a path matches any host and any port at that path</title>
+<meta http-equiv="Content-Security-Policy"
+      content="connect-src *:*/content-security-policy/support/resource.py">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/content-security-policy/support/testharness-helper.js"></script>
+<link rel="help" href="https://w3c.github.io/webappsec-csp/#grammardef-host-source">
+<link rel="help" href="https://w3c.github.io/webappsec-csp/#match-url-to-source-expression">
+<body>
+<script>
+  // "*:*" is a host-source whose host-part is the "*" wildcard and whose
+  // port-part is the "*" wildcard, followed by a path-part:
+  //
+  //   host-source = [ scheme-part "://" ] host-part [ ":" port-part ] [ path-part ]
+  //   host-part   = "*" / [ "*." ] 1*host-char *( "." 1*host-char ) [ "." ]
+  //   port-part   = 1*DIGIT / "*"
+  //
+  // Note that the "*" shortcut in step 1 of "Does url match expression in
+  // origin with redirect count?" only applies when the expression is exactly
+  // the string "*", so "*:*" with a path must be matched via the full
+  // host-source path: host-part matching accepts any domain, port-part
+  // matching accepts any port, and path-part matching still applies. It should
+  // therefore allow the given path on any host and any port, and nothing else.
+  const RESOURCE_PATH = "/content-security-policy/support/resource.py";
+  const SAME_ORIGIN = "https://{{host}}:{{ports[https][0]}}";
+  const OTHER_PORT = "https://{{host}}:{{ports[https][1]}}";
+  const OTHER_HOST = "https://{{hosts[alt][]}}:{{ports[https][0]}}";
+
+  promise_test(async t => {
+    const url = SAME_ORIGIN + RESOURCE_PATH + "?same-origin";
+    assert_no_csp_event_for_url(t, url);
+    await fetch(url);
+  }, "\"*:*\" with a path allows the path on the document's own origin");
+
+  promise_test(async t => {
+    const url = OTHER_PORT + RESOURCE_PATH + "?other-port";
+    assert_no_csp_event_for_url(t, url);
+    await fetch(url);
+  }, "\"*:*\" with a path allows the path on a different port");
+
+  promise_test(async t => {
+    const url = OTHER_HOST + RESOURCE_PATH + "?other-host";
+    assert_no_csp_event_for_url(t, url);
+    await fetch(url);
+  }, "\"*:*\" with a path allows the path on a different host");
+
+  promise_test(async t => {
+    const url = SAME_ORIGIN + "/common/blank.html";
+    await Promise.all([
+      waitUntilCSPEventForURL(t, url),
+      promise_rejects_js(t, TypeError, fetch(url)),
+    ]);
+  }, "\"*:*\" with a path does not allow other paths");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/script-src/script-src-strict_dynamic_modulepreload_parser_inserted_module.html b/third_party/blink/web_tests/external/wpt/content-security-policy/script-src/script-src-strict_dynamic_modulepreload_parser_inserted_module.html
new file mode 100644
index 0000000..dd9ad0a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/script-src/script-src-strict_dynamic_modulepreload_parser_inserted_module.html
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<html>
+
+<head>
+    <title>A modulepreloaded parser-inserted external module script is allowed with `strict-dynamic` in the script-src directive.</title>
+    <script src='/resources/testharness.js' nonce='dummy'></script>
+    <script src='/resources/testharnessreport.js' nonce='dummy'></script>
+
+    <!-- CSP served: script-src 'strict-dynamic' 'nonce-dummy' -->
+    <link rel="modulepreload" href="simpleSourcedModule.js?modulepreloaded-module">
+    <link rel="preload" as="script" href="simpleSourcedModule.js?preload-as-script">
+    <link rel="modulepreload" href="simpleSourcedModule.js?modulepreloaded-other">
+</head>
+
+<body>
+    <h1>A modulepreloaded parser-inserted external module script is allowed with `strict-dynamic` in the script-src directive.</h1>
+    <div id='log'></div>
+
+    <script nonce='dummy'>
+        async_test(function(t) {
+            window.addEventListener('message', t.step_func(function(e) {
+                if (e.data === 'modulepreloaded-module') {
+                    t.done();
+                }
+            }));
+            window.addEventListener('securitypolicyviolation', t.step_func(function(violation) {
+                if (!violation.blockedURI.includes('simpleSourcedModule.js?modulepreloaded-module')) {
+                    return;
+                }
+                assert_unreached('A modulepreloaded parser-inserted external module script is allowed with `strict-dynamic`.');
+            }));
+        }, 'Parser-inserted external module script that is also `modulepreload`ed is allowed with `strict-dynamic`.');
+    </script>
+    <script type="module" src="simpleSourcedModule.js?modulepreloaded-module"></script>
+
+    <script nonce='dummy'>
+        async_test(function(t) {
+            window.addEventListener('message', t.step_func(function(e) {
+                if (e.data === 'preload-as-script') {
+                    assert_unreached('A generic `<link rel=preload as=script>` must not allow a parser-inserted module script with `strict-dynamic`.');
+                }
+            }));
+            window.addEventListener('securitypolicyviolation', t.step_func(function(violation) {
+                if (!violation.blockedURI.includes('simpleSourcedModule.js?preload-as-script')) {
+                    return;
+                }
+                assert_equals(violation.effectiveDirective, 'script-src-elem');
+                t.done();
+            }));
+        }, 'Parser-inserted external module script with only a `<link rel=preload as=script>` is not allowed with `strict-dynamic`.');
+    </script>
+    <script type="module" src="simpleSourcedModule.js?preload-as-script"></script>
+
+    <script nonce='dummy'>
+        async_test(function(t) {
+            window.addEventListener('message', t.step_func(function(e) {
+                if (e.data === 'url-mismatch') {
+                    assert_unreached('A `modulepreload` for a different URL must not allow a parser-inserted module script with `strict-dynamic`.');
+                }
+            }));
+            window.addEventListener('securitypolicyviolation', t.step_func(function(violation) {
+                if (!violation.blockedURI.includes('simpleSourcedModule.js?url-mismatch')) {
+                    return;
+                }
+                assert_equals(violation.effectiveDirective, 'script-src-elem');
+                t.done();
+            }));
+        }, 'A `modulepreload` for a different URL does not allow a parser-inserted external module script with `strict-dynamic`.');
+    </script>
+    <script type="module" src="simpleSourcedModule.js?url-mismatch"></script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/script-src/script-src-strict_dynamic_modulepreload_parser_inserted_module.html.headers b/third_party/blink/web_tests/external/wpt/content-security-policy/script-src/script-src-strict_dynamic_modulepreload_parser_inserted_module.html.headers
new file mode 100644
index 0000000..b7918c9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/script-src/script-src-strict_dynamic_modulepreload_parser_inserted_module.html.headers
@@ -0,0 +1,5 @@
+Expires: Mon, 26 Jul 1997 05:00:00 GMT
+Cache-Control: no-store, no-cache, must-revalidate
+Cache-Control: post-check=0, pre-check=0, false
+Pragma: no-cache
+Content-Security-Policy: script-src 'strict-dynamic' 'nonce-dummy'
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/script-src/script-src-strict_dynamic_parser_inserted_module_empty_hash.html b/third_party/blink/web_tests/external/wpt/content-security-policy/script-src/script-src-strict_dynamic_parser_inserted_module_empty_hash.html
new file mode 100644
index 0000000..ae091b9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/script-src/script-src-strict_dynamic_parser_inserted_module_empty_hash.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+
+<head>
+    <title>Parser-inserted external module scripts do not match the empty-string hash with `strict-dynamic` in the script-src directive.</title>
+    <script src='/resources/testharness.js' nonce='dummy'></script>
+    <script src='/resources/testharnessreport.js' nonce='dummy'></script>
+
+    <!-- CSP served: script-src 'strict-dynamic' 'nonce-dummy' 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' (the SHA-256 of the empty string) -->
+</head>
+
+<body>
+    <h1>Parser-inserted external module scripts do not match the empty-string hash with `strict-dynamic` in the script-src directive.</h1>
+    <div id='log'></div>
+
+    <script nonce='dummy'>
+        async_test(function(t) {
+            window.addEventListener('message', t.step_func(function(e) {
+                if (e.data === 'markup-module') {
+                    assert_unreached('Parser-inserted external module script in markup must not match the empty-string hash with `strict-dynamic`.');
+                }
+            }));
+            window.addEventListener('securitypolicyviolation', t.step_func(function(violation) {
+                if (!violation.blockedURI.includes('simpleSourcedModule.js?markup-module')) {
+                    return;
+                }
+                assert_equals(violation.effectiveDirective, 'script-src-elem');
+                t.done();
+            }));
+        }, 'Parser-inserted external module script in markup does not match the empty-string hash with `strict-dynamic`.');
+    </script>
+    <script type="module" src="simpleSourcedModule.js?markup-module"></script>
+</body>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/script-src/script-src-strict_dynamic_parser_inserted_module_empty_hash.html.headers b/third_party/blink/web_tests/external/wpt/content-security-policy/script-src/script-src-strict_dynamic_parser_inserted_module_empty_hash.html.headers
new file mode 100644
index 0000000..4e53eca
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/script-src/script-src-strict_dynamic_parser_inserted_module_empty_hash.html.headers
@@ -0,0 +1,5 @@
+Expires: Mon, 26 Jul 1997 05:00:00 GMT
+Cache-Control: no-store, no-cache, must-revalidate
+Cache-Control: post-check=0, pre-check=0, false
+Pragma: no-cache
+Content-Security-Policy: script-src 'strict-dynamic' 'nonce-dummy' 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-items-computed.html b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-items-computed.html
index 24a0fa3..ed97cc2 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-items-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-align/parsing/place-items-computed.html
@@ -23,6 +23,10 @@
     <div id="target"></div>
   </div>
 <script>
+test(() => {
+  assert_equals(getComputedStyle(document.documentElement).placeItems, "normal");
+}, "Initial value");
+
 test_computed_value("place-items", "normal");
 test_computed_value("place-items", "stretch stretch", "stretch");
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-name-shadow-higher-tree-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-name-shadow-higher-tree-expected.txt
new file mode 100644
index 0000000..766b948
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-name-shadow-higher-tree-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] anchor() inside a shadow tree can reference an anchor-name defined in a higher tree
+  assert_equals: positioned element inside shadow tree should resolve anchor-name from higher tree expected 258 but got 37
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-name-shadow-higher-tree.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-name-shadow-higher-tree.html
new file mode 100644
index 0000000..21d3436
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-name-shadow-higher-tree.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Anchor Positioning Module Level 1: anchor-name from higher tree can be referenced in shadow tree</title>
+  <link rel="author" title="Jack Holden" href="mailto:jack@holdhelm.com">
+  <link rel="help" href="https://www.w3.org/TR/css-anchor-position-1/#name">
+  <meta name="assert" content="A positioned element inside a shadow tree can reference an anchor-name defined in a higher tree.">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+
+  <style>
+    #host {
+      anchor-name: --higher-anchor;
+      background: lightgray;
+      block-size: 100px;
+      inline-size: 200px;
+      margin: 50px;
+    }
+  </style>
+</head>
+<body>
+  <div id="host">
+    <template shadowrootmode="open">
+      <style>
+        #anchored {
+          background: green;
+          block-size: 50px;
+          inline-size: 50px;
+          inset-block-start: anchor(--higher-anchor start, 37px);
+          inset-inline-start: anchor(--higher-anchor end, 37px);
+          position: absolute;
+        }
+      </style>
+      <div id="anchored"></div>
+    </template>
+  </div>
+
+  <script>
+    test(() => {
+      const host = document.getElementById("host");
+      const anchored = host.shadowRoot.getElementById("anchored");
+
+      const hostRect = host.getBoundingClientRect();
+      const anchoredRect = anchored.getBoundingClientRect();
+
+      // Should be able to reference the higher-tree anchor-name.
+      assert_equals(anchoredRect.left, hostRect.right,
+        "positioned element inside shadow tree should resolve anchor-name from higher tree");
+    }, "anchor() inside a shadow tree can reference an anchor-name defined in a higher tree");
+  </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-name-shadow-lower-tree.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-name-shadow-lower-tree.html
new file mode 100644
index 0000000..85759e0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-name-shadow-lower-tree.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Anchor Positioning Module Level 1: anchor-name from lower shadow tree is not visible outside</title>
+  <link rel="author" title="Jack Holden" href="mailto:jack@holdhelm.com">
+  <link rel="help" href="https://www.w3.org/TR/css-anchor-position-1/#name">
+  <meta name="assert" content="A positioned element outside a shadow tree cannot reference an anchor-name defined in a lower shadow tree.">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+
+  <style>
+    #host {
+      inline-size: 200px;
+      block-size: 100px;
+      background: lightgray;
+      margin: 50px;
+    }
+
+    #anchored {
+      position: absolute;
+      left: anchor(--lower-anchor end, 37px);
+      top: anchor(--lower-anchor start, 37px);
+      inline-size: 50px;
+      block-size: 50px;
+      background: green;
+    }
+  </style>
+</head>
+<body>
+  <div id="host">
+    <template shadowrootmode="open">
+      <style>
+        #inner-anchor {
+          anchor-name: --lower-anchor;
+          inline-size: 100px;
+          block-size: 50px;
+          background: red;
+        }
+      </style>
+      <div id="inner-anchor"></div>
+    </template>
+  </div>
+
+  <div id="anchored"></div>
+
+  <script>
+    test(() => {
+      const anchored = document.getElementById("anchored");
+      const anchoredRect = anchored.getBoundingClientRect();
+
+      // Should not be able to reference the lower-tree anchor-name, so the fallback value (37px) will be used.
+      assert_equals(anchoredRect.left, 37,
+        "positioned element outside shadow tree must not resolve anchor-name from lower tree");
+
+      const host = document.getElementById("host");
+      const innerAnchor = host.shadowRoot.getElementById("inner-anchor");
+      const subtreeAnchorRect = innerAnchor.getBoundingClientRect();
+
+      assert_not_equals(anchoredRect.left, subtreeAnchorRect.left,
+        "positioned element outside shadow tree must not be aligned to lower-tree anchor");
+    }, "anchor() outside a shadow tree cannot reference an anchor-name defined in a lower shadow tree");
+  </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-size-simplified-crash.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-size-simplified-crash.html
new file mode 100644
index 0000000..5432609
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-size-simplified-crash.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: clamp(999999px, 10px, anchor-size(width, 5px))"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-cascade/scope-evaluation.html b/third_party/blink/web_tests/external/wpt/css/css-cascade/scope-evaluation.html
index a70d2a5..c09dc92 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-cascade/scope-evaluation.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-cascade/scope-evaluation.html
@@ -339,6 +339,69 @@
 <template>
   <style>
     @scope (.a) {
+      @scope (:has(:scope .b)) {
+        span { background-color: green; }
+      }
+    }
+  </style>
+  <div class=a>
+    <div class=c>
+      <span>not green</span>
+      <div class=b></div>
+    </div>
+  </div>
+</template>
+<script>
+test_scope(document.currentScript, () => {
+  // ':has(:scope .b)' can only match ancestors of the outer scoping root,
+  // which are never in the outer scope.
+  assert_not_green('.c > span');
+}, 'Nested scopes, :has(:scope ...) in inner from-selector');
+</script>
+
+<template>
+  <style>
+    @scope (.a) {
+      @scope (.b:has(:scope)) {
+        span { background-color: green; }
+      }
+    }
+  </style>
+  <div class=b>
+    <div class=a>
+      <span>not green</span>
+    </div>
+  </div>
+</template>
+<script>
+test_scope(document.currentScript, () => {
+  // The inner scoping root matches, but it is not in the outer scope.
+  assert_not_green('.a > span');
+}, 'Nested scopes, inner scoping root must be in the outer scope');
+</script>
+
+<template>
+  <style>
+    @scope (.a) {
+      @scope (:scope) {
+        span { background-color: green; }
+      }
+    }
+  </style>
+  <div class=a>
+    <span>green</span>
+  </div>
+</template>
+<script>
+test_scope(document.currentScript, () => {
+  // The scoping root is in its own scope.
+  assert_green('.a > span');
+}, 'Nested scopes, inner scoping root may be the outer scoping root');
+</script>
+
+<template>
+  <style>
+    @scope (.a) {
       :scope { background-color: green; }
     }
   </style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color/parsing/alpha-color-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-color/parsing/alpha-color-computed-expected.txt
new file mode 100644
index 0000000..d7b90ec2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-color/parsing/alpha-color-computed-expected.txt
@@ -0,0 +1,33 @@
+This is a testharness.js-based test.
+[FAIL] Property color value 'alpha(from red / 0.5)'
+  assert_equals: expected "color(srgb 1 0 0 / 0.5)" but got "rgba(255, 0, 0, 0.5)"
+[FAIL] Property color value 'alpha(from blue / 0)'
+  assert_equals: expected "color(srgb 0 0 1 / 0)" but got "rgba(0, 0, 255, 0)"
+[FAIL] Property color value 'alpha(from rebeccapurple / 1)'
+  assert_equals: expected "color(srgb 0.4 0.2 0.6)" but got "rgb(102, 51, 153)"
+[FAIL] Property color value 'alpha(from red / 50%)'
+  assert_equals: expected "color(srgb 1 0 0 / 0.5)" but got "rgba(255, 0, 0, 0.5)"
+[FAIL] Property color value 'alpha(from red / 0%)'
+  assert_equals: expected "color(srgb 1 0 0 / 0)" but got "rgba(255, 0, 0, 0)"
+[FAIL] Property color value 'alpha(from red)'
+  assert_equals: expected "color(srgb 1 0 0)" but got "rgb(255, 0, 0)"
+[FAIL] Property color value 'alpha(from rgba(255, 0, 0, 0.3))'
+  assert_equals: expected "color(srgb 1 0 0 / 0.3)" but got "rgba(255, 0, 0, 0.3)"
+[FAIL] Property color value 'alpha(from red / 2)'
+  assert_equals: expected "color(srgb 1 0 0)" but got "rgb(255, 0, 0)"
+[FAIL] Property color value 'alpha(from red / -1)'
+  assert_equals: expected "color(srgb 1 0 0 / 0)" but got "rgba(255, 0, 0, 0)"
+[FAIL] Property color value 'alpha(from rgba(255, 0, 0, 0.3) / 0.8)'
+  assert_equals: expected "color(srgb 1 0 0 / 0.8)" but got "rgba(255, 0, 0, 0.8)"
+[FAIL] Property color value 'alpha(from rgba(255, 0, 0, 0.8) / calc(alpha + 0.1))'
+  assert_equals: expected "color(srgb 1 0 0 / 0.9)" but got "rgba(255, 0, 0, 0.9)"
+[FAIL] Property color value 'alpha(from rebeccapurple / calc(alpha / 1))'
+  assert_equals: expected "color(srgb 0.4 0.2 0.6)" but got "rgb(102, 51, 153)"
+[FAIL] Property color value 'alpha(from alpha(from red / 0.5) / calc(alpha + 0.25))'
+  assert_equals: expected "color(srgb 1 0 0 / 0.75)" but got "rgba(255, 0, 0, 0.75)"
+[FAIL] Property color value 'alpha(from alpha(from rebeccapurple / 0%) / 100%)'
+  assert_equals: expected "color(srgb 0.4 0.2 0.6)" but got "rgb(102, 51, 153)"
+[FAIL] Property color value 'alpha(from rgb(from red r g b) / 0.8)'
+  assert_equals: expected "color(srgb 1 0 0 / 0.8)" but got "rgba(255, 0, 0, 0.8)"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color/parsing/alpha-color-computed.html b/third_party/blink/web_tests/external/wpt/css/css-color/parsing/alpha-color-computed.html
index e84d62818..5b1b119 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-color/parsing/alpha-color-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-color/parsing/alpha-color-computed.html
@@ -10,36 +10,31 @@
 <div id="target"></div>
 <script>
 
-// The expected results in the test cases below may be inconsistent between
-// cases. See these issues for clarifying alpha() resolution and serialization:
-// - https://github.com/w3c/csswg-drafts/issues/13992
-// - https://github.com/w3c/csswg-drafts/issues/13994
-
 const target = document.getElementById('target');
 
 // Basic: replace alpha on a named color.
-test_computed_value("color", "alpha(from red / 0.5)", "rgba(255, 0, 0, 0.5)");
-test_computed_value("color", "alpha(from blue / 0)", "rgba(0, 0, 255, 0)");
-test_computed_value("color", "alpha(from green / 1)", "rgb(0, 128, 0)");
+test_computed_value("color", "alpha(from red / 0.5)", "color(srgb 1 0 0 / 0.5)");
+test_computed_value("color", "alpha(from blue / 0)", "color(srgb 0 0 1 / 0)");
+test_computed_value("color", "alpha(from rebeccapurple / 1)", "color(srgb 0.4 0.2 0.6)");
 
 // Percentage alpha.
-test_computed_value("color", "alpha(from red / 50%)", "rgba(255, 0, 0, 0.5)");
-test_computed_value("color", "alpha(from red / 0%)", "rgba(255, 0, 0, 0)");
+test_computed_value("color", "alpha(from red / 50%)", "color(srgb 1 0 0 / 0.5)");
+test_computed_value("color", "alpha(from red / 0%)", "color(srgb 1 0 0 / 0)");
 
 // Omitted alpha defaults to origin's alpha.
-test_computed_value("color", "alpha(from red)", "rgb(255, 0, 0)");
-test_computed_value("color", "alpha(from rgba(255, 0, 0, 0.3))", "rgba(255, 0, 0, 0.3)");
+test_computed_value("color", "alpha(from red)", "color(srgb 1 0 0)");
+test_computed_value("color", "alpha(from rgba(255, 0, 0, 0.3))", "color(srgb 1 0 0 / 0.3)");
 
 // None alpha.
 test_computed_value("color", "alpha(from red / none)", "color(srgb 1 0 0 / none)");
 test_computed_value("color", "color-mix(in srgb, rgb(255 0 0 / 0.5), alpha(from red / none))", "color(srgb 1 0 0 / 0.5)")
 
 // Alpha clamped to [0, 1].
-test_computed_value("color", "alpha(from red / 2)", "rgb(255, 0, 0)");
-test_computed_value("color", "alpha(from red / -1)", "rgba(255, 0, 0, 0)");
+test_computed_value("color", "alpha(from red / 2)", "color(srgb 1 0 0)");
+test_computed_value("color", "alpha(from red / -1)", "color(srgb 1 0 0 / 0)");
 
 // Origin color with existing alpha.
-test_computed_value("color", "alpha(from rgba(255, 0, 0, 0.3) / 0.8)", "rgba(255, 0, 0, 0.8)");
+test_computed_value("color", "alpha(from rgba(255, 0, 0, 0.3) / 0.8)", "color(srgb 1 0 0 / 0.8)");
 
 // Preserves non-legacy color spaces.
 test_computed_value("color", "alpha(from color(display-p3 1 0 0) / 0.5)", "color(display-p3 1 0 0 / 0.5)");
@@ -47,16 +42,16 @@
 test_computed_value("color", "alpha(from color(srgb 1 0 0) / 50%)", "color(srgb 1 0 0 / 0.5)");
 
 // calc() referencing the origin's alpha keyword.
-test_computed_value("color", "alpha(from rgba(255, 0, 0, 0.8) / calc(alpha + 0.1))", "rgba(255, 0, 0, 0.9)");
-test_computed_value("color", "alpha(from green / calc(alpha / 1))", "rgb(0, 128, 0)");
+test_computed_value("color", "alpha(from rgba(255, 0, 0, 0.8) / calc(alpha + 0.1))", "color(srgb 1 0 0 / 0.9)");
+test_computed_value("color", "alpha(from rebeccapurple / calc(alpha / 1))", "color(srgb 0.4 0.2 0.6)");
 
 // Nested alpha(): the inner result is the outer origin (and its alpha).
-test_computed_value("color", "alpha(from alpha(from red / 0.5) / calc(alpha + 0.25))", "rgba(255, 0, 0, 0.75)");
-test_computed_value("color", "alpha(from alpha(from green / 0%) / 100%)", "rgb(0, 128, 0)");
+test_computed_value("color", "alpha(from alpha(from red / 0.5) / calc(alpha + 0.25))", "color(srgb 1 0 0 / 0.75)");
+test_computed_value("color", "alpha(from alpha(from rebeccapurple / 0%) / 100%)", "color(srgb 0.4 0.2 0.6)");
 
 // Other color functions as origin.
 test_computed_value("color", "alpha(from color-mix(in srgb, red, blue) / 0.5)", "color(srgb 0.5 0 0.5 / 0.5)");
-test_computed_value("color", "alpha(from rgb(from red r g b) / 0.8)", "rgba(255, 0, 0, 0.8)");
+test_computed_value("color", "alpha(from rgb(from red r g b) / 0.8)", "color(srgb 1 0 0 / 0.8)");
 
 // alpha() as the origin of other relative color functions: the `alpha` channel
 // keyword reads the alpha produced by alpha().
@@ -80,7 +75,7 @@
 // Alpha keyword references origin's alpha.
 test_currentcolor_alpha("alpha(from currentcolor / alpha)", "color(srgb 1 0 0 / 0.8)", "rgba(255, 0, 0, 0.8)");
 // None alpha.
-test_currentcolor_alpha("alpha(from currentcolor / none)", "color(srgb 0 0.50196 0 / none)", "green");
+test_currentcolor_alpha("alpha(from currentcolor / none)", "color(srgb 0.4 0.2 0.6 / none)", "rebeccapurple");
 // Percentage alpha.
 test_currentcolor_alpha("alpha(from currentcolor / 50%)", "color(srgb 1 0 0 / 0.5)", "red");
 // Calc with alpha keyword.
diff --git a/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-at-rule-serialization-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-at-rule-serialization-expected.txt
new file mode 100644
index 0000000..2f333fb2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-at-rule-serialization-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+[FAIL] @supports at-rule 1
+  assert_equals: expected "at-rule(@supports)" but got "at-rule( @supports)"
+[FAIL] @supports at-rule 2
+  assert_equals: expected "at-rule(@supports)" but got "at-rule(@supports )"
+[FAIL] @supports at-rule 11
+  assert_equals: expected "at-rule(@--123)" but got "at-rule(@--\\\\31 23)"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-at-rule-serialization.html b/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-at-rule-serialization.html
new file mode 100644
index 0000000..b5b6008
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-conditional/at-supports-at-rule-serialization.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<title>@supports at-rule</title>
+<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#support-definition-at-rules">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  function test_supports_serialization(condition, expected) {
+    test(() => {
+      let sheet = new CSSStyleSheet();
+      sheet.replaceSync(`@supports ${condition} {}`);
+      assert_equals(sheet.cssRules[0].conditionText, expected ?? condition);
+    });
+  }
+
+  // Basic at-rule support.
+  test_supports_serialization("at-rule(@supports)");
+  test_supports_serialization("at-rule( @supports)", "at-rule(@supports)");
+  test_supports_serialization("at-rule(@supports )", "at-rule(@supports)");
+  test_supports_serialization("at-rule(@media)");
+  test_supports_serialization("at-rule(@counter-style)");
+  test_supports_serialization("at-rule(@doesnotexist)");
+
+  // At-rules with special positioning requirements.
+  test_supports_serialization("at-rule(@import)");
+  test_supports_serialization("at-rule(@swash)");
+  test_supports_serialization("at-rule(@starting-style)");
+
+  // Escapes in the <at-keyword-token> are handled correctly.
+  test_supports_serialization("at-rule(@a\\ b)");
+  test_supports_serialization("at-rule(@\\31 23)");
+  test_supports_serialization("at-rule(@--\\31 23)", "at-rule(@--123)");
+
+  // Combining at-rule() with <supports-decl> using 'and'.
+  test_supports_serialization("at-rule(@supports) and (color: red)");
+  test_supports_serialization("at-rule(@media) and (display: flex)");
+  test_supports_serialization("at-rule(@doesnotexist) and (color: red)");
+  test_supports_serialization("at-rule(@supports) and (doesnotexist: red)");
+
+  // Combining at-rule() with <supports-decl> using 'or'.
+  test_supports_serialization("at-rule(@supports) or (color: red)");
+  test_supports_serialization("at-rule(@doesnotexist) or (color: red)");
+  test_supports_serialization("at-rule(@supports) or (doesnotexist: red)");
+  test_supports_serialization("at-rule(@doesnotexist) or (doesnotexist: red)");
+
+  // 'not' with at-rule().
+  test_supports_serialization("not at-rule(@supports)");
+  test_supports_serialization("not at-rule(@doesnotexist)");
+
+  // <supports-decl> first, at-rule() second.
+  test_supports_serialization("(color: red) and at-rule(@supports)");
+  test_supports_serialization("(color: red) and at-rule(@doesnotexist)");
+  test_supports_serialization("(color: red) or at-rule(@doesnotexist)");
+  test_supports_serialization("(doesnotexist: red) or at-rule(@doesnotexist)");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-font-loading/font-face-width-descriptor-aliasing-ref.html b/third_party/blink/web_tests/external/wpt/css/css-font-loading/font-face-width-descriptor-aliasing-ref.html
new file mode 100644
index 0000000..8517bb4b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-font-loading/font-face-width-descriptor-aliasing-ref.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests that the width attribute of FontFace works</title>
+
+<script>
+/* Load the font similarly to the testcase so that Gecko gives it
+   the same antialiasing treatment. */
+const font = new FontFace(
+    'Testing',
+    'url("./resources/variabletest_matching.ttf")'
+);
+document.fonts.add(font);
+</script>
+<style>
+.target {
+  font-family: "Testing";
+  font-size: 80px;
+}
+</style>
+
+<div class="target">
+  4
+</div>
+<div class="target">
+  8
+</div>
+<div class="target">
+  8
+</div>
+<div class="target">
+  8
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-font-loading/font-face-width-descriptor-aliasing.html b/third_party/blink/web_tests/external/wpt/css/css-font-loading/font-face-width-descriptor-aliasing.html
new file mode 100644
index 0000000..3ff51e8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-font-loading/font-face-width-descriptor-aliasing.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests that the width attribute of FontFace works</title>
+<link rel="author" href="mailto:descalante@mozilla.com">
+<link rel="help" href="https://drafts.csswg.org/css-font-loading/#fontface-interface">
+<link rel="help" href="https://www.w3.org/TR/css-fonts-4/#font-width-prop">
+<link rel="match" href="font-face-width-descriptor-aliasing-ref.html">
+<script>
+// First test baseline at 100%
+const font1 = new FontFace(
+    'Testing1',
+    'url("./resources/variabletest_matching.ttf")'
+);
+// test width on it's own
+const font2 = new FontFace(
+    'Testing2',
+    'url("./resources/variabletest_matching.ttf")'
+    , {width: "200%"}
+);
+// test stretch on it's own
+const font3 = new FontFace(
+    'Testing3',
+    'url("./resources/variabletest_matching.ttf")'
+    , {stretch: "200%"}
+);
+// test width takes priority over width
+const font4 = new FontFace(
+    'Testing4',
+    'url("./resources/variabletest_matching.ttf")'
+    , {stretch: "50%", width: "200%"}
+);
+document.fonts.add(font1);
+document.fonts.add(font2);
+document.fonts.add(font3);
+document.fonts.add(font4);
+</script>
+<style>
+div {
+  font-size: 80px;
+}
+#target1 {
+  font-family: "Testing1";
+}
+#target2 {
+  font-family: "Testing2";
+}
+#target3 {
+  font-family: "Testing3";
+}
+#target4 {
+  font-family: "Testing4";
+}
+
+</style>
+
+<div id="target1">
+  M
+</div>
+<div id="target2">
+  M
+</div>
+<div id="target3">
+  M
+</div>
+<div id="target4">
+  M
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-font-loading/resources/variabletest_matching.ttf b/third_party/blink/web_tests/external/wpt/css/css-font-loading/resources/variabletest_matching.ttf
new file mode 100644
index 0000000..89ed12a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-font-loading/resources/variabletest_matching.ttf
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-own-column-gap-does-not-inflate-intrinsic-width.html b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-own-column-gap-does-not-inflate-intrinsic-width.html
new file mode 100644
index 0000000..740f259
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/subgrid-own-column-gap-does-not-inflate-intrinsic-width.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Test: subgrid's own gap must not inflate intrinsic width in the subgridded axis</title>
+<link rel="author" title="Ahmad Saleem" href="mailto:ahmadsaleem@apple.com">
+<link rel="help" href="https://drafts.csswg.org/css-grid-2/#subgrids">
+<link rel="help" href="https://drafts.csswg.org/css-grid-2/#gutters">
+<meta name="assert" content="A subgrid with a large column-gap and a max-width constraint must not be inflated past its parent grid's track sum: items spanning the full subgrid in the subgridded axis must not overflow.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+#wrapper { width: 400px; }
+#outer {
+  display: grid;
+  grid-template-columns: repeat(13, 1fr);
+}
+#subgrid {
+  grid-column: 1 / -1;
+  max-width: 70ch;
+  display: grid;
+  grid-template-columns: subgrid;
+  gap: 10rem;
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+#child {
+  grid-column: 1 / -1;
+  height: 50px;
+  background: red;
+}
+</style>
+<div id="log"></div>
+<div id="wrapper">
+  <div id="outer">
+    <div id="subgrid">
+      <div id="child"></div>
+    </div>
+  </div>
+</div>
+<script>
+test(() => {
+  const wrapper = document.getElementById("wrapper");
+  const outer = document.getElementById("outer");
+  const subgrid = document.getElementById("subgrid");
+  const child = document.getElementById("child");
+
+  assert_less_than_equal(outer.offsetWidth, wrapper.offsetWidth,
+    "outer grid must not exceed its containing block (subgrid's own column-gap must not inflate parent's intrinsic width)");
+  assert_less_than_equal(subgrid.offsetWidth, outer.offsetWidth,
+    "subgrid must fit within its parent grid");
+  assert_less_than_equal(child.offsetLeft + child.offsetWidth,
+    subgrid.offsetLeft + subgrid.offsetWidth,
+    "item spanning the full subgrid must not overflow the subgrid in the subgridded axis");
+}, "Subgrid's own column-gap must not inflate intrinsic width in the subgridded axis");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-link-params/inheritance.html b/third_party/blink/web_tests/external/wpt/css/css-link-params/inheritance.html
index b73c6f4..965dd09e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-link-params/inheritance.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-link-params/inheritance.html
@@ -15,7 +15,7 @@
   <div id="target"></div>
 </div>
 <script>
-assert_not_inherited('link-parameters', 'none', 'param(--a)')
+assert_not_inherited('link-parameters', 'none', 'param(--a, b)')
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-link-params/link-parameters-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-link-params/link-parameters-computed-expected.txt
index b786032..f74e2240 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-link-params/link-parameters-computed-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-link-params/link-parameters-computed-expected.txt
@@ -9,4 +9,7 @@
   assert_true: link-parameters doesn't seem to be supported in the computed style expected true got false
 [FAIL] Property link-parameters value 'param(--a, ), param(--b, )'
   assert_true: link-parameters doesn't seem to be supported in the computed style expected true got false
+[FAIL] Property link-parameters value 'param(--a, var(--custom))'
+  assert_true: link-parameters doesn't seem to be supported in the computed style expected true got false
 Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-link-params/link-parameters-computed.html b/third_party/blink/web_tests/external/wpt/css/css-link-params/link-parameters-computed.html
index 20dc658e..8f296f96 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-link-params/link-parameters-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-link-params/link-parameters-computed.html
@@ -1,6 +1,4 @@
 <!DOCTYPE html>
-<html>
-<head>
 <meta charset="utf-8">
 <title>CSS Linked Parameters Module Level 1: getComputedStyle().linkParameters</title>
 <link rel="author" title="Tyler Thrailkill" href="mailto:tyler@programming.dev">
@@ -9,7 +7,11 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/computed-testcommon.js"></script>
-</head>
+<style>
+:root {
+  --custom: 1;
+}
+</style>
 <body>
 <div id="target"></div>
 <script>
@@ -20,6 +22,6 @@
 
     test_computed_value("link-parameters", "param(--a, )");
     test_computed_value("link-parameters", "param(--a, ), param(--b, )");
+
+    test_computed_value("link-parameters", "param(--a, var(--custom))", "param(--a, 1)");
 </script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-valid.html b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-valid.html
index 7cca198..f5aec513 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-valid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-lists/parsing/list-style-valid.html
@@ -38,6 +38,8 @@
 test_valid_value('list-style', 'square url("https://example.com/") inside', 'inside url("https://example.com/") square');
 test_valid_value('list-style', 'square linear-gradient(red,blue) inside', 'inside linear-gradient(red, blue) square');
 test_valid_value('list-style', 'disc radial-gradient(circle, #006, #00a 90%, #0000af 100%,white 100%) inside', 'inside radial-gradient(circle, rgb(0, 0, 102), rgb(0, 0, 170) 90%, rgb(0, 0, 175) 100%, white 100%)');
+// NOTE: Serializing to just `outside` wouldn't round-trip!
+test_valid_value('list-style', 'outside outside');
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/nested-multicol-spanner-reframe.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/nested-multicol-spanner-reframe.html
new file mode 100644
index 0000000..630e5a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/nested-multicol-spanner-reframe.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org/">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=2046964">
+
+<style>
+* {
+  column-width: 8px;
+  scale: 0.867 1 0;
+  white-space: pre;
+}
+.cl1 {
+  column-span: all;
+}
+.cl2 {
+  position: fixed;
+  top: 45vmax;
+}
+</style>
+<script>
+document.addEventListener("DOMContentLoaded", () => {
+  document.documentElement.offsetHeight;
+  a.rules = "groups";
+  document.documentElement.offsetHeight;
+})
+</script>
+<bdi>
+<table id="a">
+<tfoot class="cl2"></tfoot>
+</table>
+<video></video>
+<dir>a</dir>
+
+
+<div class="cl1"></div>
+
+</bdi>
+<iframe align="right"></iframe>
+<li></li>
+<menu>
+<map></map>
+</menu>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-first-in-flat-tree-order.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-first-in-flat-tree-order.html
new file mode 100644
index 0000000..edc9224
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-first-in-flat-tree-order.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS Scroll Snap: snap targets selected in flat tree order with shadow DOM slots</title>
+    <link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#scroll-snap-align"/>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <style>
+      #scroller {
+        width: 200px;
+        height: 300px;
+        overflow-y: scroll;
+        scroll-snap-type: y mandatory;
+        position: relative;
+        scrollbar-width: none;
+      }
+      div[slot] {
+        display: block;
+        scroll-snap-align: start;
+        position: absolute;
+        top: 0px;
+      }
+    </style>
+    <!--
+      #scroller is the shadow host. Its shadow root lists slot "second-in-DOM"
+      before slot "first-in-DOM", so the flat tree order is the opposite of the
+      DOM order:
+
+        DOM order:       first-in-DOM, second-in-DOM
+        Flat tree order: second-in-DOM, first-in-DOM
+
+      Both snap targets start at top: 0 and overlap visually, so flat tree
+      order is the only tiebreaker. The test verifies that the scroller tracks
+      the element that is first in flat tree order (second-in-DOM) when its
+      position changes, not the element that is first in DOM order.
+    -->
+    <div id="scroller">
+      <template shadowrootmode="open">
+         <slot name="second-in-DOM"></slot>
+         <slot name="first-in-DOM"></slot>
+         <div style="height: 100vh;"></div>
+      </template>
+      <div slot="first-in-DOM">First in DOM tree</div>
+      <div slot="second-in-DOM">Second in DOM tree</div>
+    </div>
+
+    <script>
+      const scroller = document.getElementById("scroller");
+      const targets = document.querySelectorAll("div[slot]");
+      const firstInDOM = targets[0];
+      const secondInDOM = targets[1];
+
+      promise_test(async () => {
+        assert_equals(scroller.scrollTop, 0);
+        assert_equals(scroller.scrollLeft, 0);
+
+        const scrollendPromise = new Promise(resolve => {
+          scroller.addEventListener("scrollend", resolve, { once: true });
+        });
+        secondInDOM.style.top = "10px";
+        await scrollendPromise;
+
+        assert_equals(scroller.scrollTop, 10,
+          "scroller followed the first item in flat tree");
+      }, "scroller follows the element that is first in flat tree order, not DOM order");
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/block-height-011-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/block-height-011-expected.txt
new file mode 100644
index 0000000..817ab07
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/block-height-011-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+[FAIL] .test 2
+  assert_equals: \n<div class="test" data-expected-height="50"></div>\nheight expected 50 but got 100
+[FAIL] .test 3
+  assert_equals: \n<div class="test" data-expected-height="50"></div>\nheight expected 50 but got 100
+[FAIL] .test 4
+  assert_equals: \n<div class="test" data-expected-height="50"></div>\nheight expected 50 but got 100
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/block-height-011.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/block-height-011.html
new file mode 100644
index 0000000..86ef4b7e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/block-height-011.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<title>Stretching block-level on the block axis</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#stretch-fit-sizing">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/13260">
+<meta name="assert" content="The stretch size of a block-level in the block axis
+  treats margins as zero when the containing block has neither border nor padding,
+  and doesn't establish a BFC.">
+
+<style>
+.group { display: inline-block; vertical-align: top; }
+.group > .cb { width: 100px; height: 100px; margin: 25px; }
+.group.border > .cb { border: solid; }
+.group.outline > .cb { outline: solid; }
+.test { margin: 25px; height: stretch; background: cyan; }
+</style>
+
+<div id="log"></div>
+
+<div class="group">
+  <!-- Simple case, the containing block is just the parent. -->
+  <div class="cb">
+    <div class="test"></div>
+  </div>
+
+  <!-- The block-level has an inline parent, the containing block is
+       the nearest block container. -->
+  <div class="cb">
+    <span>
+      <div class="test"></div>
+    </span>
+  </div>
+
+  <!-- Now the inline is wrapped inside an anonymous block, which doesn't
+       establish a containing block. -->
+  <div class="cb">
+    <div></div>
+    <span>
+      <div class="test"></div>
+    </span>
+  </div>
+
+  <!-- The inline now has a border, this shouldn't affect the outcome. -->
+  <div class="cb">
+    <span style="border: solid orange">
+      <div class="test"></div>
+    </span>
+  </div>
+</section>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<script>
+let borderGroup = document.querySelector(".group");
+let outlineGroup = borderGroup.cloneNode(true);
+borderGroup.classList.add("border");
+outlineGroup.classList.add("outline");
+for (let el of borderGroup.querySelectorAll(".test")) {
+  el.dataset.expectedHeight = "50";
+}
+for (let el of outlineGroup.querySelectorAll(".test")) {
+  el.dataset.expectedHeight = "100";
+}
+borderGroup.after(outlineGroup);
+
+checkLayout(".test");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-upperlower-108-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-upperlower-108-ref.html
new file mode 100644
index 0000000..8b74893
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-upperlower-108-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8"/>
+<title>CSS3 Text, text transform in textarea element: German sharp S, uppercase and selection</title>
+<meta name="assert" content="text-transform: uppercase will uppercase the German sharp S as described in Unicode's SpecialCasing.txt .">
+<link rel='author' title='Martin Robinson' href='mailto:mrobinson@igalia.com'>
+<link rel='help' href='https://drafts.csswg.org/css-text-3/#text-transform'>
+</head>
+
+<body>
+    <p class="instructions">Test passes if both characters below are selected.</p>
+    <textarea id="textarea" style="text-transform: uppercase">SS</textarea>
+    <script>
+        textarea.focus();
+        textarea.setSelectionRange(0, 2);
+    </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-upperlower-108.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-upperlower-108.html
new file mode 100644
index 0000000..d925e82
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-upperlower-108.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8"/>
+<title>CSS3 Text, text transform in textarea element: German sharp S, uppercase and selection</title>
+<meta name="assert" content="text-transform: uppercase will uppercase the German sharp S as described in Unicode's SpecialCasing.txt .">
+<link rel='author' title='Martin Robinson' href='mailto:mrobinson@igalia.com'>
+<link rel='help' href='https://drafts.csswg.org/css-text-3/#text-transform'>
+<link rel="match" href="reference/text-transform-upperlower-108-ref.html">
+</head>
+
+<body>
+    <p class="instructions">Test passes if both characters below are selected.</p>
+    <textarea id="textarea" style="text-transform: uppercase">&#x00DF;</textarea>
+    <script>
+        textarea.focus();
+        textarea.setSelectionRange(0, 1);
+    </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/transitionevent-interface.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/transitionevent-interface.html
index b763858..2c45abd 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/transitionevent-interface.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/transitionevent-interface.html
@@ -5,7 +5,6 @@
 
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="transitionevent-interface.js"></script>
 
 <div id="test"></div>
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.js
similarity index 73%
rename from third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.html
rename to third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.js
index ceb39bce..8c14ab4 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.js
@@ -1,12 +1,7 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>CSSKeywordValue.value</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-csskeywordvalue-value">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<body>
-<div id="log"></div>
-<script>
+// META: global=window,worker
+// META: title=CSSKeywordValue.value
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#dom-csskeywordvalue-csskeywordvalue
+
 'use strict';
 
 const gTestArguments = [
@@ -30,5 +25,3 @@
   assert_throws_js(TypeError, () => result.value = '');
   assert_equals(result.value, 'lemon', 'value does not change');
 }, 'Updating CSSKeywordValue.value with an empty string throws a TypeError');
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.serviceworker-expected.txt
new file mode 100644
index 0000000..ca14527e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.serviceworker-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+[FAIL] CSSKeywordValue.value can be updated to a CSS wide keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue.value can be updated to a CSS keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue.value can be updated to an unsupported CSS keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue.value can be updated to a string containing multiple tokens
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue.value can be updated to a unicode string
+  CSSKeywordValue is not defined
+[FAIL] Updating CSSKeywordValue.value with an empty string throws a TypeError
+  CSSKeywordValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.sharedworker-expected.txt
new file mode 100644
index 0000000..ca14527e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.sharedworker-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+[FAIL] CSSKeywordValue.value can be updated to a CSS wide keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue.value can be updated to a CSS keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue.value can be updated to an unsupported CSS keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue.value can be updated to a string containing multiple tokens
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue.value can be updated to a unicode string
+  CSSKeywordValue is not defined
+[FAIL] Updating CSSKeywordValue.value with an empty string throws a TypeError
+  CSSKeywordValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.worker-expected.txt
new file mode 100644
index 0000000..ca14527e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue-value.any.worker-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+[FAIL] CSSKeywordValue.value can be updated to a CSS wide keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue.value can be updated to a CSS keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue.value can be updated to an unsupported CSS keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue.value can be updated to a string containing multiple tokens
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue.value can be updated to a unicode string
+  CSSKeywordValue is not defined
+[FAIL] Updating CSSKeywordValue.value with an empty string throws a TypeError
+  CSSKeywordValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.js
similarity index 67%
rename from third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.html
rename to third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.js
index 1ebbf8d..776b6e2d 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.js
@@ -1,12 +1,7 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>CSSKeywordValue Constructor</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-csskeywordvalue-csskeywordvalue">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<body>
-<div id="log"></div>
-<script>
+// META: global=window,worker
+// META: title=CSSKeywordValue Constructor
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#dom-csskeywordvalue-value
+
 'use strict';
 
 const gTestArguments = [
@@ -25,5 +20,3 @@
                   'value is same as given by constructor');
   }, `CSSKeywordValue can be constructed from ${args.description}`);
 }
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.serviceworker-expected.txt
new file mode 100644
index 0000000..4e869bd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.serviceworker-expected.txt
@@ -0,0 +1,13 @@
+This is a testharness.js-based test.
+[FAIL] CSSKeywordValue can be constructed from a CSS wide keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue can be constructed from a CSS keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue can be constructed from an unsupported CSS keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue can be constructed from a string containing multiple tokens
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue can be constructed from a unicode string
+  CSSKeywordValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.sharedworker-expected.txt
new file mode 100644
index 0000000..4e869bd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.sharedworker-expected.txt
@@ -0,0 +1,13 @@
+This is a testharness.js-based test.
+[FAIL] CSSKeywordValue can be constructed from a CSS wide keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue can be constructed from a CSS keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue can be constructed from an unsupported CSS keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue can be constructed from a string containing multiple tokens
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue can be constructed from a unicode string
+  CSSKeywordValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.worker-expected.txt
new file mode 100644
index 0000000..4e869bd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssKeywordValue.any.worker-expected.txt
@@ -0,0 +1,13 @@
+This is a testharness.js-based test.
+[FAIL] CSSKeywordValue can be constructed from a CSS wide keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue can be constructed from a CSS keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue can be constructed from an unsupported CSS keyword
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue can be constructed from a string containing multiple tokens
+  CSSKeywordValue is not defined
+[FAIL] CSSKeywordValue can be constructed from a unicode string
+  CSSKeywordValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssRotate.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssRotate.tentative.html
index 6c234bc..b10d8a7b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssRotate.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssRotate.tentative.html
@@ -34,10 +34,10 @@
 }
 
 for (const attr of ['x', 'y', 'z']) {
-  for (const {value, desc} of gInvalidCoordTestCases) {
+  for (const {coord, desc} of gInvalidCoordTestCases) {
     test(() => {
       let result = new CSSRotate(0, 0, 0, CSS.deg(0));
-      assert_throws_js(TypeError, () => result[attr] = value);
+      assert_throws_js(TypeError, () => result[attr] = coord);
       assert_style_value_equals(result[attr], CSS.number(0));
     }, 'Updating CSSRotate.' + attr + ' to ' + desc + ' throws a TypeError');
   }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssScale.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssScale.tentative.html
index 691cf4c..deb487c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssScale.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssScale.tentative.html
@@ -24,10 +24,10 @@
 }
 
 for (const attr of ['x', 'y', 'z']) {
-  for (const {value, desc} of gInvalidCoordTestCases) {
+  for (const {coord, desc} of gInvalidCoordTestCases) {
     test(() => {
       let result = new CSSScale(0, 0, 0);
-      assert_throws_js(TypeError, () => result[attr] = value);
+      assert_throws_js(TypeError, () => result[attr] = coord);
       assert_style_value_equals(result[attr], CSS.number(0));
     }, 'Updating CSSScale.' + attr + ' to ' + desc + ' throws a TypeError');
   }
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssTranslate.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssTranslate.tentative.html
index 0554351d..8d7ac8d0 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssTranslate.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssTranslate.tentative.html
@@ -26,10 +26,10 @@
 }, 'Constructing a CSSTranslate with a percent for the Z coordinate throws a TypeError');
 
 for (const attr of ['x', 'y', 'z']) {
-  for (const {value, desc} of gInvalidCoordTestCases) {
+  for (const {coord, desc} of gInvalidCoordTestCases) {
     test(() => {
       let result = new CSSTranslate(CSS.px(0), CSS.px(0), CSS.px(0));
-      assert_throws_js(TypeError, () => result[attr] = value);
+      assert_throws_js(TypeError, () => result[attr] = coord);
       assert_style_value_equals(result[attr], CSS.px(0));
     }, 'Updating CSSTranslate.' + attr + ' to ' + desc + ' throws a TypeError');
   }
@@ -65,6 +65,15 @@
   assert_true(result.is2D);
 }, 'CSSTranslate can be constructed from CSSMathValues');
 
+test(() => {
+  const value = new CSSMathSum(CSS.px(-3.14), CSS.percent(3.14));
+  const result = new CSSTranslate(value, value);
+  assert_style_value_equals(result.x, value);
+  assert_style_value_equals(result.y, value);
+  assert_style_value_equals(result.z, CSS.px(0));
+  assert_true(result.is2D);
+}, 'CSSTranslate can be constructed from mixed length-percentage CSSMathValues');
+
 for (const attr of ['x', 'y']) {
   test(() => {
     let result = new CSSTranslate(CSS.px(0), CSS.px(0), CSS.px(0));
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.js b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.js
new file mode 100644
index 0000000..3750303d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.js
@@ -0,0 +1,11 @@
+// META: global=window,worker
+// META: title=CSSUnparsedValue: Don't crash for empty values
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#dom-cssunparsedvalue-cssunparsedvalue
+
+'use strict';
+
+// https://crbug.com/1169941
+test(() => {
+  const result = new CSSUnparsedValue(['']);
+  assert_equals('', result.toString()); // Don't crash.
+}, `Don't crash when serializing empty CSSUnparsedValue`);
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.serviceworker-expected.txt
new file mode 100644
index 0000000..939eaa1e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.serviceworker-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Don't crash when serializing empty CSSUnparsedValue
+  CSSUnparsedValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.sharedworker-expected.txt
new file mode 100644
index 0000000..939eaa1e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.sharedworker-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Don't crash when serializing empty CSSUnparsedValue
+  CSSUnparsedValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.worker-expected.txt
new file mode 100644
index 0000000..939eaa1e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.any.worker-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Don't crash when serializing empty CSSUnparsedValue
+  CSSUnparsedValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.html
deleted file mode 100644
index 34f77d92..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/cssUnparsedValue-empty.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<!DOCTYPE html>
-<title>CSSUnparsedValue: Don't crash for empty values</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-cssunparsedvalue-cssunparsedvalue">
-<link rel="help" href="https://crbug.com/1169941">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
-'use strict';
-
-test(() => {
-  const result = new CSSUnparsedValue(['']);
-  assert_equals('', result.toString()); // Don't crash.
-}, `Don't crash when serializing empty CSSUnparsedValue`);
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.js
similarity index 88%
rename from third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.html
rename to third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.js
index de263bf..a41faf0 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.js
@@ -1,13 +1,8 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>Adding Two Numeric Types</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#add-two-types">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="../../resources/testhelper.js"></script>
-<body>
-<div id="log">
-<script>
+// META: global=window,worker
+// META: script=../../resources/testhelper.js
+// META: title=Adding Two Numeric Types
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#add-two-types
+
 'use strict';
 
 const gAddTypesMathValueSubclasses = [
@@ -72,5 +67,3 @@
     assert_numeric_type_equals(result.type(), { length: 1, percentHint: 'length' });
   }, 'Adding two types with the same percent hint in the ' + subclass.name + ' constructor returns a type with that percent hint');
 }
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.serviceworker-expected.txt
new file mode 100644
index 0000000..5e991ca
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.serviceworker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Unhandled rejection: Failed to register a ServiceWorker for scope ('https://web-platform.test:8444/css/css-typed-om/stylevalue-subclasses/numeric-objects/does/not/exist') with script ('https://web-platform.test:8444/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.worker.js'): ServiceWorker script evaluation failed
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.sharedworker-expected.txt
new file mode 100644
index 0000000..83f2c550
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.sharedworker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: CSSMathSum is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.worker-expected.txt
new file mode 100644
index 0000000..83f2c550
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/add-two-types.tentative.any.worker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: CSSMathSum is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.js
similarity index 88%
rename from third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.html
rename to third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.js
index a8a65cd..ccdd7a5 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.js
@@ -1,16 +1,13 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>Arithmetic operations on CSSNumericValue tests</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-add">
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-sub">
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-mul">
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-div">
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-min">
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-max">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="../../resources/testhelper.js"></script>
-<script>
+// META: global=window,worker
+// META: script=../../resources/testhelper.js
+// META: title=Arithmetic operations on CSSNumericValue tests
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-add
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-sub
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-mul
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-div
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-min
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-max
+
 'use strict';
 
 const gArithmeticOps = [
@@ -164,5 +161,3 @@
       assert_throws_js(TypeError, () => a[methodName](b));
     }, 'CSSNumericValue.' + methodName + ' should throw TypeError when the types are different.');
 }
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.serviceworker-expected.txt
new file mode 100644
index 0000000..5a1e687
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.serviceworker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Unhandled rejection: Failed to register a ServiceWorker for scope ('https://web-platform.test:8444/css/css-typed-om/stylevalue-subclasses/numeric-objects/does/not/exist') with script ('https://web-platform.test:8444/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.worker.js'): ServiceWorker script evaluation failed
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.sharedworker-expected.txt
new file mode 100644
index 0000000..83f2c550
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.sharedworker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: CSSMathSum is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.worker-expected.txt
new file mode 100644
index 0000000..83f2c550
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/arithmetic.tentative.any.worker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Uncaught ReferenceError: CSSMathSum is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.js
similarity index 80%
rename from third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.html
rename to third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.js
index 8f2d7f49d..de799c3e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.js
@@ -1,11 +1,8 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>Creating Type From A Unit</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#create-a-type">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="../../resources/testhelper.js"></script>
-<script>
+// META: global=window,worker
+// META: script=../../resources/testhelper.js
+// META: title=Creating Type From A Unit
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#create-a-type
+
 'use strict';
 
 test(() => {
@@ -47,5 +44,3 @@
   const value = new CSSUnitValue(0, 'fr');
   assert_numeric_type_equals(value.type(), { flex: 1 });
 }, 'Creating a type from <flex> returns { flex: 1 }');
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.serviceworker-expected.txt
new file mode 100644
index 0000000..8584774b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.serviceworker-expected.txt
@@ -0,0 +1,19 @@
+This is a testharness.js-based test.
+[FAIL] Creating a type from "number" returns {}
+  CSSUnitValue is not defined
+[FAIL] Creating a type from "percent" returns { percent: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <length> returns { length: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <angle> returns { angle: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <time> returns { time: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <frequency> returns { frequency: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <resolution> returns { resolution: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <flex> returns { flex: 1 }
+  CSSUnitValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.sharedworker-expected.txt
new file mode 100644
index 0000000..8584774b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.sharedworker-expected.txt
@@ -0,0 +1,19 @@
+This is a testharness.js-based test.
+[FAIL] Creating a type from "number" returns {}
+  CSSUnitValue is not defined
+[FAIL] Creating a type from "percent" returns { percent: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <length> returns { length: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <angle> returns { angle: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <time> returns { time: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <frequency> returns { frequency: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <resolution> returns { resolution: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <flex> returns { flex: 1 }
+  CSSUnitValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.worker-expected.txt
new file mode 100644
index 0000000..8584774b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/create-a-type.tentative.any.worker-expected.txt
@@ -0,0 +1,19 @@
+This is a testharness.js-based test.
+[FAIL] Creating a type from "number" returns {}
+  CSSUnitValue is not defined
+[FAIL] Creating a type from "percent" returns { percent: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <length> returns { length: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <angle> returns { angle: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <time> returns { time: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <frequency> returns { frequency: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <resolution> returns { resolution: 1 }
+  CSSUnitValue is not defined
+[FAIL] Creating a type from <flex> returns { flex: 1 }
+  CSSUnitValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.js
similarity index 65%
rename from third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.html
rename to third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.js
index 74c7ceb..d50b766 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.js
@@ -1,13 +1,8 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>CSSMathInvert.type</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#type-of-a-cssmathvalue">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="../../resources/testhelper.js"></script>
-<body>
-<div id="log">
-<script>
+// META: global=window,worker
+// META: script=../../resources/testhelper.js
+// META: title=CSSMathInvert.type
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#type-of-a-cssmathvalue
+
 'use strict';
 
 test(() => {
@@ -26,5 +21,3 @@
   const result = new CSSMathInvert(new CSSMathInvert(x));
   assert_numeric_type_equals(result.type(), { length: 1 });
 }, 'Inverting an inverted type returns the original type');
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.serviceworker-expected.txt
new file mode 100644
index 0000000..9bff1a9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.serviceworker-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+[FAIL] Inverting a type with empty map returns the empty map
+  CSSMathInvert is not defined
+[FAIL] Inverting a type negates all its exponents
+  CSSMathProduct is not defined
+[FAIL] Inverting an inverted type returns the original type
+  CSSUnitValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.sharedworker-expected.txt
new file mode 100644
index 0000000..9bff1a9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.sharedworker-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+[FAIL] Inverting a type with empty map returns the empty map
+  CSSMathInvert is not defined
+[FAIL] Inverting a type negates all its exponents
+  CSSMathProduct is not defined
+[FAIL] Inverting an inverted type returns the original type
+  CSSUnitValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.worker-expected.txt
new file mode 100644
index 0000000..9bff1a9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathInvert-type.any.worker-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+[FAIL] Inverting a type with empty map returns the empty map
+  CSSMathInvert is not defined
+[FAIL] Inverting a type negates all its exponents
+  CSSMathProduct is not defined
+[FAIL] Inverting an inverted type returns the original type
+  CSSUnitValue is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.js b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.js
new file mode 100644
index 0000000..5c55dba05
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.js
@@ -0,0 +1,16 @@
+// META: global=window,worker
+// META: script=../../resources/testhelper.js
+// META: title=CSSMathNegate.type
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#type-of-a-cssmathvalue
+
+'use strict';
+
+test(() => {
+  const result = new CSSMathNegate(new CSSUnitValue(0, 'number'));
+  assert_numeric_type_equals(result.type(), { });
+}, 'Negating a type with empty map returns the empty map');
+
+test(() => {
+  const result = new CSSMathNegate(new CSSUnitValue(0, 'px'));
+  assert_numeric_type_equals(result.type(), { length: 1 });
+}, 'Negating a type returns the same type');
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.serviceworker-expected.txt
new file mode 100644
index 0000000..54daa0b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.serviceworker-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+[FAIL] Negating a type with empty map returns the empty map
+  CSSMathNegate is not defined
+[FAIL] Negating a type returns the same type
+  CSSMathNegate is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.sharedworker-expected.txt
new file mode 100644
index 0000000..54daa0b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.sharedworker-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+[FAIL] Negating a type with empty map returns the empty map
+  CSSMathNegate is not defined
+[FAIL] Negating a type returns the same type
+  CSSMathNegate is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.worker-expected.txt
new file mode 100644
index 0000000..54daa0b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.any.worker-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+[FAIL] Negating a type with empty map returns the empty map
+  CSSMathNegate is not defined
+[FAIL] Negating a type returns the same type
+  CSSMathNegate is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.html
deleted file mode 100644
index 5f9a2d4..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssMathNegate-type.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>CSSMathNegate.type</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#type-of-a-cssmathvalue">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="../../resources/testhelper.js"></script>
-<body>
-<div id="log">
-<script>
-'use strict';
-
-test(() => {
-  const result = new CSSMathNegate(new CSSUnitValue(0, 'number'));
-  assert_numeric_type_equals(result.type(), { });
-}, 'Negating a type with empty map returns the empty map');
-
-test(() => {
-  const result = new CSSMathNegate(new CSSUnitValue(0, 'px'));
-  assert_numeric_type_equals(result.type(), { length: 1 });
-}, 'Negating a type returns the same type');
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.js
similarity index 84%
rename from third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.html
rename to third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.js
index 840964b..710db1e 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.js
@@ -1,13 +1,8 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>Multiplying Two Numeric Types</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-multiply-two-types">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="../../resources/testhelper.js"></script>
-<body>
-<div id="log">
-<script>
+// META: global=window,worker
+// META: script=../../resources/testhelper.js
+// META: title=Multiplying Two Numeric Types
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-multiply-two-types
+
 'use strict';
 
 test(() => {
@@ -57,5 +52,3 @@
   const result = new CSSMathProduct(a, b);
   assert_numeric_type_equals(result.type(), { length: 2, percentHint: 'length' });
 }, 'Multiplying two types with same percent hint applies the percent hint');
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.serviceworker-expected.txt
new file mode 100644
index 0000000..4d9a56e4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.serviceworker-expected.txt
@@ -0,0 +1,17 @@
+This is a testharness.js-based test.
+[FAIL] Multiplying two types with different non-null percent hints throws TypeError
+  CSSMathSum is not defined
+[FAIL] Multiplying two types with same base types adds exponents
+  CSSUnitValue is not defined
+[FAIL] Multiplying two types with different base types adds exponents
+  CSSUnitValue is not defined
+[FAIL] Multiplying two types respects the sign of the exponents
+  CSSUnitValue is not defined
+[FAIL] Multiplying a type with no exponents is a no-op
+  CSSUnitValue is not defined
+[FAIL] Multiplying a type with percent hint applies the percent hint
+  CSSMathSum is not defined
+[FAIL] Multiplying two types with same percent hint applies the percent hint
+  CSSMathSum is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.sharedworker-expected.txt
new file mode 100644
index 0000000..4d9a56e4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.sharedworker-expected.txt
@@ -0,0 +1,17 @@
+This is a testharness.js-based test.
+[FAIL] Multiplying two types with different non-null percent hints throws TypeError
+  CSSMathSum is not defined
+[FAIL] Multiplying two types with same base types adds exponents
+  CSSUnitValue is not defined
+[FAIL] Multiplying two types with different base types adds exponents
+  CSSUnitValue is not defined
+[FAIL] Multiplying two types respects the sign of the exponents
+  CSSUnitValue is not defined
+[FAIL] Multiplying a type with no exponents is a no-op
+  CSSUnitValue is not defined
+[FAIL] Multiplying a type with percent hint applies the percent hint
+  CSSMathSum is not defined
+[FAIL] Multiplying two types with same percent hint applies the percent hint
+  CSSMathSum is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.worker-expected.txt
new file mode 100644
index 0000000..4d9a56e4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/cssnumericvalue-multiply-two-types.tentative.any.worker-expected.txt
@@ -0,0 +1,17 @@
+This is a testharness.js-based test.
+[FAIL] Multiplying two types with different non-null percent hints throws TypeError
+  CSSMathSum is not defined
+[FAIL] Multiplying two types with same base types adds exponents
+  CSSUnitValue is not defined
+[FAIL] Multiplying two types with different base types adds exponents
+  CSSUnitValue is not defined
+[FAIL] Multiplying two types respects the sign of the exponents
+  CSSUnitValue is not defined
+[FAIL] Multiplying a type with no exponents is a no-op
+  CSSUnitValue is not defined
+[FAIL] Multiplying a type with percent hint applies the percent hint
+  CSSMathSum is not defined
+[FAIL] Multiplying two types with same percent hint applies the percent hint
+  CSSMathSum is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.js
similarity index 90%
rename from third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.html
rename to third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.js
index 21ff94e..9b86d5cf 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.js
@@ -1,13 +1,18 @@
-<meta charset="utf-8">
-<title>CSSNumericValue.to tests</title>
-<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-to">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="../../resources/testhelper.js"></script>
-<script>
+// META: global=window,worker
+// META: script=../../resources/testhelper.js
+// META: title=CSSNumericValue.to tests
+// META: spec=https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-to
+
 'use strict';
 
 test(() => {
+  const value = new CSSUnitValue(10, 'px');
+  assert_true(value instanceof CSSUnitValue);
+  assert_true(value instanceof CSSNumericValue);
+  assert_true(value instanceof CSSStyleValue);
+}, 'CSSUnitValue is a CSSNumericValue and a CSSStyleValue');
+
+test(() => {
   assert_throws_dom("SyntaxError", () => CSS.px(1).to('lemon'));
 }, 'Converting a CSSUnitValue to an invalid unit throws SyntaxError');
 
@@ -111,5 +116,3 @@
 
   assert_style_value_equals(expr.to('ms'), CSS.ms(3500));
 }, 'Converting a complex expression to a single unit');
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.serviceworker-expected.txt
new file mode 100644
index 0000000..ad80abc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.serviceworker-expected.txt
@@ -0,0 +1,41 @@
+This is a testharness.js-based test.
+[FAIL] CSSUnitValue is a CSSNumericValue and a CSSStyleValue
+  CSSUnitValue is not defined
+[FAIL] Converting a CSSUnitValue to an invalid unit throws SyntaxError
+  assert_throws_dom: function "() => CSS.px(1).to('lemon')" threw object "ReferenceError: CSS is not defined" that is not a DOMException SyntaxError: property "code" is equal to undefined, expected 12
+[FAIL] Converting a CSSNumericValue with invalid sum value throws TypeError
+  assert_throws_js: function "() => new CSSMathMax(1, CSS.px(1)).to('number')" threw object "ReferenceError: CSSMathMax is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSNumericValue with sum value containing more than one value throws TypeError
+  assert_throws_js: function "() => new CSSMathProduct(CSS.px(1), CSS.s(1)).to('number')" threw object "ReferenceError: CSSMathProduct is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSUnitValue to an incompatible unit throws TypeError
+  assert_throws_js: function "() => CSS.px(1).to('number')" threw object "ReferenceError: CSS is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSUnitValue to its own unit returns itself
+  CSS is not defined
+[FAIL] Converting a CSSUnitValue to its canonical unit returns correct value
+  CSS is not defined
+[FAIL] Converting a CSSUnitValue to a compatible unit returns correct value
+  CSS is not defined
+[FAIL] Converting a CSSMathSum to a single unit adds the values
+  CSSMathSum is not defined
+[FAIL] Converting a CSSMathProduct to a single unit multiplies the values
+  CSSMathProduct is not defined
+[FAIL] Converting a CSSMathMin to a single unit finds the min value
+  CSSMathMin is not defined
+[FAIL] Converting a CSSMathMin to a single unit with different units throws a TypeError
+  assert_throws_js: function "() => new CSSMathMin(CSS.px(2), CSS.s(3)).to('px')" threw object "ReferenceError: CSSMathMin is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSMathMax to a single unit finds the max value
+  CSSMathMax is not defined
+[FAIL] Converting a CSSMathMax to a single unit with different units throws a TypeError
+  assert_throws_js: function "() => new CSSMathMax(CSS.px(2), CSS.s(3)).to('px')" threw object "ReferenceError: CSSMathMax is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSMathClamp to a single unit returns the clamped value
+  CSSMathClamp is not defined
+[FAIL] Converting a CSSMathClamp to a single unit with different units throws a TypeError
+  assert_throws_js: function "() => new CSSMathClamp(CSS.px(2), CSS.s(3), CSS.px(4)).to('px')" threw object "ReferenceError: CSSMathClamp is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSMathNegate to a single unit negates its value
+  CSSMathNegate is not defined
+[FAIL] Converting a CSSMathInvert to a single unit inverts its value and units
+  CSSMathProduct is not defined
+[FAIL] Converting a complex expression to a single unit
+  CSSMathSum is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.sharedworker-expected.txt
new file mode 100644
index 0000000..ad80abc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.sharedworker-expected.txt
@@ -0,0 +1,41 @@
+This is a testharness.js-based test.
+[FAIL] CSSUnitValue is a CSSNumericValue and a CSSStyleValue
+  CSSUnitValue is not defined
+[FAIL] Converting a CSSUnitValue to an invalid unit throws SyntaxError
+  assert_throws_dom: function "() => CSS.px(1).to('lemon')" threw object "ReferenceError: CSS is not defined" that is not a DOMException SyntaxError: property "code" is equal to undefined, expected 12
+[FAIL] Converting a CSSNumericValue with invalid sum value throws TypeError
+  assert_throws_js: function "() => new CSSMathMax(1, CSS.px(1)).to('number')" threw object "ReferenceError: CSSMathMax is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSNumericValue with sum value containing more than one value throws TypeError
+  assert_throws_js: function "() => new CSSMathProduct(CSS.px(1), CSS.s(1)).to('number')" threw object "ReferenceError: CSSMathProduct is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSUnitValue to an incompatible unit throws TypeError
+  assert_throws_js: function "() => CSS.px(1).to('number')" threw object "ReferenceError: CSS is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSUnitValue to its own unit returns itself
+  CSS is not defined
+[FAIL] Converting a CSSUnitValue to its canonical unit returns correct value
+  CSS is not defined
+[FAIL] Converting a CSSUnitValue to a compatible unit returns correct value
+  CSS is not defined
+[FAIL] Converting a CSSMathSum to a single unit adds the values
+  CSSMathSum is not defined
+[FAIL] Converting a CSSMathProduct to a single unit multiplies the values
+  CSSMathProduct is not defined
+[FAIL] Converting a CSSMathMin to a single unit finds the min value
+  CSSMathMin is not defined
+[FAIL] Converting a CSSMathMin to a single unit with different units throws a TypeError
+  assert_throws_js: function "() => new CSSMathMin(CSS.px(2), CSS.s(3)).to('px')" threw object "ReferenceError: CSSMathMin is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSMathMax to a single unit finds the max value
+  CSSMathMax is not defined
+[FAIL] Converting a CSSMathMax to a single unit with different units throws a TypeError
+  assert_throws_js: function "() => new CSSMathMax(CSS.px(2), CSS.s(3)).to('px')" threw object "ReferenceError: CSSMathMax is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSMathClamp to a single unit returns the clamped value
+  CSSMathClamp is not defined
+[FAIL] Converting a CSSMathClamp to a single unit with different units throws a TypeError
+  assert_throws_js: function "() => new CSSMathClamp(CSS.px(2), CSS.s(3), CSS.px(4)).to('px')" threw object "ReferenceError: CSSMathClamp is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSMathNegate to a single unit negates its value
+  CSSMathNegate is not defined
+[FAIL] Converting a CSSMathInvert to a single unit inverts its value and units
+  CSSMathProduct is not defined
+[FAIL] Converting a complex expression to a single unit
+  CSSMathSum is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.worker-expected.txt
new file mode 100644
index 0000000..ad80abc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/stylevalue-subclasses/numeric-objects/to.tentative.any.worker-expected.txt
@@ -0,0 +1,41 @@
+This is a testharness.js-based test.
+[FAIL] CSSUnitValue is a CSSNumericValue and a CSSStyleValue
+  CSSUnitValue is not defined
+[FAIL] Converting a CSSUnitValue to an invalid unit throws SyntaxError
+  assert_throws_dom: function "() => CSS.px(1).to('lemon')" threw object "ReferenceError: CSS is not defined" that is not a DOMException SyntaxError: property "code" is equal to undefined, expected 12
+[FAIL] Converting a CSSNumericValue with invalid sum value throws TypeError
+  assert_throws_js: function "() => new CSSMathMax(1, CSS.px(1)).to('number')" threw object "ReferenceError: CSSMathMax is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSNumericValue with sum value containing more than one value throws TypeError
+  assert_throws_js: function "() => new CSSMathProduct(CSS.px(1), CSS.s(1)).to('number')" threw object "ReferenceError: CSSMathProduct is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSUnitValue to an incompatible unit throws TypeError
+  assert_throws_js: function "() => CSS.px(1).to('number')" threw object "ReferenceError: CSS is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSUnitValue to its own unit returns itself
+  CSS is not defined
+[FAIL] Converting a CSSUnitValue to its canonical unit returns correct value
+  CSS is not defined
+[FAIL] Converting a CSSUnitValue to a compatible unit returns correct value
+  CSS is not defined
+[FAIL] Converting a CSSMathSum to a single unit adds the values
+  CSSMathSum is not defined
+[FAIL] Converting a CSSMathProduct to a single unit multiplies the values
+  CSSMathProduct is not defined
+[FAIL] Converting a CSSMathMin to a single unit finds the min value
+  CSSMathMin is not defined
+[FAIL] Converting a CSSMathMin to a single unit with different units throws a TypeError
+  assert_throws_js: function "() => new CSSMathMin(CSS.px(2), CSS.s(3)).to('px')" threw object "ReferenceError: CSSMathMin is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSMathMax to a single unit finds the max value
+  CSSMathMax is not defined
+[FAIL] Converting a CSSMathMax to a single unit with different units throws a TypeError
+  assert_throws_js: function "() => new CSSMathMax(CSS.px(2), CSS.s(3)).to('px')" threw object "ReferenceError: CSSMathMax is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSMathClamp to a single unit returns the clamped value
+  CSSMathClamp is not defined
+[FAIL] Converting a CSSMathClamp to a single unit with different units throws a TypeError
+  assert_throws_js: function "() => new CSSMathClamp(CSS.px(2), CSS.s(3), CSS.px(4)).to('px')" threw object "ReferenceError: CSSMathClamp is not defined" ("ReferenceError") expected instance of function "function TypeError() { [native code] }" ("TypeError")
+[FAIL] Converting a CSSMathNegate to a single unit negates its value
+  CSSMathNegate is not defined
+[FAIL] Converting a CSSMathInvert to a single unit inverts its value and units
+  CSSMathProduct is not defined
+[FAIL] Converting a complex expression to a single unit
+  CSSMathSum is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/random-item-in-container-query.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-values/random-item-in-container-query.tentative.html
new file mode 100644
index 0000000..bcb4ee83
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/random-item-in-container-query.tentative.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<title>CSS Values and Units Test: random-item() in container queries</title>
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#funcdef-random-item">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/10982">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
+<style>
+  @property --length {
+    syntax: "<length>";
+    inherits: true;
+    initial-value: 30px;
+  }
+  #container { container-type: size; --length: 30px; }
+  #target { --test1: true; --test2: true; --test3: true; }
+
+  /* Every random-item() item is 30px and --length is 30px. So if random-item() were (wrongly)
+     evaluated in the query, it would resolve to 30px and the condition WOULD match — flipping the
+     --testN guard to false, no matter which item is picked. With random-item() correctly rejected
+     (guaranteed-invalid), the condition never matches and --testN stays true. Deterministic in both
+     directions — no dependence on the random selection. */
+  @container style(--length: random-item(--foo, 30px, 30px)) { #target { --test1: false; } }
+  @container style(--length = random-item(--foo, 30px, 30px)) { #target { --test2: false; } }
+  @container style(random-item(--foo, 30px, 30px) = random-item(--foo, 30px, 30px)) { #target { --test3: false; } }
+</style>
+<div style="container-name:name">
+  <main id="cq-main"></main>
+</div>
+<div id="container">
+  <div id="target"></div>
+</div>
+<script>
+  // random functions are disallowed outside an element context, so random-item() in a @container
+  // query is rejected like random(). Whether they should be allowed here is an open question:
+  // https://github.com/w3c/csswg-drafts/issues/10982
+
+  // Size-feature form: random-item() isn't a valid <length> feature value, so the condition is
+  // valid-but-unknown (roll-independent, parse-level).
+  test_cq_condition_unknown("(width: random-item(--foo, 10px, 30px))");
+
+  // style() forms: random-item() is rejected at evaluation, so the condition never matches. Each
+  // guard stays "true" iff random-item() was rejected (see the note in the stylesheet — this is
+  // deterministic regardless of which item random-item() would pick).
+  test(() => {
+    assert_equals(getComputedStyle(target).getPropertyValue("--test1"), "true");
+  }, "random-item() is disallowed in a @container style() query");
+  test(() => {
+    assert_equals(getComputedStyle(target).getPropertyValue("--test2"), "true");
+  }, "random-item() is disallowed in a @container style() range query (custom property = random-item())");
+  test(() => {
+    assert_equals(getComputedStyle(target).getPropertyValue("--test3"), "true");
+  }, "random-item() is disallowed in a @container style() range query (random-item() = random-item())");
+
+  // Note: a custom property that merely *holds* random-item() (e.g.
+  //   #container { --v: random-item(--foo, 30px, 30px); }  @container style(--length: var(--v)) {})
+  // is intentionally not tested. random-item() is an arbitrary substitution function, so it resolves
+  // in the container element's own context (where it is allowed) before the query runs — no
+  // random-item() reaches the query. That is the open element-context question above, not a property
+  // of the @container feature; see https://github.com/w3c/csswg-drafts/issues/10982
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/document-active-view-transition.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/document-active-view-transition.html
index 6a4bcf9e..5c98713 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/document-active-view-transition.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/document-active-view-transition.html
@@ -6,7 +6,6 @@
 
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="support/utils.js"></script>
 
 <style>
 #target {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/scoped/element-active-view-transition.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/scoped/element-active-view-transition.html
index f1fba29d..df11626 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/scoped/element-active-view-transition.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/scoped/element-active-view-transition.html
@@ -6,7 +6,6 @@
 
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="support/utils.js"></script>
 
 <style>
 #target {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/crashtests/bidi-inline-continuation-first-in-flow.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/crashtests/bidi-inline-continuation-first-in-flow.html
new file mode 100644
index 0000000..645851d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/crashtests/bidi-inline-continuation-first-in-flow.html
@@ -0,0 +1,194 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=2053578">
+
+<!-- The operations in <script> are generated from one of the runs from
+     the original testcase (Bug 2053578 Comment 5) that triggers
+     the assertion. -->
+
+<body></body>
+
+<script>
+var n1 = document.createElement("div");
+n1.style.width = "50px";
+var n2 = document.createElement("bdo");
+n2.setAttribute("dir", "rtl");
+var n3 = document.createTextNode("كلمة");
+n2.appendChild(n3);
+n1.appendChild(n2);
+var n4 = document.createElement("em");
+var n5 = document.createElement("span");
+var n6 = document.createElement("bdo");
+var n7 = document.createTextNode("word m mix");
+n6.appendChild(n7);
+n5.appendChild(n6);
+var n8 = document.createTextNode("מלל");
+n5.appendChild(n8);
+var n9 = document.createElement("b");
+n9.setAttribute("dir", "ltr");
+n9.style.unicodeBidi = "bidi-override";
+var n10 = document.createTextNode("alpha");
+n9.appendChild(n10);
+var n11 = document.createTextNode("نص مرحبا كلمة");
+n9.appendChild(n11);
+var n12 = document.createTextNode("مرحبا نص نص");
+n9.appendChild(n12);
+n5.appendChild(n9);
+n4.appendChild(n5);
+var n13 = document.createElement("span");
+n13.style.unicodeBidi = "bidi-override";
+var n14 = document.createElement("bdi");
+var n15 = document.createTextNode("اختبار");
+n14.appendChild(n15);
+n13.appendChild(n14);
+var n16 = document.createElement("em");
+var n17 = document.createTextNode("בדיקה מלל m نص");
+n16.appendChild(n17);
+var n18 = document.createTextNode("كلمة");
+n16.appendChild(n18);
+var n19 = document.createTextNode("نص مرحبا كلمة");
+n16.appendChild(n19);
+n13.appendChild(n16);
+var n20 = document.createElement("span");
+var n21 = document.createTextNode("שלום מלל בדיקה שלום");
+n20.appendChild(n21);
+n13.appendChild(n20);
+n4.appendChild(n13);
+n1.appendChild(n4);
+document.body.appendChild(n1);
+var n22 = document.createElement("bdo");
+n22.style.unicodeBidi = "plaintext";
+var n23 = document.createElement("span");
+var n24 = document.createTextNode("עברית שלום مرحبا alpha");
+n23.appendChild(n24);
+n22.appendChild(n23);
+n14.style.direction = "ltr";
+var n25 = document.createTextNode("كلمة שלום");
+n2.insertBefore(n25, n3);
+n21.remove();
+n9.setAttribute("dir", "ltr");
+var n26 = document.createTextNode("בדיקה mm");
+n4.appendChild(n26);
+n12.data = "שלום עברית שלום עברית";
+n7.remove();
+n2.style.direction = "ltr";
+n1.style.width = "102px";
+var n27 = document.createTextNode("עברית mm m");
+n22.appendChild(n27);
+n22.setAttribute("dir", "rtl");
+n4.style.direction = "";
+n14.style.direction = "rtl";
+n22.insertBefore(n9, n23);
+var n28 = document.createElement("br");
+n9.insertBefore(n28, n12);
+n14.style.unicodeBidi = "embed";
+n5.insertBefore(n22, n6);
+n9.style.direction = "";
+n9.style.unicodeBidi = "bidi-override";
+n22.remove();
+var n29 = document.createTextNode("עברית שלום");
+n5.insertBefore(n29, n6);
+n18.data = "mix";
+n19.data = "alpha m word";
+n20.style.direction = "ltr";
+var n30 = document.createTextNode("mm עברית עברית نص");
+n6.appendChild(n30);
+n6.style.unicodeBidi = "isolate";
+n20.style.unicodeBidi = "embed";
+n16.setAttribute("dir", "rtl");
+n2.insertBefore(n20, n25);
+var n31 = document.createTextNode("שלום مرحبا mix mm");
+n2.insertBefore(n31, n20);
+var n32 = document.createTextNode("m בדיקה word mix");
+n20.appendChild(n32);
+n16.removeAttribute("dir");
+n16.setAttribute("dir", "auto");
+n5.remove();
+n17.remove();
+n31.data = "בדיקה mm";
+n14.style.unicodeBidi = "isolate";
+n4.remove();
+n20.style.direction = "";
+n20.style.direction = "rtl";
+n3.data = "בדיקה שלום word";
+n25.data = "mm";
+n25.data = "m mix word";
+n1.style.direction = "rtl";
+n31.data = "كلمة اختبار";
+n20.remove();
+n3.data = "שלום نص";
+n31.remove();
+n2.setAttribute("dir", "ltr");
+n2.removeAttribute("dir");
+n1.insertBefore(n4, n2);
+n19.data = "מלל word";
+n15.remove();
+n13.setAttribute("dir", "ltr");
+n25.remove();
+n2.setAttribute("dir", "ltr");
+n14.appendChild(n2);
+n3.data = "مرحبا اختبار مرحبا";
+n18.data = "שלום";
+n16.insertBefore(n20, n18);
+var n33 = document.createTextNode("كلمة نص mm");
+n2.appendChild(n33);
+n19.data = "اختبار مرحبا كلمة كلمة";
+n18.data = "mm mix mix";
+var n34 = document.createElement("br");
+n4.insertBefore(n34, n13);
+n13.style.unicodeBidi = "isolate-override";
+n13.remove();
+n34.remove();
+n26.data = "كلمة كلمة مرحبا";
+n4.setAttribute("dir", "rtl");
+n26.data = "mm word m mm";
+n26.data = "اختبار نص";
+n4.setAttribute("dir", "auto");
+var n35 = document.createTextNode("مرحبا מלל كلمة שלום");
+n4.insertBefore(n35, n26);
+n4.style.unicodeBidi = "embed";
+n4.style.direction = "";
+var n36 = document.createTextNode("كلمة mix בדיקה نص");
+n4.insertBefore(n36, n35);
+n36.remove();
+n4.removeAttribute("dir");
+n4.style.unicodeBidi = "";
+n1.style.width = "87px";
+n26.data = "שלום מלל מלל עברית";
+var n37 = document.createElement("br");
+n4.insertBefore(n37, n35);
+n35.data = "مرحبا نص اختبار";
+n4.setAttribute("dir", "rtl");
+n33.data = "word alpha mm";
+var n38 = document.createTextNode("שלום word");
+n16.insertBefore(n38, n20);
+n2.setAttribute("dir", "auto");
+n2.remove();
+n37.remove();
+var n39 = document.createTextNode("עברית اختبار בדיקה");
+n13.appendChild(n39);
+var n40 = document.createTextNode("בדיקה m نص نص");
+n13.insertBefore(n40, n14);
+var n41 = document.createElement("br");
+n20.appendChild(n41);
+n35.data = "alpha";
+n16.remove();
+n39.data = "نص mm בדיקה اختبار";
+n40.data = "mix mix word m";
+n35.data = "שלום בדיקה";
+n35.data = "m mix mm m";
+n40.data = "مرحبا اختبار";
+n13.style.unicodeBidi = "embed";
+n4.insertBefore(n16, n26);
+var n42 = document.createTextNode("نص");
+n13.insertBefore(n42, n40);
+n41.remove();
+n20.insertBefore(n2, n32);
+document.body.offsetHeight;
+n39.data = "مرحبا alpha עברית word";
+n18.data = "שלום";
+n2.setAttribute("dir", "auto");
+document.body.offsetHeight;
+n20.style.direction = "ltr";
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/orthogonal-parent-shrink-to-fit-002.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/orthogonal-parent-shrink-to-fit-002.html
new file mode 100644
index 0000000..e8be2b5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/orthogonal-parent-shrink-to-fit-002.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Writing Modes Test: Shrink-to-fit with an orthogonal atomic inline child</title>
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes-4/#orthogonal-flows" title="7.3. Orthogonal Flows">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-3/#intrinsic-contribution">
+<meta name="assert" content="A shrink-to-fit box sized to an orthogonal atomic inline child uses that child's block-axis size as the child's intrinsic inline-size contribution.">
+<meta name="flags" content="ahem dom combo">
+<link rel="author" title="Karl Dubost" href="https://github.com/karlcow">
+<!-- Companion to orthogonal-parent-shrink-to-fit-001.html, which covers orthogonal
+     block and orthogonal inline children. This file covers the remaining case: an
+     orthogonal ATOMIC INLINE child (display:inline-block), which reaches a separate
+     intrinsic-width code path in some implementations.
+     See https://bugs.webkit.org/show_bug.cgi?id=187454 -->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+.test {
+    border:thin solid;
+    font:20px/1 Ahem;
+}
+.target {
+    color:blue;
+    height:3em; /* height: 3em is not required. It keeps the test focused
+    exclusively on the shrink-to-fit algorithm. */
+    writing-mode:vertical-rl;
+}
+.border {
+    border-right:blue solid .5em;
+}
+.next {
+    color:orange;
+}
+.inline-block {
+    display:inline-block;
+}
+.float {
+    float:left;
+}
+h3 {
+    clear:both;
+}
+h3.fail {
+    color:red;
+}
+table {
+    border-spacing:0px;
+}
+td {
+    padding:0px;
+}
+</style>
+<div id="log"></div>
+<div id="container">
+<p>Test passes if the X-position of the <b>left</b> edge of the orange box and the <b>right</b> edge of the blue box are the same.
+<p>If script is enabled, there should be one or more PASS and no FAIL.
+<h3>1: Shrink-to-fit inline-block with a child of orthogonal atomic inline</h3>
+<div class="test">
+    <div class="inline-block"><span class="target inline-block">HH</span></div><span class="next">ZZ</span>
+</div>
+<h3>2: Shrink-to-fit inline-block with a child of orthogonal atomic inline with borders</h3>
+<div class="test">
+    <div class="inline-block"><span class="target inline-block border">HHH</span></div><span class="next">ZZ</span>
+</div>
+<h3>3: Shrink-to-fit inline-block with a child of orthogonal atomic inline in inline-block</h3>
+<div class="test">
+    <div class="inline-block"><div class="inline-block"><span class="target inline-block">HH</span></div></div><span class="next">ZZ</span>
+</div>
+<h3>4: Shrink-to-fit inline-block with a child of orthogonal atomic inline with borders in inline-block</h3>
+<div class="test">
+    <div class="inline-block"><div class="inline-block"><span class="target inline-block border">HHH</span></div></div><span class="next">ZZ</span>
+</div>
+<h3>5: Shrink-to-fit float with a child of orthogonal atomic inline</h3>
+<div class="test">
+    <div class="float"><span class="target inline-block">HH</span></div><span class="next">ZZ</span>
+</div>
+<h3>6: Shrink-to-fit float with a child of orthogonal atomic inline with borders</h3>
+<div class="test">
+    <div class="float"><span class="target inline-block border">HHH</span></div><span class="next">ZZ</span>
+</div>
+<h3>7: Shrink-to-fit float with a child of orthogonal atomic inline in inline-block</h3>
+<div class="test">
+    <div class="float"><div class="inline-block"><span class="target inline-block">HH</span></div></div><span class="next">ZZ</span>
+</div>
+<h3>8: Shrink-to-fit float with a child of orthogonal atomic inline with borders in inline-block</h3>
+<div class="test">
+    <div class="float"><div class="inline-block"><span class="target inline-block border">HHH</span></div></div><span class="next">ZZ</span>
+</div>
+<h3>9: Shrink-to-fit table-cell with a child of orthogonal atomic inline</h3>
+<div class="test">
+    <table><tr><td><span class="target inline-block">HH</span></td><td class="next">ZZ</td></tr></table>
+</div>
+<h3>10: Shrink-to-fit table-cell with a child of orthogonal atomic inline with borders</h3>
+<div class="test">
+    <table><tr><td><span class="target inline-block border">HHH</span></td><td class="next">ZZ</td></tr></table>
+</div>
+<h3>11: Shrink-to-fit table-cell with a child of orthogonal atomic inline in inline-block</h3>
+<div class="test">
+    <table><tr><td><div class="inline-block"><span class="target inline-block">HH</span></div></td><td class="next">ZZ</td></tr></table>
+</div>
+<h3>12: Shrink-to-fit table-cell with a child of orthogonal atomic inline with borders in inline-block</h3>
+<div class="test">
+    <table><tr><td><div class="inline-block"><span class="target inline-block border">HHH</span></div></td><td class="next">ZZ</td></tr></table>
+</div>
+</div>
+<script>
+run();
+
+function run() {
+    Array.prototype.forEach.call(document.querySelectorAll(".test"), function (node) {
+        var title = node.previousElementSibling.textContent;
+        test(function () {
+            try {
+                var targetNode = node.querySelector(".target");
+                var fontSize = parseFloat(getComputedStyle(targetNode).fontSize);
+                var targetBounds = targetNode.getBoundingClientRect();
+                assert_less_than_equal(targetBounds.width, fontSize * 2.01, "writing-mode is vertical")
+                var nextNode = node.querySelector(".next");
+                var nextBounds = nextNode.getBoundingClientRect();
+                assert_equals(nextBounds.left - targetBounds.right, 0, "the left edge of the orange box touches the right edge of the blue box");
+            } catch (e) {
+                node.previousElementSibling.classList.add("fail");
+                throw e;
+            }
+        }, title);
+    });
+    done();
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/svg-mutation-fedropshadow-stddeviation-ref.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/svg-mutation-fedropshadow-stddeviation-ref.html
new file mode 100644
index 0000000..8cfd3db
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/svg-mutation-fedropshadow-stddeviation-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>Filter Effects: mutating feDropShadow stdDeviation updates both x and y components (reference)</title>
+<link rel="help" href="https://drafts.fxtf.org/filter-effects-1/#feDropShadowElement">
+<svg width="300" height="200">
+  <filter id="shadow" filterUnits="userSpaceOnUse" x="0" y="0" width="300" height="200">
+    <feDropShadow id="drop" dx="120" dy="0" stdDeviation="20 20" flood-color="black" flood-opacity="1"/>
+  </filter>
+  <rect x="20" y="80" width="40" height="40" fill="green" filter="url(#shadow)"/>
+</svg>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/svg-mutation-fedropshadow-stddeviation.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/svg-mutation-fedropshadow-stddeviation.html
new file mode 100644
index 0000000..d5f1b1cc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/svg-mutation-fedropshadow-stddeviation.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Filter Effects: mutating feDropShadow stdDeviation updates both x and y components</title>
+<link rel="help" href="https://drafts.fxtf.org/filter-effects-1/#feDropShadowElement">
+<link rel="help" href="https://drafts.fxtf.org/filter-effects-1/#element-attrdef-fedropshadow-stddeviation">
+<link rel="match" href="svg-mutation-fedropshadow-stddeviation-ref.html">
+<meta name="assert" content="Changing the stdDeviation attribute of an already-built feDropShadow must update both the x and y standard-deviation of the effect, not just x.">
+<script src="/common/rendering-utils.js"></script>
+<script src="/common/reftest-wait.js"></script>
+<svg width="300" height="200">
+  <filter id="shadow" filterUnits="userSpaceOnUse" x="0" y="0" width="300" height="200">
+    <feDropShadow id="drop" dx="120" dy="0" stdDeviation="2 2" flood-color="black" flood-opacity="1"/>
+  </filter>
+  <rect x="20" y="80" width="40" height="40" fill="green" filter="url(#shadow)"/>
+</svg>
+<script>
+  waitForAtLeastOneFrame().then(() => {
+    document.getElementById("drop").setAttribute("stdDeviation", "20 20");
+    takeScreenshot();
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/motion/offset-path-ray-sides-fourth-quadrant-ref.html b/third_party/blink/web_tests/external/wpt/css/motion/offset-path-ray-sides-fourth-quadrant-ref.html
new file mode 100644
index 0000000..35f3fca
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/motion/offset-path-ray-sides-fourth-quadrant-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS Motion Path: ray() 'sides' size with a fourth-quadrant angle</title>
+    <style>
+      #container {
+        width: 400px;
+        height: 400px;
+      }
+      #target {
+        position: relative;
+        left: 200px;
+        top: 160px;
+        width: 40px;
+        height: 40px;
+        background-color: green;
+        transform-origin: 0px 0px;
+        /* ray(315deg sides) from (200,160) hits the top edge: length = 160 / cos(45deg).
+           With offset-rotate:0 this is a pure translation of length * (sin315, -cos315) = (-160, -160). */
+        transform: translate(-160px, -160px);
+      }
+    </style>
+  </head>
+  <body>
+    <div id="container"><div id="target"></div></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/motion/offset-path-ray-sides-fourth-quadrant.html b/third_party/blink/web_tests/external/wpt/css/motion/offset-path-ray-sides-fourth-quadrant.html
new file mode 100644
index 0000000..1b9552b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/motion/offset-path-ray-sides-fourth-quadrant.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS Motion Path: ray() 'sides' size with a fourth-quadrant angle</title>
+    <link rel="author" title="Chris Dumez" href="mailto:cdumez@apple.com">
+    <link rel="help" href="https://drafts.fxtf.org/motion-1/#ray-function">
+    <link rel="match" href="offset-path-ray-sides-fourth-quadrant-ref.html">
+    <meta name="assert" content="ray(315deg sides) from (200,160) in a 400x400 containing block resolves its length against the top edge (160 / cos(45deg)), translating the box by (-160,-160). Angles in the (270deg, 360deg) range must fold down to an acute angle from the vertical axis.">
+    <style>
+      #container {
+        width: 400px;
+        height: 400px;
+      }
+      #target {
+        position: relative;
+        left: 200px;
+        top: 160px;
+        width: 40px;
+        height: 40px;
+        background-color: green;
+        transform-origin: 0px 0px;
+        offset-path: ray(315deg sides);
+        offset-distance: 100%;
+        offset-rotate: 0deg;
+        offset-position: auto;
+      }
+    </style>
+  </head>
+  <body>
+    <div id="container"><div id="target"></div></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/digital-credentials/user-agent-allows-protocol.https.html b/third_party/blink/web_tests/external/wpt/digital-credentials/user-agent-allows-protocol.https.html
new file mode 100644
index 0000000..980d1065
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/digital-credentials/user-agent-allows-protocol.https.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset="utf-8" />
+<title>
+    Digital Credentials: User agent allows the required presentation protocols
+</title>
+<link rel="help" href="https://github.com/w3c-fedid/digital-credentials/pull/454" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body></body>
+<script type="module">
+  const REQUIRED_PRESENTATION_PROTOCOLS = [
+    "org-iso-mdoc",
+    "openid4vp-v1-signed",
+    "openid4vp-v1-multisigned",
+    "openid4vp-v1-unsigned",
+  ];
+
+  for (const protocol of REQUIRED_PRESENTATION_PROTOCOLS) {
+    test(() => {
+      assert_true(
+        DigitalCredential.userAgentAllowsProtocol(protocol),
+        `User agent must support the "${protocol}" presentation protocol`,
+      );
+    }, `Presentation protocol support: ${protocol}`);
+  }
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/dom/nodes/Node-compareDocumentPosition.html b/third_party/blink/web_tests/external/wpt/dom/nodes/Node-compareDocumentPosition.html
index afae60aa..158eb4d8 100644
--- a/third_party/blink/web_tests/external/wpt/dom/nodes/Node-compareDocumentPosition.html
+++ b/third_party/blink/web_tests/external/wpt/dom/nodes/Node-compareDocumentPosition.html
@@ -29,13 +29,22 @@
       // constraint that this is to be consistent, together and terminate these
       // steps."
       if (furthestAncestor(reference) !== furthestAncestor(other)) {
-        // TODO: Test that it's consistent.
-        assert_in_array(result, [Node.DOCUMENT_POSITION_DISCONNECTED +
-                                 Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC +
-                                 Node.DOCUMENT_POSITION_PRECEDING,
-                                 Node.DOCUMENT_POSITION_DISCONNECTED +
-                                 Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC +
-                                 Node.DOCUMENT_POSITION_FOLLOWING]);
+        var expected1 = Node.DOCUMENT_POSITION_DISCONNECTED +
+                        Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC +
+                        Node.DOCUMENT_POSITION_PRECEDING;
+        var expected2 = Node.DOCUMENT_POSITION_DISCONNECTED +
+                        Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC +
+                        Node.DOCUMENT_POSITION_FOLLOWING;
+        assert_in_array(result, [expected1, expected2]);
+
+        // TODO: What does "consistent" mean exactly? Is it transitive? A total order?
+        // Here we test that the relation is anticommutative: a < b => b > a; a > b => b < a
+        var anticommutativeResult = other.compareDocumentPosition(reference);
+        if (result === expected1) {
+          assert_equals(anticommutativeResult, expected2);
+        } else {
+          assert_equals(anticommutativeResult, expected1);
+        }
         return;
       }
 
diff --git a/third_party/blink/web_tests/external/wpt/editing/crashtests/set-documentElement-innerHTML-when-embed-in-designMode-has-focus.html b/third_party/blink/web_tests/external/wpt/editing/crashtests/set-documentElement-innerHTML-when-embed-in-designMode-has-focus.html
new file mode 100644
index 0000000..08e331ed
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/editing/crashtests/set-documentElement-innerHTML-when-embed-in-designMode-has-focus.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script>
+"use strict";
+
+addEventListener("DOMContentLoaded", () => {
+  document.designMode = "on";
+  document.querySelector("embed").focus();
+  document.documentElement.innerHTML = "";
+}, {once: true});
+</script>
+</head>
+<body>
+<embed>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/editing/plaintext-only/backspace-end-of-plaintext-only-pre-ref.html b/third_party/blink/web_tests/external/wpt/editing/plaintext-only/backspace-end-of-plaintext-only-pre-ref.html
new file mode 100644
index 0000000..357c82c0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/editing/plaintext-only/backspace-end-of-plaintext-only-pre-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test that newline is still visible after deleting contents of last line of pre plaintext-only editor.</title>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<style>pre { border: 2px solid black; }</style>
+<pre contenteditable="plaintext-only">a
+b
+<br></pre>
+<script>
+const editor = document.querySelector('pre');
+editor.focus();
+getSelection().selectAllChildren(editor);
+getSelection().collapseToEnd();
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/editing/plaintext-only/backspace-end-of-plaintext-only-pre.html b/third_party/blink/web_tests/external/wpt/editing/plaintext-only/backspace-end-of-plaintext-only-pre.html
new file mode 100644
index 0000000..3e61a35
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/editing/plaintext-only/backspace-end-of-plaintext-only-pre.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Test that newline is still visible after deleting contents of last line of pre plaintext-only editor.</title>
+<link rel="match" href="backspace-end-of-plaintext-only-pre-ref.html">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1969451">
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<style>pre { border: 2px solid black; }</style>
+<pre contenteditable="plaintext-only">a
+b
+c</pre>
+<script>
+const editor = document.querySelector('pre');
+editor.focus();
+const textNode = editor.firstChild;
+getSelection().collapse(textNode, textNode.length);
+test_driver.send_keys(editor, '\uE003') // backspace
+  .then(() => document.documentElement.classList.remove('reftest-wait'));
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/entries-api/filesystementry-getParent-expected.txt b/third_party/blink/web_tests/external/wpt/entries-api/filesystementry-getParent-expected.txt
new file mode 100644
index 0000000..6b03220
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/entries-api/filesystementry-getParent-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+[FAIL] getParent() on a root-level file entry returns the root directory
+  Cannot read properties of null (reading 'getParent')
+[FAIL] getParent() on the root directory returns the root itself
+  Cannot read properties of null (reading 'filesystem')
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/entries-api/filesystementry-getParent.html b/third_party/blink/web_tests/external/wpt/entries-api/filesystementry-getParent.html
new file mode 100644
index 0000000..f7fc5c8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/entries-api/filesystementry-getParent.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Entries API: FileSystemEntry.getParent() with script-created DataTransfer</title>
+<link rel=help href="https://wicg.github.io/entries-api/#dom-filesystementry-getparent">
+<link rel=author title="Euclid Ye" href="mailto:yezhizhenjiakang@gmail.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+async_test(t => {
+  const file = new File(['hello world'], 'test.txt', { type: 'text/plain' });
+  const dt = new DataTransfer();
+  dt.items.add(file);
+
+  const entry = dt.items[0].webkitGetAsEntry();
+
+  entry.getParent(
+    t.step_func_done(parent => {
+      assert_true(parent.isDirectory);
+      assert_false(parent.isFile);
+      assert_equals(parent.name, '');
+      assert_equals(parent.fullPath, '/');
+    }),
+    t.unreached_func('getParent() should not invoke the error callback')
+  );
+}, 'getParent() on a root-level file entry returns the root directory');
+
+async_test(t => {
+  const file = new File(['hello world'], 'test.txt', { type: 'text/plain' });
+  const dt = new DataTransfer();
+  dt.items.add(file);
+
+  const entry = dt.items[0].webkitGetAsEntry();
+  const root = entry.filesystem.root;
+
+  root.getParent(
+    t.step_func_done(parent => {
+      assert_true(parent.isDirectory, 'parent should be a directory');
+      assert_equals(parent.name, '', 'root name is empty string');
+      assert_equals(parent.fullPath, '/', 'parent of root is root itself');
+    }),
+    t.unreached_func('getParent() should not invoke the error callback')
+  );
+}, 'getParent() on the root directory returns the root itself');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/entries-api/webkitGetAsEntry-expected.txt b/third_party/blink/web_tests/external/wpt/entries-api/webkitGetAsEntry-expected.txt
new file mode 100644
index 0000000..e79594a3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/entries-api/webkitGetAsEntry-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+[FAIL] webkitGetAsEntry() returns FileSystemFileEntry with correct attributes
+  assert_not_equals: got disallowed value null
+[FAIL] FileSystemFileEntry.file() invokes callback with a File
+  Cannot read properties of null (reading 'file')
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/entries-api/webkitGetAsEntry.html b/third_party/blink/web_tests/external/wpt/entries-api/webkitGetAsEntry.html
new file mode 100644
index 0000000..bd6f07e3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/entries-api/webkitGetAsEntry.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Entries API: DataTransferItem.webkitGetAsEntry() with script-created DataTransfer</title>
+<link rel=help href="https://wicg.github.io/entries-api/#dom-datatransferitem-webkitgetasentry">
+<link rel="author" title="Euclid Ye" href="mailto:yezhizhenjiakang@gmail.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+test(t => {
+  const dt = new DataTransfer();
+  dt.items.add('hello', 'text/plain');
+  const item = dt.items[0];
+  assert_equals(item.kind, 'string');
+  assert_equals(item.webkitGetAsEntry(), null,
+    'webkitGetAsEntry() should return null for non-File items');
+}, 'webkitGetAsEntry() returns null for text items');
+
+test(t => {
+  const file = new File(['hello world'], 'test.txt', { type: 'text/plain' });
+  const dt = new DataTransfer();
+  dt.items.add(file);
+
+  const item = dt.items[0];
+  assert_equals(item.kind, 'file');
+
+  const entry = item.webkitGetAsEntry();
+  assert_not_equals(entry, null);
+  assert_true(entry.isFile);
+  assert_false(entry.isDirectory);
+  assert_equals(entry.name, 'test.txt');
+  assert_equals(entry.fullPath, '/test.txt');
+  assert_true(entry.filesystem instanceof FileSystem);
+  assert_true(entry.filesystem.root.isDirectory);
+}, 'webkitGetAsEntry() returns FileSystemFileEntry with correct attributes');
+
+async_test(t => {
+  const file = new File(['hello world'], 'test.txt', { type: 'text/plain' });
+  const dt = new DataTransfer();
+  dt.items.add(file);
+
+  const entry = dt.items[0].webkitGetAsEntry();
+
+  entry.file(
+    t.step_func_done(f => {
+      assert_class_string(f, 'File');
+      assert_equals(f.name, 'test.txt');
+      assert_equals(f.size, 11);
+    }),
+    t.unreached_func('file() should not invoke the error callback')
+  );
+}, 'FileSystemFileEntry.file() invokes callback with a File');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/compression-dictionary/compressed-large-resources-002.tentative.https.html b/third_party/blink/web_tests/external/wpt/fetch/compression-dictionary/compressed-large-resources-002.tentative.https.html
index 8b38442..6d0b32a 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/compression-dictionary/compressed-large-resources-002.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/fetch/compression-dictionary/compressed-large-resources-002.tentative.https.html
@@ -32,15 +32,13 @@
       // Append the compressed subframe.
       const iframe = document.createElement("iframe");
       iframe.src = `./resources/static/${config.subframe_compressed}`;
-      window.iframeMessageReceived = false;
-      function iframeListener(event) {
-        window.iframeMessageReceived = (event.data === "ready");
-      }
-      window.addEventListener("message", iframeListener);
-      t.add_cleanup(_ => {
-        iframe.remove();
-        window.iframeMessageReceived = undefined;
-        window.removeEventListener("message", iframeListener);
+      const waitMessageFromIframe = new Promise(resolve => {
+        function iframeListener(event) { resolve(event.data === "ready"); }
+        window.addEventListener("message", iframeListener, {once: true});
+        t.add_cleanup(_ =>  {
+          iframe.remove();
+          window.removeEventListener("message", iframeListener);
+        });
       });
       await new Promise((resolve, reject) => {
         iframe.onload = resolve;
@@ -48,8 +46,8 @@
         document.body.appendChild(iframe);
       });
 
-      // Verify script in HTML page  was executed.
-      assert_true(window.iframeMessageReceived);
+      // Verify script in HTML page was executed.
+      assert_true(await waitMessageFromIframe, "iframe posted its ready message");
     }, `Loading ${config.subframe_compressed} using ${config.subframe_dictionary} as a dictionary works`);
   });
 });
diff --git a/third_party/blink/web_tests/external/wpt/fetch/compression-dictionary/dictionary-fetch-with-link-connect-src-nonce.tentative.https.html.headers b/third_party/blink/web_tests/external/wpt/fetch/compression-dictionary/dictionary-fetch-with-link-connect-src-nonce.tentative.https.html.headers
index 97f2d0d..df072f9 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/compression-dictionary/dictionary-fetch-with-link-connect-src-nonce.tentative.https.html.headers
+++ b/third_party/blink/web_tests/external/wpt/fetch/compression-dictionary/dictionary-fetch-with-link-connect-src-nonce.tentative.https.html.headers
@@ -1,2 +1,3 @@
-Content-Security-Policy: connect-src 'nonce-abc'
+Reporting-Endpoints: dummy-endpoint="/reporting/resources/report.py?reportID=6d7c97ca-8fe0-11f1-ad1d-47a790fa3ab2"
+Content-Security-Policy: connect-src 'none'; report-to dummy-endpoint
 Link: </registration?missingNonceFromHeader> ; rel="compression-dictionary"
diff --git a/third_party/blink/web_tests/external/wpt/fetch/compression-dictionary/dictionary-fetch-with-link-connect-src.tentative.https.html.headers b/third_party/blink/web_tests/external/wpt/fetch/compression-dictionary/dictionary-fetch-with-link-connect-src.tentative.https.html.headers
index 40a6b3f..b621e20 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/compression-dictionary/dictionary-fetch-with-link-connect-src.tentative.https.html.headers
+++ b/third_party/blink/web_tests/external/wpt/fetch/compression-dictionary/dictionary-fetch-with-link-connect-src.tentative.https.html.headers
@@ -1,3 +1,4 @@
-Content-Security-Policy: connect-src *:*/allowedRegistration
+Reporting-Endpoints: dummy-endpoint="/reporting/resources/report.py?reportID=6d7c5a9e-8fe0-11f1-930d-5f24093adacc"
+Content-Security-Policy: connect-src *:*/allowedRegistration; report-to dummy-endpoint
 Link: </blockedRegistration?blockedFromHeader> ; rel="compression-dictionary"
 Link: </allowedRegistration?allowedFromHeader> ; rel="compression-dictionary"
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/sandboxing/sandbox-disallow-forms.html b/third_party/blink/web_tests/external/wpt/html/browsers/sandboxing/sandbox-disallow-forms.html
new file mode 100644
index 0000000..7c925a9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/sandboxing/sandbox-disallow-forms.html
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Form submission in an iframe sandboxed without allow-forms</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#sandboxed-forms-browsing-context-flag">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<body>
+<div id="log"></div>
+<script>
+const STASH_PUT = `${location.origin}/fetch/api/resources/stash-put.py`;
+
+function takeStash(key) {
+  return fetch(`/fetch/api/resources/stash-take.py?key=${key}`)
+      .then(response => response.json());
+}
+
+// The stash entry only appears once the submission reaches the server, so poll
+// for it rather than guessing how long that takes.
+async function waitForStash(t, key) {
+  for (let i = 0; i < 20; i++) {
+    const value = await takeStash(key);
+    if (value !== null) {
+      return value;
+    }
+    await new Promise(resolve => t.step_timeout(resolve, 100));
+  }
+  return null;
+}
+
+// A GET submission replaces the action's query string with the entry list, so
+// the key stash-put.py reads has to travel as form data. The target is the
+// sandboxed document's own child iframe, so a wrongly performed submission
+// doesn't tear down the document that reports back.
+const MAKE_FORM = `
+  function makeForm(doc, key) {
+    const form = doc.createElement("form");
+    form.method = "get";
+    form.action = "${STASH_PUT}";
+    form.target = "target";
+    for (const [name, value] of [["key", key], ["value", "submitted"]]) {
+      const input = doc.createElement("input");
+      input.type = "hidden";
+      input.name = name;
+      input.value = value;
+      form.appendChild(input);
+    }
+    const button = doc.createElement("button");
+    button.type = "submit";
+    form.appendChild(button);
+    doc.body.appendChild(form);
+    return { form, button };
+  }
+`;
+
+function runInSandboxedFrame(t, sandbox, script) {
+  const iframe = document.createElement("iframe");
+  iframe.sandbox = sandbox;
+  t.add_cleanup(() => iframe.remove());
+
+  const done = new Promise(resolve => {
+    window.addEventListener("message", function onMessage(event) {
+      if (event.source === iframe.contentWindow && event.data === "done") {
+        window.removeEventListener("message", onMessage);
+        resolve();
+      }
+    });
+  });
+
+  iframe.srcdoc = `
+    <iframe name="target"></iframe>
+    <script>
+      ${MAKE_FORM}
+      ${script}
+      parent.postMessage("done", "*");
+    <\/script>
+  `;
+  document.body.appendChild(iframe);
+  return done;
+}
+
+promise_test(async t => {
+  const key = token();
+  await runInSandboxedFrame(t, "allow-scripts allow-forms", `
+    makeForm(document, "${key}").form.submit();
+  `);
+  assert_equals(await waitForStash(t, key), "submitted");
+}, "A document sandboxed with allow-forms can submit a form");
+
+promise_test(async t => {
+  const key = token();
+  await runInSandboxedFrame(t, "allow-scripts", `
+    makeForm(document, "${key}").form.submit();
+  `);
+  await new Promise(resolve => t.step_timeout(resolve, 1000));
+  assert_equals(await takeStash(key), null);
+}, "A document sandboxed without allow-forms cannot submit a form");
+
+promise_test(async t => {
+  const key = token();
+  // A data document has no sandbox flags of its own, so a submission can be
+  // built there and the form then moved into the sandboxed document.
+  await runInSandboxedFrame(t, "allow-scripts", `
+    const dataDoc = document.implementation.createHTMLDocument("");
+    const { form, button } = makeForm(dataDoc, "${key}");
+    button.click();
+    form.submit();
+
+    document.body.appendChild(document.adoptNode(form));
+    button.click();
+  `);
+  await new Promise(resolve => t.step_timeout(resolve, 1000));
+  assert_equals(await takeStash(key), null);
+}, "A document sandboxed without allow-forms cannot perform a form submission " +
+   "built in a data document");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/tables/table-border-attributes-computed-style-expected.txt b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/tables/table-border-attributes-computed-style-expected.txt
new file mode 100644
index 0000000..c1b365a4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/tables/table-border-attributes-computed-style-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+[FAIL] adding bordercolor does not change which borders exist
+  assert_equals: table with border=1 and bordercolor: border-top-style expected "outset" but got "solid"
+[FAIL] setting rules=rows leaves the cells with no border
+  assert_equals: cell of a table with rules=rows: border-top-width expected "0px" but got "1px"
+[FAIL] setting rules=rows puts the rule on the row
+  assert_equals: row border-block-start-style expected "solid" but got "none"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/tables/table-border-attributes-computed-style.html b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/tables/table-border-attributes-computed-style.html
new file mode 100644
index 0000000..fbba6ed3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/tables/table-border-attributes-computed-style.html
@@ -0,0 +1,145 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Table border and bordercolor attributes: computed borders, including when they change</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#tables-2">
+<link rel="author" title="Karl Dubost" href="mailto:karlcow@apple.com">
+<link rel="author" title="Apple" href="https://www.apple.com/">
+<meta name="assert" content="A border colour on its own never creates a visible border; the border attribute creates an outset border on the table and inset borders on the cells; adding bordercolor does not change which borders exist.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+  Two things are checked here, in one table that is mutated step by step so that
+  the effect of each attribute is isolated.
+
+  1. A colour alone must not produce a border. Neither the bordercolor content
+     attribute nor the border-color CSS property sets a border style or width, and
+     the initial border style is none, so there is nothing to paint. There is
+     already a reftest for the rendering of this (table-bordercolor-001.html);
+     this test pins the computed values, and pins that adding bordercolor to a
+     table that already has borders does not alter which borders exist.
+
+  2. The border attribute:
+
+       table[border] { border-style: outset; }        /* only if not zero */
+       table[border] > tbody > tr > td, ... {
+         border-width: 1px;
+         border-style: inset;
+       }
+
+     and then what happens when rules is added on top of it, which replaces the
+     cells' inset border.
+
+  border="1" is used rather than border="" throughout: the UA stylesheet entry is
+  annotated "only if border is not equivalent to zero", and whether the empty
+  string counts as zero is not something this test takes a position on.
+
+  The table and the cell each carry their own inline border-color so that the
+  colour assertions test the presentational hints rather than inheritance.
+-->
+
+<div id="log"></div>
+<div id="container"></div>
+
+<script>
+const YELLOW = "rgb(255, 255, 0)";
+const RED = "rgb(255, 0, 0)";
+
+const table = document.createElement("table");
+table.setAttribute("style", "border-color: yellow");
+const row = table.insertRow(-1);
+const cell = row.insertCell(-1);
+cell.setAttribute("style", "border-color: red");
+document.getElementById("container").append(table);
+
+const SIDES = ["top", "right", "bottom", "left"];
+
+function assertUniformBorder(element, width, style, label) {
+  const computed = getComputedStyle(element);
+  for (const side of SIDES) {
+    assert_equals(computed.getPropertyValue(`border-${side}-width`), width,
+                  `${label}: border-${side}-width`);
+    assert_equals(computed.getPropertyValue(`border-${side}-style`), style,
+                  `${label}: border-${side}-style`);
+  }
+}
+
+function assertBorderColor(element, color, label) {
+  const computed = getComputedStyle(element);
+  for (const side of SIDES) {
+    assert_equals(computed.getPropertyValue(`border-${side}-color`), color,
+                  `${label}: border-${side}-color`);
+  }
+}
+
+// A colour with no style and no width paints nothing.
+test(() => {
+  assertUniformBorder(table, "0px", "none", "table with only border-color");
+  assertBorderColor(table, YELLOW, "table");
+}, "border-color on its own gives the table no border");
+
+test(() => {
+  assertUniformBorder(cell, "0px", "none", "cell with only border-color");
+  assertBorderColor(cell, RED, "cell");
+}, "border-color on its own gives the cell no border");
+
+// The border attribute is what actually creates the borders.
+test(() => {
+  table.setAttribute("border", "1");
+  assertUniformBorder(table, "1px", "outset", "table with border=1");
+  assertBorderColor(table, YELLOW, "table");
+}, "the border attribute gives the table an outset border");
+
+test(() => {
+  assertUniformBorder(cell, "1px", "inset", "cell of a table with border=1");
+  assertBorderColor(cell, RED, "cell");
+}, "the border attribute gives the cells an inset border");
+
+// Adding a colour must not change which borders exist.
+test(() => {
+  table.setAttribute("bordercolor", "green");
+  assertUniformBorder(table, "1px", "outset", "table with border=1 and bordercolor");
+  assertUniformBorder(cell, "1px", "inset", "cell with border=1 and bordercolor");
+}, "adding bordercolor does not change which borders exist");
+
+// rules replaces the cells' inset border. Under rules=cols the rule belongs to
+// the cells' inline edges; the block edges get nothing.
+test(() => {
+  table.rules = "cols";
+  const computed = getComputedStyle(cell);
+  assert_equals(computed.borderInlineStartStyle, "solid", "cell border-inline-start-style");
+  assert_equals(computed.borderInlineEndStyle, "solid", "cell border-inline-end-style");
+  assert_equals(computed.borderInlineStartWidth, "1px", "cell border-inline-start-width");
+  assert_equals(computed.borderInlineEndWidth, "1px", "cell border-inline-end-width");
+  assert_equals(computed.borderBlockStartStyle, "none", "cell border-block-start-style");
+  assert_equals(computed.borderBlockEndStyle, "none", "cell border-block-end-style");
+  assert_equals(computed.borderBlockStartWidth, "0px", "cell border-block-start-width");
+  assert_equals(computed.borderBlockEndWidth, "0px", "cell border-block-end-width");
+}, "setting rules=cols replaces the cells' inset border with a rule on their inline edges");
+
+// Under rules=rows the rule moves off the cells entirely and onto the row, even
+// though the border attribute is still present.
+test(() => {
+  table.rules = "rows";
+  assertUniformBorder(cell, "0px", "none", "cell of a table with rules=rows");
+  assertBorderColor(cell, RED, "cell");
+}, "setting rules=rows leaves the cells with no border");
+
+test(() => {
+  const computed = getComputedStyle(row);
+  assert_equals(computed.borderBlockStartStyle, "solid", "row border-block-start-style");
+  assert_equals(computed.borderBlockEndStyle, "solid", "row border-block-end-style");
+  assert_equals(computed.borderBlockStartWidth, "1px", "row border-block-start-width");
+  assert_equals(computed.borderBlockEndWidth, "1px", "row border-block-end-width");
+  assert_equals(computed.borderInlineStartStyle, "none", "row border-inline-start-style");
+  assert_equals(computed.borderInlineEndStyle, "none", "row border-inline-end-style");
+}, "setting rules=rows puts the rule on the row");
+
+// With rules still set and the border attribute gone, the table's own border
+// becomes hidden so that it wins over any border set on the cells.
+test(() => {
+  table.removeAttribute("border");
+  assertUniformBorder(table, "0px", "hidden", "table with rules=rows and no border attribute");
+  assertBorderColor(table, YELLOW, "table");
+}, "removing the border attribute while rules is set hides the table's own border");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/tables/table-rules-computed-style-expected.txt b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/tables/table-rules-computed-style-expected.txt
new file mode 100644
index 0000000..7da870a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/tables/table-rules-computed-style-expected.txt
@@ -0,0 +1,17 @@
+This is a testharness.js-based test.
+[FAIL] rules=rows gives the cells the expected border
+  assert_equals: td: border-block-start-style expected "none" but got "solid"
+[FAIL] rules=rows gives the rows the expected border
+  assert_equals: tbody > tr: border-block-start-style expected "solid" but got "none"
+[FAIL] rules=rows applies to a row that is a direct child of the table
+  assert_equals: table > tr: border-block-start-style expected "solid" but got "none"
+[FAIL] rules=ROWS is matched ASCII case-insensitively
+  assert_equals: tbody > tr: border-block-start-style expected "solid" but got "none"
+[FAIL] rules=rows follows the writing mode: the rule is on the row's block edges
+  assert_equals: tbody > tr in vertical-rl: border-block-start-style expected "solid" but got "none"
+[FAIL] rules=cols follows the writing mode: the rule is on the cells' inline edges
+  assert_equals: td in vertical-rl: border-block-start-style expected "none" but got "solid"
+[FAIL] rules=groups follows the writing mode: the rules are on the groups' block and inline edges
+  assert_equals: tbody in vertical-rl: border-block-start-style expected "solid" but got "none"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/tables/table-rules-computed-style.html b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/tables/table-rules-computed-style.html
new file mode 100644
index 0000000..20c8a25
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/tables/table-rules-computed-style.html
@@ -0,0 +1,262 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Table rules attribute: computed borders on the table, row groups, rows, cells and column groups</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#tables-2">
+<link rel="author" title="Karl Dubost" href="mailto:karlcow@apple.com">
+<link rel="author" title="Apple" href="https://www.apple.com/">
+<meta name="assert" content="The rules attribute puts the rule between rows on the row itself and the rule between columns on the cells, and gives the cells no border where the rule is not theirs.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!--
+  The rendering section gives the rules attribute five presentational hints. The
+  part that is easy to get wrong, and that no test covered before this one, is
+  WHICH ELEMENT owns each rule:
+
+    table[rules=rows i] > tr, table[rules=rows i] > thead > tr,
+    table[rules=rows i] > tbody > tr, table[rules=rows i] > tfoot > tr {
+      border-block-width: 1px;
+      border-block-style: solid;
+    }
+
+  while the cells of rules=none, rules=groups and rules=rows are explicitly
+  given no border:
+
+    ... table[rules=rows i] > tbody > tr > td, ... {
+      border-width: 1px;
+      border-style: none;
+    }
+
+  so under rules=rows the horizontal rule belongs to the ROW and the cells have
+  no border, whereas under rules=cols the vertical rule belongs to the CELLS.
+
+  Two things worth knowing before reading the expectations below:
+
+  * The spec says border-width: 1px even on sides whose style is none. A computed
+    border width is 0px whenever the style is none, so those sides are asserted
+    as 0px. That is not a contradiction with the spec text.
+  * The rules are written with the flow-relative border properties
+    (border-block-*, border-inline-*), so they follow the writing mode. These
+    tests read the same flow-relative properties, and the vertical-rl section at
+    the end pins the mapping explicitly.
+-->
+
+<div id="log"></div>
+<div id="container"></div>
+
+<script>
+const SOLID = { style: "solid", width: "1px" };
+const NONE = { style: "none", width: "0px" };
+
+// Build an expectation for all four flow-relative sides.
+function borders(block, inline) {
+  return {
+    "block-start": block, "block-end": block,
+    "inline-start": inline, "inline-end": inline,
+  };
+}
+const NO_BORDER = borders(NONE, NONE);
+const BLOCK_ONLY = borders(SOLID, NONE);
+const INLINE_ONLY = borders(NONE, SOLID);
+const ALL_SIDES = borders(SOLID, SOLID);
+
+function assertBorders(element, expected, label) {
+  const style = getComputedStyle(element);
+  for (const side of ["block-start", "block-end", "inline-start", "inline-end"]) {
+    assert_equals(style.getPropertyValue(`border-${side}-style`), expected[side].style,
+                  `${label}: border-${side}-style`);
+    assert_equals(style.getPropertyValue(`border-${side}-width`), expected[side].width,
+                  `${label}: border-${side}-width`);
+  }
+}
+
+const container = document.getElementById("container");
+
+// One table per case, with a column group, all three row groups, and both cell
+// element types. Nothing here styles the elements under test, and the cells are
+// deliberately empty: these tests only read computed values, and text in the
+// cells would otherwise be dumped into implementations' text baselines.
+function buildTable(rulesAttribute) {
+  const html = `
+    <table${rulesAttribute}>
+      <colgroup><col><col></colgroup>
+      <thead><tr><th></th><th></th></tr></thead>
+      <tbody><tr><td></td><td></td></tr></tbody>
+      <tfoot><tr><td></td><td></td></tr></tfoot>
+    </table>`;
+  const host = document.createElement("div");
+  host.innerHTML = html;
+  container.append(host);
+  const table = host.querySelector("table");
+  return {
+    table,
+    colgroup: host.querySelector("colgroup"),
+    thead: host.querySelector("thead"),
+    tbody: host.querySelector("tbody"),
+    tfoot: host.querySelector("tfoot"),
+    headerRow: host.querySelector("thead > tr"),
+    bodyRow: host.querySelector("tbody > tr"),
+    footerRow: host.querySelector("tfoot > tr"),
+    th: host.querySelector("th"),
+    td: host.querySelector("tbody td"),
+  };
+}
+
+// The rule between rows lives on the row; the rule between columns lives on the
+// cells; the rule between groups lives on the row groups and column groups.
+const CASES = [
+  {
+    value: "none",
+    cells: NO_BORDER, rows: NO_BORDER, rowGroups: NO_BORDER, columnGroups: NO_BORDER,
+  },
+  {
+    value: "groups",
+    cells: NO_BORDER, rows: NO_BORDER, rowGroups: BLOCK_ONLY, columnGroups: INLINE_ONLY,
+  },
+  {
+    value: "rows",
+    cells: NO_BORDER, rows: BLOCK_ONLY, rowGroups: NO_BORDER, columnGroups: NO_BORDER,
+  },
+  {
+    value: "cols",
+    cells: INLINE_ONLY, rows: NO_BORDER, rowGroups: NO_BORDER, columnGroups: NO_BORDER,
+  },
+  {
+    value: "all",
+    cells: ALL_SIDES, rows: NO_BORDER, rowGroups: NO_BORDER, columnGroups: NO_BORDER,
+  },
+];
+
+for (const testCase of CASES) {
+  const value = testCase.value;
+
+  test(() => {
+    const parts = buildTable(` rules="${value}"`);
+    // table[rules=...] { border-style: hidden; border-collapse: collapse; }
+    assert_equals(getComputedStyle(parts.table).borderCollapse, "collapse",
+                  "border-collapse on the table");
+    for (const side of ["top", "right", "bottom", "left"]) {
+      assert_equals(getComputedStyle(parts.table).getPropertyValue(`border-${side}-style`),
+                    "hidden", `border-${side}-style on the table`);
+    }
+  }, `rules=${value} collapses the table's borders and hides its own border`);
+
+  test(() => {
+    const parts = buildTable(` rules="${value}"`);
+    assertBorders(parts.td, testCase.cells, "td");
+    assertBorders(parts.th, testCase.cells, "th");
+  }, `rules=${value} gives the cells the expected border`);
+
+  test(() => {
+    const parts = buildTable(` rules="${value}"`);
+    assertBorders(parts.bodyRow, testCase.rows, "tbody > tr");
+    assertBorders(parts.headerRow, testCase.rows, "thead > tr");
+    assertBorders(parts.footerRow, testCase.rows, "tfoot > tr");
+  }, `rules=${value} gives the rows the expected border`);
+
+  test(() => {
+    const parts = buildTable(` rules="${value}"`);
+    assertBorders(parts.thead, testCase.rowGroups, "thead");
+    assertBorders(parts.tbody, testCase.rowGroups, "tbody");
+    assertBorders(parts.tfoot, testCase.rowGroups, "tfoot");
+  }, `rules=${value} gives the row groups the expected border`);
+
+  test(() => {
+    const parts = buildTable(` rules="${value}"`);
+    assertBorders(parts.colgroup, testCase.columnGroups, "colgroup");
+  }, `rules=${value} gives the column group the expected border`);
+}
+
+// table[rules=rows i] > tr is its own selector in the spec. The parser always
+// inserts a tbody, so this shape has to be built with DOM calls.
+test(() => {
+  const table = document.createElement("table");
+  table.setAttribute("rules", "rows");
+  const row = document.createElement("tr");
+  const cell = document.createElement("td");
+  row.append(cell);
+  table.append(row);
+  container.append(table);
+
+  assertBorders(row, BLOCK_ONLY, "table > tr");
+  assertBorders(cell, NO_BORDER, "table > tr > td");
+}, "rules=rows applies to a row that is a direct child of the table");
+
+// The attribute value is matched ASCII case-insensitively.
+test(() => {
+  const parts = buildTable(` rules="ROWS"`);
+  assert_equals(getComputedStyle(parts.table).borderCollapse, "collapse");
+  assertBorders(parts.bodyRow, BLOCK_ONLY, "tbody > tr");
+  assertBorders(parts.td, NO_BORDER, "td");
+}, "rules=ROWS is matched ASCII case-insensitively");
+
+// Case-insensitive matching is ASCII-only, so U+017F LATIN SMALL LETTER LONG S
+// must not match the "s" of "rows".
+for (const { label, attribute } of [
+  { label: "an unrecognised value", attribute: ` rules="rowz"` },
+  { label: "U+017F, which is not an ASCII case match for s", attribute: ` rules="rowſ"` },
+  { label: "the empty string", attribute: ` rules=""` },
+  { label: "no value", attribute: ` rules` },
+  { label: "no attribute at all", attribute: `` },
+]) {
+  test(() => {
+    const parts = buildTable(attribute);
+    assert_equals(getComputedStyle(parts.table).borderCollapse, "separate",
+                  "border-collapse on the table");
+    for (const side of ["top", "right", "bottom", "left"]) {
+      assert_equals(getComputedStyle(parts.table).getPropertyValue(`border-${side}-style`),
+                    "none", `border-${side}-style on the table`);
+    }
+    assertBorders(parts.bodyRow, NO_BORDER, "tbody > tr");
+    assertBorders(parts.td, NO_BORDER, "td");
+    assertBorders(parts.tbody, NO_BORDER, "tbody");
+    assertBorders(parts.colgroup, NO_BORDER, "colgroup");
+  }, `rules with ${label} sets no rule`);
+}
+
+// The hints use the flow-relative properties, so in a vertical writing mode the
+// rule between rows runs down the page rather than across it. Reading the
+// physical properties as well pins which way round the mapping goes: in
+// vertical-rl the block axis is horizontal, so block-start is the right edge.
+test(() => {
+  const parts = buildTable(` rules="rows"`);
+  parts.table.style.writingMode = "vertical-rl";
+
+  assertBorders(parts.bodyRow, BLOCK_ONLY, "tbody > tr in vertical-rl");
+
+  const rowStyle = getComputedStyle(parts.bodyRow);
+  assert_equals(rowStyle.borderRightStyle, "solid", "physical right edge is the block-start edge");
+  assert_equals(rowStyle.borderLeftStyle, "solid", "physical left edge is the block-end edge");
+  assert_equals(rowStyle.borderTopStyle, "none", "physical top edge is an inline edge");
+  assert_equals(rowStyle.borderBottomStyle, "none", "physical bottom edge is an inline edge");
+}, "rules=rows follows the writing mode: the rule is on the row's block edges");
+
+test(() => {
+  const parts = buildTable(` rules="cols"`);
+  parts.table.style.writingMode = "vertical-rl";
+
+  assertBorders(parts.td, INLINE_ONLY, "td in vertical-rl");
+
+  const cellStyle = getComputedStyle(parts.td);
+  assert_equals(cellStyle.borderTopStyle, "solid", "physical top edge is the inline-start edge");
+  assert_equals(cellStyle.borderBottomStyle, "solid", "physical bottom edge is the inline-end edge");
+  assert_equals(cellStyle.borderRightStyle, "none", "physical right edge is a block edge");
+  assert_equals(cellStyle.borderLeftStyle, "none", "physical left edge is a block edge");
+}, "rules=cols follows the writing mode: the rule is on the cells' inline edges");
+
+test(() => {
+  const parts = buildTable(` rules="groups"`);
+  parts.table.style.writingMode = "vertical-rl";
+
+  assertBorders(parts.tbody, BLOCK_ONLY, "tbody in vertical-rl");
+  assertBorders(parts.colgroup, INLINE_ONLY, "colgroup in vertical-rl");
+
+  const groupStyle = getComputedStyle(parts.tbody);
+  assert_equals(groupStyle.borderRightStyle, "solid", "row group: physical right edge is the block-start edge");
+  assert_equals(groupStyle.borderTopStyle, "none", "row group: physical top edge is an inline edge");
+
+  const columnGroupStyle = getComputedStyle(parts.colgroup);
+  assert_equals(columnGroupStyle.borderTopStyle, "solid", "column group: physical top edge is the inline-start edge");
+  assert_equals(columnGroupStyle.borderRightStyle, "none", "column group: physical right edge is a block edge");
+}, "rules=groups follows the writing mode: the rules are on the groups' block and inline edges");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-submit-action.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-submit-action.html
new file mode 100644
index 0000000..16c114b1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/form-submit-action.html
@@ -0,0 +1,82 @@
+<!doctype html>
+<meta charset="utf-8" />
+<title>button.action</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fs-action" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/form-with-submitter.js"></script>
+<body>
+    <script>
+        "use strict";
+
+        const test_cases = [
+            {
+                name: "formaction specified, expecting to navigate to formaction",
+                formAction: null,
+                submitterFormAction: "a.html",
+                formOwnerAction: null,
+                expected: "a.html",
+            },
+            {
+                name: "no formaction, expect to navigate to document URL (base URL in this case)",
+                formAction: "a.html",
+                submitterFormAction: "",
+                formOwnerAction: null,
+                expected: null,
+            },
+            {
+                name: "fallback to form action",
+                formAction: "a.html",
+                submitterFormAction: null,
+                formOwnerAction: null,
+                expected: "a.html",
+            },
+            {
+                name: "missing form action",
+                formAction: null,
+                submitterFormAction: null,
+                formOwnerAction: null,
+                expected: null,
+            },
+            {
+                name: "form owner is different from parent form",
+                formAction: null,
+                submitterFormAction: null,
+                formOwnerAction: "a.html",
+                expected: "a.html",
+            },
+        ];
+
+        for (const elementType of ["button", "input"]) {
+            let id = 1;
+            for (const testCase of test_cases) {
+                async_test((t) => {
+                    if (testCase.formOwnerAction !== null) {
+                        id++;
+                    }
+                    let frame = populateForm(
+                        id,
+                        elementType,
+                        testCase.formAction,
+                        testCase.submitterFormAction,
+                        testCase.formOwnerAction
+                    );
+                    frame.addEventListener(
+                        "load",
+                        t.step_func_done(() => {
+                            let expected = new URL(document.URL);
+                            if (testCase.expected !== null) {
+                                expected = new URL(testCase.expected, location.href);
+                            }
+
+                            assert_equals(new URL(frame.contentWindow.location.href).pathname, expected.pathname);
+                        })
+                    );
+
+                    document.getElementById(`submit-${elementType}-${id}`).click();
+                }, `${elementType} - ${testCase.name}`);
+                id++;
+            }
+        }
+    </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/resources/form-with-submitter.js b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/resources/form-with-submitter.js
new file mode 100644
index 0000000..af01441
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/resources/form-with-submitter.js
@@ -0,0 +1,40 @@
+function populateForm(testId, elementType, formAction, submitterFormAction, formOwnerAction) {
+  const frame = document.createElement("iframe");
+  const form = document.createElement("form");
+  const submitter = document.createElement(elementType);
+  let formOwner = null;
+  frame.name = `form-test-frame-${elementType}-${testId}`;
+  form.id = `form-test-${elementType}-${testId}`;
+  submitter.id = `submit-${elementType}-${testId}`;
+
+  if (formOwnerAction !== null) {
+    formOwner = document.createElement("form");
+    formOwner.target = frame.name;
+    formOwner.id = `form-test-${elementType}-${testId-1}`;
+    formOwner.setAttribute("action", formOwnerAction);
+    submitter.setAttribute(
+      "form",
+      formOwner.id
+    );
+    document.body.append(formOwner);
+  } else {
+    form.target = frame.name;
+  }
+
+  if (formAction !== null) {
+    form.setAttribute("action", formAction);
+  }
+
+  submitter.type = "submit";
+
+  if (submitterFormAction !== null) {
+    submitter.setAttribute(
+      "formaction",
+      submitterFormAction
+    );
+  }
+
+  form.appendChild(submitter);
+  document.body.append(frame, form);
+  return frame;
+}
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/submit-button-clicked-in-data-document.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/submit-button-clicked-in-data-document.html
new file mode 100644
index 0000000..e42c59c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/submit-button-clicked-in-data-document.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Clicking a submit button in a document without a browsing context</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/targetted-form.js"></script>
+<body>
+<div id="log"></div>
+<script>
+promise_test(async t => {
+  const frameName = "data-document-submit-target";
+  const iframe = document.createElement("iframe");
+  iframe.name = frameName;
+  document.body.appendChild(iframe);
+  t.add_cleanup(() => iframe.remove());
+
+  const dataDoc = document.implementation.createHTMLDocument("");
+  const form = dataDoc.createElement("form");
+  form.action = new URL("/common/blank.html", location.href).href;
+  form.target = frameName;
+  const button = dataDoc.createElement("button");
+  button.type = "submit";
+  form.appendChild(button);
+  dataDoc.body.appendChild(form);
+
+  // Cannot submit anything, and must not leave the form deferring the
+  // submissions that come after it.
+  button.click();
+
+  document.body.appendChild(document.adoptNode(form));
+  t.add_cleanup(() => form.remove());
+
+  const loaded = loadPromise(iframe);
+  form.submit();
+  await loaded;
+}, "A form is still submittable after its submit button was clicked while the " +
+   "form was in a data document");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent.html
index cb84109c..71c47460 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent.html
@@ -171,4 +171,56 @@
   assert_equals(selectedcontent.textContent, 'two',
     '<selectedcontent> should be updated when <select> is connected to the document.');
 }, '<seletedcontent> behavior in disconnected <select>.');
+
+function makeOptionSelectedSelect() {
+  const select = document.createElement('select');
+  select.innerHTML = `
+    <button><selectedcontent></selectedcontent></button>
+    <option class=one><span class=one>span</span> one</option>
+    <option class=two><span class=two>span</span> two</option>`;
+  document.body.appendChild(select);
+  return select;
+}
+
+test(() => {
+  const select = makeOptionSelectedSelect();
+  const optionTwo = select.querySelector('option.two');
+  const selectedcontent = select.querySelector('selectedcontent');
+
+  assert_equals(selectedcontent.innerHTML, select.querySelector('option.one').innerHTML,
+    'The <selectedcontent> should initially reflect the selected <option>.');
+
+  optionTwo.selected = true;
+  assert_equals(selectedcontent.innerHTML, optionTwo.innerHTML,
+    'Setting HTMLOptionElement.selected should synchronously update the <selectedcontent>.');
+}, 'Setting HTMLOptionElement.selected updates the <selectedcontent>.');
+
+test(() => {
+  const select = makeOptionSelectedSelect();
+  const optionTwo = select.querySelector('option.two');
+  const selectedcontent = select.querySelector('selectedcontent');
+
+  optionTwo.setAttribute('selected', '');
+  assert_equals(selectedcontent.innerHTML, optionTwo.innerHTML,
+    'Setting the selected content attribute should synchronously update the <selectedcontent>.');
+}, 'Setting the option selected attribute updates the <selectedcontent>.');
+
+promise_test(async () => {
+  const select = makeOptionSelectedSelect();
+  const optionTwo = select.querySelector('option.two');
+  const selectedcontent = select.querySelector('selectedcontent');
+  let mutationCount = 0;
+  const observer = new MutationObserver(() => mutationCount++);
+  observer.observe(selectedcontent, {childList: true, subtree: true});
+
+  optionTwo.selected = true;
+  assert_equals(selectedcontent.innerHTML, optionTwo.innerHTML,
+    'The <selectedcontent> should be updated synchronously.');
+
+  // Wait a task to ensure no redundant async update fires.
+  await new Promise(resolve => step_timeout(resolve, 0));
+  assert_equals(mutationCount, 1,
+    'Only one mutation batch should fire (the synchronous update), not a redundant async one.');
+  observer.disconnect();
+}, 'Setting HTMLOptionElement.selected does not cause a redundant async selectedcontent update.');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-hint-dialog-dismisses-unrelated-auto-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-hint-dialog-dismisses-unrelated-auto-expected.txt
new file mode 100644
index 0000000..afd4ed3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-hint-dialog-dismisses-unrelated-auto-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+[FAIL] dialog.show() nested in a hint dismisses unrelated auto popovers but keeps the ancestor hint
+  assert_false: the unrelated auto must be dismissed expected false got true
+[FAIL] dialog.showModal() nested in a hint dismisses unrelated auto popovers but keeps the ancestor hint
+  assert_false: the unrelated auto must be dismissed expected false got true
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-hint-dialog-dismisses-unrelated-auto.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-hint-dialog-dismisses-unrelated-auto.html
new file mode 100644
index 0000000..4c8f17dc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-hint-dialog-dismisses-unrelated-auto.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Showing a dialog nested in a hint popover dismisses unrelated auto popovers</title>
+<link rel="author" href="mailto:bfulgham@webkit.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html#popover-hint">
+<link rel=help href="https://github.com/whatwg/html/issues/12304">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div popover=hint id=hint>Hint
+  <dialog id=dialogInHint>Dialog</dialog>
+</div>
+<div popover id=unrelatedAuto>Unrelated auto</div>
+
+<script>
+  function hintSupported() {
+    const element = document.createElement('div');
+    element.setAttribute('popover', 'hint');
+    return element.popover === 'hint';
+  }
+
+  function closeAll() {
+    if (dialogInHint.open)
+      dialogInHint.close();
+    hint.hidePopover();
+    unrelatedAuto.hidePopover();
+  }
+
+  // A dialog / fullscreen element joining the top layer light-dismisses every popover that is not
+  // one of its ancestors. When the dialog is nested inside a popover=hint, the ancestor hint must
+  // be kept, but an unrelated open popover=auto elsewhere must still be dismissed. These use the
+  // same Document::hidePopoversForTopLayerElement() path, so dialog show() / showModal() cover the
+  // fullscreen case as well.
+  function testDialog(showDialog, description) {
+    promise_test(async t => {
+      assert_implements_optional(hintSupported(), 'popover=hint is not supported');
+      t.add_cleanup(closeAll);
+
+      // Show the auto first: showing an auto closes all hints, so the hint must be shown last.
+      unrelatedAuto.showPopover();
+      hint.showPopover();
+      assert_true(unrelatedAuto.matches(':popover-open'), 'unrelated auto is open');
+      assert_true(hint.matches(':popover-open'), 'ancestor hint is open');
+
+      showDialog();
+      assert_true(dialogInHint.open, 'dialog is open');
+      assert_true(hint.matches(':popover-open'), 'the dialog\'s ancestor hint must stay open');
+      assert_false(unrelatedAuto.matches(':popover-open'), 'the unrelated auto must be dismissed');
+    }, description);
+  }
+
+  testDialog(() => dialogInHint.show(),
+    'dialog.show() nested in a hint dismisses unrelated auto popovers but keeps the ancestor hint');
+  testDialog(() => dialogInHint.showModal(),
+    'dialog.showModal() nested in a hint dismisses unrelated auto popovers but keeps the ancestor hint');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-light-dismiss-hint.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-light-dismiss-hint.html
index c21397d..25fe6ab 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-light-dismiss-hint.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-light-dismiss-hint.html
@@ -37,7 +37,7 @@
   #improperlynestedauto1 {left:100px; top:500px;}
   #hint1        {left:200px; top:100px;}
   #hint2        {left:200px; top:200px;}
-  #improperlynestedauto1 {left:200px; top:400px;}
+  #improperlynestedauto2 {left:200px; top:300px;}
   #manual1      {left:300px; top:100px;}
   #outside {width:25px;height:25px}
 </style>
@@ -95,6 +95,13 @@
   }
 
   promise_test(async (t) => {
+    const popovers = [hint1, hint2, improperlynestedauto2, manual1];
+    openall(popovers, t);
+    await clickOn(improperlynestedauto2);
+    assertState(popovers, [true, true, true, true]);
+  }, 'Clicking an auto nested inside a hint keeps the whole chain open');
+
+  promise_test(async (t) => {
     openall(setA, t);
     await clickOn(outside);
     assertState(setA, [false,false,false,false,true]);
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-manual-hide-does-not-close-auto.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-manual-hide-does-not-close-auto.html
new file mode 100644
index 0000000..eb46f00
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-manual-hide-does-not-close-auto.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Hiding a manual popover must not close open auto popovers</title>
+<link rel="author" href="mailto:bfulgham@webkit.org">
+<link rel=help href="https://html.spec.whatwg.org/multipage/popover.html#hide-popover-algorithm">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div popover id=autoPopover>Auto</div>
+<div popover=manual id=manualPopover>Manual</div>
+
+<script>
+  function closeAll() {
+    autoPopover.hidePopover();
+    manualPopover.hidePopover();
+  }
+
+  test(t => {
+    t.add_cleanup(closeAll);
+    autoPopover.showPopover();
+    manualPopover.showPopover();
+    assert_true(autoPopover.matches(':popover-open'), 'auto is open');
+    assert_true(manualPopover.matches(':popover-open'), 'manual is open');
+
+    manualPopover.hidePopover();
+    assert_false(manualPopover.matches(':popover-open'), 'manual is closed after hidePopover()');
+    assert_true(autoPopover.matches(':popover-open'), 'auto must stay open when a manual popover is hidden');
+  }, 'Hiding a manual popover shown after an auto popover does not close the auto popover');
+
+  test(t => {
+    t.add_cleanup(closeAll);
+    manualPopover.showPopover();
+    autoPopover.showPopover();
+    assert_true(autoPopover.matches(':popover-open'), 'auto is open');
+    assert_true(manualPopover.matches(':popover-open'), 'manual is open');
+
+    manualPopover.hidePopover();
+    assert_false(manualPopover.matches(':popover-open'), 'manual is closed after hidePopover()');
+    assert_true(autoPopover.matches(':popover-open'), 'auto must stay open when a manual popover is hidden');
+  }, 'Hiding a manual popover shown before an auto popover does not close the auto popover');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/text-module/repeated-imports.any.js b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/text-module/repeated-imports.any.js
index 5470231..d2e2b09 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/text-module/repeated-imports.any.js
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/text-module/repeated-imports.any.js
@@ -42,13 +42,12 @@
       import(`./serve-text-then-js.py?key=${uuid_token}`),
       "Dynamic import of text without a type attribute should fail");
 
-    // Import using the same specifier/module type pair again; this time we get JS,
-    // but the import should still fail because the module map entry for this
-    // specifier/module type pair already contains a failure.
-    await promise_rejects_js(test, TypeError,
-      import(`./serve-text-then-js.py?key=${uuid_token}`),
-      "import should always fail if the same specifier/type attribute pair failed previously");
-}, "An import should always fail if the same specifier/type attribute pair failed previously");
+    // Import using the same specifier/module type pair again; this time we get JS.
+    // The import should succeed because the previous failure did not create a
+    // module map entry, so the module is fetched again.
+    const result_js = await import(`./serve-text-then-js.py?key=${uuid_token}`);
+    assert_equals(result_js.default, "world");
+}, "An import should succeed even if the same specifier/type attribute pair previously failed due to a MIME type mismatch");
 
 promise_test(async test => {
     const uuid_token = token();
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/animated-img-timeout-ref.html b/third_party/blink/web_tests/external/wpt/jpegxl/animated-img-timeout-ref.html
new file mode 100644
index 0000000..a495de1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/jpegxl/animated-img-timeout-ref.html
@@ -0,0 +1,4 @@
+<title>Animated JPEG XL in <img> reference</title>
+<link rel="stylesheet" href="resources/jpegxl-reftest.css">
+<img src="resources/green-32x32.png">
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/animated-img-timeout.html b/third_party/blink/web_tests/external/wpt/jpegxl/animated-img-timeout.html
new file mode 100644
index 0000000..0640cbc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/jpegxl/animated-img-timeout.html
@@ -0,0 +1,14 @@
+<html class="reftest-wait">
+<title>Animated JPEG XL in <img></title>
+<link rel="help" href="https://github.com/web-platform-tests/interop-jpegxl">
+<link rel="match" href="animated-img-timeout-ref.html">
+<link rel="stylesheet" href="resources/jpegxl-reftest.css">
+<script>
+  function loaded() {
+    setTimeout(() => {
+      document.documentElement.classList.remove("reftest-wait");
+    }, 1000);
+  }
+</script>
+<img src="resources/animated-red-green.jxl" onload="loaded()">
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/css-background-image-ref.html b/third_party/blink/web_tests/external/wpt/jpegxl/css-background-image-ref.html
new file mode 100644
index 0000000..617df03c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/jpegxl/css-background-image-ref.html
@@ -0,0 +1,10 @@
+<html>
+<title>JPEG XL CSS background-image reference</title>
+<style>
+  img {
+    image-rendering: pixelated;
+    width: 120px; height: 120px;
+  }
+</style>
+<img src="resources/3x3_srgb_lossless.png">
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/css-background-image.html b/third_party/blink/web_tests/external/wpt/jpegxl/css-background-image.html
new file mode 100644
index 0000000..d782f42
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/jpegxl/css-background-image.html
@@ -0,0 +1,23 @@
+<html class="reftest-wait">
+<title>JPEG XL CSS background-image</title>
+<link rel="help" href="https://github.com/web-platform-tests/interop-jpegxl">
+<link rel="match" href="css-background-image-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<style>
+  #bg {
+    background-image: url(resources/3x3_srgb_lossless.jxl);
+    background-size: cover;
+    image-rendering: pixelated;
+    width: 120px; height: 120px;
+  }
+</style>
+<div id="bg"></div>
+<script>
+  new Promise(r => {
+    const img = new Image();
+    img.onload = r;
+    img.onerror = r;
+    img.src = "resources/3x3_srgb_lossless.jxl";
+  }).then(takeScreenshot);
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/css-before-content-ref.html b/third_party/blink/web_tests/external/wpt/jpegxl/css-before-content-ref.html
new file mode 100644
index 0000000..d5072fd91
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/jpegxl/css-before-content-ref.html
@@ -0,0 +1,10 @@
+<html>
+<title>JPEG XL CSS ::before content reference</title>
+<style>
+  #content::before {
+    content: url(resources/3x3_srgb_lossless.png);
+    display: block;
+  }
+</style>
+<div id="content"></div>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/css-before-content.html b/third_party/blink/web_tests/external/wpt/jpegxl/css-before-content.html
new file mode 100644
index 0000000..f623e102
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/jpegxl/css-before-content.html
@@ -0,0 +1,21 @@
+<html class="reftest-wait">
+<title>JPEG XL CSS ::before content</title>
+<link rel="help" href="https://github.com/web-platform-tests/interop-jpegxl">
+<link rel="match" href="css-before-content-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<style>
+  #content::before {
+    content: url(resources/3x3_srgb_lossless.jxl);
+    display: block;
+  }
+</style>
+<div id="content"></div>
+<script>
+  new Promise(r => {
+    const img = new Image();
+    img.onload = r;
+    img.onerror = r;
+    img.src = "resources/3x3_srgb_lossless.jxl";
+  }).then(takeScreenshot);
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/css-image-set-ref.html b/third_party/blink/web_tests/external/wpt/jpegxl/css-image-set-ref.html
new file mode 100644
index 0000000..2c56883
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/jpegxl/css-image-set-ref.html
@@ -0,0 +1,10 @@
+<html>
+<title>JPEG XL CSS image-set() reference</title>
+<style>
+  img {
+    image-rendering: pixelated;
+    width: 120px; height: 120px;
+  }
+</style>
+<img src="resources/3x3_srgb_lossless.png">
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/css-image-set.html b/third_party/blink/web_tests/external/wpt/jpegxl/css-image-set.html
new file mode 100644
index 0000000..f388227
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/jpegxl/css-image-set.html
@@ -0,0 +1,23 @@
+<html class="reftest-wait">
+<title>JPEG XL CSS image-set()</title>
+<link rel="help" href="https://github.com/web-platform-tests/interop-jpegxl">
+<link rel="match" href="css-image-set-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<style>
+  #set {
+    background-image: image-set(url(resources/3x3_srgb_lossless.jxl) 1x);
+    background-size: cover;
+    image-rendering: pixelated;
+    width: 120px; height: 120px;
+  }
+</style>
+<div id="set"></div>
+<script>
+  new Promise(r => {
+    const img = new Image();
+    img.onload = r;
+    img.onerror = r;
+    img.src = "resources/3x3_srgb_lossless.jxl";
+  }).then(takeScreenshot);
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/css-list-style-image-ref.html b/third_party/blink/web_tests/external/wpt/jpegxl/css-list-style-image-ref.html
new file mode 100644
index 0000000..afe2051
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/jpegxl/css-list-style-image-ref.html
@@ -0,0 +1,9 @@
+<html>
+<title>JPEG XL CSS list-style-image reference</title>
+<style>
+  li {
+    list-style-image: url(resources/3x3_srgb_lossless.png);
+  }
+</style>
+<ul><li>item</li></ul>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/css-list-style-image.html b/third_party/blink/web_tests/external/wpt/jpegxl/css-list-style-image.html
new file mode 100644
index 0000000..f4c7df1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/jpegxl/css-list-style-image.html
@@ -0,0 +1,20 @@
+<html class="reftest-wait">
+<title>JPEG XL CSS list-style-image</title>
+<link rel="help" href="https://github.com/web-platform-tests/interop-jpegxl">
+<link rel="match" href="css-list-style-image-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<style>
+  li {
+    list-style-image: url(resources/3x3_srgb_lossless.jxl);
+  }
+</style>
+<ul><li>item</li></ul>
+<script>
+  new Promise(r => {
+    const img = new Image();
+    img.onload = r;
+    img.onerror = r;
+    img.src = "resources/3x3_srgb_lossless.jxl";
+  }).then(takeScreenshot);
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/jpegxl/resources/animated-red-green.jxl b/third_party/blink/web_tests/external/wpt/jpegxl/resources/animated-red-green.jxl
new file mode 100644
index 0000000..1e458a8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/jpegxl/resources/animated-red-green.jxl
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/image-element-when-fully-active.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/image-element-when-fully-active.html
new file mode 100644
index 0000000..f157575
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/image-element-when-fully-active.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Largest Contentful Paint: element is null after iframe removal.</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe src="resources/iframe-stores-image-entry.html" id="ifr"></iframe>
+<link rel="author" title="Shubham Gupta" href="mailto:shubhamg13.work@gmail.com">
+<script>
+  setup({"hide_test_state": true});
+  let t = async_test('Element is null after iframe with LCP image is removed');
+  window.triggerTest = t.step_func_done(entry => {
+    assert_not_equals(entry.element, null);
+    const iframe = document.getElementById('ifr');
+    iframe.remove();
+    assert_equals(entry.element, null);
+  });
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/image-removed-after-paint.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/image-removed-after-paint.html
new file mode 100644
index 0000000..1d4fd6d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/image-removed-after-paint.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>LCP: element removed after image paints</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/largest-contentful-paint-helpers.js"></script>
+<link rel="author" title="Shubham Gupta" href="mailto:shubhamg13.work@gmail.com">
+<body>
+<script>
+promise_test(async (t) => {
+  const img = document.createElement('img');
+  img.src = '/images/blue.png';
+  img.id = 'test-image';
+  document.body.appendChild(img);
+
+  const { promise, resolve } = Promise.withResolvers();
+  const observer = new PerformanceObserver((list) => {
+    resolve(list.getEntries()[0]);
+  });
+  observer.observe({ type: 'largest-contentful-paint', buffered: true });
+
+  const entry = await promise;
+
+  assert_not_equals(entry.element, null,
+    'entry.element should be non-null when element is in DOM');
+
+  img.remove();
+  await raf();
+  await raf();
+
+  assert_equals(entry.element, null,
+    'entry.element should be null after element is removed');
+}, 'Element removed after LCP entry should reflect in entry.element');
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/resources/iframe-stores-image-entry.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/resources/iframe-stores-image-entry.html
new file mode 100644
index 0000000..58468d0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/resources/iframe-stores-image-entry.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<body>
+<img src="/images/blue.png" width="100" height="50" id="iframe-image">
+<script>
+  const observer = new PerformanceObserver(entryList => {
+    window.parent.triggerTest(entryList.getEntries()[0]);
+  });
+  observer.observe({type: 'largest-contentful-paint', buffered: true});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/lint.ignore b/third_party/blink/web_tests/external/wpt/lint.ignore
index 3d309a4c..7fa400b 100644
--- a/third_party/blink/web_tests/external/wpt/lint.ignore
+++ b/third_party/blink/web_tests/external/wpt/lint.ignore
@@ -170,6 +170,7 @@
 SET TIMEOUT: annotation-model/scripts/ajv.min.js
 SET TIMEOUT: apng/animated-png-timeout.html
 SET TIMEOUT: avif/animated-avif-timeout.html
+SET TIMEOUT: jpegxl/animated-img-timeout.html
 SET TIMEOUT: cookies/resources/testharness-helpers.js
 SET TIMEOUT: common/reftest-wait.js
 SET TIMEOUT: compute-pressure/resources/support-iframe.html
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-003.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-003.html
new file mode 100644
index 0000000..cc4e0d1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/tabindex-003.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>MathML tabIndex attribute: resetting an explicitly set value</title>
+<link rel="help" href="https://w3c.github.io/mathml-core/#attributes-common-to-html-and-mathml-elements">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#attr-tabindex">
+<meta name="assert" content="Verify that an explicitly set tabIndex is reset to the default value when the tabindex attribute is changed to a value that is not a valid integer, or removed">
+
+<script src="/mathml/support/mathml-fragments.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+  <div id="log"></div>
+  <math>
+  </math>
+  <script>
+    // tabindex is defined by HTML, which parses the attribute as an integer and
+    // falls back to the default tab index for any value that fails to parse.
+    // That includes the empty string, garbage, and out-of-range integers.
+    const invalidValues = ["", "invalid", " ", "9999999999", "-9999999999"];
+
+    // The HTML rules for parsing integers stop at the first non-digit and keep
+    // the digits collected so far, so these are valid despite the trailing junk.
+    const valuesWithTrailingGarbage = [["1.5", 1], ["4abc", 4], ["-2 ", -2]];
+
+    Object.keys(MathMLFragments).forEach(elName => {
+        const mathEl = document.querySelector('math');
+
+        // <a> needs an href to be focusable by default, and so to have a
+        // default tab index of 0 rather than -1.
+        const href = elName == 'a' ? ' href="#"' : '';
+        mathEl.innerHTML = `<${elName} id="el"${href}></${elName}>`;
+        const el = mathEl.querySelector('#el');
+        const expectedDefault = elName == 'a' ? 0 : -1;
+
+        invalidValues.forEach(invalidValue => {
+            test(() => {
+                el.setAttribute("tabindex", "3");
+                assert_equals(el.tabIndex, 3, "valid value");
+
+                el.setAttribute("tabindex", invalidValue);
+                assert_equals(el.getAttribute("tabindex"), invalidValue);
+                assert_equals(el.tabIndex, expectedDefault, "invalid value");
+            }, `changing tabindex from "3" to "${invalidValue}" resets ${elName}.tabIndex`);
+        });
+
+        test(() => {
+            valuesWithTrailingGarbage.forEach(([value, expectedTabIndex]) => {
+                el.setAttribute("tabindex", "3");
+                assert_equals(el.tabIndex, 3, "valid value");
+
+                el.setAttribute("tabindex", value);
+                assert_equals(el.tabIndex, expectedTabIndex, `"${value}"`);
+            });
+            el.removeAttribute("tabindex");
+        }, `trailing garbage after the digits of ${elName}'s tabindex is ignored`);
+
+        test(() => {
+            el.setAttribute("tabindex", "3");
+            assert_equals(el.tabIndex, 3, "valid value");
+
+            el.removeAttribute("tabindex");
+            assert_equals(el.tabIndex, expectedDefault, "attribute removed");
+        }, `removing tabindex resets ${elName}.tabIndex`);
+    });
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/picture-in-picture/request-picture-in-picture-consumes-user-activation.html b/third_party/blink/web_tests/external/wpt/picture-in-picture/request-picture-in-picture-consumes-user-activation.html
new file mode 100644
index 0000000..06182d0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/picture-in-picture/request-picture-in-picture-consumes-user-activation.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<title>Test that request Picture-in-Picture consumes the user activation</title>
+<script src="/common/media.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/picture-in-picture-helpers.js"></script>
+<body></body>
+<script>
+// Runs `action` synchronously from a click event handler, so that the requests
+// below are made while the user activation is live, as a page would make them.
+async function runFromClickHandler(t, action) {
+  const button = document.createElement('button');
+  button.textContent = 'This test requires user interaction. Please click here.';
+  document.body.appendChild(button);
+  t.add_cleanup(() => button.remove());
+
+  const result = new Promise(resolve => {
+    button.addEventListener('click', () => { resolve(action()); }, { once: true });
+  });
+  await test_driver.click(button);
+  return result;
+}
+
+promise_test(async t => {
+  const video = await loadVideo();
+  document.body.appendChild(video);
+  t.add_cleanup(async () => {
+    if (document.fullscreenElement)
+      await document.exitFullscreen();
+    if (document.pictureInPictureElement)
+      await document.exitPictureInPicture();
+    video.remove();
+  });
+
+  // https://w3c.github.io/picture-in-picture/#dom-htmlvideoelement-requestpictureinpicture
+  // requestPictureInPicture() consumes the user activation, so a fullscreen
+  // request made from the same activation must be denied.
+  //
+  // Each outcome is captured with handlers attached synchronously: the requests
+  // settle while the click is still being dispatched, so awaiting them later
+  // would report an unhandled rejection. Awaiting the outcomes rather than the
+  // requests themselves also keeps a wrongly granted fullscreen request from
+  // timing out the test: in that case the Picture-in-Picture request, whose
+  // presentation mode change is superseded by fullscreen, never settles.
+  const settled = promise => promise.then(() => 'fulfilled', error => error);
+  const outcomes = await runFromClickHandler(t, () => ({
+    // play() is only here to mirror how a page enters Picture-in-Picture from a
+    // click; its result is not what is under test.
+    play: settled(video.play()),
+    pictureInPicture: settled(video.requestPictureInPicture()),
+    fullscreen: settled(video.requestFullscreen()),
+  }));
+
+  const fullscreenOutcome = await outcomes.fullscreen;
+  assert_true(fullscreenOutcome instanceof TypeError,
+      'requestFullscreen() should be denied with a TypeError: requestPictureInPicture() ' +
+      `consumed the user activation (got ${fullscreenOutcome})`);
+  assert_equals(document.fullscreenElement, null, 'there should be no fullscreen element');
+
+  assert_equals(await outcomes.pictureInPicture, 'fulfilled', 'requestPictureInPicture()');
+  assert_equals(document.pictureInPictureElement, video,
+      'video should be the Picture-in-Picture element');
+}, 'request Picture-in-Picture consumes the user activation, denying a fullscreen request from the same activation');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/touch-scroll-axis-lock-with-scroll-chain.html b/third_party/blink/web_tests/external/wpt/pointerevents/touch-scroll-axis-lock-with-scroll-chain.html
new file mode 100644
index 0000000..46c31b0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/touch-scroll-axis-lock-with-scroll-chain.html
@@ -0,0 +1,101 @@
+<!doctype html>
+<title>Touch scroll chain: axis lock with direction change on a horizontal scroller</title>
+<meta name="variant" content="?touch">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<link rel="help" href="https://w3c.github.io/pointerevents/#determining-supported-direct-manipulation-behavior" />
+<link rel="author" title="Euclid Ye" href="mailto:yezhizhenjiakang@gmail.com">
+<link rel=help href="https://github.com/servo/servo/issues/46954">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="pointerevent_support.js"></script>
+<style>
+  /* A horizontal-only scroller nested in a vertically-scrollable page. */
+  #tall-top { height: 20vh; }
+  #tall-bottom { height: 80vh; }
+  #strip { display: flex; overflow-x: auto; min-height: 200px; }
+  .cell { flex: 0 0 140px; }
+</style>
+<div id="tall-top"></div>
+<div id="strip">
+  <span class="cell">1</span><span class="cell">2</span><span class="cell">3</span>
+  <span class="cell">4</span><span class="cell">5</span><span class="cell">6</span>
+  <span class="cell">7</span><span class="cell">8</span><span class="cell">9</span>
+  <span class="cell">10</span><span class="cell">11</span><span class="cell">12</span>
+</div>
+<div id="tall-bottom"></div>
+
+<script>
+  "use strict";
+  const pointer_type = "touch";
+  const strip = document.getElementById("strip");
+
+  assert_true(location.search.length > 0 &&
+      location.search.substring(1) === pointer_type,
+      "Test URL has 'touch' pointer-type");
+
+  // Build a multi-direction touch swipe. Each entry in `directions` is a
+  // semantic scroll direction matching the convention used by
+  // `touchScrollInTarget` in pointerevent_support.js:
+  //   "down"/"up" => scroll the page down/up (finger moves up/down),
+  //   "right"/"left" => scroll the target right/left (finger moves left/right).
+  const offset = 30;
+  const steps_per_direction = 3;
+  const deltas = {
+    "down":  [0, -offset],
+    "up":    [0, offset],
+    "right": [-offset, 0],
+    "left":  [offset, 0],
+  };
+
+  function swipeChain(target, directions) {
+    const rect = target.getBoundingClientRect();
+    const [startX, startY] = getInViewCenterPoint(rect);
+    let x = startX, y = startY;
+    let actions = new test_driver.Actions()
+      .addPointer("touchPointer1", "touch")
+      .pointerMove(startX, startY, {origin: "viewport"})
+      .pointerDown();
+    for (const direction of directions) {
+      const [dx, dy] = deltas[direction];
+      for (let i = 0; i < steps_per_direction; i++) {
+        x += dx;
+        y += dy;
+        actions = actions.pointerMove(x, y, {origin: "viewport"});
+      }
+    }
+    return actions.pause(100).pointerUp().send();
+  }
+
+  // Subtest 1: start by scrolling the page down (vertical), then try to scroll
+  // the strip right (horizontal). The gesture is locked to its initial
+  // (vertical) dominant axis, so the page scrolls and the strip must not move.
+  promise_test(async () => {
+    window.scrollTo(0, 0);
+    strip.scrollLeft = 0;
+
+    await swipeChain(strip, ["down", "right"]);
+
+    assert_greater_than(window.scrollY, 0,
+        "a vertical-then-horizontal swipe on a horizontal scroller should scroll the page");
+    assert_equals(strip.scrollLeft, 0,
+        "the strip must not scroll horizontally once the gesture is locked to the vertical axis");
+  }, "Vertical-then-horizontal swipe locks to vertical: page scrolls, strip does not");
+
+  // Subtest 2: start by scrolling the strip right (horizontal), then try to
+  // scroll the page down (vertical). The gesture is locked to its initial
+  // (horizontal) dominant axis, so the strip scrolls and the page must not move.
+  promise_test(async () => {
+    window.scrollTo(0, 0);
+    strip.scrollLeft = 0;
+
+    await swipeChain(strip, ["right", "down"]);
+
+    assert_greater_than(strip.scrollLeft, 0,
+        "a horizontal-then-vertical swipe on a horizontal scroller should scroll the strip");
+    assert_equals(window.scrollY, 0,
+        "the page must not scroll vertically once the gesture is locked to the horizontal axis");
+  }, "Horizontal-then-vertical swipe locks to horizontal: strip scrolls, page does not");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/link-preload-as-case-insensitive.html b/third_party/blink/web_tests/external/wpt/preload/link-preload-as-case-insensitive.html
new file mode 100644
index 0000000..429fd2a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/link-preload-as-case-insensitive.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>&lt;link rel=preload&gt;: the as attribute's keywords are matched ASCII case-insensitively</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/semantics.html#attr-link-as">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#enumerated-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/preload_helper.js"></script>
+
+<!-- Discovered by the preload scanner while parsing, then processed by the link element. -->
+<link rel="preload" as="image"  href="resources/square.png?case=image-lowercase">
+<link rel="preload" as="IMAGE"  href="resources/square.png?case=image-uppercase">
+<link rel="preload" as="script" href="resources/dummy.js?case=script-lowercase">
+<link rel="preload" as="SCRIPT" href="resources/dummy.js?case=script-uppercase">
+<link rel="preload" as="style"  href="resources/dummy.css?case=style-lowercase">
+<link rel="preload" as="StYlE"  href="resources/dummy.css?case=style-mixedcase">
+
+<!-- U+017F LATIN SMALL LETTER LONG S uppercases to "S" under full Unicode case folding, but the
+     keywords are matched ASCII case-insensitively, so these are not keywords at all. -->
+<link rel="preload" as="&#x17F;cript" href="resources/dummy.js?case=script-long-s">
+<link rel="preload" as="&#x17F;tyle"  href="resources/dummy.css?case=style-long-s">
+
+<script>
+// The listeners below are attached synchronously while parsing, before any load or error event can
+// be dispatched, so no event is missed.
+const settled = new Map();
+for (const link of document.querySelectorAll("link[rel=preload]")) {
+    const key = new URL(link.href).searchParams.get("case");
+    settled.set(key, new Promise(resolve => {
+        link.onload = () => resolve("load");
+        link.onerror = () => resolve("error");
+    }));
+}
+
+setup(verifyPreloadAndRTSupport);
+
+const asciiLowercase = value => value.replace(/[A-Z]/g, character => character.toLowerCase());
+
+const KEYWORDS = [
+    { keyword: "image",  url: "resources/square.png", lowercase: "image-lowercase",  variant: "image-uppercase" },
+    { keyword: "script", url: "resources/dummy.js",   lowercase: "script-lowercase", variant: "script-uppercase" },
+    { keyword: "style",  url: "resources/dummy.css",  lowercase: "style-lowercase",  variant: "style-mixedcase" },
+];
+
+for (const { keyword, url, lowercase, variant } of KEYWORDS) {
+    promise_test(async () => {
+        assert_equals(await settled.get(lowercase), "load", `as="${keyword}" fires load`);
+        assert_equals(await settled.get(variant), "load", "differently cased keyword fires load");
+        verifyNumberOfResourceTimingEntries(`${url}?case=${variant}`, 1);
+    }, `A differently cased as="${keyword}" preloads like as="${keyword}" (${variant})`);
+}
+
+promise_test(async () => {
+    const link = document.createElement("link");
+    link.rel = "preload";
+    link.setAttribute("as", "ImAgE");
+    link.href = "resources/square.png?case=image-created-by-script";
+    const settledLink = new Promise(resolve => {
+        link.onload = () => resolve("load");
+        link.onerror = () => resolve("error");
+    });
+    document.head.appendChild(link);
+    assert_equals(await settledLink, "load", "differently cased keyword fires load");
+    verifyNumberOfResourceTimingEntries("resources/square.png?case=image-created-by-script", 1);
+}, `A differently cased as="image" preloads on a link element created by script`);
+
+// A link element with an invalid `as` value has nothing to load, so it never settles. Wait for
+// every valid preload above to settle and for the document to load instead: an implementation that
+// matched the keywords with full Unicode case folding would have started its fetch while parsing,
+// alongside the ones that do settle.
+const everyValidPreloadSettled = Promise.all([
+    ...KEYWORDS.flatMap(({ lowercase, variant }) => [settled.get(lowercase), settled.get(variant)]),
+    new Promise(resolve => addEventListener("load", resolve)),
+]);
+
+const NON_ASCII_KEYWORDS = [
+    { value: "ſcript", keyword: "script", url: "resources/dummy.js",  case: "script-long-s" },
+    { value: "ſtyle",  keyword: "style",  url: "resources/dummy.css", case: "style-long-s" },
+];
+
+for (const { value, keyword, url, case: variant } of NON_ASCII_KEYWORDS) {
+    promise_test(async () => {
+        await everyValidPreloadSettled;
+        verifyNumberOfResourceTimingEntries(`${url}?case=${variant}`, 0);
+    }, `as="${value}" (U+017F) does not preload like as="${keyword}"`);
+}
+
+test(() => {
+    // The canonical keyword of the state is reflected, not the attribute's value. A value that is
+    // not an ASCII case-insensitive match for a keyword reflects the empty string.
+    const keywords = KEYWORDS.map(({ keyword }) => keyword);
+    for (const link of document.querySelectorAll("link[rel=preload]")) {
+        const attributeValue = link.getAttribute("as");
+        const state = asciiLowercase(attributeValue);
+        assert_equals(link.as, keywords.includes(state) ? state : "", `as="${attributeValue}" reflects`);
+    }
+}, "The as IDL attribute reflects the canonical keyword of the state");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/modulepreload-already-in-module-map.html b/third_party/blink/web_tests/external/wpt/preload/modulepreload-already-in-module-map.html
new file mode 100644
index 0000000..017d4b3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/modulepreload-already-in-module-map.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>link rel=modulepreload for a URL already in the module map fires the right event</title>
+<meta name="timeout" content="long">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-single-module-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+// When a <link rel=modulepreload> targets a URL that is already present in the
+// module map (because a previous import()/<script type=module> fetched it, or
+// is still fetching it), it must fire the same event a fresh preload would:
+// "load" when fetching produces a (non-null) module script - even one with a
+// parse error - and "error" when fetching fails.
+
+// NOTE: crossorigin is what makes these tests exercise the "already in the
+// module map" path in implementations that key their preload cache on the CORS
+// mode: the preload and the import() of the same URL then don't share a preload
+// entry, so the preload has to be satisfied from the module map. Keep it.
+//
+// The still-fetching tests below append the link in the same task as the
+// import() call, relying on "fetch a single module script" having put the URL in
+// the module map as fetching before import() returns.
+function makeModulePreload(url) {
+  const link = document.createElement("link");
+  link.rel = "modulepreload";
+  link.as = "script";
+  link.crossOrigin = "";
+  link.href = url;
+  return link;
+}
+
+function attachAndWaitForLoad(link) {
+  return new Promise((resolve, reject) => {
+    link.addEventListener("load", resolve, { once: true });
+    link.addEventListener("error", () => reject(new Error("fired error")),
+                          { once: true });
+    document.head.appendChild(link);
+  });
+}
+
+function attachAndWaitForError(link) {
+  return new Promise((resolve, reject) => {
+    link.addEventListener("error", resolve, { once: true });
+    link.addEventListener("load", () => reject(new Error("fired load")),
+                          { once: true });
+    document.head.appendChild(link);
+  });
+}
+
+async function expectRejection(promise, description) {
+  await promise.then(
+    () => assert_unreached(description),
+    () => {});
+}
+
+promise_test(async _ => {
+  const url = "./resources/dummy-module.js?already-fetched-load";
+  await import(url);
+  await attachAndWaitForLoad(makeModulePreload(url));
+}, "already-fetched module fires load");
+
+promise_test(async _ => {
+  const url = "./resources/syntax-error.js?already-fetched-syntax-error";
+  await expectRejection(import(url), "import of a syntax-error module rejects");
+  // A parse error still produces a module script, so the preload succeeds and
+  // fires load; the error only surfaces when the module is evaluated.
+  await attachAndWaitForLoad(makeModulePreload(url));
+}, "already-fetched module with a syntax error fires load");
+
+promise_test(async _ => {
+  const url = "./resources/not-exist.js?already-fetched-network-error";
+  await expectRejection(import(url), "import of a missing module rejects");
+  await attachAndWaitForError(makeModulePreload(url));
+}, "already-fetched module that failed to fetch fires error");
+
+promise_test(async _ => {
+  const url = "./resources/dummy-module.js?fetching-load";
+  // Do not await: the modulepreload is appended while the import is still
+  // fetching the same URL.
+  const importPromise = import(url);
+  await attachAndWaitForLoad(makeModulePreload(url));
+  await importPromise;
+}, "module still fetching that succeeds fires load");
+
+promise_test(async _ => {
+  const url = "./resources/not-exist.js?fetching-network-error";
+  const importPromise = import(url).catch(() => {});
+  await attachAndWaitForError(makeModulePreload(url));
+  await importPromise;
+}, "module still fetching that fails fires error");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/modulepreload-consumed-by-script.html b/third_party/blink/web_tests/external/wpt/preload/modulepreload-consumed-by-script.html
new file mode 100644
index 0000000..898ba8f4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/modulepreload-consumed-by-script.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>link rel=modulepreload fires its event when a script element consumes the same module</title>
+<meta name="timeout" content="long">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/links.html#link-type-modulepreload">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="./resources/preload-module-helper.js"></script>
+<body>
+<script>
+// A <link rel=modulepreload> can be satisfied by a fetch of its URL that is
+// already in flight, and a <script type=module> for that URL added while the
+// fetch is still outstanding can then consume that same preload as its own load.
+// The link element must get its event either way: it is the element that asked
+// for the preload, and what the module ends up being used for afterwards is none
+// of its business.
+
+promise_test(async t => {
+  const uuid = token();
+  const url = moduleUrl({ block: 1, uuid });
+
+  // Puts the URL in the module map as fetching, and the server holds the
+  // response until the release below, so it stays that way.
+  const importPromise = import(url).catch(() => {});
+
+  // Satisfied by the fetch above rather than by a fetch of its own.
+  const link = makeModulePreload(url);
+  const linkEvent = eventFor(link);
+  document.head.appendChild(link);
+
+  // Consumes the preload while that fetch is still outstanding.
+  const script = document.createElement("script");
+  script.type = "module";
+  script.crossOrigin = "";
+  script.src = url;
+  const scriptEvent = eventFor(script);
+  document.body.appendChild(script);
+
+  await release(uuid);
+
+  assert_equals(await linkEvent, "load", "the modulepreload fired load");
+  assert_equals(await scriptEvent, "load", "the script element loaded as well");
+  await importPromise;
+}, "modulepreload satisfied by an in-flight fetch fires load when a script " +
+   "element consumes it");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/modulepreload-fires-before-dependencies.html b/third_party/blink/web_tests/external/wpt/preload/modulepreload-fires-before-dependencies.html
new file mode 100644
index 0000000..6ac79920
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/modulepreload-fires-before-dependencies.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>link rel=modulepreload fires its event when the top-level module is fetched, before dependencies</title>
+<meta name="timeout" content="long">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/links.html#link-type-modulepreload">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="./resources/preload-module-helper.js"></script>
+<body>
+<script>
+// Per the "modulepreload" link type: "the appropriate load or error events will
+// occur after the specified module is fetched, and will not wait for any
+// dependencies." These tests give the top-level module a dependency that the
+// server holds open until the test releases it, and only release it after the
+// modulepreload event has fired, so the event is observed strictly before the
+// dependency completes. An implementation that waits for dependencies never
+// fires and fails by timing out. Covers the three top-level states: still
+// fetching, already fetched, and already fetched with a syntax error.
+
+// Case 1: the top-level module is still fetching (a concurrent import() of the
+// same URL is in flight) when the preload is added.
+promise_test(async t => {
+  const depUuid = token();
+  const topUuid = token();
+  const dep = moduleUrl({ block: 1, uuid: depUuid });
+  // The top-level blocks as well, so that it is unambiguously still fetching
+  // when the preload is added below.
+  const url = moduleUrl({ block: 1, import: dep, uuid: topUuid });
+
+  const importPromise = import(url).catch(() => {});
+
+  // import() has put the URL in the module map as fetching, so this preload can
+  // only be satisfied by that in-flight fetch.
+  const link = makeModulePreload(url);
+  const done = eventFor(link);
+  document.head.appendChild(link);
+
+  await release(topUuid);
+  const event = await done;
+
+  assert_false(hasResourceLoaded(dep),
+      "the dependency was still outstanding when the event fired");
+  await release(depUuid);
+  assert_equals(event, "load");
+  await importPromise;
+}, "still-fetching top-level fires load before its dependencies");
+
+// Case 2: the top-level module is already fetched when the preload is added,
+// while its dependency is still outstanding.
+promise_test(async t => {
+  const depUuid = token();
+  const topUuid = token();
+  const dep = moduleUrl({ block: 1, uuid: depUuid });
+  const url = moduleUrl({ import: dep, uuid: topUuid });
+
+  const importPromise = import(url).catch(() => {});
+  // Once the top-level's response has been received it is in the module map as
+  // fetched, and its dependency is what is still outstanding.
+  await resourceLoaded(url);
+
+  const link = makeModulePreload(url);
+  const done = eventFor(link);
+  document.head.appendChild(link);
+
+  const event = await done;
+
+  assert_false(hasResourceLoaded(dep),
+      "the dependency was still outstanding when the event fired");
+  await release(depUuid);
+  assert_equals(event, "load");
+  await importPromise;
+}, "already-fetched top-level fires load before its dependencies");
+
+// Case 3: the top-level module is already fetched but has a syntax error. A
+// parse error still produces a module script, so the preload fires load; there
+// are no dependencies to wait for.
+promise_test(async t => {
+  const url = moduleUrl({ syntaxerror: 1, uuid: token() });
+  await import(url).then(
+      () => assert_unreached("import of a syntax-error module should reject"),
+      () => {});
+
+  const link = makeModulePreload(url);
+  const done = eventFor(link);
+  document.head.appendChild(link);
+  assert_equals(await done, "load",
+      "already-fetched syntax-error top-level fires load");
+}, "already-fetched syntax-error top-level fires load");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/modulepreload-sri-expected.txt b/third_party/blink/web_tests/external/wpt/preload/modulepreload-sri-expected.txt
new file mode 100644
index 0000000..bc420a9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/modulepreload-sri-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] A module script should load even if an earlier modulepreload for the same URL failed its integrity check
+  promise_test: Unhandled rejection with value: object "Error: script should have loaded"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/preload/modulepreload-sri-importmap-expected.txt b/third_party/blink/web_tests/external/wpt/preload/modulepreload-sri-importmap-expected.txt
new file mode 100644
index 0000000..05ff8bf75
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/modulepreload-sri-importmap-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] A module script should load even if an earlier modulepreload for the same URL failed its integrity check, with an import map registered
+  promise_test: Unhandled rejection with value: object "Error: script should have loaded"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/preload/modulepreload-sri-importmap.html b/third_party/blink/web_tests/external/wpt/preload/modulepreload-sri-importmap.html
index d633fe1d..6c230e7 100644
--- a/third_party/blink/web_tests/external/wpt/preload/modulepreload-sri-importmap.html
+++ b/third_party/blink/web_tests/external/wpt/preload/modulepreload-sri-importmap.html
@@ -1,5 +1,6 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>modulepreload with an invalid integrity does not poison a later import (with an import map)</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script type="importmap">
@@ -9,17 +10,35 @@
     }
   }
 </script>
-<link rel="modulepreload" href="resources/module1.js" integrity="sha384-invalid">
-<script type="module" src="resources/module1.js" id="myscript"></script>
 <body>
 <script>
-  // compared to modulepreload.html, this tests behavior when elements are
-  // initially on an HTML page instead of being added by JS
-  promise_test(() => {
-    return new Promise((resolve, reject) => {
-      let myscript = document.querySelector('#myscript');
-      myscript.onerror = resolve;
-      myscript.onload = reject;
+  // As modulepreload-sri.html, but with an import map registered. The import map
+  // does not affect resolution of module1.js, so it must not change the result:
+  // a modulepreload whose integrity check fails is a fetch error, which is not
+  // cached in the module map, so a later no-integrity <script type=module> for
+  // the same URL fetches it again and loads successfully.
+  // https://html.spec.whatwg.org/#fetch-a-single-module-script
+  //
+  // The <script> is only inserted after the modulepreload has finished, so the
+  // two never join a single in-flight fetch.
+  promise_test(async () => {
+    const link = document.createElement('link');
+    link.rel = 'modulepreload';
+    link.href = 'resources/module1.js';
+    link.integrity = 'sha384-invalid';
+    await new Promise((resolve, reject) => {
+      link.onload = () => reject(new Error('modulepreload should have failed its integrity check'));
+      link.onerror = resolve;
+      document.head.appendChild(link);
     });
-  }, "Script should not be loaded if modulepreload's integrity is invalid");
+
+    const script = document.createElement('script');
+    script.type = 'module';
+    script.src = 'resources/module1.js';
+    await new Promise((resolve, reject) => {
+      script.onload = resolve;
+      script.onerror = () => reject(new Error('script should have loaded'));
+      document.body.appendChild(script);
+    });
+  }, "A module script should load even if an earlier modulepreload for the same URL failed its integrity check, with an import map registered");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/modulepreload-sri.html b/third_party/blink/web_tests/external/wpt/preload/modulepreload-sri.html
index ea32a6a..00c8f7b1 100644
--- a/third_party/blink/web_tests/external/wpt/preload/modulepreload-sri.html
+++ b/third_party/blink/web_tests/external/wpt/preload/modulepreload-sri.html
@@ -1,18 +1,36 @@
 <!doctype html>
 <meta charset=utf-8>
+<title>modulepreload with an invalid integrity does not poison a later import</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<link rel="modulepreload" href="resources/module1.js" integrity="sha384-invalid">
-<script type="module" src="resources/module1.js" id="myscript"></script>
 <body>
 <script>
-  // compared to modulepreload.html, this tests behavior when elements are
-  // initially on an HTML page instead of being added by JS
-  promise_test(() => {
-    return new Promise((resolve, reject) => {
-      let myscript = document.querySelector('#myscript');
-      myscript.onerror = resolve;
-      myscript.onload = reject;
+  // A modulepreload whose integrity check fails is a fetch error, which per the
+  // HTML spec is not cached in the module map. A later <script type=module> for
+  // the same URL that has no integrity requirement must therefore be able to
+  // fetch it again and load successfully.
+  // https://html.spec.whatwg.org/#fetch-a-single-module-script
+  //
+  // The <script> is only inserted after the modulepreload has finished, so the
+  // two never join a single in-flight fetch.
+  promise_test(async () => {
+    const link = document.createElement('link');
+    link.rel = 'modulepreload';
+    link.href = 'resources/module1.js';
+    link.integrity = 'sha384-invalid';
+    await new Promise((resolve, reject) => {
+      link.onload = () => reject(new Error('modulepreload should have failed its integrity check'));
+      link.onerror = resolve;
+      document.head.appendChild(link);
     });
-  }, "Script should not be loaded if modulepreload's integrity is invalid");
+
+    const script = document.createElement('script');
+    script.type = 'module';
+    script.src = 'resources/module1.js';
+    await new Promise((resolve, reject) => {
+      script.onload = resolve;
+      script.onerror = () => reject(new Error('script should have loaded'));
+      document.body.appendChild(script);
+    });
+  }, "A module script should load even if an earlier modulepreload for the same URL failed its integrity check");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/preload-imagesrcset-in-markup.html b/third_party/blink/web_tests/external/wpt/preload/preload-imagesrcset-in-markup.html
new file mode 100644
index 0000000..fe744cc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/preload-imagesrcset-in-markup.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<title>A preload declared in markup with both href and imagesrcset only fetches the imagesrcset candidate</title>
+<meta name="timeout" content="long">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/links.html#preload">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/images.html#create-a-source-set">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/preload/resources/preload_helper.js"></script>
+<!--
+  dynamic-adding-preload-imagesrcset.html covers the same combination from
+  script, which the preload scanner never sees. These links are in markup so
+  the scanner does see them.
+
+  Keep the 1x candidate below: without it, href joins the source set as the 1x
+  candidate and may legitimately be fetched.
+-->
+<link rel="preload" as="image" href="resources/square.png?markup-w-href"
+      imagesrcset="resources/square.png?markup-w-200 200w, resources/square.png?markup-w-400 400w, resources/square.png?markup-w-800 800w"
+      imagesizes="400px">
+<link rel="preload" as="image" href="resources/square.png?markup-x-href"
+      imagesrcset="resources/square.png?markup-x-1x 1x, resources/square.png?markup-x-2x 2x">
+<body>
+<script>
+    function candidateFetches(urls) {
+        return urls.reduce((total, url) => total + numberOfResourceTimingEntries(url), 0);
+    }
+
+    promise_setup(() => new Promise(resolve => {
+        if (document.readyState === "complete")
+            resolve();
+        else
+            addEventListener("load", resolve);
+    }));
+
+    promise_test(async t => {
+        verifyPreloadAndRTSupport();
+        const candidates = [
+            "resources/square.png?markup-w-200",
+            "resources/square.png?markup-w-400",
+            "resources/square.png?markup-w-800",
+        ];
+        await t.step_wait(() => candidateFetches(candidates) >= 1,
+                          "a w-descriptor candidate should be preloaded", 3000);
+        verifyNumberOfResourceTimingEntries("resources/square.png?markup-w-href", 0);
+        assert_equals(candidateFetches(candidates), 1,
+                      "exactly one w-descriptor candidate should be preloaded");
+    }, "Preload in markup with href and imagesrcset (w descriptors) does not fetch href");
+
+    promise_test(async t => {
+        verifyPreloadAndRTSupport();
+        const candidates = [
+            "resources/square.png?markup-x-1x",
+            "resources/square.png?markup-x-2x",
+        ];
+        await t.step_wait(() => candidateFetches(candidates) >= 1,
+                          "an x-descriptor candidate should be preloaded", 3000);
+        verifyNumberOfResourceTimingEntries("resources/square.png?markup-x-href", 0);
+        assert_equals(candidateFetches(candidates), 1,
+                      "exactly one x-descriptor candidate should be preloaded");
+    }, "Preload in markup with href and imagesrcset (x descriptors) does not fetch href");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/resources/preload-module-helper.js b/third_party/blink/web_tests/external/wpt/preload/resources/preload-module-helper.js
new file mode 100644
index 0000000..f498e256
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/resources/preload-module-helper.js
@@ -0,0 +1,62 @@
+// Helpers for the modulepreload tests that drive resources/preload-module.py.
+// Paths are resolved against the test page, which is expected to be in
+// preload/.
+
+const MODULE_HANDLER = "./resources/preload-module.py";
+
+function moduleUrl(params) {
+  const url = new URL(MODULE_HANDLER, location.href);
+  for (const [key, value] of Object.entries(params)) {
+    url.searchParams.set(key, value);
+  }
+  return url.href;
+}
+
+// NOTE: crossorigin is what makes these tests exercise the "already in the
+// module map" path in implementations that key their preload cache on the CORS
+// mode: the preload and the import() of the same URL then don't share a preload
+// entry, so the preload has to be satisfied from the module map. Keep it.
+function makeModulePreload(url) {
+  const link = document.createElement("link");
+  link.rel = "modulepreload";
+  link.as = "script";
+  link.crossOrigin = "";
+  link.href = url;
+  return link;
+}
+
+function eventFor(element) {
+  return new Promise(resolve => {
+    element.addEventListener("load", () => resolve("load"), { once: true });
+    element.addEventListener("error", () => resolve("error"), { once: true });
+  });
+}
+
+// Lets a request blocked by preload-module.py's block=1 finish.
+function release(uuid) {
+  return fetch(moduleUrl({ release: 1, uuid }));
+}
+
+// Whether the response for `url` has been received. Resource timing is what the
+// tests use to observe the server's progress, rather than asking the server, so
+// that nothing has to keep mutable state that concurrent requests could race
+// over.
+function hasResourceLoaded(url) {
+  return performance.getEntriesByName(url).length > 0;
+}
+
+function resourceLoaded(url) {
+  return new Promise(resolve => {
+    if (hasResourceLoaded(url)) {
+      resolve();
+      return;
+    }
+    const observer = new PerformanceObserver(entries => {
+      if (entries.getEntries().some(entry => entry.name === url)) {
+        observer.disconnect();
+        resolve();
+      }
+    });
+    observer.observe({ type: "resource", buffered: true });
+  });
+}
diff --git a/third_party/blink/web_tests/external/wpt/preload/resources/preload-module.py b/third_party/blink/web_tests/external/wpt/preload/resources/preload-module.py
new file mode 100644
index 0000000..78a0a4dd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/resources/preload-module.py
@@ -0,0 +1,50 @@
+import json
+import time
+
+
+# Serves a JavaScript module for modulepreload tests, letting the test control
+# when each module is served.
+#
+# Query parameters:
+#   uuid=<uuid>     Names the request so that the test can block and release it.
+#                   Required by all of the below.
+#   block=1         Block the response until a release=1 request with the same
+#                   uuid arrives (used to keep a module outstanding while the
+#                   test observes something else).
+#   release=1       Release the blocked request with the same uuid.
+#   import=<url>    Emit `import "<url>";` so the module has a dependency.
+#   syntaxerror=1   Emit a module with a parse error.
+#   (otherwise)     Emit an empty module.
+#
+# Only release=1 ever writes to the stash and only a blocked request ever reads
+# from it, so there is no read-modify-write to race over.
+def main(request, response):
+    params = request.GET
+    uuid = params.first(b"uuid").decode()
+
+    if b"release" in params:
+        try:
+            request.server.stash.put(uuid, True)
+        except Exception:
+            # Already released and not yet taken; nothing to do.
+            pass
+        return (200, [(b"Content-Type", b"text/javascript"),
+                      (b"Cache-Control", b"no-store")], b"")
+
+    if b"block" in params:
+        remaining = 300  # ~30s safety timeout
+        while remaining > 0 and not request.server.stash.take(uuid):
+            time.sleep(0.1)
+            remaining -= 1
+
+    headers = [(b"Content-Type", b"text/javascript"),
+               (b"Cache-Control", b"no-store")]
+
+    if b"syntaxerror" in params:
+        return (200, headers, b"if (")
+
+    if b"import" in params:
+        dep = params.first(b"import").decode()
+        return (200, headers, ("import " + json.dumps(dep) + ";\n").encode())
+
+    return (200, headers, b"export default 1;\n")
diff --git a/third_party/blink/web_tests/external/wpt/push-api/cross-origin-nested.tentative.https.sub.window-expected.txt b/third_party/blink/web_tests/external/wpt/push-api/cross-origin-nested.tentative.https.sub.window-expected.txt
new file mode 100644
index 0000000..0ddc4442
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/push-api/cross-origin-nested.tentative.https.sub.window-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Error: Should have the permission granted to continue but found denied
+[NOTRUN] third party iframe
+[NOTRUN] nested first party iframe
+[NOTRUN] third party worker
+[NOTRUN] nested first party worker
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/push-api/cross-origin-nested.tentative.https.sub.window.js b/third_party/blink/web_tests/external/wpt/push-api/cross-origin-nested.tentative.https.sub.window.js
new file mode 100644
index 0000000..3ce84a7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/push-api/cross-origin-nested.tentative.https.sub.window.js
@@ -0,0 +1,56 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/notifications/resources/helpers.js
+
+// The syntax below will give us a third party URL.
+// See: https://web-platform-tests.org/writing-tests/server-features.html
+const thirdPartyIframe =
+  'https://{{hosts[alt][]}}:{{ports[https][0]}}/push-api/resources/cross-origin-nested-parent.sub.html';
+const promises = new Map();
+
+// Firefox and Chrome deny notification permission in a third party partitioned
+// iframe even if the permission is granted for origin of the iframe.
+
+// Set up the listeners and then create a third party iframe.
+// The iframe will again create a first party iframe.
+promise_setup(async () => {
+  await trySettingPermission("granted");
+
+  // parent: the third party iframe
+  // child: the first party iframe in the third party one (ABA)
+  for (const iframe of ["parent", "child"]) {
+    // from the iframe window, or the worker opened from there
+    for (const worker of ["", "Worker"]) {
+      const sender = iframe + worker;
+      promises.set(sender, new Promise(r => window.addEventListener("message", ev => {
+        if (ev.data.sender === sender) {
+          r(ev.data);
+        }
+      })));
+    }
+  }
+
+  const iframe = document.createElement("iframe");
+  iframe.src = thirdPartyIframe;
+  document.body.append(iframe);
+})
+
+promise_test(async t => {
+  const result = await promises.get("parent");
+  assert_false(result.subscribed, `subscription should not happen`);
+}, "third party iframe");
+
+promise_test(async t => {
+  const result = await promises.get("child");
+  assert_false(result.subscribed, `subscription should not happen`);
+}, "nested first party iframe");
+
+promise_test(async t => {
+  const result = await promises.get("parentWorker");
+  assert_false(result.subscribed, `subscription should not happen`);
+}, "third party worker");
+
+promise_test(async t => {
+  const result = await promises.get("childWorker");
+  assert_false(result.subscribed, `subscription should not happen`);
+}, "nested first party worker");
diff --git a/third_party/blink/web_tests/external/wpt/push-api/cross-origin-same-site-request.tentative.https.sub.window-expected.txt b/third_party/blink/web_tests/external/wpt/push-api/cross-origin-same-site-request.tentative.https.sub.window-expected.txt
new file mode 100644
index 0000000..1eca828
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/push-api/cross-origin-same-site-request.tentative.https.sub.window-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Error: Should have the permission granted to continue but found denied
+[NOTRUN] same-site cross-origin iframe
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/push-api/cross-origin-same-site-request.tentative.https.sub.window.js b/third_party/blink/web_tests/external/wpt/push-api/cross-origin-same-site-request.tentative.https.sub.window.js
new file mode 100644
index 0000000..e27708c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/push-api/cross-origin-same-site-request.tentative.https.sub.window.js
@@ -0,0 +1,32 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/notifications/resources/helpers.js
+
+// The syntax below will give us a same-site cross-origin URL.
+// See: https://web-platform-tests.org/writing-tests/server-features.html
+const sameSiteIframe =
+  'https://{{hosts[][www1]}}:{{ports[https][0]}}/push-api/resources/cross-origin-nested-child.sub.html';
+let promise;
+
+// Firefox and Chrome deny notification permission in a same-site cross-origin
+// iframe even if the permission is granted for origin of the iframe.
+
+// Set up the listeners and then create a same-site iframe.
+promise_setup(async () => {
+  await trySettingPermission("granted");
+
+  promise = new Promise(r => window.addEventListener("message", ev => {
+    if (ev.data.sender === "child") {
+      r(ev.data);
+    }
+  }));
+
+  const iframe = document.createElement("iframe");
+  iframe.src = sameSiteIframe;
+  document.body.append(iframe);
+})
+
+promise_test(async t => {
+  const result = await promise;
+  assert_false(result.subscribed, `subscription should not happen`);
+}, "same-site cross-origin iframe");
diff --git a/third_party/blink/web_tests/external/wpt/push-api/cross-origin-serviceworker.tentative.https.sub.window-expected.txt b/third_party/blink/web_tests/external/wpt/push-api/cross-origin-serviceworker.tentative.https.sub.window-expected.txt
new file mode 100644
index 0000000..bdb9221
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/push-api/cross-origin-serviceworker.tentative.https.sub.window-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Error: Should have the permission granted to continue but found denied
+[NOTRUN] third party serviceworker
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/push-api/cross-origin-serviceworker.tentative.https.sub.window.js b/third_party/blink/web_tests/external/wpt/push-api/cross-origin-serviceworker.tentative.https.sub.window.js
new file mode 100644
index 0000000..8bfc641
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/push-api/cross-origin-serviceworker.tentative.https.sub.window.js
@@ -0,0 +1,29 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/notifications/resources/helpers.js
+
+// The syntax below will give us a third party URL.
+// See: https://web-platform-tests.org/writing-tests/server-features.html
+const thirdPartyOrigin = 'https://{{hosts[alt][]}}:{{ports[https][0]}}';
+const thirdPartyIframe =
+  `${thirdPartyOrigin}/push-api/resources/cross-origin-serviceworker-iframe.sub.html`;
+let promise;
+
+promise_setup(async () => {
+  await trySettingPermission("granted");
+
+  promise = new Promise(r => window.addEventListener("message", ev => {
+    if ("subscribed" in ev.data) {
+      r(ev.data)
+    }
+  }));
+
+  const iframe = document.createElement("iframe");
+  iframe.src = thirdPartyIframe;
+  document.body.append(iframe);
+})
+
+promise_test(async t => {
+  const result = await promise;
+  assert_false(result.subscribed, `subscription should not happen`);
+}, "third party serviceworker");
diff --git a/third_party/blink/web_tests/external/wpt/push-api/resources/cross-origin-nested-child.sub.html b/third_party/blink/web_tests/external/wpt/push-api/resources/cross-origin-nested-child.sub.html
new file mode 100644
index 0000000..b353030
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/push-api/resources/cross-origin-nested-child.sub.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Push subscription nested iframe first party child</title>
+<script src="/notifications/resources/helpers.js"></script>
+<body>
+<script type="module">
+import { permissionTest } from "./permission-helper.js"
+
+await unregisterAllServiceWorker();
+const registration = await navigator.serviceWorker.register("../noop-sw.js");
+
+permissionTest('https://{{hosts[][]}}:{{ports[https][0]}}', "child", registration);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/push-api/resources/cross-origin-nested-parent.sub.html b/third_party/blink/web_tests/external/wpt/push-api/resources/cross-origin-nested-parent.sub.html
new file mode 100644
index 0000000..5d95e1d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/push-api/resources/cross-origin-nested-parent.sub.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Push subscription nested iframe third party parent</title>
+<body>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/notifications/resources/helpers.js"></script>
+<script type="module">
+import { permissionTest } from "./permission-helper.js"
+
+// Grant the permission so that we can confirm the permission being denied
+// regardless of the actual permission status.
+test_driver.set_test_context(window.top);
+await test_driver.set_permission({ name: "notifications" }, "granted");
+
+await unregisterAllServiceWorker();
+const registration = await navigator.serviceWorker.register("../noop-sw.js");
+
+const firstPartyOrigin = 'https://{{hosts[][]}}:{{ports[https][0]}}';
+
+permissionTest(firstPartyOrigin, "parent", registration);
+
+const nestedChild =
+  `${firstPartyOrigin}/push-api/resources/cross-origin-nested-child.sub.html`;
+const iframe = document.createElement("iframe");
+iframe.src = nestedChild;
+document.body.append(iframe);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/push-api/resources/cross-origin-serviceworker-iframe.sub.html b/third_party/blink/web_tests/external/wpt/push-api/resources/cross-origin-serviceworker-iframe.sub.html
new file mode 100644
index 0000000..d6b61be7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/push-api/resources/cross-origin-serviceworker-iframe.sub.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Push subscription cross origin iframe with service worker</title>
+<body>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/notifications/resources/helpers.js"></script>
+<script type="module">
+const firstPartyOrigin = 'https://{{hosts[][]}}:{{ports[https][0]}}';
+
+// As our service worker cannot directly ping the first party window,
+// let's be a proxy here.
+navigator.serviceWorker.addEventListener("message", ev => {
+  window.top.postMessage(ev.data, firstPartyOrigin);
+});
+
+// Grant the permission so that we can confirm the permission being denied
+// regardless of the actual permission status.
+test_driver.set_test_context(window.top);
+await test_driver.set_permission({ name: "notifications" }, "granted");
+
+await unregisterAllServiceWorker();
+const registration = await navigator.serviceWorker.register("permission-sw.js");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/push-api/resources/permission-helper.js b/third_party/blink/web_tests/external/wpt/push-api/resources/permission-helper.js
new file mode 100644
index 0000000..27a16b7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/push-api/resources/permission-helper.js
@@ -0,0 +1,26 @@
+import { createVapid } from "./vapid.js";
+
+export function permissionTest(origin, sender, registration) {
+  function ping(message) {
+    if (!globalThis.WorkerGlobalScope) {
+      window.top.postMessage(message, origin);
+    } else {
+      globalThis.postMessage(message);
+    }
+  }
+
+  (async () => {
+    const vapid = await createVapid();
+    const subscribed = await registration.pushManager.subscribe({
+      applicationServerKey: vapid.publicKey
+    }).then(() => true, _ => false)
+    ping({ sender, subscribed });
+  })();
+
+  if (!globalThis.WorkerGlobalScope) {
+    const workerUrl = new URL(`./permission-worker.js`, import.meta.url);
+    workerUrl.searchParams.set("sender", `${sender}Worker`);
+    const worker = new Worker(workerUrl, { type: "module" });
+    worker.onmessage = ev => ping(ev.data, origin);
+  }
+}
diff --git a/third_party/blink/web_tests/external/wpt/push-api/resources/permission-sw.js b/third_party/blink/web_tests/external/wpt/push-api/resources/permission-sw.js
new file mode 100644
index 0000000..66782c6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/push-api/resources/permission-sw.js
@@ -0,0 +1,19 @@
+importScripts("/notifications/resources/helpers.js");
+
+// Send the result to the clients of this service worker.
+async function ping(message) {
+  const clients = await self.clients.matchAll({ includeUncontrolled: true });
+  for (const client of clients) {
+    client.postMessage(message);
+  }
+  for (const n of await registration.getNotifications()) {
+    n.close();
+  }
+}
+
+(async () => {
+  await untilActivate();
+
+  const subscribed = await registration.pushManager.subscribe().then(() => true, err => false);
+  ping({ subscribed });
+})();
diff --git a/third_party/blink/web_tests/external/wpt/push-api/resources/permission-worker.js b/third_party/blink/web_tests/external/wpt/push-api/resources/permission-worker.js
new file mode 100644
index 0000000..a8214c4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/push-api/resources/permission-worker.js
@@ -0,0 +1,5 @@
+import { permissionTest } from "./permission-helper.js"
+
+const registration = await navigator.serviceWorker.getRegistration();
+const params = new URL(import.meta.url).searchParams;
+permissionTest(null, params.get("sender"), registration);
diff --git a/third_party/blink/web_tests/external/wpt/push-api/worker-subscribe.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/push-api/worker-subscribe.https.window-expected.txt
new file mode 100644
index 0000000..fe10ed7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/push-api/worker-subscribe.https.window-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Error: Should have the permission granted to continue but found denied
+[NOTRUN] Subscribing within a worker
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/push-api/worker-subscribe.https.window.js b/third_party/blink/web_tests/external/wpt/push-api/worker-subscribe.https.window.js
new file mode 100644
index 0000000..b492d027
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/push-api/worker-subscribe.https.window.js
@@ -0,0 +1,22 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/notifications/resources/helpers.js
+
+promise_setup(async () => {
+  await trySettingPermission("granted");
+  await prepareActiveServiceWorker("push-sw.js");
+});
+
+promise_test(async () => {
+  // (no `sender` URL parameter needed as this worker is the only sender)
+  const worker = new Worker("./resources/permission-worker.js", { type: "module" });
+  const { promise, resolve } = Promise.withResolvers();
+  worker.onmessage = ev => {
+    if ("subscribed" in ev.data) {
+      resolve(ev.data)
+    }
+  };
+
+  const result = await promise;
+  assert_true(result.subscribed, `subscription should happen`);
+}, "Subscribing within a worker");
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-name-shadow-expected.txt b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-name-shadow-expected.txt
new file mode 100644
index 0000000..cfc9228
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-name-shadow-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+[FAIL] Outer animation can not see scroll timeline defined by :host
+  assert_equals: expected "x" but got "y"
+[FAIL] Outer animation can not see scroll timeline defined by ::slotted
+  assert_equals: expected "x" but got "y"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-name-shadow.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-name-shadow.html
index a535b2a..b2a54cd 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-name-shadow.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-name-shadow.html
@@ -3,6 +3,7 @@
 <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timelines-named">
 <link rel="help" src="https://github.com/w3c/csswg-drafts/issues/8135">
 <link rel="help" src="https://github.com/w3c/csswg-drafts/issues/8192">
+<link rel="help" src="https://github.com/w3c/csswg-drafts/issues/13974">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/web-animations/testcommon.js"></script>
@@ -55,8 +56,8 @@
     assert_equals(target.getAnimations().length, 1);
     let anim = target.getAnimations()[0];
     assert_not_equals(anim.timeline, null);
-    assert_equals(anim.timeline.axis, 'y');
-  }, 'Outer animation can see scroll timeline defined by :host');
+    assert_equals(anim.timeline.axis, 'x');
+  }, 'Outer animation can not see scroll timeline defined by :host');
 </script>
 
 
@@ -93,8 +94,8 @@
     assert_equals(target.getAnimations().length, 1);
     let anim = target.getAnimations()[0];
     assert_not_equals(anim.timeline, null);
-    assert_equals(anim.timeline.axis, 'y');
-  }, 'Outer animation can see scroll timeline defined by ::slotted');
+    assert_equals(anim.timeline.axis, 'x');
+  }, 'Outer animation can not see scroll timeline defined by ::slotted');
 </script>
 
 
@@ -139,42 +140,42 @@
   }, 'Inner animation can see scroll timeline defined by ::part');
 </script>
 
-
-<template id=scroll_timeline_shadow>
+<template id=scroll_timeline_scoped_ancestor>
   <style>
-    .target {
-      animation: anim 10s linear;
-      animation-timeline: --timeline;
-    }
-    .host {
-      scroll-timeline: --timeline x;
+    .scroller {
+      scroll-timeline: --timeline;
     }
   </style>
   <div class=scroller>
-    <div class=host>
+    <div id=outer_host>
       <template shadowrootmode=open shadowrootclonable>
-        <style>
-          div {
-            scroll-timeline: --timeline y;
-          }
-        </style>
-        <div>
-          <slot></slot>
+        <div id=inner_host>
+          <template shadowrootmode=open shadowrootclonable>
+            <style>
+              /* Technically also loosely matched, but... */
+              @keyframes anim2 {
+                from { z-index: 100; }
+                to { z-index: 100; }
+              }
+
+              .target {
+                animation: anim2 10s linear;
+                animation-timeline: --timeline;
+              }
+            </style>
+            <div class=target></div>
+          </template>
         </div>
       </template>
-      <div class=target></div>
     </div>
   </div>
-  <style>
-  </style>
 </template>
 <script>
   promise_test(async (t) => {
-    inflate(t, scroll_timeline_shadow);
-    let target = main.querySelector('.target');
+    inflate(t, scroll_timeline_scoped_ancestor);
+    let target = outer_host.shadowRoot.querySelector('#inner_host').shadowRoot.querySelector('.target');
     assert_equals(target.getAnimations().length, 1);
     let anim = target.getAnimations()[0];
     assert_not_equals(anim.timeline, null);
-    assert_equals(anim.timeline.axis, 'y');
-  }, 'Slotted element can see scroll timeline within the shadow');
+  }, 'Animation inside shadow DOM can see the scroll timeline defined by the ancestor DOM');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-name-shadow-expected.txt b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-name-shadow-expected.txt
new file mode 100644
index 0000000..b72a07e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-name-shadow-expected.txt
@@ -0,0 +1,7 @@
+This is a testharness.js-based test.
+[FAIL] Outer animation can not see view timeline defined by :host
+  assert_equals: expected "x" but got "y"
+[FAIL] Outer animation can not see view timeline defined by ::slotted
+  assert_equals: expected "x" but got "y"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-name-shadow.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-name-shadow.html
index 11902a3..3d8723ef 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-name-shadow.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-name-shadow.html
@@ -3,6 +3,7 @@
 <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#view-timelines-named">
 <link rel="help" src="https://github.com/w3c/csswg-drafts/issues/8135">
 <link rel="help" src="https://github.com/w3c/csswg-drafts/issues/8192">
+<link rel="help" src="https://github.com/w3c/csswg-drafts/issues/13974">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/web-animations/testcommon.js"></script>
@@ -22,7 +23,6 @@
   }
 </style>
 
-
 <template id=view_timeline_host>
   <style>
     .target {
@@ -56,8 +56,8 @@
     assert_equals(target.getAnimations().length, 1);
     let anim = target.getAnimations()[0];
     assert_not_equals(anim.timeline, null);
-    assert_equals(anim.timeline.axis, 'y');
-  }, 'Outer animation can see view timeline defined by :host');
+    assert_equals(anim.timeline.axis, 'x');
+  }, 'Outer animation can not see view timeline defined by :host');
 </script>
 
 
@@ -94,8 +94,8 @@
     assert_equals(target.getAnimations().length, 1);
     let anim = target.getAnimations()[0];
     assert_not_equals(anim.timeline, null);
-    assert_equals(anim.timeline.axis, 'y');
-  }, 'Outer animation can see view timeline defined by ::slotted');
+    assert_equals(anim.timeline.axis, 'x');
+  }, 'Outer animation can not see view timeline defined by ::slotted');
 </script>
 
 
@@ -140,42 +140,42 @@
   }, 'Inner animation can see view timeline defined by ::part');
 </script>
 
-
-<template id=view_timeline_shadow>
+<template id=view_timeline_scoped_ancestor>
   <style>
-    .target {
-      animation: anim 10s linear;
-      animation-timeline: --timeline;
-    }
-    .host {
-      view-timeline: --timeline x;
+    #outer_host {
+      view-timeline: --timeline;
     }
   </style>
   <div class=scroller>
-    <div class=host>
+    <div id=outer_host>
       <template shadowrootmode=open shadowrootclonable>
-        <style>
-          div {
-            view-timeline: --timeline y;
-          }
-        </style>
-        <div>
-          <slot></slot>
+        <div id=inner_host>
+          <template shadowrootmode=open shadowrootclonable>
+            <style>
+              /* Technically also loosely matched, but... */
+              @keyframes anim {
+                from { z-index: 100; }
+                to { z-index: 100; }
+              }
+
+              .target {
+                animation: anim 10s linear;
+                animation-timeline: --timeline;
+              }
+            </style>
+            <div class=target></div>
+          </template>
         </div>
       </template>
-      <div class=target></div>
     </div>
   </div>
-  <style>
-  </style>
 </template>
 <script>
   promise_test(async (t) => {
-    inflate(t, view_timeline_shadow);
-    let target = main.querySelector('.target');
+    inflate(t, view_timeline_scoped_ancestor);
+    let target = outer_host.shadowRoot.querySelector('#inner_host').shadowRoot.querySelector('.target');
     assert_equals(target.getAnimations().length, 1);
     let anim = target.getAnimations()[0];
     assert_not_equals(anim.timeline, null);
-    assert_equals(anim.timeline.axis, 'y');
-  }, 'Slotted element can see view timeline within the shadow');
+  }, 'Animation inside shadow DOM can see the view timeline defined by the ancestor DOM');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/intermediate-transform.html b/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/intermediate-transform.html
index 89a4a64..24ec968d 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/intermediate-transform.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/intermediate-transform.html
@@ -34,9 +34,7 @@
 <body>
 <div id="container">
     <div id="content">
-      <div class="spacer"></div>
-      <div id="target"></div>
-      <div class="spacer"></div>
+      <div class="spacer"></div><div id="target"></div><div class="spacer"></div>
     </div>
   </div>
 </body>
@@ -50,7 +48,7 @@
                                 { timeline: timeline });
     // Make sure the scroll overflow is ready.
     await waitForAnimationFrames(2);
-    assert_px_equals(timeline.startOffset, 604, 'startOffset');
-    assert_px_equals(timeline.endOffset, 904, 'endOffset');
+    assert_px_equals(timeline.startOffset, 600, 'startOffset');
+    assert_px_equals(timeline.endOffset, 900, 'endOffset');
   });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/view-timeline-inset-expected.txt b/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/view-timeline-inset-expected.txt
new file mode 100644
index 0000000..ea6b70d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/view-timeline-inset-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] view timeline inset as string
+  assert_throws_js: function "() => {\n    new ViewTimeline({\n      subject: target,\n      inset: "1em 2em"\n    });\n  }" did not throw
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/view-timeline-inset.html b/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/view-timeline-inset.html
index 03d762d..ff3abfe 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/view-timeline-inset.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/view-timeline-inset.html
@@ -124,51 +124,6 @@
   }, 'view timeline with inset auto.');
 
 promise_test(async t => {
-  t.add_cleanup(() => {
-    target.classList.remove('big-font');
-  });
-  const anim = await runTimelineInsetTest(t, {
-    inset: [ CSS.em(1), CSS.em(2) ],
-    startOffset: 632,
-    endOffset: 884
-  });
-  verifyTimelineOffsets(anim, 632, 884);
-  target.classList.add('big-font');
-  await runTimelineBoundsTest(t, {
-    anim: anim,
-    startOffset: 640,
-    endOffset: 880,
-  }, 'Adjust for font size increase')
-      .then(anim => verifyTimelineOffsets(anim, 640, 880));
-}, 'view timeline with font relative inset.');
-
-promise_test(async t => {
-  const vw = window.innerWidth;
-  const vh = window.innerHeight;
-  const vmin = Math.min(vw, vh);
-
-  // Compute viewport-relative equivalent values from absolute inset sizes.
-  // This makes the test invariant to absolute viewport size.
-  const startInsetPx = 10;
-  const endInsetPx = 20;
-
-  const vwStart = 100 * startInsetPx / vw;
-  const vwEnd = 100 * endInsetPx / vw;
-  const vminStart = 100 * startInsetPx / vmin;
-  const vminEnd = 100 * endInsetPx / vmin;
-  await runTimelineInsetTest(t, {
-    inset: [ CSS.vw(vwStart), CSS.vw(vwEnd) ],
-    startOffset: 600 + endInsetPx,
-    endOffset: 900 - startInsetPx
-  });
-  await runTimelineInsetTest(t, {
-    inset: [ CSS.vmin(vminStart), CSS.vmin(vminEnd) ],
-    startOffset: 600 + endInsetPx,
-    endOffset: 900 - startInsetPx
-  });
-}, 'view timeline with viewport relative insets.');
-
-promise_test(async t => {
   await runTimelineInsetTest(t, {
     inset: "10px",
     startOffset: 610,
@@ -194,14 +149,25 @@
     startOffset: 600,
     endOffset: 900
   });
-  await runTimelineInsetTest(t, {
-    inset: "1em 2em",
-    startOffset: 632,
-    endOffset: 884
+
+  // Relative units are not allowed.
+  // https://github.com/w3c/csswg-drafts/issues/13852
+  assert_throws_js(TypeError, () => {
+    new ViewTimeline({
+      subject: target,
+      inset: "1em 2em"
+    });
   });
   assert_throws_js(TypeError, () => {
     new ViewTimeline({
       subject: target,
+      inset: "1vw 2vh"
+    });
+  });
+
+  assert_throws_js(TypeError, () => {
+    new ViewTimeline({
+      subject: target,
       inset: "go fish"
     });
   });
@@ -212,7 +178,6 @@
       inset: "1 2"
     });
   });
-
 }, 'view timeline inset as string');
 
 promise_test(async t => {
@@ -229,8 +194,6 @@
       inset: [ CSS.px(10), CSS.px(10), CSS.px(10) ]
     });
   });
-
-
 }, 'view timeline with invalid inset');
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/view-timeline-on-display-none-element.html b/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/view-timeline-on-display-none-element.html
index 1cc23fe..f56c303 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/view-timeline-on-display-none-element.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/view-timeline-on-display-none-element.html
@@ -50,7 +50,9 @@
   t.add_cleanup(() => {
     anim.cancel();
   });
-  anim.rangeStart = "1em";
+  // We use a unit that does not require an element to resolve given:
+  // https://github.com/web-platform-tests/interop/issues/1309
+  anim.rangeStart = "10px";
   container.scrollLeft = 750;
   await waitForNextFrame();
   assert_equals(getComputedStyle(target).opacity, "1",
diff --git a/third_party/blink/web_tests/external/wpt/selection/contenteditable/selection-outside-focused-editing-host.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/selection/contenteditable/selection-outside-focused-editing-host.tentative-expected.txt
new file mode 100644
index 0000000..733d1853
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/selection/contenteditable/selection-outside-focused-editing-host.tentative-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+[FAIL] Modifying selection should do nothing
+  assert_true: expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/selection/contenteditable/selection-outside-focused-editing-host.tentative.html b/third_party/blink/web_tests/external/wpt/selection/contenteditable/selection-outside-focused-editing-host.tentative.html
new file mode 100644
index 0000000..1ab72de0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/selection/contenteditable/selection-outside-focused-editing-host.tentative.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+"use strict";
+
+addEventListener("DOMContentLoaded", () => {
+  const editingHost = document.querySelector("div[contenteditable]");
+  editingHost.focus();
+  test(() => {
+    // Move selection outside the editing host.
+    const nonEditableTextInSpan = document.querySelector("div").querySelector("span").firstChild;
+    getSelection().collapse(
+      nonEditableTextInSpan,
+      "non-editable".length
+    );
+    assert_equals(
+      getSelection().focusNode,
+      nonEditableTextInSpan,
+      "Collapsing selection outside the focused editing host should work"
+    );
+    test(() => {
+      assert_equals(document.activeElement, editingHost);
+    }, "Collapsing selection outside the focused editing host shouldn't cause the blur");
+    test(() => {
+      getSelection().modify("extend", "right", "character");
+      assert_true(getSelection().isCollapsed);
+      assert_equals(
+        getSelection().focusNode,
+        nonEditableTextInSpan
+      );
+      assert_equals(
+        getSelection().focusOffset,
+        "non-editable".length
+      );
+    }, "Modifying selection should do nothing");
+  });
+
+}, {once: true});
+</script>
+</head>
+<body>
+<div><span>non-editable</span> text</div>
+<div contenteditable>editable text</div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/selection/shadow-dom/cross-shadow-boundary-extend.html b/third_party/blink/web_tests/external/wpt/selection/shadow-dom/cross-shadow-boundary-extend.html
new file mode 100644
index 0000000..7be40902
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/selection/shadow-dom/cross-shadow-boundary-extend.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1982627">
+<title>Test that extending Selection collapses it if crossing a shadow DOM boundary</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../selection-test-utils.js"></script>
+<body>
+
+<p id="before">Before shadow root</p>
+<div>
+  <template shadowrootmode="open">
+    <p id="inside">Inside shadow root</p>
+  </template>
+</div>
+<p id="after">After shadow root</p>
+
+<script>
+const before = document.getElementById('before');
+const shadowRoot = document.querySelector('div').shadowRoot;
+const inside = shadowRoot.querySelector('p');
+const after = document.getElementById('after');
+
+test(() => {
+  const selection = getSelection();
+  selection.collapse(before);
+  selection.extend(inside);
+  assert_equals(selection.rangeCount, 1, 'Selection should have exactly 1 range after extend().');
+  const range = selection.getRangeAt(0);
+  assert_equals(SelectionTestUtils.getRangeDescription(range), '(<p id="inside">, 0)',
+    'Selection should be collapsed inside the shadow DOM after extending into shadow DOM.');
+  range.setEnd(after, 0);
+  assert_equals(SelectionTestUtils.getRangeDescription(range), '(<p id="after">, 0)',
+    'Selection should be collapsed outside of the shadow DOM after setting endpoint outside of shadow DOM.');
+});
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/shadow-dom/crashtests/modify-sibling-of-shadowroot-after-attach.html b/third_party/blink/web_tests/external/wpt/shadow-dom/crashtests/modify-sibling-of-shadowroot-after-attach.html
new file mode 100644
index 0000000..d7825631
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/shadow-dom/crashtests/modify-sibling-of-shadowroot-after-attach.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="help" href="https://github.com/servo/servo/issues/46024">
+
+<div id="shadowroot">
+    <span id="insideshadowroot"></span>
+</div>
+<script>
+  onload = _ => {
+    document.body.offsetTop;
+    insideshadowroot.innerText = "Changed";
+    shadowroot.attachShadow({mode: "open"});
+    document.body.appendChild(document.createElement("div"));
+  }
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/svg/animations/animateMotion-rotate-angle-expected.txt b/third_party/blink/web_tests/external/wpt/svg/animations/animateMotion-rotate-angle-expected.txt
new file mode 100644
index 0000000..fe940ff4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/animations/animateMotion-rotate-angle-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+[FAIL] rotate="45" applies a constant 45 degree rotation
+  assert_approx_equals: angle: matrix a expected 0.7071067811865476 +/- 0.0001 but got 1
+[FAIL] rotate="-90" applies a constant -90 degree rotation
+  assert_approx_equals: angle-negative: matrix a expected 6.123233995736766e-17 +/- 0.0001 but got 1
+[FAIL] rotate="30" applies a constant 30 degree rotation to a path animation
+  assert_approx_equals: angle-path: matrix a expected 0.8660254037844387 +/- 0.0001 but got 1
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/svg/animations/animateMotion-rotate-angle.html b/third_party/blink/web_tests/external/wpt/svg/animations/animateMotion-rotate-angle.html
new file mode 100644
index 0000000..ee8f3ef
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/animations/animateMotion-rotate-angle.html
@@ -0,0 +1,104 @@
+<!doctype html>
+<title>&lt;animateMotion> with a numeric 'rotate' value</title>
+<link rel="help" href="https://w3c.github.io/svgwg/specs/animations/#RotateAttribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<svg id="svg" width="400" height="400">
+  <!-- rotate="<number>" applies a constant rotation of that many degrees. -->
+  <rect id="angle" width="10" height="10">
+    <animateMotion from="0,0" to="200,0" rotate="45" fill="freeze" dur="4s"/>
+  </rect>
+  <rect id="angle-negative" width="10" height="10">
+    <animateMotion from="0,0" to="200,0" rotate="-90" fill="freeze" dur="4s"/>
+  </rect>
+  <!-- The same holds for path animations; 'path' is not special w.r.t. 'rotate'. -->
+  <rect id="angle-path" width="10" height="10">
+    <animateMotion path="M0,0 L200,0" rotate="30" fill="freeze" dur="4s"/>
+  </rect>
+  <!-- Explicit zero, absent, and unparseable values all mean no rotation:
+       the initial value of 'rotate' is 0. -->
+  <rect id="angle-zero" width="10" height="10">
+    <animateMotion from="0,0" to="200,0" rotate="0" fill="freeze" dur="4s"/>
+  </rect>
+  <rect id="no-rotate" width="10" height="10">
+    <animateMotion from="0,0" to="200,0" fill="freeze" dur="4s"/>
+  </rect>
+  <rect id="bogus-rotate" width="10" height="10">
+    <animateMotion from="0,0" to="200,0" rotate="bogus" fill="freeze" dur="4s"/>
+  </rect>
+  <!-- Control: rotate="auto" follows the direction of motion. -->
+  <rect id="auto" width="10" height="10">
+    <animateMotion from="0,0" to="0,200" rotate="auto" fill="freeze" dur="4s"/>
+  </rect>
+</svg>
+<script>
+const rootSVGElement = document.getElementById('svg');
+
+// The rotation is post-multiplied onto the translation computed for the motion,
+// so the resulting matrix is [cos, sin, -sin, cos, tx, ty].
+function assert_animation_transform(id, angleInDegrees, tx, ty) {
+  const ctm = document.getElementById(id).getCTM();
+  const radians = angleInDegrees * Math.PI / 180;
+  const epsilon = 1e-4;
+  assert_approx_equals(ctm.a, Math.cos(radians), epsilon, `${id}: matrix a`);
+  assert_approx_equals(ctm.b, Math.sin(radians), epsilon, `${id}: matrix b`);
+  assert_approx_equals(ctm.c, -Math.sin(radians), epsilon, `${id}: matrix c`);
+  assert_approx_equals(ctm.d, Math.cos(radians), epsilon, `${id}: matrix d`);
+  assert_approx_equals(ctm.e, tx, epsilon, `${id}: translation x`);
+  assert_approx_equals(ctm.f, ty, epsilon, `${id}: translation y`);
+}
+
+// Document begin is the moment the load event is triggered, so the timeline
+// cannot be sampled until after that event has been dispatched. Yield once
+// before pausing and driving it manually.
+const ready = new Promise(resolve => {
+  window.addEventListener('load', () => requestAnimationFrame(() => {
+    rootSVGElement.pauseAnimations();
+    resolve();
+  }));
+});
+
+promise_test(async () => {
+  await ready;
+  rootSVGElement.setCurrentTime(2);
+  assert_animation_transform('angle', 45, 100, 0);
+  rootSVGElement.setCurrentTime(4);
+  assert_animation_transform('angle', 45, 200, 0);
+}, 'rotate="45" applies a constant 45 degree rotation');
+
+promise_test(async () => {
+  await ready;
+  rootSVGElement.setCurrentTime(2);
+  assert_animation_transform('angle-negative', -90, 100, 0);
+}, 'rotate="-90" applies a constant -90 degree rotation');
+
+promise_test(async () => {
+  await ready;
+  rootSVGElement.setCurrentTime(2);
+  assert_animation_transform('angle-path', 30, 100, 0);
+}, 'rotate="30" applies a constant 30 degree rotation to a path animation');
+
+promise_test(async () => {
+  await ready;
+  rootSVGElement.setCurrentTime(2);
+  assert_animation_transform('angle-zero', 0, 100, 0);
+}, 'rotate="0" applies no rotation');
+
+promise_test(async () => {
+  await ready;
+  rootSVGElement.setCurrentTime(2);
+  assert_animation_transform('no-rotate', 0, 100, 0);
+}, 'an absent rotate attribute applies no rotation');
+
+promise_test(async () => {
+  await ready;
+  rootSVGElement.setCurrentTime(2);
+  assert_animation_transform('bogus-rotate', 0, 100, 0);
+}, 'an unparseable rotate value applies no rotation');
+
+promise_test(async () => {
+  await ready;
+  rootSVGElement.setCurrentTime(2);
+  assert_animation_transform('auto', 90, 0, 100);
+}, 'rotate="auto" follows the direction of motion');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/svg/animations/svgintegeroptionalinteger-animation-invalid-value-2.html b/third_party/blink/web_tests/external/wpt/svg/animations/svgintegeroptionalinteger-animation-invalid-value-2.html
new file mode 100644
index 0000000..f872b10
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/animations/svgintegeroptionalinteger-animation-invalid-value-2.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<title>Test SVGIntegerOptionalInteger animation with invalid value: String value in by attribute.</title>
+<link rel="help" href="https://www.w3.org/TR/2001/REC-smil-animation-20010904/#ByAttribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<svg id="svg">
+  <defs>
+    <filter id="effect">
+      <feConvolveMatrix id="matrix" order="3 3" kernelMatrix="0 0 0 0 1 0 0 0 0">
+        <animate attributeName="order" begin="0s" dur="4s" by="A B" />
+      </feConvolveMatrix>
+    </filter>
+  </defs>
+  <rect x="10" y="10" width="80" height="80" fill="blue" filter="url(#effect)" />
+</svg>
+
+<script>
+
+async_test(t => {
+    const svg = document.getElementById("svg");
+    const matrix = document.getElementById("matrix")
+
+    window.addEventListener('load', t.step_func(() => {
+        // Pause before seeking so the timeline can't advance past 2s.
+        svg.pauseAnimations();
+        svg.setCurrentTime(2);
+
+        requestAnimationFrame(t.step_func_done(() => {
+          assert_equals(matrix.orderX.baseVal, 3);
+          assert_equals(matrix.orderY.baseVal, 3);
+          assert_equals(matrix.orderX.animVal, matrix.orderX.baseVal);
+          assert_equals(matrix.orderY.animVal, matrix.orderY.baseVal);
+        }));
+    }));
+  });
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/svg/animations/svgnumber-animation-invalid-value-2.html b/third_party/blink/web_tests/external/wpt/svg/animations/svgnumber-animation-invalid-value-2.html
new file mode 100644
index 0000000..80b81a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/animations/svgnumber-animation-invalid-value-2.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<title>Test SVGNumber animation with invalid values: String value in by attribute.</title>
+<link rel="help" href="https://www.w3.org/TR/2001/REC-smil-animation-20010904/#ByAttribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<svg id="svg">
+    <defs>
+        <filter id="effect">
+            <feComposite id="composite" in="SourceGraphic" in2="SourceAlpha" operator="arithmetic" k1="1">
+                <animate attributeName="k1" begin="0s" dur="4s" by="ERROR" />
+            </feComposite>
+        </filter>
+    </defs>
+    <rect id="rect" x="0" y="0" width="100" height="100" fill="green" filter="url(#effect)" />
+</svg>
+
+<script>
+
+async_test(t => {
+    const svg = document.getElementById("svg");
+    const composite = document.getElementById("composite");
+
+    window.addEventListener('load', t.step_func(() => {
+        // Pause before seeking so the timeline can't advance past 2s.
+        svg.pauseAnimations();
+        svg.setCurrentTime(2);
+
+        requestAnimationFrame(t.step_func_done(() => {
+          assert_equals(composite.k1.baseVal, 1);
+          assert_equals(composite.k1.animVal, composite.k1.baseVal);
+        }));
+    }));
+  });
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/svg/animations/svgnumber-to-animation-valid-value-1.html b/third_party/blink/web_tests/external/wpt/svg/animations/svgnumber-to-animation-valid-value-1.html
new file mode 100644
index 0000000..4fc4dd3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/animations/svgnumber-to-animation-valid-value-1.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>Test SVGNumber to-animation with a valid value still animates.</title>
+<link rel="help" href="https://www.w3.org/TR/2001/REC-smil-animation-20010904/#ToAttribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<svg id="svg">
+    <defs>
+        <filter id="effect">
+            <feComposite id="composite" in="SourceGraphic" in2="SourceAlpha" operator="arithmetic" k1="1">
+                <animate attributeName="k1" begin="0s" dur="4s" to="3" />
+            </feComposite>
+        </filter>
+    </defs>
+    <rect id="rect" x="0" y="0" width="100" height="100" fill="green" filter="url(#effect)" />
+</svg>
+
+<script>
+
+// A to-animation has an empty 'from'; the start value comes from the base value at
+// runtime. This guards against the empty-'from' path incorrectly rejecting the animation.
+async_test(t => {
+    const svg = document.getElementById("svg");
+    const composite = document.getElementById("composite");
+
+    window.addEventListener('load', t.step_func(() => {
+        // Pause before seeking so the timeline can't advance past 2s, which keeps the
+        // sampled value exact.
+        svg.pauseAnimations();
+        svg.setCurrentTime(2);
+
+        requestAnimationFrame(t.step_func_done(() => {
+          assert_equals(composite.k1.baseVal, 1);
+          // Linear interpolation from base (1) to 'to' (3) at the mid-point.
+          assert_equals(composite.k1.animVal, 2);
+        }));
+    }));
+  });
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/svg/animations/svgnumberoptionalnumber-animation-invalid-value-2.html b/third_party/blink/web_tests/external/wpt/svg/animations/svgnumberoptionalnumber-animation-invalid-value-2.html
new file mode 100644
index 0000000..c5827f9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/animations/svgnumberoptionalnumber-animation-invalid-value-2.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>Test SVGNumberOptionalNumber animation with invalid values: String value in by attribute.</title>
+<link rel="help" href="https://www.w3.org/TR/2001/REC-smil-animation-20010904/#ByAttribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<svg id="svg">
+    <defs>
+        <filter>
+            <feGaussianBlur id="blur" stdDeviation="5 5">
+                <animate attributeName="stdDeviation" begin="0s" dur="4s" by="A Z" />
+            </feGaussianBlur>
+        </filter>
+    </defs>
+    <rect x="50" y="50" width="100" height="100" fill="green" filter="url(#filter)"></rect>
+</svg>
+
+<script>
+
+async_test(t => {
+    const svg = document.getElementById("svg");
+    const feGaussianBlur = document.getElementById("blur");
+
+    window.addEventListener('load', t.step_func(() => {
+        // Pause before seeking so the timeline can't advance past 2s.
+        svg.pauseAnimations();
+        svg.setCurrentTime(2);
+
+        requestAnimationFrame(t.step_func_done(() => {
+          assert_equals(feGaussianBlur.stdDeviationX.baseVal, 5);
+          assert_equals(feGaussianBlur.stdDeviationY.baseVal, 5);
+          assert_equals(feGaussianBlur.stdDeviationX.animVal, feGaussianBlur.stdDeviationX.baseVal);
+          assert_equals(feGaussianBlur.stdDeviationY.animVal, feGaussianBlur.stdDeviationY.baseVal);
+        }));
+    }));
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/svg/animations/svgpath-animation-invalid-value-2.html b/third_party/blink/web_tests/external/wpt/svg/animations/svgpath-animation-invalid-value-2.html
new file mode 100644
index 0000000..4b93ceb5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/animations/svgpath-animation-invalid-value-2.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>Test SVGPath animation with invalid values: Malformed path in to attribute.</title>
+<link rel="help" href="https://www.w3.org/TR/2001/REC-smil-animation-20010904/#ToAttribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/animated-path-helpers.js"></script>
+
+<svg id="svg">
+  <path id="path" d="M 40 40 L 60 40 L 60 60 L 40 60 z" fill="green">
+    <animate attributeName="d" from="M 0 0 L 10 0 L 10 10 z" to="ERROR" begin="0s" dur="4s" fill="freeze" />
+  </path>
+</svg>
+
+<script>
+
+async_test(t => {
+    const svg = document.getElementById("svg");
+    const path = document.getElementById("path");
+
+    window.addEventListener('load', t.step_func(() => {
+        // Pause before seeking so the timeline can't advance past 2s.
+        svg.pauseAnimations();
+        svg.setCurrentTime(2);
+
+        requestAnimationFrame(t.step_func_done(() => {
+          assert_animated_path_equals(path,
+                               "M 40 40 L 60 40 L 60 60 L 40 60 z");
+        }));
+    }));
+  });
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/svg/embedded/svg-zero-size-crash.html b/third_party/blink/web_tests/external/wpt/svg/embedded/svg-zero-size-crash.html
new file mode 100644
index 0000000..97b22a0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/embedded/svg-zero-size-crash.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<svg viewBox style="width: 0">
+  <polyline points="0,0 1000000000,1"></polyline>
+</svg>
diff --git a/third_party/blink/web_tests/external/wpt/svg/geometry/svg-geometry-apis-in-non-rendered-elements.html b/third_party/blink/web_tests/external/wpt/svg/geometry/svg-geometry-apis-in-non-rendered-elements.html
new file mode 100644
index 0000000..afc9bde
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/geometry/svg-geometry-apis-in-non-rendered-elements.html
@@ -0,0 +1,164 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Geometry APIs on SVG elements that generate no box</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-getclientrects">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect">
+<link rel="help" href="https://drafts.csswg.org/css-display-3/#valdef-display-none">
+<link rel="author" title="Karl Dubost" href="mailto:k_dubost@apple.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+  #css-display-none { display: none; }
+  #visibility-hidden-rect { visibility: hidden; }
+  /* Ahem keeps the <text> measurement deterministic across platforms. The
+     expected result is 0 either way, but an engine that fails this reports the
+     width it measured, and that should not vary per platform. */
+  #text-in-display-none { font: 10px/1 Ahem; }
+</style>
+<body>
+
+<!--
+  CSSOM View, getClientRects():
+    1. If the element on which it was invoked does not have an associated box
+       return an empty DOMRectList object and stop this algorithm.
+    2. If the element has an associated SVG layout box return [...] a single
+       DOMRect [...]
+
+  Step 1 precedes step 2, so the SVG branch is only reachable for an element
+  that has a box. CSS Display 3 says display:none causes the element and its
+  descendants "to not generate any boxes at all".
+
+  svg-get-bounding-client-rect-in-non-rendered-elements.html covers a <rect>
+  in <g display=none>, <defs>, <pattern> and <symbol>. This file covers the
+  rest of the space: the other hidden container types, display:none applied
+  from a stylesheet, nesting depth, containers rather than shapes, non-shape
+  leaf elements, and dynamic changes. It also pins the cases that DO generate
+  boxes, so that a fix suppressing too much is caught here.
+-->
+
+<svg width="200" height="200" id="root">
+  <mask id="mask"><rect id="in-mask" width="10" height="10"/></mask>
+  <clipPath id="clip-path"><rect id="in-clippath" width="10" height="10"/></clipPath>
+  <marker id="marker"><rect id="in-marker" width="10" height="10"/></marker>
+  <linearGradient id="gradient"><rect id="in-gradient" width="10" height="10"/></linearGradient>
+  <filter id="filter"><rect id="in-filter" width="10" height="10"/></filter>
+  <symbol id="symbol"><rect id="in-symbol" width="10" height="10"/></symbol>
+
+  <g id="css-display-none"><rect id="in-css-display-none" width="10" height="10"/></g>
+
+  <g display="none"><g><g><rect id="deeply-nested" width="10" height="10"/></g></g></g>
+
+  <defs><g id="g-in-defs"><rect width="10" height="10"/></g></defs>
+
+  <g display="none">
+    <text id="text-in-display-none" x="0" y="10">text</text>
+    <image id="image-in-display-none" width="10" height="10"/>
+    <foreignObject id="foreignobject-in-display-none" width="10" height="10"></foreignObject>
+    <use id="use-in-display-none" href="#rendered-rect"/>
+  </g>
+
+  <rect id="rendered-rect" width="10" height="10"/>
+  <rect id="visibility-hidden-rect" width="10" height="10"/>
+  <g opacity="0"><rect id="in-opacity-zero-g" width="10" height="10"/></g>
+  <g visibility="hidden"><rect id="in-visibility-hidden-g" width="10" height="10"/></g>
+</svg>
+
+<script>
+const SVG_NS = "http://www.w3.org/2000/svg";
+
+// getBoundingClientRect() and getClientRects() are asserted as separate
+// subtests so that neither can mask the other: an engine may return the
+// correct all-zero rect from getBoundingClientRect() while still returning a
+// non-empty list from getClientRects(), and that is worth reporting on its
+// own.
+function assertNoBox(id, description) {
+  test(function() {
+    const rect = document.getElementById(id).getBoundingClientRect();
+    assert_equals(rect.width, 0, "width");
+    assert_equals(rect.height, 0, "height");
+    assert_equals(rect.x, 0, "x");
+    assert_equals(rect.y, 0, "y");
+  }, description + ", getBoundingClientRect()");
+
+  test(function() {
+    assert_equals(document.getElementById(id).getClientRects().length, 0);
+  }, description + ", getClientRects()");
+}
+
+function assertHasBox(id, description) {
+  test(function() {
+    const rect = document.getElementById(id).getBoundingClientRect();
+    assert_equals(rect.width, 10, "width");
+    assert_equals(rect.height, 10, "height");
+  }, description + ", getBoundingClientRect()");
+
+  test(function() {
+    assert_equals(document.getElementById(id).getClientRects().length, 1);
+  }, description + ", getClientRects()");
+}
+
+// Hidden container element types other than the ones already covered.
+assertNoBox("in-mask", "rect in <mask> has no box");
+assertNoBox("in-clippath", "rect in <clipPath> has no box");
+assertNoBox("in-marker", "rect in <marker> has no box");
+assertNoBox("in-gradient", "rect in <linearGradient> has no box");
+assertNoBox("in-filter", "rect in <filter> has no box");
+// The existing test only checks <symbol> itself and a child carrying its own
+// display:none. A plain child of <symbol> is the interesting case.
+assertNoBox("in-symbol", "rect in <symbol> has no box");
+
+// display:none from a stylesheet rather than the presentation attribute.
+assertNoBox("in-css-display-none", "rect in <g> with CSS display:none has no box");
+
+// The element being measured is not a child of the hidden container.
+assertNoBox("deeply-nested", "rect three levels below <g display=none> has no box");
+
+// A container element rather than a shape.
+assertNoBox("g-in-defs", "<g> in <defs> has no box");
+
+// Leaf element types other than shapes.
+assertNoBox("text-in-display-none", "<text> in <g display=none> has no box");
+assertNoBox("image-in-display-none", "<image> in <g display=none> has no box");
+assertNoBox("foreignobject-in-display-none", "<foreignObject> in <g display=none> has no box");
+assertNoBox("use-in-display-none", "<use> in <g display=none> has no box");
+
+// Boxes that do exist. Neither visibility nor opacity removes a box, so a fix
+// for the cases above must not suppress these.
+assertHasBox("rendered-rect", "rendered rect has a box");
+assertHasBox("visibility-hidden-rect", "rect with visibility:hidden has a box");
+assertHasBox("in-opacity-zero-g", "rect in <g opacity=0> has a box");
+assertHasBox("in-visibility-hidden-g", "rect in <g visibility=hidden> has a box");
+
+test(function() {
+  const g = document.createElementNS(SVG_NS, "g");
+  const rect = document.createElementNS(SVG_NS, "rect");
+  rect.setAttribute("width", "10");
+  rect.setAttribute("height", "10");
+  g.appendChild(rect);
+  this.add_cleanup(() => g.remove());
+  document.getElementById("root").appendChild(g);
+
+  assert_equals(rect.getBoundingClientRect().width, 10, "width while rendered");
+
+  g.setAttribute("display", "none");
+  assert_equals(rect.getBoundingClientRect().width, 0,
+    "width after the parent becomes display:none");
+
+  g.removeAttribute("display");
+  assert_equals(rect.getBoundingClientRect().width, 10,
+    "width after the parent is rendered again");
+}, "display is honoured when toggled on an ancestor after layout");
+
+test(function() {
+  const svg = document.createElementNS(SVG_NS, "svg");
+  const rect = document.createElementNS(SVG_NS, "rect");
+  rect.setAttribute("width", "10");
+  rect.setAttribute("height", "10");
+  svg.appendChild(rect);
+
+  assert_equals(rect.getBoundingClientRect().width, 0, "width");
+  assert_equals(rect.getClientRects().length, 0, "getClientRects().length");
+}, "rect in a subtree that was never inserted has no box");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/svg/interact/scripted/focus-tabindex-invalid-value.svg b/third_party/blink/web_tests/external/wpt/svg/interact/scripted/focus-tabindex-invalid-value.svg
new file mode 100644
index 0000000..c005276f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/interact/scripted/focus-tabindex-invalid-value.svg
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:h="http://www.w3.org/1999/xhtml">
+    <title>SVG Test: focus - resetting an explicitly set tabindex</title>
+    <metadata>
+        <h:link rel="help" href="https://w3c.github.io/svgwg/svg2-draft/interact.html#Focus"/>
+        <h:link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#attr-tabindex"/>
+        <h:meta name="assert" content="Verify that an explicitly set tabIndex is reset to the default value when the tabindex attribute is changed to a value that is not a valid integer, or removed"/>
+    </metadata>
+    <a id="anchor" href="#"></a>
+    <rect id="rect"></rect>
+    <g id="g"></g>
+    <circle id="circle"></circle>
+    <text id="text"></text>
+    <svg id="nested"></svg>
+    <h:script src="/resources/testharness.js"/>
+    <h:script src="/resources/testharnessreport.js"/>
+    <script><![CDATA[
+    // tabindex is defined by HTML, which parses the attribute as an integer and
+    // falls back to the default tab index for any value that fails to parse.
+    // That includes the empty string, garbage, and out-of-range integers.
+    const invalidValues = ["", "invalid", " ", "9999999999", "-9999999999"];
+
+    // The HTML rules for parsing integers stop at the first non-digit and keep
+    // the digits collected so far, so these are valid despite the trailing junk.
+    const valuesWithTrailingGarbage = [["1.5", 1], ["4abc", 4], ["-2 ", -2]];
+
+    // [id, default tabIndex]
+    const elements = [
+        ["anchor", 0],
+        ["rect", -1],
+        ["g", -1],
+        ["circle", -1],
+        ["text", -1],
+        ["nested", -1],
+    ];
+
+    for (const [id, expectedDefault] of elements) {
+        const el = document.getElementById(id);
+
+        test(() => {
+            assert_equals(el.tabIndex, expectedDefault);
+        }, `default value of ${id}.tabIndex`);
+
+        for (const invalidValue of invalidValues) {
+            test(() => {
+                el.setAttribute("tabindex", "3");
+                assert_equals(el.tabIndex, 3, "valid value");
+
+                el.setAttribute("tabindex", invalidValue);
+                assert_equals(el.getAttribute("tabindex"), invalidValue);
+                assert_equals(el.tabIndex, expectedDefault, "invalid value");
+
+                el.removeAttribute("tabindex");
+            }, `changing tabindex from "3" to "${invalidValue}" resets ${id}.tabIndex`);
+        }
+
+        test(() => {
+            valuesWithTrailingGarbage.forEach(([value, expectedTabIndex]) => {
+                el.setAttribute("tabindex", "3");
+                assert_equals(el.tabIndex, 3, "valid value");
+
+                el.setAttribute("tabindex", value);
+                assert_equals(el.tabIndex, expectedTabIndex, `"${value}"`);
+            });
+            el.removeAttribute("tabindex");
+        }, `trailing garbage after the digits of ${id}'s tabindex is ignored`);
+
+        test(() => {
+            el.setAttribute("tabindex", "3");
+            assert_equals(el.tabIndex, 3, "valid value");
+
+            el.removeAttribute("tabindex");
+            assert_equals(el.tabIndex, expectedDefault, "attribute removed");
+        }, `removing tabindex resets ${id}.tabIndex`);
+    }
+    ]]></script>
+</svg>
diff --git a/third_party/blink/web_tests/external/wpt/svg/types/scripted/SVGAnimatedRect.html b/third_party/blink/web_tests/external/wpt/svg/types/scripted/SVGAnimatedRect.html
index a4842dc..af7ffd92 100644
--- a/third_party/blink/web_tests/external/wpt/svg/types/scripted/SVGAnimatedRect.html
+++ b/third_party/blink/web_tests/external/wpt/svg/types/scripted/SVGAnimatedRect.html
@@ -25,6 +25,7 @@
   numRef.x = 100;
   assert_equals(numRef.x, 100);
   assert_equals(svgElement.viewBox.baseVal.x, 100);
+  assert_true(svgElement.hasAttribute("viewBox"));
 
   // Check that assigning to baseVal has no effect, as no setter is defined.
   svgElement.viewBox.baseVal = -1;
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-constantsourcenode-interface/constant-source-onended-not-connected.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-constantsourcenode-interface/constant-source-onended-not-connected.html
new file mode 100644
index 0000000..2d080b5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-constantsourcenode-interface/constant-source-onended-not-connected.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      AudioScheduledSourceNode fires onended when not connected to the destination
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      // A scheduled source node that is started and stopped must fire its
+      // "ended" event once it reaches its stop time, even if it was never
+      // connected to the destination.
+      // https://webaudio.github.io/web-audio-api/#dom-audioscheduledsourcenode-onended
+
+      const sampleRate = 44100;
+      // Number of frames the source will run; fairly arbitrary.
+      const numberOfFrames = 32;
+      // Number of frames to render; should be larger than numberOfFrames.
+      const renderFrames = 16 * numberOfFrames;
+
+      promise_test(async () => {
+        const context = new OfflineAudioContext(1, renderFrames, sampleRate);
+        const source = new ConstantSourceNode(context);
+        // Intentionally do not connect the source to context.destination.
+        const ended = new Promise(resolve => source.onended = resolve);
+
+        source.start();
+        source.stop(numberOfFrames / context.sampleRate);
+        context.startRendering();
+
+        await ended;
+      }, 'ConstantSourceNode fires onended when not connected to the destination');
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/__init__.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/__init__.py
index 1711977..f9936db0 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/__init__.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/__init__.py
@@ -66,11 +66,11 @@
     assert info["originalOpener"] == original_opener
 
 
-async def assert_document_status(bidi_session, context, visible, focused):
+async def assert_document_status(bidi_session, context, get_document_focus, visible, focused):
     state = "visible" if visible else "hidden"
 
     assert await get_visibility_state(bidi_session, context) == state
-    assert await get_document_focus(bidi_session, context) is focused
+    assert await get_document_focus(context) is focused
 
 
 def assert_navigation_info(event, expected_navigation_info):
@@ -103,16 +103,6 @@
         assert event["userContext"] == expected_navigation_info["userContext"]
 
 
-async def get_document_focus(bidi_session, context: Mapping[str, Any]) -> str:
-    result = await bidi_session.script.call_function(
-        function_declaration="""() => {
-        return document.hasFocus();
-    }""",
-        target=ContextTarget(context["context"]),
-        await_promise=False)
-    return result["value"]
-
-
 async def get_visibility_state(bidi_session, context: Mapping[str, Any]) -> str:
     result = await bidi_session.script.call_function(
         function_declaration="""() => {
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/activate/activate.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/activate/activate.py
index f6e5b9d2..35e4a34 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/activate/activate.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/activate/activate.py
@@ -8,18 +8,18 @@
 
 
 @pytest.mark.parametrize("type_hint", ["tab", "window"])
-async def test_switch_between_contexts(bidi_session, top_context, type_hint):
+async def test_switch_between_contexts(bidi_session, top_context, get_document_focus, type_hint):
     is_window = type_hint == "window"
 
     new_context = await bidi_session.browsing_context.create(type_hint=type_hint)
 
     await bidi_session.browsing_context.activate(context=top_context["context"])
-    await assert_document_status(bidi_session, top_context, visible=True, focused=True)
-    await assert_document_status(bidi_session, new_context, visible=is_window, focused=False)
+    await assert_document_status(bidi_session, top_context, get_document_focus, visible=True, focused=True)
+    await assert_document_status(bidi_session, new_context, get_document_focus, visible=is_window, focused=False)
 
     await bidi_session.browsing_context.activate(context=new_context["context"])
-    await assert_document_status(bidi_session, top_context, visible=is_window, focused=False)
-    await assert_document_status(bidi_session, new_context, visible=True, focused=True)
+    await assert_document_status(bidi_session, top_context, get_document_focus, visible=is_window, focused=False)
+    await assert_document_status(bidi_session, new_context, get_document_focus, visible=True, focused=True)
 
 
 async def test_keeps_element_focused(bidi_session, inline, new_tab, top_context):
@@ -42,21 +42,21 @@
     assert await is_element_focused(bidi_session, new_tab, "input")
 
 
-async def test_multiple_activation(bidi_session, inline, new_tab):
+async def test_multiple_activation(bidi_session, inline, new_tab, get_document_focus):
     await bidi_session.browsing_context.navigate(
         context=new_tab["context"],
         url=inline(
             "<input><script>document.querySelector('input').focus();</script>"),
         wait="complete")
 
-    await assert_document_status(bidi_session, new_tab, visible=True, focused=True)
+    await assert_document_status(bidi_session, new_tab, get_document_focus, visible=True, focused=True)
     assert await is_element_focused(bidi_session, new_tab, "input")
 
     await bidi_session.browsing_context.activate(context=new_tab["context"])
-    await assert_document_status(bidi_session, new_tab, visible=True, focused=True)
+    await assert_document_status(bidi_session, new_tab, get_document_focus, visible=True, focused=True)
     assert await is_element_focused(bidi_session, new_tab, "input")
 
     # Activate again.
     await bidi_session.browsing_context.activate(context=new_tab["context"])
-    await assert_document_status(bidi_session, new_tab, visible=True, focused=True)
+    await assert_document_status(bidi_session, new_tab, get_document_focus, visible=True, focused=True)
     assert await is_element_focused(bidi_session, new_tab, "input")
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/create/background.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/create/background.py
index dc5d1e22..cc06015 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/create/background.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/create/background.py
@@ -3,23 +3,23 @@
 
 pytestmark = pytest.mark.asyncio
 
-from .. import get_document_focus, get_visibility_state
+from .. import get_visibility_state
 
 
 @pytest.mark.parametrize("type_hint", ["tab", "window"])
-async def test_background_default_false(bidi_session, type_hint):
+async def test_background_default_false(bidi_session, get_document_focus, type_hint):
     new_context = await bidi_session.browsing_context.create(type_hint=type_hint)
 
     try:
         assert await get_visibility_state(bidi_session, new_context) == "visible"
-        assert await get_document_focus(bidi_session, new_context) is True
+        assert await get_document_focus(new_context) is True
     finally:
         await bidi_session.browsing_context.close(context=new_context["context"])
 
 
 @pytest.mark.parametrize("type_hint", ["tab", "window"])
 @pytest.mark.parametrize("background", [True, False])
-async def test_background(bidi_session, top_context, type_hint, background):
+async def test_background(bidi_session, top_context, get_document_focus, type_hint, background):
     new_context = await bidi_session.browsing_context.create(type_hint=type_hint, background=background)
 
     try:
@@ -28,7 +28,7 @@
         else:
             assert await get_visibility_state(bidi_session, new_context) == "visible"
 
-        assert await get_document_focus(bidi_session, new_context) != background
+        assert await get_document_focus(new_context) != background
     finally:
         await bidi_session.browsing_context.close(context=new_context["context"])
 
@@ -36,7 +36,7 @@
 @pytest.mark.parametrize("type_hint", ["tab", "window"])
 @pytest.mark.parametrize("background", [True, False])
 async def test_create_in_parallel(
-    bidi_session, top_context, wait_for_future_safe, type_hint, background
+    bidi_session, top_context, wait_for_future_safe, get_document_focus, type_hint, background
 ):
     # Create 2 browsing contexts in quick succession, without waiting for
     # the individual commands to resolve.
@@ -55,11 +55,11 @@
         if background:
             # if background was true, the initial tab should still be selected
             assert await get_visibility_state(bidi_session, top_context) == "visible"
-            assert await get_document_focus(bidi_session, top_context)
+            assert await get_document_focus(top_context)
         else:
             # otherwise either context 1 or 2 might end up with the visibility and focus.
-            context_1_focus = await get_document_focus(bidi_session, context_1)
-            context_2_focus = await get_document_focus(bidi_session, context_2)
+            context_1_focus = await get_document_focus(context_1)
+            context_2_focus = await get_document_focus(context_2)
             assert context_1_focus or context_2_focus
 
             context_1_visible = await get_visibility_state(bidi_session, context_1) == "visible"
@@ -68,4 +68,4 @@
 
     finally:
         await bidi_session.browsing_context.close(context=context_1["context"])
-        await bidi_session.browsing_context.close(context=context_2["context"])
\ No newline at end of file
+        await bidi_session.browsing_context.close(context=context_2["context"])
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/create/type.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/create/type.py
index f96598fa..db46704 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/create/type.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/create/type.py
@@ -9,22 +9,30 @@
 
 
 @pytest.mark.parametrize("type_hint", ["tab", "window"])
-async def test_type(bidi_session, wait_for_event, wait_for_future_safe, subscribe_events, top_context, type_hint):
+async def test_type(
+    bidi_session,
+    wait_for_event,
+    wait_for_future_safe,
+    subscribe_events,
+    top_context,
+    get_document_focus,
+    type_hint
+):
     await subscribe_events(["browsingContext.contextCreated"])
     is_window = type_hint == "window"
 
     contexts = await bidi_session.browsing_context.get_tree(max_depth=0)
     assert len(contexts) == 1
 
-    await assert_document_status(bidi_session, top_context, visible=True, focused=True)
+    await assert_document_status(bidi_session, top_context, get_document_focus, visible=True, focused=True)
 
     on_entry = wait_for_event("browsingContext.contextCreated")
     new_context = await bidi_session.browsing_context.create(type_hint=type_hint)
     context_info = await wait_for_future_safe(on_entry)
     assert contexts[0]["context"] != new_context["context"]
 
-    await assert_document_status(bidi_session, new_context, visible=True, focused=True)
-    await assert_document_status(bidi_session, top_context, visible=is_window, focused=False)
+    await assert_document_status(bidi_session, new_context, get_document_focus, visible=True, focused=True)
+    await assert_document_status(bidi_session, top_context, get_document_focus, visible=is_window, focused=False)
 
     # Check there is an additional browsing context
     contexts = await bidi_session.browsing_context.get_tree(max_depth=0)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/external/webauthn/META.yml b/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/external/webauthn/META.yml
new file mode 100644
index 0000000..3dce173
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/external/webauthn/META.yml
@@ -0,0 +1,4 @@
+spec: https://w3c.github.io/webauthn/
+suggested_reviewers:
+  - nsatragno
+  - whimboo
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/bidi/fixtures.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/bidi/fixtures.py
index dad41f9..c79ed28 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/bidi/fixtures.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/bidi/fixtures.py
@@ -124,6 +124,20 @@
 
 
 @pytest_asyncio.fixture
+async def get_document_focus(bidi_session):
+    async def get_document_focus(context):
+        result = await bidi_session.script.call_function(
+            function_declaration="""() => {
+            return document.hasFocus();
+        }""",
+            target=ContextTarget(context["context"]),
+            await_promise=False)
+        return result["value"]
+
+    return get_document_focus
+
+
+@pytest_asyncio.fixture
 async def subscribe_events(bidi_session):
     subscriptions = []
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-events.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-events.html
index 57b691a..cc3cdfa7 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-events.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCSctpTransport-events.html
@@ -45,10 +45,12 @@
   await pc1.setRemoteDescription(answer);
   await pc1ConnectedWaiter;
   await pc2ConnectedWaiter;
-  const pc1ClosedWaiter = waitForState(pc1.sctp, 'closed');
   const pc2ClosedWaiter = waitForState(pc2.sctp, 'closed');
   pc1.close();
-  await pc1ClosedWaiter;
+  // We do not expect to see a statechange event on pc1, since we closed it.
+  // See https://github.com/w3c/webrtc-pc/issues/3126
+  // The spec might change on this, but until that is decided we should not put
+  // an expectation like this in an Interop 2026 test.
   await pc2ClosedWaiter;
 }, 'SctpTransport reaches connected and closed state');
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/protocol/rtp-headerextensions.html b/third_party/blink/web_tests/external/wpt/webrtc/protocol/rtp-headerextensions.html
index d1d8bb6..65650e59 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/protocol/rtp-headerextensions.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/protocol/rtp-headerextensions.html
@@ -84,18 +84,23 @@
   const offer = await pc.createOffer();
   const section = SDPUtils.splitSections(offer.sdp)[1];
   const extensions = SDPUtils.matchPrefix(section, 'a=extmap:')
-    .map(line => SDPUtils.parseExtmap(line));
-  const extension_not_mid = extensions.find(e => e.uri !== 'urn:ietf:params:rtp-hdrext:sdes:mid');
+    .map(line => SDPUtils.parseExtmap(line)).reduce(
+      (acc, ext) => { acc[ext.direction].push(ext); return acc;},
+      {sendrecv: [], sendonly: [], recvonly: []});
+  const extension_not_mid = extensions.sendrecv.find(e => e.uri !== 'urn:ietf:params:rtp-hdrext:sdes:mid');
   await pc.setRemoteDescription({type :'offer', sdp: offer.sdp.replace(extension_not_mid.uri, 'bogus')});
 
   await pc.setLocalDescription();
   const answer_section = SDPUtils.splitSections(pc.localDescription.sdp)[1];
   const answer_extensions = SDPUtils.matchPrefix(answer_section, 'a=extmap:')
-    .map(line => SDPUtils.parseExtmap(line));
-  assert_equals(answer_extensions.length, extensions.length - 1);
-  assert_false(!!extensions.find(e => e.uri === 'bogus'));
-  for (const answer_extension of answer_extensions) {
-    assert_true(!!extensions.find(e => e.uri === answer_extension.uri));
+    .map(line => SDPUtils.parseExtmap(line)).reduce(
+      (acc, ext) => { acc[ext.direction].push(ext); return acc;},
+      {sendrecv: [], sendonly: [], recvonly: []});
+
+  assert_equals(answer_extensions.sendrecv.length, extensions.sendrecv.length - 1);
+  assert_false(!!extensions.sendrecv.find(e => e.uri === 'bogus'));
+  for (const answer_extension of answer_extensions.sendrecv) {
+    assert_true(!!extensions.sendrecv.find(e => e.uri === answer_extension.uri));
   }
 }, 'Negotiates the subset of supported extensions offered');
 
diff --git a/third_party/blink/web_tests/external/wpt/webtransport/receive-stream-pull.https.any.js b/third_party/blink/web_tests/external/wpt/webtransport/receive-stream-pull.https.any.js
new file mode 100644
index 0000000..24c2d5b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webtransport/receive-stream-pull.https.any.js
@@ -0,0 +1,111 @@
+// META: global=window,worker
+// META: script=resources/webtransport-test-helpers.sub.js
+// META: timeout=long
+
+// Tests the pull steps of a WebTransportReceiveStream:
+// https://w3c.github.io/webtransport/#webtransportreceivestream-pull-bytes
+
+// Returns a bidirectional stream whose readable end receives |data| echoed back
+// by the server, followed by FIN.
+async function echo_bidirectional_stream(wt, data) {
+  const bidi_stream = await wt.createBidirectionalStream();
+  const writer = bidi_stream.writable.getWriter();
+  await writer.write(data);
+  await writer.close();
+  return bidi_stream;
+}
+
+function ascending_bytes(length) {
+  const data = new Uint8Array(length);
+  for (let i = 0; i < data.byteLength; ++i) {
+    data[i] = i;
+  }
+  return data;
+}
+
+promise_test(async t => {
+  const wt = new WebTransport(webtransport_url('echo.py'));
+  await wt.ready;
+
+  const data = ascending_bytes(64);
+  const bidi_stream = await echo_bidirectional_stream(wt, data);
+
+  // Give the echoed bytes and the FIN a chance to be received before anything
+  // reads them, so that the reads below are served from buffered bytes. The test
+  // is valid either way: a read that arrives first waits for the bytes instead.
+  await wait(100);
+
+  const chunks = await read_stream(bidi_stream.readable);
+  const received = new Uint8Array(chunks.reduce((length, chunk) => length + chunk.byteLength, 0));
+  let offset = 0;
+  for (const chunk of chunks) {
+    received.set(chunk, offset);
+    offset += chunk.byteLength;
+  }
+
+  // No bytes may be lost when the stream is closed while bytes are still waiting
+  // to be given to the readable end.
+  assert_array_equals(received, data);
+  wt.close();
+}, 'Bytes received before any read are given to later reads');
+
+promise_test(async t => {
+  const wt = new WebTransport(webtransport_url('echo.py'));
+  await wt.ready;
+
+  const data = ascending_bytes(64);
+  const bidi_stream = await echo_bidirectional_stream(wt, data);
+  await wait(100);
+
+  // Read one byte at a time. Each view is smaller than what the server sent, so
+  // the bytes that do not fit in it must be kept for the following reads.
+  const reader = bidi_stream.readable.getReader({mode: 'byob'});
+  for (let i = 0; i < data.byteLength; ++i) {
+    const {value: view, done} = await reader.read(new Uint8Array(1));
+    assert_false(done, `read ${i} should not be done`);
+    assert_array_equals(view, data.subarray(i, i + 1), `read ${i}`);
+  }
+
+  // All the bytes have been read and the server ended its stream, so the next
+  // read closes the readable end.
+  const {value: view, done} = await reader.read(new Uint8Array(1));
+  assert_true(done, 'the last read should be done');
+  assert_equals(view.byteLength, 0, 'the last read should not fill the view');
+  await reader.closed;
+  reader.releaseLock();
+  wt.close();
+}, 'A BYOB read smaller than the received bytes keeps the rest for later reads');
+
+promise_test(async t => {
+  const wt = new WebTransport(webtransport_url('echo.py'));
+  await wt.ready;
+
+  const bidi_stream = await wt.createBidirectionalStream();
+
+  // Read before anything is received. The read has to wait until either a byte is
+  // received or the server ends its stream.
+  const reader = bidi_stream.readable.getReader();
+  const read = reader.read();
+
+  const data = ascending_bytes(8);
+  const writer = bidi_stream.writable.getWriter();
+  await writer.write(data);
+
+  const {value: chunk, done} = await read;
+  assert_false(done, 'the pending read should not be done');
+  assert_greater_than(chunk.byteLength, 0, 'the pending read should receive at least one byte');
+
+  // Read the rest in case the bytes did not all arrive in a single chunk.
+  let received = Array.from(chunk);
+  await writer.close();
+  while (received.length < data.byteLength) {
+    const {value: chunk, done} = await reader.read();
+    assert_false(done, 'the stream should not end before all its bytes are read');
+    received = received.concat(Array.from(chunk));
+  }
+  assert_array_equals(received, Array.from(data));
+
+  assert_true((await reader.read()).done, 'the read after FIN should be done');
+  reader.releaseLock();
+  wt.close();
+}, 'A read waits for bytes that have not been received yet');
diff --git a/third_party/blink/web_tests/external/wpt/xhr/getallresponseheaders-after-reuse.html b/third_party/blink/web_tests/external/wpt/xhr/getallresponseheaders-after-reuse.html
new file mode 100644
index 0000000..92f0d5c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/xhr/getallresponseheaders-after-reuse.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>XMLHttpRequest: getAllResponseHeaders() after reusing the object</title>
+<link rel="help" href="https://xhr.spec.whatwg.org/#the-getallresponseheaders()-method">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id="log"></div>
+<script>
+function load(client, url) {
+  return new Promise((resolve, reject) => {
+    client.onload = () => resolve(client.getAllResponseHeaders());
+    client.onerror = () => reject(new Error("unexpected error loading " + url));
+    client.open("GET", url);
+    client.send();
+  });
+}
+
+promise_test(async () => {
+  const client = new XMLHttpRequest();
+  assert_equals(await load(client, "resources/headers-basic.asis"),
+                "foo-test: 1, 2, 3\r\n", "first response");
+  assert_equals(await load(client, "resources/headers-www-authenticate.asis"),
+                "www-authenticate: 1, 2, 3, 4\r\n", "second response");
+}, "getAllResponseHeaders() must not return the previous response's headers");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/xhr/responseurl-after-abort.any.js b/third_party/blink/web_tests/external/wpt/xhr/responseurl-after-abort.any.js
new file mode 100644
index 0000000..e5ca4408
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/xhr/responseurl-after-abort.any.js
@@ -0,0 +1,91 @@
+// META: title=XMLHttpRequest: responseURL is the empty string after abort()
+
+// https://xhr.spec.whatwg.org/#the-abort()-method
+// abort() sets the response to a network error, both via the request error steps (step 2) and
+// when the state is already done (step 3). A network error's URL is null, so responseURL must
+// return the empty string, just like status is 0 and getAllResponseHeaders() is "".
+
+const url = "resources/well-formed.xml";
+
+function assertNetworkError(client) {
+  assert_equals(client.status, 0, "status");
+  assert_equals(client.statusText, "", "statusText");
+  assert_equals(client.getAllResponseHeaders(), "", "getAllResponseHeaders()");
+  assert_equals(client.responseURL, "", "responseURL");
+}
+
+async_test(test => {
+  const client = new XMLHttpRequest();
+  client.onreadystatechange = test.step_func(() => {
+    if (client.readyState !== 2)
+      return;
+    assert_equals(client.status, 200);
+    assert_not_equals(client.responseURL, "", "responseURL before abort()");
+    client.abort();
+    assertNetworkError(client);
+  });
+  client.onloadend = test.step_func_done(() => {
+    assertNetworkError(client);
+  });
+  client.open("GET", url);
+  client.send(null);
+}, "abort() during HEADERS_RECEIVED clears responseURL");
+
+async_test(test => {
+  const client = new XMLHttpRequest();
+  let aborted = false;
+  client.onreadystatechange = test.step_func(() => {
+    if (client.readyState !== 3 || aborted)
+      return;
+    aborted = true;
+    assert_equals(client.status, 200);
+    assert_not_equals(client.responseURL, "", "responseURL before abort()");
+    client.abort();
+    assertNetworkError(client);
+  });
+  client.onloadend = test.step_func_done(() => {
+    assert_true(aborted, "reached LOADING");
+    assertNetworkError(client);
+  });
+  client.open("GET", url);
+  client.send(null);
+}, "abort() during LOADING clears responseURL");
+
+async_test(test => {
+  const client = new XMLHttpRequest();
+  client.onload = test.step_func_done(() => {
+    assert_equals(client.readyState, 4);
+    assert_not_equals(client.responseURL, "", "responseURL before abort()");
+    client.abort();
+    assert_equals(client.readyState, 0);
+    assertNetworkError(client);
+  });
+  client.open("GET", url);
+  client.send(null);
+}, "abort() during DONE clears responseURL (async)");
+
+test(() => {
+  const client = new XMLHttpRequest();
+  client.open("GET", url, false);
+  client.send(null);
+  assert_equals(client.readyState, 4);
+  assert_not_equals(client.responseURL, "", "responseURL before abort()");
+  client.abort();
+  assert_equals(client.readyState, 0);
+  assertNetworkError(client);
+}, "abort() during DONE clears responseURL (sync)");
+
+async_test(test => {
+  const client = new XMLHttpRequest();
+  client.open("GET", url);
+  client.send(null);
+  client.abort();
+  assertNetworkError(client);
+  // A reused XMLHttpRequest must report the new response's URL, not the aborted one.
+  client.onload = test.step_func_done(() => {
+    assert_equals(client.status, 200);
+    assert_equals(client.responseURL, new URL(url + "?reused", location.href).href);
+  });
+  client.open("GET", url + "?reused");
+  client.send(null);
+}, "responseURL reflects the new response after abort() and reuse");
diff --git a/third_party/blink/web_tests/external/wpt/xhr/send-usp.any.js b/third_party/blink/web_tests/external/wpt/xhr/send-usp.any.js
index b0baf4a..dcc532a 100644
--- a/third_party/blink/web_tests/external/wpt/xhr/send-usp.any.js
+++ b/third_party/blink/web_tests/external/wpt/xhr/send-usp.any.js
@@ -43,4 +43,72 @@
   for (var i = 0; i < NUM_TESTS; i++) {
     usp.append("a" + i, String.fromCharCode(i));
   }
-  x.send(usp)
+  x.send(usp);
+
+// Content-Type and request body handling for send(URLSearchParams).
+// https://xhr.spec.whatwg.org/#dom-xmlhttprequest-send
+[
+  {
+    method: "POST",
+    authorContentType: null,
+    expectedContentType: "application/x-www-form-urlencoded;charset=UTF-8",
+    expectedBody: "a=b",
+    description: "POST uses the URLSearchParams MIME type when no Content-Type was set"
+  },
+  {
+    method: "POST",
+    authorContentType: "text/plain",
+    expectedContentType: "text/plain",
+    expectedBody: "a=b",
+    description: "POST keeps an author-set Content-Type"
+  },
+  {
+    // The spec only reconciles the charset parameter when the body is a Document or a string, but
+    // Blink, Gecko and WebKit all do it for URLSearchParams too, and the body is always UTF-8 here.
+    method: "POST",
+    authorContentType: "text/plain;charset=windows-1252",
+    expectedContentType: "text/plain;charset=UTF-8",
+    expectedBody: "a=b",
+    description: "POST replaces the charset parameter of an author-set Content-Type"
+  },
+  {
+    method: "GET",
+    authorContentType: null,
+    expectedContentType: "NO",
+    expectedBody: "",
+    description: "GET sends neither a body nor a Content-Type"
+  },
+  {
+    method: "HEAD",
+    authorContentType: null,
+    expectedContentType: "NO",
+    expectedBody: null,
+    description: "HEAD sends neither a body nor a Content-Type"
+  },
+  {
+    method: "GET",
+    authorContentType: "text/plain",
+    expectedContentType: "text/plain",
+    expectedBody: "",
+    description: "GET keeps an author-set Content-Type"
+  }
+].forEach(({ method, authorContentType, expectedContentType, expectedBody, description }) => {
+  promise_test(t => {
+    const client = new XMLHttpRequest();
+    client.open(method, "resources/content.py");
+    if (authorContentType !== null)
+      client.setRequestHeader("Content-Type", authorContentType);
+    client.send(new URLSearchParams("a=b"));
+    return new Promise((resolve, reject) => {
+      client.onload = resolve;
+      client.onerror = () => reject(new Error("Network error"));
+    }).then(() => {
+      assert_equals(client.getResponseHeader("X-Request-Method"), method, "request method");
+      assert_equals(client.getResponseHeader("X-Request-Content-Type"), expectedContentType, "Content-Type request header");
+      if (expectedBody !== null)
+        assert_equals(client.response, expectedBody, "request body");
+      if (expectedBody === "")
+        assert_equals(client.getResponseHeader("X-Request-Content-Length"), "NO", "Content-Length request header");
+    });
+  }, `XMLHttpRequest.send(URLSearchParams): ${description}`);
+});
diff --git a/third_party/blink/web_tests/fast/block/min-max-height-percent-height-child.html b/third_party/blink/web_tests/fast/block/min-max-height-percent-height-child.html
index e4eeae86..da45b71 100644
--- a/third_party/blink/web_tests/fast/block/min-max-height-percent-height-child.html
+++ b/third_party/blink/web_tests/fast/block/min-max-height-percent-height-child.html
@@ -33,23 +33,23 @@
         <h1><a href="https://bugs.webkit.org/show_bug.cgi?id=26559">Bug 26559</a>: When a block's height is determined by min-height/max-height, children with percentage heights are sized incorrectly</h1>
         <h1>Tests to make sure a child with percentage height is computed properly when the parent has a max or min height set.</h1>
         <h2>Each of the following tests should display a green rectangle with a 1 pixel red border on all sides.</h2>
-        <h3>Parent with fixed height and min set</h2>
+        <h3>Parent with fixed height and min set</h3>
         <div id="simple-min" class="parent" style="width: 50px; height: 50px; min-height: 75px">
             <div class="child" style="width: 100%; height: 100%" data-expected-width="50" data-expected-height="75"></div>
         </div>
-        <h3>Parent with fixed height and max set</h2>
+        <h3>Parent with fixed height and max set</h3>
         <div id="simple-max" class="parent" style="max-height: 25px; width: 50px; height: 50px">
             <div class="child" style="width: 100%; height: 100%" data-expected-width="50" data-expected-height="25"></div>
         </div>
-        <h3>Parent with fixed height and min greater than max</h2>
+        <h3>Parent with fixed height and min greater than max</h3>
         <div id="min-greater-max" class="parent" style="width: 50px; height: 50px; min-height: 75px; max-height: 25px">
             <div class="child" style="width: 100%; height: 100%" data-expected-width="50" data-expected-height="75"></div>
         </div>
-        <h3>Vertical parent with fixed width and max set</h2>
+        <h3>Vertical parent with fixed width and max set</h3>
         <div id="vertical-max" class="parent" style="width: 50px; max-width: 25px; height: 50px">
             <div class="child" style="width: 100%; height: 100%" data-expected-width="25" data-expected-height="50"></div>
         </div>
-        <h3>Vertical parent with fixed width and min set</h2>
+        <h3>Vertical parent with fixed width and min set</h3>
         <div id="vertical-min" class="parent" style="width: 50px; min-width: 75px; height: 50px">
             <div class="child" style="width: 100%; height: 100%" data-expected-width="75" data-expected-height="50"></div>
         </div>
@@ -102,11 +102,11 @@
             XXXXX
         </div>
         <h2>The following two tests will have a 6 pixel red border.</h2>
-        <h3>Parent with fixed height and padding and max set</h2>
+        <h3>Parent with fixed height and padding and max set</h3>
         <div id="parent-padding-max" class="parent" style="max-height: 25px; width: 50px; padding: 5px; height: 50px">
             <div class="child" style="width: 100%; height: 100%" data-expected-width="50" data-expected-height="25"></div>
         </div>
-        <h3>Parent with fixed height and padding and min set</h2>
+        <h3>Parent with fixed height and padding and min set</h3>
         <div id="parent-padding-min" class="parent" style="width: 50px; padding: 5px; height: 50px; min-height: 75px">
             <div class="child" style="width: 100%; height: 100%" data-expected-width="50" data-expected-height="75"></div>
         </div>
diff --git a/third_party/blink/web_tests/fast/css/absolute-inline-alignment-2-expected.html b/third_party/blink/web_tests/fast/css/absolute-inline-alignment-2-expected.html
index 86a923d3..ec92bd75 100644
--- a/third_party/blink/web_tests/fast/css/absolute-inline-alignment-2-expected.html
+++ b/third_party/blink/web_tests/fast/css/absolute-inline-alignment-2-expected.html
@@ -17,9 +17,9 @@
         background-color: rgba(0, 0, 255, 0.5);
     }
 </style>
-<div dir="rtl">&#x200b;<span id="absolute"></span>Lorem ipsum dolor sit amet</span></div>
-<div dir="ltr">&#x200b;<span id="absolute"></span>Lorem ipsum dolor sit amet</span></div>
-<div>&#x200b;<span dir="rtl"><span id="absolute"></span>Lorem ipsum dolor sit amet</span></span></div>
+<div dir="rtl">&#x200b;<span id="absolute"></span>Lorem ipsum dolor sit amet</div>
+<div dir="ltr">&#x200b;<span id="absolute"></span>Lorem ipsum dolor sit amet</div>
+<div>&#x200b;<span dir="rtl"><span id="absolute"></span>Lorem ipsum dolor sit amet</span></div>
 <div>&#x200b;<span id="absolute"></span>Lorem ipsum dolor sit amet</div>
-<div style="text-align: left;">&#x200b;<span dir="rtl"><span id="absolute"></span>Lorem ipsum dolor sit amet</span></span></div>
-<div style="text-align: right;">&#x200b;<span dir="rtl"><span id="absolute"></span>Lorem ipsum dolor sit amet</span></span></div>
+<div style="text-align: left;">&#x200b;<span dir="rtl"><span id="absolute"></span>Lorem ipsum dolor sit amet</span></div>
+<div style="text-align: right;">&#x200b;<span dir="rtl"><span id="absolute"></span>Lorem ipsum dolor sit amet</span></div>
diff --git a/third_party/blink/web_tests/fast/css/absolute-inline-alignment-2.html b/third_party/blink/web_tests/fast/css/absolute-inline-alignment-2.html
index 34a43f62..4c1b8ff 100644
--- a/third_party/blink/web_tests/fast/css/absolute-inline-alignment-2.html
+++ b/third_party/blink/web_tests/fast/css/absolute-inline-alignment-2.html
@@ -17,9 +17,9 @@
         background-color: rgba(0, 0, 255, 0.5);
     }
 </style>
-<div dir="rtl"><span id="absolute"></span> Lorem ipsum dolor sit amet</span></div>
-<div dir="ltr"><span id="absolute"></span> Lorem ipsum dolor sit amet</span></div>
-<div><span dir="rtl"><span id="absolute"></span> Lorem ipsum dolor sit amet</span></span></div>
+<div dir="rtl"><span id="absolute"></span> Lorem ipsum dolor sit amet</div>
+<div dir="ltr"><span id="absolute"></span> Lorem ipsum dolor sit amet</div>
+<div><span dir="rtl"><span id="absolute"></span> Lorem ipsum dolor sit amet</span></div>
 <div> <span id="absolute"></span>Lorem ipsum dolor sit amet</div>
-<div style="text-align: left;"><span dir="rtl"><span id="absolute"></span> Lorem ipsum dolor sit amet</span></span></div>
-<div style="text-align: right;"><span dir="rtl"><span id="absolute"></span> Lorem ipsum dolor sit amet</span></span></div>
+<div style="text-align: left;"><span dir="rtl"><span id="absolute"></span> Lorem ipsum dolor sit amet</span></div>
+<div style="text-align: right;"><span dir="rtl"><span id="absolute"></span> Lorem ipsum dolor sit amet</span></div>
diff --git a/third_party/blink/web_tests/fast/css/pseudo-any.html b/third_party/blink/web_tests/fast/css/pseudo-any.html
index 6044726..d709ffa 100644
--- a/third_party/blink/web_tests/fast/css/pseudo-any.html
+++ b/third_party/blink/web_tests/fast/css/pseudo-any.html
@@ -67,11 +67,11 @@
 <i>i</i>
 <p>p</p>
 <a href="http://webkit.org">a</a>
-<span id="span1">span<span>
+<span id="span1">span</span>
 <li class="foo">li</li>
 <u id="bar">u</u>
 <s>s</s>
-<div id="container-div"><input type='text'><a href='http://www.example.com/'></a><div></div><a name='foo'></div>
+<div id="container-div"><input type='text'><a href='http://www.example.com/'></a><div></div><a name='foo'></a></div>
 <div><span>div span</span></div>
 <q>div~q</q>
 <q>q+q</q>
diff --git a/third_party/blink/web_tests/fast/css/readwrite-contenteditable-expected.html b/third_party/blink/web_tests/fast/css/readwrite-contenteditable-expected.html
index 67a08ed9..ca750a06 100644
--- a/third_party/blink/web_tests/fast/css/readwrite-contenteditable-expected.html
+++ b/third_party/blink/web_tests/fast/css/readwrite-contenteditable-expected.html
@@ -10,7 +10,7 @@
 <div class="readonly">Read-only <span class="readwrite">Read-write</span></div>
 <div class="readwrite">Read-write <span>Read-write</span></div>
 <div class="readwrite">Read-write <span class="readonly">Read-only</span></div>
-<div class="readwrite">Read-write <span class="readonly">Read-only <span>Read-only</span</span></div>
+<div class="readwrite">Read-write <span class="readonly">Read-only <span>Read-only</span></span></div>
 
 <div <span class="readonly">Read-only <span class="readwrite">Read-write</span></div>
 <div <span class="readonly">Read-only <span class="readwrite">Read-write <span>Read-write</span></span></div>
diff --git a/third_party/blink/web_tests/fast/css/readwrite-contenteditable.html b/third_party/blink/web_tests/fast/css/readwrite-contenteditable.html
index f5767a9..fe471d1 100644
--- a/third_party/blink/web_tests/fast/css/readwrite-contenteditable.html
+++ b/third_party/blink/web_tests/fast/css/readwrite-contenteditable.html
@@ -10,7 +10,7 @@
 <div>Read-only <span contenteditable="true">Read-write</span></div>
 <div contenteditable="true">Read-write <span>Read-write</span></div>
 <div contenteditable="true">Read-write <span contenteditable="false">Read-only</span></div>
-<div contenteditable="true">Read-write <span contenteditable="false">Read-only <span>Read-only</span</span></div>
+<div contenteditable="true">Read-write <span contenteditable="false">Read-only <span>Read-only</span></span></div>
 
 <div>Read-only <span contenteditable>Read-write</span></div>
 <div>Read-only <span contenteditable>Read-write <span contenteditable="inherit">Read-write</span></span></div>
diff --git a/third_party/blink/web_tests/fast/dom/StyleSheet/detached-stylesheet-without-wrapper.html b/third_party/blink/web_tests/fast/dom/StyleSheet/detached-stylesheet-without-wrapper.html
index cb428eb9..7b43038b 100644
--- a/third_party/blink/web_tests/fast/dom/StyleSheet/detached-stylesheet-without-wrapper.html
+++ b/third_party/blink/web_tests/fast/dom/StyleSheet/detached-stylesheet-without-wrapper.html
@@ -8,7 +8,7 @@
 <script src="../../../resources/js-test.js"></script>
 </head>
 <body>
-<span id="test-element"></div>
+<span id="test-element"></span>
 <script>
 description("Test that detached CSSOM subtrees keeps a reference to CSS stylesheets lacking wrappers.");
 
diff --git a/third_party/blink/web_tests/fast/dom/inner-text-001.html b/third_party/blink/web_tests/fast/dom/inner-text-001.html
index 75e58ce..ab892ce 100644
--- a/third_party/blink/web_tests/fast/dom/inner-text-001.html
+++ b/third_party/blink/web_tests/fast/dom/inner-text-001.html
@@ -24,14 +24,14 @@
 
 <body style="font-size: 12px;">
 <div contenteditable id="test" style="border-style:solid; border-color:red">
-<h2>Test innerText</h1>
+<h2>Test innerText</h2>
 <h3>Check lists, tables, styled text, and images.</h3>
 Block flow elements get line breaks before and after.  Table cells are tab separated.
 <ul>
 <li>Item 1</li>
 <li>Item 2</li>
 </ul>
-<table border=1><tr><td>1</td><td>2</td><td><span">3</span></td></tr><tr><td>4</td><td>5</td><td><span">6</span></td></tr></table>
+<table border=1><tr><td>1</td><td>2</td><td><span>3</span></td></tr><tr><td>4</td><td>5</td><td><span>6</span></td></tr></table>
 <br>
 Right <h4 style="display:inline">here</h4> is an inline block.
 <br>
diff --git a/third_party/blink/web_tests/fast/events/moving-text-should-fire-drop-and-dragend-events-2.html b/third_party/blink/web_tests/fast/events/moving-text-should-fire-drop-and-dragend-events-2.html
index be8588d..5b56084 100644
--- a/third_party/blink/web_tests/fast/events/moving-text-should-fire-drop-and-dragend-events-2.html
+++ b/third_party/blink/web_tests/fast/events/moving-text-should-fire-drop-and-dragend-events-2.html
@@ -3,7 +3,7 @@
 <body>
 <p>This tests dragging text within the same contenteditable element. To manually test, move the target below to the destination.
 You should see source:dragstart, destination:drop, and source:dragend in the log.</p>
-<div id="source" contenteditable><span>Drag and drop this <span id="target">target</span> from here</div>
+<div id="source" contenteditable><span>Drag and drop this <span id="target">target</span> from here</span></div>
 <div id="destination" contenteditable><span>to here, the_destination.</span></div>
 <pre id="log">
 Log:
diff --git a/third_party/blink/web_tests/fast/events/resources/big-page-with-overflow-hidden-and-anchor-scroll.html b/third_party/blink/web_tests/fast/events/resources/big-page-with-overflow-hidden-and-anchor-scroll.html
index 63f4446..aac9a2d 100644
--- a/third_party/blink/web_tests/fast/events/resources/big-page-with-overflow-hidden-and-anchor-scroll.html
+++ b/third_party/blink/web_tests/fast/events/resources/big-page-with-overflow-hidden-and-anchor-scroll.html
@@ -9,7 +9,7 @@
     <a id="clickme" href="#anchor">Click here</a>
     <h2 id="anchor">Any text</h2>
     <div id="textInFrame" class="picture">
-        <span> click and hold and drag mouse up to see if it scrolls.</div>
+        <span> click and hold and drag mouse up to see if it scrolls.</span>
     </div>
    <div id="console"></div>
 </body>
diff --git a/third_party/blink/web_tests/fast/events/resources/popup-blocking-click-in-iframe-otherFrame.html b/third_party/blink/web_tests/fast/events/resources/popup-blocking-click-in-iframe-otherFrame.html
index e94bdc05..b29faff 100644
--- a/third_party/blink/web_tests/fast/events/resources/popup-blocking-click-in-iframe-otherFrame.html
+++ b/third_party/blink/web_tests/fast/events/resources/popup-blocking-click-in-iframe-otherFrame.html
@@ -9,6 +9,6 @@
         </script>
     </head>
     <body style="border: 0; margin: 0" onload="prepareEventHandler()">
-        <span id="clickable">Click this text to open a window.</a>
+        <span id="clickable">Click this text to open a window.</span>
     </body>
 </html>
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/gesture-tap-reset-selection-range.html b/third_party/blink/web_tests/fast/events/touch/gesture/gesture-tap-reset-selection-range.html
index f5e46699..afb9bae 100644
--- a/third_party/blink/web_tests/fast/events/touch/gesture/gesture-tap-reset-selection-range.html
+++ b/third_party/blink/web_tests/fast/events/touch/gesture/gesture-tap-reset-selection-range.html
@@ -5,7 +5,7 @@
 <script src="../../../../resources/testdriver.js"></script>
 <script src="../../../../resources/testdriver-actions.js"></script>
 <script src="../../../..//resources/testdriver-vendor.js"></script>
-<div><span id="dragme">Make Selection Here<span></div>
+<div><span id="dragme">Make Selection Here</span></div>
 <div id="log"></div>
 <script>
 promise_test(async t => {
diff --git a/third_party/blink/web_tests/fast/forms/willvalidate.html b/third_party/blink/web_tests/fast/forms/willvalidate.html
index a4328fee..18fb57d 100644
--- a/third_party/blink/web_tests/fast/forms/willvalidate.html
+++ b/third_party/blink/web_tests/fast/forms/willvalidate.html
@@ -68,7 +68,7 @@
 
 debug('');
 debug('Fieldset element');
-parent.innerHTML = '<form><fieldset><p>Fieldset test</p></fieldtset></form>';
+parent.innerHTML = '<form><fieldset><p>Fieldset test</p></fieldset></form>';
 shouldBeFalse('document.getElementsByTagName("fieldset")[0].willValidate');
 
 debug('');
diff --git a/third_party/blink/web_tests/fast/media/mq-relative-constraints-10.html b/third_party/blink/web_tests/fast/media/mq-relative-constraints-10.html
index 7858e173..3238f11 100644
--- a/third_party/blink/web_tests/fast/media/mq-relative-constraints-10.html
+++ b/third_party/blink/web_tests/fast/media/mq-relative-constraints-10.html
@@ -20,6 +20,6 @@
     </style>
 </head>
 <body>
-    <p><span id="t1">This</span> <span id="t2">text</span> <span id="t3">should<span> <span id="t4">be green.</span></p>
+    <p><span id="t1">This</span> <span id="t2">text</span> <span id="t3">should</span> <span id="t4">be green.</span></p>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/fast/selectors/attr-list-01-expected.html b/third_party/blink/web_tests/fast/selectors/attr-list-01-expected.html
index 9fcc032..03020f76 100644
--- a/third_party/blink/web_tests/fast/selectors/attr-list-01-expected.html
+++ b/third_party/blink/web_tests/fast/selectors/attr-list-01-expected.html
@@ -1,2 +1,2 @@
 <!DOCTYPE html>
-<p style="color:green">This text should be green</span>
+<p style="color:green">This text should be green</p>
diff --git a/third_party/blink/web_tests/fast/spatial-navigation/snav-navigate-visible-elements-only2.html b/third_party/blink/web_tests/fast/spatial-navigation/snav-navigate-visible-elements-only2.html
index fd93c47..94df617 100644
--- a/third_party/blink/web_tests/fast/spatial-navigation/snav-navigate-visible-elements-only2.html
+++ b/third_party/blink/web_tests/fast/spatial-navigation/snav-navigate-visible-elements-only2.html
@@ -44,7 +44,7 @@
     <!DOCTYPE html>
     <body id='body_in_iframe' style='font: 15px Ahem'>
       <a id='link_in_iframe' href='a'>Link in iframe</a>
-    </bdoy>
+    </body>
   "></iframe>
 
   <a id="end" style="top: 560px;" href="a">End</a>
diff --git a/third_party/blink/web_tests/fast/table/table-rowspan-wrong-height-with-only-spanning-cells.html b/third_party/blink/web_tests/fast/table/table-rowspan-wrong-height-with-only-spanning-cells.html
index 3bdeebc..c9dd1ce 100644
--- a/third_party/blink/web_tests/fast/table/table-rowspan-wrong-height-with-only-spanning-cells.html
+++ b/third_party/blink/web_tests/fast/table/table-rowspan-wrong-height-with-only-spanning-cells.html
@@ -4,7 +4,7 @@
 </style>
 <script src="../../resources/check-layout.js"></script>
 <body onload="checkLayout('tr')">
-    <h3>Test for chromium bug : <a href="https://code.google.com/p/chromium/issues/detail?id=396655">396655</a>. Tables with specific merge cell configuration render with extra height to tr elements.</h4>
+    <h3>Test for chromium bug : <a href="https://code.google.com/p/chromium/issues/detail?id=396655">396655</a>. Tables with specific merge cell configuration render with extra height to tr elements.</h3>
     <p>We have the below table. Because cell A and cell D overlap in the second row, the height of the second row is the 1px difference between A and B and the 1px difference between C and D.</p>
     <pre>+-----+-----+</pre>
     <pre>|     |  B  |</pre>
diff --git a/third_party/blink/web_tests/html/details_summary/summary-invisible-click.html b/third_party/blink/web_tests/html/details_summary/summary-invisible-click.html
index 3889288..5d19c61 100644
--- a/third_party/blink/web_tests/html/details_summary/summary-invisible-click.html
+++ b/third_party/blink/web_tests/html/details_summary/summary-invisible-click.html
@@ -5,7 +5,7 @@
 <details style="display:none">
 <summary>Click me</summary>
 Blah, blah, ...
-</detail>
+</details>
 
 <script>
 test(() => {
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/matched-network-conditions-offline-frame.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/matched-network-conditions-offline-frame.js
index 109ce7e..e090fda 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/matched-network-conditions-offline-frame.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/matched-network-conditions-offline-frame.js
@@ -30,17 +30,30 @@
   await session.navigate('resources/page-with-iframe.html');
   await loadPromise;
 
+  await dp.Target.setAutoAttach(
+      {autoAttach: true, waitForDebuggerOnStart: true, flatten: true});
+
+  let resolveNavigated;
   const navigated = new Promise(resolve => {
-    dp.Page.onFrameNavigated(event => {
-      const frame = event.params.frame;
-      if (frame.parentId) {
-        if (frame.url.startsWith('chrome-error://')) {
-          resolve('failed to load');
-        } else if (frame.url.includes('frames/frame.html')) {
-          resolve('loaded');
-        }
-      }
-    });
+    resolveNavigated = resolve;
+  });
+
+  const checkFrameNavigated = event => {
+    const frame = event.params.frame;
+    if (frame.url.startsWith('chrome-error://')) {
+      resolveNavigated('failed to load');
+    } else if (frame.url.includes('frames/frame.html')) {
+      resolveNavigated('loaded');
+    }
+  };
+
+  dp.Page.onFrameNavigated(checkFrameNavigated);
+
+  dp.Target.onAttachedToTarget(async event => {
+    const dp2 = session.createChild(event.params.sessionId).protocol;
+    await dp2.Page.enable();
+    dp2.Page.onFrameNavigated(checkFrameNavigated);
+    await dp2.Runtime.runIfWaitingForDebugger();
   });
 
   await session.evaluate((url) => {
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-synchronous-form.html b/third_party/blink/web_tests/http/tests/security/xss-DENIED-synchronous-form.html
index a03a11d2..14809cd1 100644
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-synchronous-form.html
+++ b/third_party/blink/web_tests/http/tests/security/xss-DENIED-synchronous-form.html
@@ -28,6 +28,6 @@
 <script>
 log('Waiting for iframe...');
 </script>
-<iframe src="http://localhost:8000/security/resources/iframe-for-synchronous-form.html"></ifame>
+<iframe src="http://localhost:8000/security/resources/iframe-for-synchronous-form.html"></iframe>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/http/tests/webfont/font-face-revalidation-expected.html b/third_party/blink/web_tests/http/tests/webfont/font-face-revalidation-expected.html
index cf6f087b..70252d0 100644
--- a/third_party/blink/web_tests/http/tests/webfont/font-face-revalidation-expected.html
+++ b/third_party/blink/web_tests/http/tests/webfont/font-face-revalidation-expected.html
@@ -5,4 +5,4 @@
 
 You should see a black square below.
 </p>
-<div style="font-family: Ahem">test</span>
+<div style="font-family: Ahem">test</div>
diff --git a/third_party/blink/web_tests/http/tests/webfont/font-face-revalidation.html b/third_party/blink/web_tests/http/tests/webfont/font-face-revalidation.html
index 16f868af..36bffbe 100644
--- a/third_party/blink/web_tests/http/tests/webfont/font-face-revalidation.html
+++ b/third_party/blink/web_tests/http/tests/webfont/font-face-revalidation.html
@@ -25,4 +25,4 @@
 
 You should see a black square below.
 </p>
-<div style="font-family: ahem">test</span>
+<div style="font-family: ahem">test</div>
diff --git a/third_party/blink/web_tests/transitions/repeated-firing-background-color.html b/third_party/blink/web_tests/transitions/repeated-firing-background-color.html
index 818cdc0..12bd0c4 100644
--- a/third_party/blink/web_tests/transitions/repeated-firing-background-color.html
+++ b/third_party/blink/web_tests/transitions/repeated-firing-background-color.html
@@ -27,7 +27,7 @@
 </head>
 <body>
 
-<div id="foo">This should not be continually firing transitions</span>
+<div id="foo">This should not be continually firing transitions</div>
 
 <script type="text/javascript" charset="utf-8">
     // Force a layout so that changing the classname below causes an animation.
diff --git a/third_party/blink/web_tests/virtual/popover-hint-new-behavior-disabled/external/wpt/html/semantics/popovers/popover-light-dismiss-hint-expected.txt b/third_party/blink/web_tests/virtual/popover-hint-new-behavior-disabled/external/wpt/html/semantics/popovers/popover-light-dismiss-hint-expected.txt
index f23c8f5a..68df76f 100644
--- a/third_party/blink/web_tests/virtual/popover-hint-new-behavior-disabled/external/wpt/html/semantics/popovers/popover-light-dismiss-hint-expected.txt
+++ b/third_party/blink/web_tests/virtual/popover-hint-new-behavior-disabled/external/wpt/html/semantics/popovers/popover-light-dismiss-hint-expected.txt
@@ -1,4 +1,6 @@
 This is a testharness.js-based test.
+[FAIL] Clicking an auto nested inside a hint keeps the whole chain open
+  assert_equals: All popovers should be able to be open at once, index 0 (hint1) expected true but got false
 [FAIL] Auto can be nested inside hint (nothing should throw, auto demoted to hint)
   assert_equals: Error, index 2 (innerhint1) expected true but got false
 Harness: the test ran to completion.
diff --git a/third_party/boringssl/README.chromium b/third_party/boringssl/README.chromium
index 61ea1677..84f3494f 100644
--- a/third_party/boringssl/README.chromium
+++ b/third_party/boringssl/README.chromium
@@ -1,7 +1,7 @@
 Name: BoringSSL
 URL: https://boringssl.googlesource.com/boringssl
 Version: N/A
-Revision: e055b59aa175e41614664d04c8b57f243bbba78f
+Revision: 922245af6eda11b3f101ab5f542093eb5e7d1a74
 Update Mechanism: Autoroll
 License: Apache-2.0, BSD-3-Clause
 License File: src/LICENSE
diff --git a/third_party/boringssl/src b/third_party/boringssl/src
index e055b59..922245a 160000
--- a/third_party/boringssl/src
+++ b/third_party/boringssl/src
@@ -1 +1 @@
-Subproject commit e055b59aa175e41614664d04c8b57f243bbba78f
+Subproject commit 922245af6eda11b3f101ab5f542093eb5e7d1a74
diff --git a/third_party/crossbench b/third_party/crossbench
index 6423f30..4b6b619 160000
--- a/third_party/crossbench
+++ b/third_party/crossbench
@@ -1 +1 @@
-Subproject commit 6423f30d7ced32b24acc63178459aa917778ebc8
+Subproject commit 4b6b619a4bf65ef82bf3218b866d3cb0e997c6f3
diff --git a/third_party/depot_tools b/third_party/depot_tools
index fa1fac8..33025ff4 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit fa1fac8477c70532274e7244777a846537004750
+Subproject commit 33025ff4f2a57667af0b8302d1547b8c58922cf9
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index bdcd978..b9f3712 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-14-3-75-g656cb7777
-Revision: 656cb777798fa420a13faba3758779e9ed6c4798
+Version: VER-2-14-3-81-g9e9d3b73f
+Revision: 9e9d3b73f31367dbb4261f93c727a277f6632c77
 Update Mechanism: Autoroll
 CPEPrefix: cpe:/a:freetype:freetype:2.14.3
 License: FTL
diff --git a/third_party/freetype/src b/third_party/freetype/src
index 656cb77..9e9d3b7 160000
--- a/third_party/freetype/src
+++ b/third_party/freetype/src
@@ -1 +1 @@
-Subproject commit 656cb777798fa420a13faba3758779e9ed6c4798
+Subproject commit 9e9d3b73f31367dbb4261f93c727a277f6632c77
diff --git a/third_party/ktfmt/DIR_METADATA b/third_party/ktfmt/DIR_METADATA
new file mode 100644
index 0000000..544842ac
--- /dev/null
+++ b/third_party/ktfmt/DIR_METADATA
@@ -0,0 +1,7 @@
+monorail: {
+  component: "Build"
+}
+team_email: "build@chromium.org"
+buganizer_public: {
+  component_id: 1456832
+}
diff --git a/third_party/ktfmt/ktfmt b/third_party/ktfmt/ktfmt
new file mode 100755
index 0000000..c5b9c94
--- /dev/null
+++ b/third_party/ktfmt/ktfmt
@@ -0,0 +1,21 @@
+#!/bin/bash
+# Copyright 2026 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+THIS_DIR=$(dirname "$0")
+
+if [[ -n "$JAVA_HOME" ]]; then
+  JAVA="$JAVA_HOME/bin/java"
+elif [[ $(uname) = Linux ]]; then
+  # Chromium bundles Java prebuilts only for Linux.
+  JAVA="$THIS_DIR/../jdk/current/bin/java"
+else
+  JAVA=java
+fi
+
+exec "$JAVA" \
+    -Xms512M \
+    -jar "$THIS_DIR/cipd/ktfmt.jar" \
+    --google-style \
+    "$@"
diff --git a/third_party/lens_server_proto/BUILD.gn b/third_party/lens_server_proto/BUILD.gn
index b3f9d7e..3413ddb 100644
--- a/third_party/lens_server_proto/BUILD.gn
+++ b/third_party/lens_server_proto/BUILD.gn
@@ -62,7 +62,11 @@
   proto_in_dir = "."
   import_dirs = [
     "//",
+    "//components/optimization_guide/proto/features",
     "//third_party/omnibox_proto",
   ]
-  deps = [ "//third_party/omnibox_proto" ]
+  deps = [
+    "//components/optimization_guide/proto:optimization_guide_proto",
+    "//third_party/omnibox_proto",
+  ]
 }
diff --git a/third_party/lens_server_proto/README.chromium b/third_party/lens_server_proto/README.chromium
index 3039e116..7b05f17 100644
--- a/third_party/lens_server_proto/README.chromium
+++ b/third_party/lens_server_proto/README.chromium
@@ -2,8 +2,8 @@
 Short Name: lens_overlay_proto
 URL: This is the canonical public repository
 Update Mechanism: Manual
-Version: 945119661
-Date: 2026-07-09
+Version: 960988109
+Date: 2026-08-07
 License: BSD-3-Clause
 License File: LICENSE
 Shipped: yes
diff --git a/third_party/lens_server_proto/aim_communication.proto b/third_party/lens_server_proto/aim_communication.proto
index 9bf5ec0..04e9182c 100644
--- a/third_party/lens_server_proto/aim_communication.proto
+++ b/third_party/lens_server_proto/aim_communication.proto
@@ -8,9 +8,14 @@
 
 package lens;
 
+// From third_party/omnibox_proto
 import "tool_mode.proto";
 import "tool_substate.proto";
 import "model_mode.proto";
+
+// From components/optimization_guide/proto/features
+import "actions_data.proto";
+
 import "aim_query.proto";
 import "modality_chip_props.proto";
 
@@ -45,6 +50,9 @@
   // lockInput() method
   LOCK_INPUT = 26;
 
+  // AiModeExitTool event
+  EXIT_TOOL = 29;
+
   // openLinkInSidePanelMode() method
   OPEN_LINK_IN_SIDE_PANEL_MODE = 31;
 }
@@ -108,15 +116,13 @@
   InjectedInputUpdatePayload payload = 1;
 }
 
-// LINT.IfChange(ExitToolPayload)
 // Payload for the ExitTool Event.
 message ExitToolPayload {
   // The tool to exit.
-  ToolMode tool_mode = 1;
+  int32 tool_mode = 1;
   // The new tool mode we are switching to.
-  ToolMode new_tool_mode = 2;
+  int32 new_tool_mode = 2;
 }
-// LINT.ThenChange()
 
 // Message from client to AIM to exit a tool.
 message ExitTool {
@@ -143,6 +149,7 @@
     SetCobrowsingDisplayMode set_cobrowsing_display_mode = 4;
     InjectedInputUpdate injected_input_update = 5;
     ExitTool exit_tool = 6;
+    ActionsResult actions_result = 7;
   }
 }
 
@@ -163,10 +170,25 @@
     UnlockInput unlock_input = 12;
     LockInput lock_input = 13;
     OpenLinkInSidePanelMode open_link_in_side_panel_mode = 14;
+    ExecuteActions execute_actions = 15;
     UpdateInputPlateStateRequest update_input_plate_state = 16;
   }
 }
 
+// Message AIM sends to clients to execute actions.
+// NOTE: This message is strictly for prototyping WebMCP and script tool
+// integration in AIM at this time.
+message ExecuteActions {
+  optimization_guide.proto.Actions actions = 1;
+}
+
+// Message clients send to AIM with the results of executed actions.
+// NOTE: This message is strictly for prototyping WebMCP and script tool
+// integration in AIM at this time.
+message ActionsResult {
+  optimization_guide.proto.ActionsResult actions_result = 1;
+}
+
 // Message AIM sends to clients to unlock the input plate.
 message UnlockInput {}
 
diff --git a/third_party/lens_server_proto/aim_query.proto b/third_party/lens_server_proto/aim_query.proto
index 5d43512..8381d32 100644
--- a/third_party/lens_server_proto/aim_query.proto
+++ b/third_party/lens_server_proto/aim_query.proto
@@ -47,6 +47,7 @@
   // Deprecated fields to match the Silk LensImageQueryData.
   reserved 3, 5;
 }
+
 // Metadata for context for the turn.
 message ContextTurnMetadata {
   int64 context_id = 1;
@@ -61,6 +62,7 @@
     TabMetadata tab_metadata = 2;
   }
 }
+
 // The AIM model mode that is applied to a query.
 // Next ID: 4
 enum ModelMode {
@@ -78,7 +80,7 @@
 // In the AIM Composebox, tools can be added to a query to enter a
 // specific mode. For example, some modes include "deep search", "create
 // images", and "canvas".
-// Next ID: 11
+// Next ID: 12
 enum ToolMode {
   TOOL_MODE_UNSPECIFIED = 0;
   TOOL_MODE_DEEP_SEARCH = 1;
@@ -91,6 +93,7 @@
   TOOL_MODE_DISABLE_SUGGEST = 8;
   TOOL_MODE_AIM = 9;
   TOOL_MODE_AIM_GEN_PROMPT = 10;
+  TOOL_MODE_AGENT_TASK = 11;
 }
 
 // The set of data that's required to generate a response from the LLM.
diff --git a/third_party/lens_server_proto/lens_overlay_content_metadata.proto b/third_party/lens_server_proto/lens_overlay_content_metadata.proto
index 45e43a3..58dfdbe 100644
--- a/third_party/lens_server_proto/lens_overlay_content_metadata.proto
+++ b/third_party/lens_server_proto/lens_overlay_content_metadata.proto
@@ -34,5 +34,5 @@
   string drive_id = 1;
 
   // Optional. The resource key required to access link-shared Drive files.
-  string resource_key = 2;
+  string resource_key = 2 [deprecated = true];
 }
diff --git a/third_party/lens_server_proto/lens_overlay_contextual_inputs.proto b/third_party/lens_server_proto/lens_overlay_contextual_inputs.proto
index 3a175961..137be034 100644
--- a/third_party/lens_server_proto/lens_overlay_contextual_inputs.proto
+++ b/third_party/lens_server_proto/lens_overlay_contextual_inputs.proto
@@ -31,6 +31,7 @@
   // contextual searchbox.
   CONTEXTUAL_INPUT_UPLOAD_TYPE_CONTEXTUAL_SEARCHBOX_INITIAL_QUERY = 5;
 }
+
 // A container for lens inputs sent into search. Each LensOverlayContextualInput
 // represents one input file, such as an image or pdf.
 message LensOverlayContextualInputs {
diff --git a/third_party/lens_server_proto/lens_overlay_interaction_request_metadata.proto b/third_party/lens_server_proto/lens_overlay_interaction_request_metadata.proto
index ec5916c9..a99c532 100644
--- a/third_party/lens_server_proto/lens_overlay_interaction_request_metadata.proto
+++ b/third_party/lens_server_proto/lens_overlay_interaction_request_metadata.proto
@@ -81,5 +81,8 @@
 
   QueryMetadata query_metadata = 4;
 
+  // Whether the request will be used for AIM fulfillment.
+  bool is_aim_fulfillment_context = 5;
+
   reserved 3;
 }
diff --git a/third_party/lens_server_proto/lens_overlay_lens_file.proto b/third_party/lens_server_proto/lens_overlay_lens_file.proto
index 09e2341..12deb2b8 100644
--- a/third_party/lens_server_proto/lens_overlay_lens_file.proto
+++ b/third_party/lens_server_proto/lens_overlay_lens_file.proto
@@ -29,5 +29,15 @@
   // tabs.
   string page_url = 7;
 
+  // The uri of the annotated page content. Applies to Webpages in browser
+  // tabs.
+  string annotated_page_content_uri = 8;
+
+  // The uri of the file. Applies to remote PDFs.
+  string file_uri = 9;
+
+  // The uri of the viewport screenshot. Applies to both remote PDFs and
+  // Webpages in browser tabs.
+  string screenshot_uri = 10;
   reserved 4;
 }
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium
index e224e8b6..170c4fa 100644
--- a/third_party/metrics_proto/README.chromium
+++ b/third_party/metrics_proto/README.chromium
@@ -21,4 +21,4 @@
 
 
 Local Modifications:
-Added PERIODIC_HEAP_CHURN_COLLECTION to sampled_profile.proto locally (pending upstream export).
+Unchanged from the output of the export script.
diff --git a/third_party/metrics_proto/sampled_profile.proto b/third_party/metrics_proto/sampled_profile.proto
index 761fd99..16fb645c 100644
--- a/third_party/metrics_proto/sampled_profile.proto
+++ b/third_party/metrics_proto/sampled_profile.proto
@@ -60,9 +60,6 @@
     // between trigger events conform to the Poisson process with certain mean
     // interval between collections.
     PERIODIC_HEAP_COLLECTION = 7;
-
-    // The heap churn profile was triggered by periodic sampling of alloc-frees.
-    PERIODIC_HEAP_CHURN_COLLECTION = 8;
   }
 
   TriggerEvent trigger_event = 1;
diff --git a/third_party/spirv-tools/src b/third_party/spirv-tools/src
index 5b5a252..388f5af 160000
--- a/third_party/spirv-tools/src
+++ b/third_party/spirv-tools/src
@@ -1 +1 @@
-Subproject commit 5b5a25231a3543559b4f2cb27fe725a08b76b5d0
+Subproject commit 388f5af1a6d4a4df4ab9bf69a1817255952e6ef1
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index 909979d..e6f723a 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit 909979df82b1ef3a50180031299f94d54b7d4e0b
+Subproject commit e6f723a0c78be663f0b10b074c66c454973bb23f
diff --git a/third_party/vulkan-loader/src b/third_party/vulkan-loader/src
index e5715c8..fbfe4e7 160000
--- a/third_party/vulkan-loader/src
+++ b/third_party/vulkan-loader/src
@@ -1 +1 @@
-Subproject commit e5715c8a3959f2a40a6bc13a3a88a3862843b105
+Subproject commit fbfe4e7b3126dba48e4551fef5c6a19f15d6de12
diff --git a/third_party/vulkan-tools/src b/third_party/vulkan-tools/src
index d9c3539..572d10d 160000
--- a/third_party/vulkan-tools/src
+++ b/third_party/vulkan-tools/src
@@ -1 +1 @@
-Subproject commit d9c353983147b260354764f8d8cc452a8d8c2a36
+Subproject commit 572d10d787b74601ea09b696521c950c259ae815
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src
index 4a33de0..06171f9 160000
--- a/third_party/vulkan-validation-layers/src
+++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@
-Subproject commit 4a33de058dae899fdec1718e9daf81a5eda3bc2d
+Subproject commit 06171f9489fa80fe0cafcb25f9dfc1dcd3408f1a
diff --git a/tools/autotest/autotest_test.py b/tools/autotest/autotest_test.py
index b1df109..f27f99bc 100755
--- a/tools/autotest/autotest_test.py
+++ b/tools/autotest/autotest_test.py
@@ -473,6 +473,32 @@
     self.assertIn('chrome/test:browser_tests', targets)
     self.assertIn('chrome/test:unit_tests', targets)
 
+  def test_no_matching_targets_with_raw_targets(self):
+
+    def side_effect(cmd, *args, **kwargs):
+      if any(c.startswith('--type=executable') for c in cmd):
+        return ''
+      return '//components/payments:passkey_unknown_target'
+
+    self.mock_run_command.side_effect = side_effect
+    with self.assertRaises(Exception):
+      target_finder.FindTestTargets(self.mock_cache, self.out_dir, ['foo.cc'])
+    self.mock_exit.assert_called_once()
+    exit_msg = self.mock_exit.call_args[0][0]
+    self.assertIn('did not match any test targets', exit_msg)
+    self.assertIn('TEST_TARGET_ALLOWLIST', exit_msg)
+    self.assertIn('//components/payments:passkey_unknown_target', exit_msg)
+
+  def test_no_matching_targets_no_raw_targets(self):
+    self.mock_run_command.runnable_targets = []
+    self.mock_run_command.non_runnable_targets = []
+    with self.assertRaises(Exception):
+      target_finder.FindTestTargets(self.mock_cache, self.out_dir, ['foo.cc'])
+    self.mock_exit.assert_called_once()
+    exit_msg = self.mock_exit.call_args[0][0]
+    self.assertIn('did not match any test targets', exit_msg)
+    self.assertNotIn('TEST_TARGET_ALLOWLIST', exit_msg)
+
 
 class FindRelatedTestFilesTest(TestCase):
 
@@ -669,6 +695,20 @@
     called_args = self.mock_run_command.call_args[0][0]
     self.assertIn('(\\bFooTest\\b)', called_args)
 
+  def test_parameterized_test_syntax(self):
+    test_file = 'FooTest.cc'
+    self.fs.create_file(test_file,
+                        contents='IN_PROC_BROWSER_TEST_P(FooTest, Bar) {}')
+
+    self.mock_run_command.return_value = test_file
+
+    files, filter = file_finder.SearchForTestsByName(['FooTest.Bar'],
+                                                     quiet=True,
+                                                     remote_search=False)
+
+    self.assertEqual([test_file], files)
+    self.assertEqual('FooTest.Bar:FooTest.Bar/*:*/FooTest.Bar/*', filter)
+
 
 # Tests execution of multiple test targets to ensure correct flag isolation.
 class RunTestTargetsTest(TestCase):
@@ -802,6 +842,21 @@
     self.assertEqual(result.exit_code, 1)
     self.mock_run.assert_not_called()
 
+  def test_main_suite(self):
+    self.mock_run.return_value = 0
+    runner = CliRunner()
+    result = runner.invoke(
+        main.main, ['-C', self.out_dir, '--suite', 'chrome_junit_tests'])
+    self.assertEqual(result.exit_code, 0)
+    self.mock_build.assert_called_once_with(self.out_dir,
+                                            ['chrome_junit_tests'], False,
+                                            False, False)
+    self.mock_run.assert_called_once()
+    call_args, call_kwargs = self.mock_run.call_args
+    self.assertEqual(call_args[1], ['chrome_junit_tests'])
+    self.assertIsNone(call_args[2])
+    self.assertTrue(call_kwargs.get('is_suite'))
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/tools/autotest/finders/file_finder.py b/tools/autotest/finders/file_finder.py
index b468f7754..ceeb1d5 100644
--- a/tools/autotest/finders/file_finder.py
+++ b/tools/autotest/finders/file_finder.py
@@ -185,7 +185,7 @@
     if '.' not in term:
       return '*' + term + '*'
     # Otherwise run any parameterized tests with this prefix.
-    return f'{term}:{term}/*'
+    return f'{term}:{term}/*:*/{term}/*'
 
   pattern: str = '|'.join(f'({GetPatternForTerm(t)})' for t in terms)
 
diff --git a/tools/autotest/finders/target_finder.py b/tools/autotest/finders/target_finder.py
index c09043ba..5bdbc2bc 100644
--- a/tools/autotest/finders/target_finder.py
+++ b/tools/autotest/finders/target_finder.py
@@ -82,7 +82,8 @@
   return targets
 
 
-def _FindTestTargetsViaGnRefs(out_dir: str, gn_paths: list[str]) -> list[str]:
+def _FindTestTargetsViaGnRefs(
+    out_dir: str, gn_paths: list[str]) -> tuple[list[str], list[str]]:
   gn_path: str = os.path.join(str(const.DEPOT_TOOLS_DIR), 'gn.py')
 
   cmd: list[str] = [
@@ -119,7 +120,7 @@
     if not test_targets and targets:
       test_targets = _ParseRefsOutput(
           command.RunCommand(cmd + ['--type=executable']))
-  return test_targets
+  return test_targets, targets
 
 
 def FindTestTargets(
@@ -142,18 +143,21 @@
     web_test_paths = {p for p in paths if file_finder.IsWebTestFile(p)}
     gn_paths = [p for p in paths if p not in web_test_paths]
     test_targets = []
+    raw_targets = []
 
     if gn_paths:
-      test_targets = _FindTestTargetsViaGnRefs(out_dir, gn_paths)
+      test_targets, raw_targets = _FindTestTargetsViaGnRefs(out_dir, gn_paths)
 
     if web_test_paths:
       test_targets.append('//blink_tests')
 
-  if not test_targets:
-    command.ExitWithMessage(
-        f'"{paths}" did not match any test targets. Consider adding'
-        f' one of the following targets to _TEST_TARGET_ALLOWLIST within '
-        f'{__file__}: \n' + '\n'.join(targets))
+    if not test_targets:
+      msg = f'"{paths}" did not match any test targets.'
+      if raw_targets:
+        msg += (f' Consider adding one of the following targets to '
+                f'TEST_TARGET_ALLOWLIST within {const.__file__}: \n' +
+                '\n'.join(raw_targets))
+      command.ExitWithMessage(msg)
 
   test_targets.sort()
   target_cache.Store(paths, test_targets)
diff --git a/tools/autotest/main.py b/tools/autotest/main.py
index c8b1b4be..86b8bcb4 100644
--- a/tools/autotest/main.py
+++ b/tools/autotest/main.py
@@ -193,16 +193,16 @@
   web_test_files = {f for f in filenames if file_finder.IsWebTestFile(f)}
   gn_files = [f for f in filenames if f not in web_test_files]
 
+  targets = []
+  used_cache = False
   if config.target:
     targets = [t.removeprefix('//') for t in config.target]
-    used_cache = False
-  else:
-    if not filenames and not direct_suites:
-      command.ExitWithMessage('No associated test files found.')
-
+  elif filenames:
     targets, used_cache = target_finder.FindTestTargets(
         target_cache, out_dir, filenames, config.run_all, config.run_changed
         or config.run_related, config.target_index, config.files)
+  elif not direct_suites:
+    command.ExitWithMessage('No associated test files found.')
 
   # Add any direct suites
   for suite in direct_suites:
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index a605ba7..c71d7e62 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -722,6 +722,8 @@
   <variant name="_PdfSearchifyFeature" summary="For PDF Searchify feature."/>
   <variant name="_PdfTextAnnotations"
       summary="For PDF Text Annotations feature."/>
+  <variant name="_PdfTranslateBubble"
+      summary="For PDF translate bubble feature."/>
   <variant name="_PerformanceInterventionDialog"
       summary="For triggering the performance intervention dialog and toolbar
                button feature."/>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 83f5617..5931fbc 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -9447,7 +9447,6 @@
   <int value="-1612418793"
       label="ExperimentalAccessibilitySwitchAccessSetupGuide:enabled"/>
   <int value="-1612253158" label="HardwareSecureDecryptionExperiment:enabled"/>
-  <int value="-1612045044" label="GlicOptInDialogLinkA11yFix:enabled"/>
   <int value="-1611828357" label="FocusModeYTM:enabled"/>
   <int value="-1611758030" label="AndroidWebContentsDarkMode:disabled"/>
   <int value="-1611614160" label="HiddenNetworkMigration:disabled"/>
@@ -9798,6 +9797,7 @@
   <int value="-1502515694" label="ProfilePickerTextVariations:disabled"/>
   <int value="-1501764956" label="AccessibilitySnapshotStressTests:enabled"/>
   <int value="-1501658124" label="IsolatedWebAppAutomaticUpdates:enabled"/>
+  <int value="-1501593786" label="GlicCaaGuestError:enabled"/>
   <int value="-1500811568" label="MuteNotificationSnoozeAction:disabled"/>
   <int value="-1500810366" label="DataControlsDragEnforcement:enabled"/>
   <int value="-1500005130" label="AIMSuppressVerbatimMatch:disabled"/>
@@ -14456,6 +14456,7 @@
   <int value="-41263570" label="OmniboxToolbelt:enabled"/>
   <int value="-41254374" label="WebRtcMetronomeTaskQueue:enabled"/>
   <int value="-40935502" label="ContextualSuggestionsSlimPeekUI:enabled"/>
+  <int value="-39994593" label="GlicCaaGuestError:disabled"/>
   <int value="-38819793" label="PCCTMinimumHeight:disabled"/>
   <int value="-38525285" label="AccessibilityManifestV3SelectToSpeak:enabled"/>
   <int value="-38147034"
@@ -17872,7 +17873,6 @@
   <int value="1045152062" label="SecurePaymentConfirmationBrowser:enabled"/>
   <int value="1045179357" label="NtpRealboxLensSearch:disabled"/>
   <int value="1045199610" label="AlignPdfDefaultPrintSettingsWithHTML:enabled"/>
-  <int value="1045923302" label="GlicOptInDialogLinkA11yFix:disabled"/>
   <int value="1046157767" label="Glic:disabled"/>
   <int value="1046878091" label="PasswordForceSaving:enabled"/>
   <int value="1046981538" label="OfflinePagesShowAlternateDinoPage:disabled"/>
diff --git a/tools/metrics/histograms/metadata/actor/histograms.xml b/tools/metrics/histograms/metadata/actor/histograms.xml
index b6a07c3..c171379 100644
--- a/tools/metrics/histograms/metadata/actor/histograms.xml
+++ b/tools/metrics/histograms/metadata/actor/histograms.xml
@@ -409,7 +409,7 @@
 </histogram>
 
 <histogram name="Actor.NavigationGating.ConfirmedListSize2" units="origins"
-    expires_after="2026-02-02">
+    expires_after="2027-02-02">
   <owner>dylancutler@google.com</owner>
   <owner>johannhof@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index bb2be43..7a514fa 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -1293,7 +1293,7 @@
 </histogram>
 
 <histogram name="Blink.DigitalCredentials.Create.HasTransientUserActivation"
-    enum="Boolean" expires_after="2026-09-05">
+    enum="Boolean" expires_after="2027-07-05">
   <owner>mamir@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
   <summary>
@@ -1313,7 +1313,7 @@
 </histogram>
 
 <histogram name="Blink.DigitalCredentials.Get.HasTransientUserActivation"
-    enum="Boolean" expires_after="2026-09-05">
+    enum="Boolean" expires_after="2027-07-05">
   <owner>mamir@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
   <summary>
@@ -5416,7 +5416,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.Infobar" enum="WebOTPServiceInfobarAction"
-    expires_after="2026-09-12">
+    expires_after="2027-06-01">
   <owner>yigu@chromium.org</owner>
   <owner>goto@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
@@ -5447,7 +5447,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.TimeCancel" units="ms"
-    expires_after="2026-09-12">
+    expires_after="2027-06-01">
   <owner>yigu@chromium.org</owner>
   <owner>goto@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
@@ -5458,7 +5458,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.TimeCancelOnKeyboardDismissal" units="ms"
-    expires_after="2026-09-12">
+    expires_after="2027-06-01">
   <owner>yigu@chromium.org</owner>
   <owner>goto@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
@@ -5469,7 +5469,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.TimeCancelOnSuccess" units="ms"
-    expires_after="2026-09-12">
+    expires_after="2027-06-01">
   <owner>yigu@chromium.org</owner>
   <owner>goto@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
@@ -5480,7 +5480,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.TimeContinueOnSuccess" units="ms"
-    expires_after="2026-09-12">
+    expires_after="2027-06-01">
   <owner>yigu@chromium.org</owner>
   <owner>goto@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
@@ -5514,7 +5514,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.TimeUserCancel" units="ms"
-    expires_after="2026-09-12">
+    expires_after="2027-06-01">
   <owner>yigu@chromium.org</owner>
   <owner>goto@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/dev/enums.xml b/tools/metrics/histograms/metadata/dev/enums.xml
index 0f8bd1b7..37928652 100644
--- a/tools/metrics/histograms/metadata/dev/enums.xml
+++ b/tools/metrics/histograms/metadata/dev/enums.xml
@@ -1594,6 +1594,25 @@
   <int value="1" label="Stopped"/>
 </enum>
 
+<enum name="DevToolsResendRequestType">
+  <int value="0" label="XHR"/>
+  <int value="1" label="Fetch"/>
+  <int value="2" label="Script"/>
+  <int value="3" label="Stylesheet"/>
+  <int value="4" label="Image"/>
+  <int value="5" label="Media"/>
+  <int value="6" label="Font"/>
+  <int value="7" label="WASM"/>
+  <int value="8" label="Manifest"/>
+  <int value="9" label="TextTrack"/>
+  <int value="10" label="SourceMapScript"/>
+  <int value="11" label="SourceMapStyleSheet"/>
+  <int value="12" label="Document"/>
+  <int value="13" label="Prefetch"/>
+  <int value="14" label="Ping"/>
+  <int value="15" label="Other"/>
+</enum>
+
 <enum name="DevToolsSwatchActivated">
   <int value="0" label="Variable link"/>
   <int value="1" label="Animation name link"/>
diff --git a/tools/metrics/histograms/metadata/dev/histograms.xml b/tools/metrics/histograms/metadata/dev/histograms.xml
index 25fe440..6953c0f6 100644
--- a/tools/metrics/histograms/metadata/dev/histograms.xml
+++ b/tools/metrics/histograms/metadata/dev/histograms.xml
@@ -118,6 +118,18 @@
   </summary>
 </histogram>
 
+<histogram name="DevTools.EditResendRequest" enum="DevToolsResendRequestType"
+    expires_after="2027-06-18">
+  <owner>dsv@chromium.org</owner>
+  <owner>dgarber@microsoft.com</owner>
+  <component>1456922</component>
+  <summary>
+    Records the resource type of a network request that the user selects to edit
+    and resend. Fired at the start of resendFromConsole(), at the point that a
+    fetch command is pasted into the console for user edit.
+  </summary>
+</histogram>
+
 <histogram name="DevTools.ExperimentDisabled" enum="DevtoolsExperiments"
     expires_after="2027-03-01">
   <owner>yangguo@google.com</owner>
@@ -482,6 +494,17 @@
   </summary>
 </histogram>
 
+<histogram name="DevTools.ResendRequest" enum="DevToolsResendRequestType"
+    expires_after="2027-06-18">
+  <owner>dsv@chromium.org</owner>
+  <owner>dgarber@microsoft.com</owner>
+  <component>1456922</component>
+  <summary>
+    Records the resource type of a network request resent from DevTools. Fired
+    at the start of resendRequest(), regardless of success or failure.
+  </summary>
+</histogram>
+
 <histogram name="DevTools.SourcesPanelFileDebugged" enum="DevToolsMediaType"
     expires_after="M160">
   <owner>bmeurer@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
index 8275afe..c8dfafe 100644
--- a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
+++ b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
@@ -551,6 +551,8 @@
       summary="educating users about PDF searchify feature"/>
   <variant name="IPH_PdfTextAnnotations"
       summary="educating users about PDF Text Annotations feature"/>
+  <variant name="IPH_PdfTranslateBubble"
+      summary="educating users about PDF translate bubble feature"/>
   <variant name="IPH_PerformanceInterventionDialog"
       summary="triggering the performance intervention dialog and toolbar
                button"/>
diff --git a/tools/metrics/histograms/metadata/glic/enums.xml b/tools/metrics/histograms/metadata/glic/enums.xml
index d2d8ffb..d2f8690c 100644
--- a/tools/metrics/histograms/metadata/glic/enums.xml
+++ b/tools/metrics/histograms/metadata/glic/enums.xml
@@ -433,7 +433,7 @@
   <int value="52" label="SubscribeToPinCandidates"/>
   <int value="53" label="(Obsolete) UnsubscribeFromPinCandidates"/>
   <int value="54" label="GetZeroStateSuggestionsForFocusedTab"/>
-  <int value="55" label="GetZeroStateSuggestionsAndSubscribe"/>
+  <int value="55" label="(Obsolete) GetZeroStateSuggestionsAndSubscribe"/>
   <int value="56" label="SetClosedCaptioningSetting"/>
   <int value="57" label="DropScrollToHighlight"/>
   <int value="58" label="MaybeRefreshUserStatus"/>
diff --git a/tools/metrics/histograms/metadata/glic/histograms.xml b/tools/metrics/histograms/metadata/glic/histograms.xml
index fce5b5ae..4f8cc50 100644
--- a/tools/metrics/histograms/metadata/glic/histograms.xml
+++ b/tools/metrics/histograms/metadata/glic/histograms.xml
@@ -51,7 +51,6 @@
   <variant name="GetOsMicrophonePermissionStatus"/>
   <variant name="GetSkill"/>
   <variant name="GetUserProfileInfo"/>
-  <variant name="GetZeroStateSuggestionsAndSubscribe"/>
   <variant name="GetZeroStateSuggestionsForFocusedTab"/>
   <variant name="InterruptActorTask"/>
   <variant name="JournalClear"/>
@@ -583,6 +582,33 @@
   </summary>
 </histogram>
 
+<histogram name="Glic.CUI.SubmitQuery.Latency{Outcome}" units="ms"
+    expires_after="2027-06-15">
+  <owner>dewittj@chromium.org</owner>
+  <owner>glic@google.com</owner>
+  <summary>
+    Records the latency of submitting a query in Glic {Outcome}.
+  </summary>
+  <token key="Outcome">
+    <variant name="" summary="regardless of the outcome"/>
+    <variant name=".Abandoned"
+        summary="when the interaction was abandoned by the user closing or
+                 hiding the UI before the response started"/>
+    <variant name=".Failed" summary="when the interaction failed"/>
+    <variant name=".FailedLatency"
+        summary="when the interaction succeeded but exceeded the latency
+                 threshold"/>
+    <variant name=".Success" summary="when the interaction succeeded"/>
+  </token>
+</histogram>
+
+<histogram name="Glic.CUI.SubmitQuery.Outcome" enum="GlicCuiOutcome"
+    expires_after="2027-06-15">
+  <owner>dewittj@chromium.org</owner>
+  <owner>glic@google.com</owner>
+  <summary>Records the outcome of submitting a query in Glic.</summary>
+</histogram>
+
 <histogram name="Glic.CUI.WindowEntryPointInvocation.Latency{Outcome}"
     units="ms" expires_after="2027-06-15">
   <owner>dewittj@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index df0d8cd2..1b030de6 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -1939,6 +1939,16 @@
   </summary>
 </histogram>
 
+<histogram name="Finds.ExecutionTime" units="ms" expires_after="2027-06-01">
+  <owner>wylieb@chromium.org</owner>
+  <owner>bjfong@google.com</owner>
+  <summary>
+    Records the end-to-end time taken from receiving a request for suggestions
+    (ExecuteModelAndScheduleNotification) to delivering those suggestions back
+    to the caller.
+  </summary>
+</histogram>
+
 <histogram name="Finds.Result" enum="FindsResult" expires_after="2027-06-01">
   <owner>wylieb@chromium.org</owner>
   <owner>bjfong@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/privacy/histograms.xml b/tools/metrics/histograms/metadata/privacy/histograms.xml
index edddb49b..d54ced9 100644
--- a/tools/metrics/histograms/metadata/privacy/histograms.xml
+++ b/tools/metrics/histograms/metadata/privacy/histograms.xml
@@ -1048,28 +1048,6 @@
   </summary>
 </histogram>
 
-<histogram name="PrivacySandbox.NoticeRequirement.IsVariationCountryEmpty"
-    enum="Boolean" expires_after="2026-02-01">
-  <owner>chrstne@google.com</owner>
-  <owner>koilos@google.com</owner>
-  <summary>
-    Records whether or not the variation country is empty for profiles. This
-    histogram will be used to determine if the variation country being empty
-    leads to an increased spike of Privacy Sandbox notice APIs being disabled.
-  </summary>
-</histogram>
-
-<histogram name="PrivacySandbox.NoticeRequirement.IsVariationServiceReady"
-    enum="Boolean" expires_after="2026-02-01">
-  <owner>chrstne@google.com</owner>
-  <owner>koilos@google.com</owner>
-  <summary>
-    Records whether or not the variation service is ready for profiles. This
-    histogram will be used to determine if the variation service not being ready
-    leads to an increased spike of Privacy Sandbox notice APIs being disabled.
-  </summary>
-</histogram>
-
 <histogram name="PrivacySandbox.ProtectedAudience.JoiningTopFrameDisplayed"
     enum="BooleanShown" expires_after="2025-01-05">
   <owner>sauski@google.com</owner>
@@ -1083,42 +1061,6 @@
   </summary>
 </histogram>
 
-<histogram name="PrivacySandbox.{ApiType}" enum="PrivacySandboxApiAllowed"
-    expires_after="never">
-<!-- expires-never: critical system health metric for monitoring -->
-
-  <owner>linnan@chromium.org</owner>
-  <owner>sauski@chromium.org</owner>
-  <summary>
-    Whether or not the {ApiType} operation was allowed by Privacy Sandbox
-    settings and why. Recorded at the time settings are checked when performing
-    the operation.
-
-    This is a critical histogram monitored by alerts. Please make sure future
-    versions of this histogram are properly set up for alerting. The attribution
-    reporting and private aggregation variants are monitored by
-    measurement-api-dev+alerts@google.com.
-  </summary>
-  <token key="ApiType">
-    <variant name="IsFledgeBuyAllowed" summary="IsFledgeBuyAllowed"/>
-    <variant name="IsFledgeJoinAllowed" summary="IsFledgeJoinAllowed"/>
-    <variant name="IsFledgeLeaveAllowed" summary="IsFledgeLeaveAllowed"/>
-    <variant name="IsFledgeReadAllowed" summary="IsFledgeReadAllowed"/>
-    <variant name="IsFledgeSellAllowed" summary="IsFledgeSellAllowed"/>
-    <variant name="IsFledgeUpdateAllowed" summary="IsFledgeUpdateAllowed"/>
-    <variant name="IsPrivacySandboxReportingDestinationAttested"
-        summary="IsPrivacySandboxReportingDestinationAttested"/>
-    <variant name="IsPrivateAggregationAllowed"
-        summary="IsPrivateAggregationAllowed"/>
-    <variant name="IsSharedStorageAllowed" summary="IsSharedStorageAllowed"/>
-    <variant name="IsSharedStorageSelectURLAllowed"
-        summary="IsSharedStorageSelectURLAllowed"/>
-    <variant name="IsTopicsAllowed" summary="IsTopicsAllowed"/>
-    <variant name="IsTopicsAllowedForContext"
-        summary="IsTopicsAllowedForContext"/>
-  </token>
-</histogram>
-
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/user_education/histograms.xml b/tools/metrics/histograms/metadata/user_education/histograms.xml
index cd1a6a9..c18730e 100644
--- a/tools/metrics/histograms/metadata/user_education/histograms.xml
+++ b/tools/metrics/histograms/metadata/user_education/histograms.xml
@@ -52,6 +52,9 @@
       summary="For plus address creation; shown in the autofill popup."/>
   <variant name="PlusAddressFallbackFromContextMenu"
       summary="Plus address manual fallback entry point in the context menu."/>
+  <variant name="ReadAnythingLineFocus"
+      summary="Shown on the Line Focus menu item in Reading Mode settings
+               menu."/>
   <variant name="SendTabToSelfEnhancedDesktopUI"
       summary="Promotion for Send to Your Device in the tab and webpage
                context menus."/>
diff --git a/tools/metrics/private_metrics/dwa.xml b/tools/metrics/private_metrics/dwa.xml
index 34f8dd3..78ae41c 100644
--- a/tools/metrics/private_metrics/dwa.xml
+++ b/tools/metrics/private_metrics/dwa.xml
@@ -68,19 +68,6 @@
   </metric>
 </event>
 
-<event name="PrivacySandbox.IsPrivateAggregationAllowed">
-  <owner>alexmt@chromium.org</owner>
-  <owner>linnan@chromium.org</owner>
-  <owner>measurement-api-dev+metrics@google.com</owner>
-  <metric name="Status" enum="PrivacySandboxApiAllowed">
-    <summary>
-      Whether or not the IsPrivateAggregationAllowed operation was allowed by
-      Privacy Sandbox settings and why. Recorded at the time settings are
-      checked when performing the operation.
-    </summary>
-  </metric>
-</event>
-
 <event name="SharedStorage.RunFinishedInWorklet">
   <owner>yaoxia@google.com</owner>
   <owner>cammie@google.com</owner>
diff --git a/tools/metrics/ukm/decode_template.py b/tools/metrics/ukm/decode_template.py
index 9c830d8..f5ae6b8 100644
--- a/tools/metrics/ukm/decode_template.py
+++ b/tools/metrics/ukm/decode_template.py
@@ -4,9 +4,12 @@
 
 """A template for generating hash decoding code."""
 
+import os
+
 import setup_modules  # pylint: disable=unused-import
 
 import chromium_src.tools.metrics.ukm.codegen as codegen
+import chromium_src.tools.metrics.ukm.ukm_model as ukm_model
 
 HEADER = codegen.Template(basename="ukm_decode.h",
                           file_template="""
@@ -40,41 +43,116 @@
                           event_template="",
                           metric_template="")
 
-IMPL = codegen.Template(basename="ukm_decode.cc",
-                        file_template="""
+_IMPL_FILE_TEMPLATE = """
 // Generated from gen_builders.py.  DO NOT EDIT!
 // source: ukm.xml
 
 #include "{file.dir_path}/ukm_decode.h"
-#include "{file.dir_path}/ukm_builders.h"
+
+#include <cstdint>
+#include <iterator>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "base/metrics/metrics_hashes.h"
 
 namespace ukm {{
 namespace builders {{
 
+namespace {{
+
+// Entry and metric names, in decode-map order: each entry name is
+// followed by the names of its metrics.
+// TODO(crbug.com/541711739): These strings duplicate the kEntryName /
+// k*Name constants in ukm_builders.cc. Make ukm_builders.cc share this
+// string table instead of defining its own copies.
+constexpr char kNames[] =
+{name_strings};
+
+// Number of metrics for each entry, in the order the entry names appear
+// in kNames.
+constexpr uint16_t kMetricCounts[] = {{
+{metric_count_rows}}};
+
+}}  // namespace
+
 const DecodeMap& GetDecodeMap() {{
-  static const base::NoDestructor<DecodeMap> decode_map({{
-    {event_code}
-  }});
+  // The hashes are computed at runtime rather than emitted as
+  // codegen-time constants: this is an intentional trade-off that keeps
+  // ukm_decode.cc as compact name tables instead of a per-metric
+  // initializer with an 8-byte hash immediate each, saving binary size.
+  // The one-time construction cost (low single-digit ms) is paid when
+  // the first UKM entry is recorded.
+  static const base::NoDestructor<DecodeMap> decode_map([] {{
+    std::vector<std::pair<uint64_t, EntryDecoder>> entries;
+    entries.reserve(std::size(kMetricCounts));
+    std::string_view name = kNames;
+    for (const uint16_t metric_count : kMetricCounts) {{
+      const std::string_view entry_name = name;
+      // data() + size() points at the NUL separator; + 1 steps to the
+      // next name.
+      name = std::string_view(name.data() + name.size() + 1);
+      std::vector<std::pair<uint64_t, const char*>> metrics;
+      metrics.reserve(metric_count);
+      for (uint32_t i = 0; i < metric_count; ++i) {{
+        metrics.emplace_back(base::HashMetricName(name), name.data());
+        name = std::string_view(name.data() + name.size() + 1);
+      }}
+      entries.emplace_back(
+          base::HashMetricName(entry_name),
+          EntryDecoder{{entry_name.data(),
+                        MetricDecodeMap(std::move(metrics))}});
+    }}
+    return DecodeMap(std::move(entries));
+  }}());
   return *decode_map;
 }}
 
 }}  // namespace builders
 }}  // namespace ukm
-""",
-                        event_template="""
-    {{
-      UINT64_C({event.hash}),
-      {{
-        {event.name}::kEntryName,
-        {{
-          {metric_code}
-        }}
-      }}
-    }},
-""",
-                        metric_template="""
-    {{{event.name}::k{metric.name}NameHash, {event.name}::k{metric.name}Name}},
-""")
+"""
+
+_NAME_STRING_TEMPLATE = '    "{name}\\0"\n'
+_METRIC_COUNT_ROW_TEMPLATE = '    {metric_count},\n'
+
+
+class DecodeImplTemplate:
+  """Template for producing ukm_decode.cc from ukm.xml.
+
+  Emits the entry and metric names as data tables rather than per-metric
+  code, which is considerably smaller in the compiled binary.
+  """
+
+  def __init__(self):
+    self.basename = 'ukm_decode.cc'
+
+  def _stamp_file_code(self, relpath, data):
+    file_info = codegen.codegen_shared.FileInfo(relpath, self.basename)
+    names = []
+    metric_count_rows = ''
+    for event in data[ukm_model._EVENT_TYPE.tag]:
+      event_info = codegen.EventInfo(event)
+      metrics = event[ukm_model._METRIC_TYPE.tag]
+      assert len(metrics) <= 0xFFFF, (
+          f'Too many metrics in {event_info.raw_name} for uint16_t count')
+      names.append(event_info.raw_name)
+      names.extend(codegen.MetricInfo(metric).raw_name for metric in metrics)
+      metric_count_rows += _METRIC_COUNT_ROW_TEMPLATE.format(
+          metric_count=len(metrics))
+
+    name_strings = ''.join(
+        _NAME_STRING_TEMPLATE.format(name=name) for name in names)
+    return _IMPL_FILE_TEMPLATE.format(file=file_info,
+                                      name_strings=name_strings,
+                                      metric_count_rows=metric_count_rows)
+
+  def write_file(self, outdir, relpath, data):
+    with open(os.path.join(outdir, self.basename), 'w') as output:
+      output.write(self._stamp_file_code(relpath, data))
+
+
+IMPL = DecodeImplTemplate()
 
 
 def write_files(outdir, relpath, data):
diff --git a/tools/metrics/ukm/gen_builders_test.py b/tools/metrics/ukm/gen_builders_test.py
index 8a3dc81..1dd259b 100755
--- a/tools/metrics/ukm/gen_builders_test.py
+++ b/tools/metrics/ukm/gen_builders_test.py
@@ -123,11 +123,24 @@
     self.assertIn("// Generated from gen_builders.py.  DO NOT EDIT!",
                   decode_impl_output)
     self.assertIn("namespace builders", decode_impl_output)
+    self.assertIn('    "{eventName}\\0"'.format(eventName=event_info.raw_name),
+                  decode_impl_output)
     self.assertIn(
-        """
-    {{{eventName}::k{metricName}NameHash, {eventName}::k{metricName}Name}},""".
-        format(eventName=event_info.name,
-               metricName=metric_info.name), decode_impl_output)
+        '    "{metricName}\\0"'.format(metricName=metric_info.raw_name),
+        decode_impl_output)
+    self.assertIn('constexpr uint16_t kMetricCounts[]', decode_impl_output)
+    self.assertIn(
+        '    {metricCount},'.format(
+            metricCount=len(event[ukm_model._METRIC_TYPE.tag])),
+        decode_impl_output)
+    self.assertNotIn(
+        '{eventName}::kEntryName'.format(eventName=event_info.name),
+        decode_impl_output)
+    self.assertNotIn(
+        '{eventName}::k{metricName}Name'.format(eventName=event_info.name,
+                                                metricName=metric_info.name),
+        decode_impl_output)
+    self.assertIn('base::HashMetricName', decode_impl_output)
 
 
 if __name__ == '__main__':
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 01a9ae5..3dc11df 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/f3737359473279c63dd8cdf9c7d51cdc0ca1fe56/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "5fd63cdbd13feb0e2e487c3dcbf811ee3b0a1103",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/7b573c1c00f5d5890f496a87b4876a995b6a1c66/trace_processor_shell"
+            "hash": "bf55ab41283d633b3ac123e636fe85b3ff826aa2",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/70af586ff6aef1ffc53178aef055bd09e625b209/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/core/tbmv3/trace_processor.py b/tools/perf/core/tbmv3/trace_processor.py
index b4227f3..7d38f57 100644
--- a/tools/perf/core/tbmv3/trace_processor.py
+++ b/tools/perf/core/tbmv3/trace_processor.py
@@ -393,6 +393,7 @@
     query_file.close()
     _RunTraceProcessor(
         trace_processor_path,
+        '--allow-sql-file-access',
         '-q',
         query_file.name,
         proto_file,
diff --git a/tools/perf/core/tbmv3/trace_processor_unittest.py b/tools/perf/core/tbmv3/trace_processor_unittest.py
index 436d45d..5eef4a8 100644
--- a/tools/perf/core/tbmv3/trace_processor_unittest.py
+++ b/tools/perf/core/tbmv3/trace_processor_unittest.py
@@ -38,9 +38,10 @@
     return trace_file.name
 
   def testConvertProtoTraceToJson(self):
-    with mock.patch(RUN_METHOD):
+    with mock.patch(RUN_METHOD) as run_patch:
       trace_processor.ConvertProtoTraceToJson(self.tp_path, '/path/to/proto',
                                               '/path/to/json')
+      self.assertIn('--allow-sql-file-access', run_patch.call_args[0])
 
   def testRunMetricNoRepeated(self):
     metric_output = """
diff --git a/ui/accessibility/android/BUILD.gn b/ui/accessibility/android/BUILD.gn
index 440fe6334..632f3b77 100644
--- a/ui/accessibility/android/BUILD.gn
+++ b/ui/accessibility/android/BUILD.gn
@@ -13,7 +13,6 @@
     "javatest/src/org/chromium/ui/accessibility/testservice/EventMatcher.aidl",
     "javatest/src/org/chromium/ui/accessibility/testservice/IAccessibilityTestHelperService.aidl",
     "javatest/src/org/chromium/ui/accessibility/testservice/NodeMatcher.aidl",
-    "javatest/src/org/chromium/ui/accessibility/testservice/WaitForParams.aidl",
   ]
 }
 
diff --git a/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/AccessibilityTestHelperService.java b/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/AccessibilityTestHelperService.java
index 4583670..74ea109 100644
--- a/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/AccessibilityTestHelperService.java
+++ b/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/AccessibilityTestHelperService.java
@@ -8,12 +8,9 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.IBinder;
-import android.view.accessibility.AccessibilityEvent;
 
 import org.chromium.base.Log;
 
-import java.util.function.IntFunction;
-
 /**
  * Helper service to provide a bridge between instrumentation tests and the
  * AccessibilityTestService.
@@ -24,16 +21,25 @@
     private final IAccessibilityTestHelperService.Stub mBinder =
             new IAccessibilityTestHelperService.Stub() {
                 @Override
-                public boolean waitFor(WaitForParams params) {
+                public boolean waitForEvent(EventMatcher matcher, long timeoutMs) {
                     Log.i(
                             TAG,
-                            "waitFor called, timeoutMs: "
-                                    + params.timeoutMs
+                            "waitForEvent called, timeoutMs: "
+                                    + timeoutMs
                                     + ", eventMatcher: "
-                                    + eventMatcherToString(params.eventMatcher)
+                                    + AccessibilityTestService.eventMatcherToString(matcher));
+                    return AccessibilityTestService.waitForEvent(matcher, timeoutMs);
+                }
+
+                @Override
+                public boolean waitForNode(NodeMatcher matcher, long timeoutMs) {
+                    Log.i(
+                            TAG,
+                            "waitForNode called, timeoutMs: "
+                                    + timeoutMs
                                     + ", nodeMatcher: "
-                                    + nodeMatcherToString(params.nodeMatcher));
-                    return AccessibilityTestService.tryWaitFor(params);
+                                    + AccessibilityTestService.nodeMatcherToString(matcher));
+                    return AccessibilityTestService.waitForNode(matcher, timeoutMs);
                 }
 
                 @Override
@@ -74,96 +80,4 @@
         super.onDestroy();
         Log.i(TAG, "onDestroy");
     }
-
-    private String contentChangeTypesToString(int types) {
-        return flagsToString(
-                types, AccessibilityTestHelperService::singleContentChangeTypeToString);
-    }
-
-    private static String flagsToString(int flags, IntFunction<String> getFlagName) {
-        if (flags == 0) {
-            return "UNDEFINED";
-        }
-
-        // Parsing out the bits from flags bitmask, querying the corresponding
-        // value using getFlagName function parameter and appending to the
-        // return value.
-        StringBuilder builder = new StringBuilder();
-        int count = 0;
-        while (flags != 0) {
-            final int flag = 1 << Integer.numberOfTrailingZeros(flags);
-            flags &= ~flag;
-            if (count > 0) builder.append(", ");
-            builder.append(getFlagName.apply(flag));
-            count++;
-        }
-        return builder.toString();
-    }
-
-    private static String singleContentChangeTypeToString(int type) {
-        switch (type) {
-            case AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED:
-                return "UNDEFINED";
-            case AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE:
-                return "SUBTREE";
-            case AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT:
-                return "TEXT";
-            case AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION:
-                return "CONTENT_DESCRIPTION";
-            case AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION:
-                return "STATE_DESCRIPTION";
-            case AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_TITLE:
-                return "PANE_TITLE";
-            case AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED:
-                return "PANE_APPEARED";
-            case AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED:
-                return "PANE_DISAPPEARED";
-            case AccessibilityEvent.CONTENT_CHANGE_TYPE_DRAG_STARTED:
-                return "DRAG_STARTED";
-            case AccessibilityEvent.CONTENT_CHANGE_TYPE_DRAG_CANCELLED:
-                return "DRAG_CANCELLED";
-            case AccessibilityEvent.CONTENT_CHANGE_TYPE_DRAG_DROPPED:
-                return "DRAG_DROPPED";
-            case AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_INVALID:
-                return "CONTENT_INVALID";
-            case AccessibilityEvent.CONTENT_CHANGE_TYPE_ERROR:
-                return "ERROR";
-            case AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED:
-                return "ENABLED";
-            default:
-                return "UNKNOWN: " + Integer.toString(type);
-        }
-    }
-
-    private String eventMatcherToString(EventMatcher matcher) {
-        if (matcher == null) {
-            return "null";
-        }
-        return "EventMatcher{eventType="
-                + matcher.eventType
-                + ", contentChangeTypes="
-                + contentChangeTypesToString(matcher.contentChangeTypes)
-                + ", sourceMatcher="
-                + nodeMatcherToString(matcher.sourceMatcher)
-                + "}";
-    }
-
-    private String nodeMatcherToString(NodeMatcher matcher) {
-        if (matcher == null) {
-            return "null";
-        }
-        return "NodeMatcher{className='"
-                + matcher.className
-                + ", text='"
-                + matcher.text
-                + ", hasInputFocused="
-                + matcher.hasInputFocused
-                + ", inputFocused="
-                + matcher.inputFocused
-                + ", hasAccessibilityFocused="
-                + matcher.hasAccessibilityFocused
-                + ", accessibilityFocused="
-                + matcher.accessibilityFocused
-                + "}";
-    }
 }
diff --git a/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/AccessibilityTestService.java b/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/AccessibilityTestService.java
index 89d4f33..18d9b604 100644
--- a/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/AccessibilityTestService.java
+++ b/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/AccessibilityTestService.java
@@ -22,6 +22,9 @@
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.function.IntFunction;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
 
 import javax.annotation.concurrent.GuardedBy;
 
@@ -43,10 +46,6 @@
 
     public interface AccessibilityServiceListener {
         default void onAccessibilityEvent(AccessibilityEvent event) {}
-
-        default boolean shouldCacheEvent(AccessibilityEvent event) {
-            return true;
-        }
     }
 
     @GuardedBy("sLock")
@@ -59,96 +58,82 @@
         return sInstance;
     }
 
-    public static boolean tryWaitFor(WaitForParams params) {
-        CompletableFuture<Boolean> future = new CompletableFuture<>();
-        final boolean hasEventMatcher = params.eventMatcher != null;
-        final boolean hasNodeMatcher = params.nodeMatcher != null;
-
-        if (!hasEventMatcher && !hasNodeMatcher) {
-            Log.e(TAG, "Neither event nor node params are set in tryWaitFor");
-            return false;
-        }
-
-        final boolean initialEventReceived;
-        synchronized (sLock) {
-            clearListenerLocked();
-
-            if (hasEventMatcher) {
-                if (searchAndConsumeEventCacheLocked(params.eventMatcher)) {
-                    Log.i(TAG, "Found event in cache.");
-                    if (hasNodeMatcher) {
-                        if (findNode(
-                                params.nodeMatcher,
-                                "Found node in tree immediately after event cache match.")) {
+    public static boolean waitForEvent(EventMatcher eventMatcher, long timeoutMs) {
+        return waitForCondition(
+                () -> {
+                    synchronized (sLock) {
+                        if (searchAndConsumeEventCacheLocked(eventMatcher)) {
+                            Log.i(
+                                    TAG,
+                                    "Found event in cache: " + eventMatcherToString(eventMatcher));
                             return true;
                         }
-                        initialEventReceived = true;
-                    } else {
+                        return false;
+                    }
+                },
+                (event) -> {
+                    if (eventMatches(event, eventMatcher)) {
+                        Log.i(TAG, "Found event: " + eventMatcherToString(eventMatcher));
+                        synchronized (sLock) {
+                            // We should clear the entire cache after having matched an event to
+                            // avoid matching against any earlier event later through the test.
+                            clearEventCacheLocked();
+                        }
                         return true;
                     }
-                } else {
-                    initialEventReceived = false;
-                }
-            } else {
-                if (findNode(params.nodeMatcher, "Found node in tree immediately.")) {
-                    return true;
-                }
-                initialEventReceived = true;
-            }
+                    return false;
+                },
+                timeoutMs,
+                "Timed out waiting for event: " + eventMatcherToString(eventMatcher));
+    }
+
+    public static boolean waitForNode(NodeMatcher nodeMatcher, long timeoutMs) {
+        return waitForCondition(
+                () -> {
+                    if (findNode(nodeMatcher)) {
+                        Log.i(TAG, "Found node instantly: " + nodeMatcherToString(nodeMatcher));
+                        return true;
+                    }
+                    return false;
+                },
+                (event) -> {
+                    if (findNode(nodeMatcher)) {
+                        Log.i(
+                                TAG,
+                                "Found node: "
+                                        + nodeMatcherToString(nodeMatcher)
+                                        + " after event: "
+                                        + event);
+                        return true;
+                    }
+                    return false;
+                },
+                timeoutMs,
+                "Timed out waiting for node: " + nodeMatcherToString(nodeMatcher));
+    }
+
+    private static boolean waitForCondition(
+            Supplier<Boolean> immediateCheck,
+            Predicate<AccessibilityEvent> eventCheck,
+            long timeoutMs,
+            String timeoutLogMessage) {
+        synchronized (sLock) {
+            clearListenerLocked();
         }
 
-        if (hasEventMatcher) {
-            synchronized (sLock) {
-                // We only clear the cache as long as we are matching an event which we didn't find
-                // in the cache.
-                clearEventCacheLocked();
-            }
+        if (immediateCheck != null && immediateCheck.get()) {
+            return true;
         }
 
-        final java.util.concurrent.atomic.AtomicBoolean eventReceived =
-                new java.util.concurrent.atomic.AtomicBoolean(initialEventReceived);
-
+        CompletableFuture<Boolean> future = new CompletableFuture<>();
         AccessibilityServiceListener listener =
                 new AccessibilityServiceListener() {
                     @Override
                     public void onAccessibilityEvent(AccessibilityEvent event) {
-                        synchronized (sLock) {
-                            if (!eventReceived.get()) {
-                                if (eventMatches(event, params.eventMatcher)) {
-                                    Log.i(TAG, "  Event MATCHED.");
-                                    eventReceived.set(true);
-                                    if (hasNodeMatcher) {
-                                        if (findNode(
-                                                params.nodeMatcher,
-                                                "Found node in tree after event.")) {
-                                            future.complete(true);
-                                        }
-                                    } else {
-                                        future.complete(true);
-                                    }
-                                }
-                            } else {
-                                if (hasNodeMatcher) {
-                                    if (findNode(params.nodeMatcher, "Found node in tree.")) {
-                                        future.complete(true);
-                                    }
-                                }
-                            }
+                        if (eventCheck.test(event)) {
+                            future.complete(true);
                         }
                     }
-
-                    @Override
-                    public boolean shouldCacheEvent(AccessibilityEvent event) {
-                        if (hasEventMatcher && !hasNodeMatcher) {
-                            return false;
-                        }
-                        // If we're matching both an event and a node, we only should cache incoming
-                        // events as long as the desired event has been received.
-                        if (hasEventMatcher && hasNodeMatcher) {
-                            return eventReceived.get();
-                        }
-                        return true;
-                    }
                 };
 
         synchronized (sLock) {
@@ -156,12 +141,12 @@
         }
 
         try {
-            return future.get(params.timeoutMs, TimeUnit.MILLISECONDS);
+            return future.get(timeoutMs, TimeUnit.MILLISECONDS);
         } catch (TimeoutException e) {
-            Log.w(TAG, "Timed out waiting");
+            Log.w(TAG, timeoutLogMessage);
             return false;
         } catch (Exception e) {
-            Log.e(TAG, "Error waiting", e);
+            Log.e(TAG, "Error waiting for condition", e);
             return false;
         } finally {
             synchronized (sLock) {
@@ -170,7 +155,7 @@
         }
     }
 
-    private static boolean findNode(NodeMatcher nodeMatcher, String infoLog) {
+    private static boolean findNode(NodeMatcher nodeMatcher) {
         AccessibilityTestService instance = getInstance();
         if (instance == null) {
             Log.e(TAG, "AccessibilityTestService's instance was null when looking after node.");
@@ -178,7 +163,6 @@
         }
         AccessibilityNodeInfo root = instance.getRootInActiveWindow();
         if (root == null) return false;
-        Log.i(TAG, infoLog);
         return findNodeRecursive(root, nodeMatcher) != null;
     }
 
@@ -188,7 +172,7 @@
 
         CharSequence nodeClassName = node.getClassName();
         CharSequence nodeText = node.getText();
-        Log.i(TAG, "  findNodeRecursive: " + nodeClassName + " - " + nodeText);
+        Log.d(TAG, "  findNodeRecursive: " + nodeClassName + " - " + nodeText);
 
         if (nodeMatches(node, nodeMatcher)) {
             return node;
@@ -467,13 +451,9 @@
     public void onAccessibilityEvent(AccessibilityEvent event) {
         Log.i(TAG, "onAccessibilityEvent: " + event);
         synchronized (sLock) {
-            boolean shouldCache = true;
+            sEventCache.add(AccessibilityEvent.obtain(event));
             if (sListener != null) {
                 sListener.onAccessibilityEvent(event);
-                shouldCache = sListener.shouldCacheEvent(event);
-            }
-            if (shouldCache) {
-                sEventCache.add(AccessibilityEvent.obtain(event));
             }
         }
     }
@@ -484,4 +464,95 @@
     private static String getExtendedSelectionString(int offset, int offsetType) {
         return "{" + offset + ", " + (offsetType == OFFSET_TYPE_TEXT ? "text" : "child") + "}";
     }
+
+    public static String eventMatcherToString(EventMatcher matcher) {
+        if (matcher == null) {
+            return "null";
+        }
+        return "EventMatcher{eventType="
+                + matcher.eventType
+                + ", contentChangeTypes="
+                + contentChangeTypesToString(matcher.contentChangeTypes)
+                + ", sourceMatcher="
+                + nodeMatcherToString(matcher.sourceMatcher)
+                + "}";
+    }
+
+    public static String nodeMatcherToString(NodeMatcher matcher) {
+        if (matcher == null) {
+            return "null";
+        }
+        return "NodeMatcher{className='"
+                + matcher.className
+                + ", text='"
+                + matcher.text
+                + ", hasInputFocused="
+                + matcher.hasInputFocused
+                + ", inputFocused="
+                + matcher.inputFocused
+                + ", hasAccessibilityFocused="
+                + matcher.hasAccessibilityFocused
+                + ", accessibilityFocused="
+                + matcher.accessibilityFocused
+                + "}";
+    }
+
+    private static String contentChangeTypesToString(int types) {
+        return flagsToString(types, AccessibilityTestService::singleContentChangeTypeToString);
+    }
+
+    private static String flagsToString(int flags, IntFunction<String> getFlagName) {
+        if (flags == 0) {
+            return "UNDEFINED";
+        }
+
+        // Parsing out the bits from flags bitmask, querying the corresponding
+        // value using getFlagName function parameter and appending to the
+        // return value.
+        StringBuilder builder = new StringBuilder();
+        int count = 0;
+        while (flags != 0) {
+            final int flag = 1 << Integer.numberOfTrailingZeros(flags);
+            flags &= ~flag;
+            if (count > 0) builder.append(", ");
+            builder.append(getFlagName.apply(flag));
+            count++;
+        }
+        return builder.toString();
+    }
+
+    private static String singleContentChangeTypeToString(int type) {
+        switch (type) {
+            case AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED:
+                return "UNDEFINED";
+            case AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE:
+                return "SUBTREE";
+            case AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT:
+                return "TEXT";
+            case AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION:
+                return "CONTENT_DESCRIPTION";
+            case AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION:
+                return "STATE_DESCRIPTION";
+            case AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_TITLE:
+                return "PANE_TITLE";
+            case AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED:
+                return "PANE_APPEARED";
+            case AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED:
+                return "PANE_DISAPPEARED";
+            case AccessibilityEvent.CONTENT_CHANGE_TYPE_DRAG_STARTED:
+                return "DRAG_STARTED";
+            case AccessibilityEvent.CONTENT_CHANGE_TYPE_DRAG_CANCELLED:
+                return "DRAG_CANCELLED";
+            case AccessibilityEvent.CONTENT_CHANGE_TYPE_DRAG_DROPPED:
+                return "DRAG_DROPPED";
+            case AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_INVALID:
+                return "CONTENT_INVALID";
+            case AccessibilityEvent.CONTENT_CHANGE_TYPE_ERROR:
+                return "ERROR";
+            case AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED:
+                return "ENABLED";
+            default:
+                return "UNKNOWN: " + Integer.toString(type);
+        }
+    }
 }
diff --git a/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/IAccessibilityTestHelperService.aidl b/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/IAccessibilityTestHelperService.aidl
index 9149f3a..5210a03 100644
--- a/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/IAccessibilityTestHelperService.aidl
+++ b/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/IAccessibilityTestHelperService.aidl
@@ -5,17 +5,31 @@
 package org.chromium.ui.accessibility.testservice;
 
 import android.os.Bundle;
+import org.chromium.ui.accessibility.testservice.EventMatcher;
 import org.chromium.ui.accessibility.testservice.NodeMatcher;
-import org.chromium.ui.accessibility.testservice.WaitForParams;
 
 interface IAccessibilityTestHelperService {
     /**
-     * Waits for an accessibility event/node matching the given query parameters.
+     * Waits for an accessibility event matching the given matcher.
+     * Returns true if the condition is met within the timeout, false otherwise.
+     * We will first attempt to look after the event in the cache. If we do, we
+     * will erase the cache up to the point of the matched event (inclusive). If
+     * we don't, we'll listen after incoming events. After an event matched, no
+     * event having arrived earlier than that should stay in the cache.
+     *
+     * @param matcher The event matching criteria.
+     * @param timeoutMs The maximum time to wait in milliseconds.
+     */
+    boolean waitForEvent(in EventMatcher matcher, long timeoutMs);
+
+    /**
+     * Waits for an accessibility node matching the given matcher.
      * Returns true if the condition is met within the timeout, false otherwise.
      *
-     * @param params The wait parameters.
+     * @param matcher The node matching criteria.
+     * @param timeoutMs The maximum time to wait in milliseconds.
      */
-    boolean waitFor(in WaitForParams params);
+    boolean waitForNode(in NodeMatcher matcher, long timeoutMs);
 
     /**
      * Finds a node matching the matcher and performs the given action on it.
diff --git a/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/WaitForParams.aidl b/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/WaitForParams.aidl
deleted file mode 100644
index 11087b8..0000000
--- a/ui/accessibility/android/javatest/src/org/chromium/ui/accessibility/testservice/WaitForParams.aidl
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2026 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.ui.accessibility.testservice;
-
-import org.chromium.ui.accessibility.testservice.EventMatcher;
-import org.chromium.ui.accessibility.testservice.NodeMatcher;
-
-parcelable WaitForParams {
-    /** Expected parameters for the event to wait for. Null if not waiting for an event. */
-    EventMatcher eventMatcher;
-    /** Expected parameters for the node to wait for. Null if not waiting for a node. */
-    NodeMatcher nodeMatcher;
-    /** The maximum time to wait in milliseconds. */
-    long timeoutMs;
-}
diff --git a/ui/accessibility/ax_node.cc b/ui/accessibility/ax_node.cc
index be5c229..b5b6660 100644
--- a/ui/accessibility/ax_node.cc
+++ b/ui/accessibility/ax_node.cc
@@ -2172,7 +2172,19 @@
 }
 
 bool AXNode::IsIgnoredChildTreeHost() const {
-  if (!IsIgnored() || !GetHostedChildTreeManager()) {
+  if (!IsIgnored()) {
+    return false;
+  }
+
+  const AXTreeManager* child_tree_manager = GetHostedChildTreeManager();
+  if (!child_tree_manager) {
+    return false;
+  }
+
+  // Only consider this a child tree host if the child tree can also point back
+  // at it. A malformed child tree, or one that is not ready, may not have the
+  // parent tree ID and would therefore not be walkable.
+  if (child_tree_manager->GetParentTreeID() != tree_->GetAXTreeID()) {
     return false;
   }
 
diff --git a/ui/accessibility/ax_node_unittest.cc b/ui/accessibility/ax_node_unittest.cc
index 9b698f9c..810bcbd3 100644
--- a/ui/accessibility/ax_node_unittest.cc
+++ b/ui/accessibility/ax_node_unittest.cc
@@ -577,6 +577,29 @@
   EXPECT_EQ(After(), Before()->GetNextUnignoredSiblingCrossingTreeBoundary());
 }
 
+TEST_F(AXNodeIgnoredChildTreeHostTest, AHostWithoutAParentTreeIdStaysHidden) {
+  child_manager_.reset();
+  AXTreeUpdate orphan_update = child_update_;
+  orphan_update.tree_data.parent_tree_id = AXTreeIDUnknown();
+  child_manager_ = std::make_unique<ConnectableAXTreeManager>(
+      std::make_unique<AXTree>(orphan_update));
+
+  EXPECT_FALSE(Host()->IsIgnoredChildTreeHost());
+
+  size_t count = ParentRoot()->GetUnignoredChildCountCrossingTreeBoundary();
+  for (size_t index = 0; index < count; ++index) {
+    EXPECT_NE(nullptr,
+              ParentRoot()->GetUnignoredChildAtIndexCrossingTreeBoundary(index))
+        << "No child at index " << index << " of " << count;
+  }
+  EXPECT_EQ(nullptr,
+            ParentRoot()->GetUnignoredChildAtIndexCrossingTreeBoundary(count));
+
+  EXPECT_EQ(Before(),
+            ParentRoot()->GetFirstUnignoredChildCrossingTreeBoundary());
+  EXPECT_EQ(After(), Before()->GetNextUnignoredSiblingCrossingTreeBoundary());
+}
+
 TEST_F(AXNodeIgnoredChildTreeHostTest, TheHostedRootComesBack) {
   child_manager_.reset();
   ASSERT_EQ(2u, ParentRoot()->GetUnignoredChildCountCrossingTreeBoundary());
diff --git a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
index 7497ff7..87668a46 100644
--- a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
+++ b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
@@ -25,6 +25,7 @@
 import android.os.IBinder;
 import android.os.Process;
 import android.os.SystemClock;
+import android.util.ArraySet;
 import android.util.TypedValue;
 import android.view.Display;
 import android.view.KeyEvent;
@@ -81,8 +82,8 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
@@ -191,7 +192,8 @@
     private @Nullable ComponentCallbacks mComponentCallbacks;
 
     // We track all animations over content and provide a drawing placeholder for them.
-    private final HashSet<Animator> mAnimationsOverContent = new HashSet<>();
+    // ArraySet avoids heap entry overhead for tracking the small active animation set.
+    private final Set<Animator> mAnimationsOverContent = new ArraySet<>();
     private @Nullable View mAnimationPlaceholderView;
 
     /** A mechanism for observing and updating the application window's bottom inset. */
diff --git a/ui/android/java/src/org/chromium/ui/modelutil/PropertyModel.java b/ui/android/java/src/org/chromium/ui/modelutil/PropertyModel.java
index 9fd1266..7531c99 100644
--- a/ui/android/java/src/org/chromium/ui/modelutil/PropertyModel.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/PropertyModel.java
@@ -9,6 +9,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
+import android.util.ArrayMap;
 
 import androidx.annotation.DrawableRes;
 import androidx.annotation.StringRes;
@@ -21,6 +22,7 @@
 import org.chromium.build.annotations.Nullable;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
@@ -295,7 +297,7 @@
      * @param keys The key types supported by this model.
      */
     public PropertyModel(PropertyKey... keys) {
-        this(buildData(keys));
+        this(buildData(Arrays.asList(keys)));
     }
 
     /**
@@ -304,7 +306,7 @@
      * @param keys The key types supported by this model.
      */
     public PropertyModel(Collection<PropertyKey> keys) {
-        this(buildData(keys.toArray(new PropertyKey[keys.size()])));
+        this(buildData(keys));
     }
 
     private PropertyModel(Map<PropertyKey, ValueContainer> startingValues) {
@@ -572,6 +574,10 @@
                 mTransformers;
 
         public Builder(PropertyKey... keys) {
+            this(buildData(Arrays.asList(keys)));
+        }
+
+        public Builder(Collection<PropertyKey> keys) {
             this(buildData(keys));
         }
 
@@ -673,7 +679,9 @@
                 throw new IllegalArgumentException("Transforming key already exists.");
             }
             mData.put(key, null);
-            if (mTransformers == null) mTransformers = new HashMap<>();
+            // Transforming keys are typically limited to 1-2 properties (e.g. text or icon
+            // transformation in views), so capacity 2 minimizes memory footprint without rehashing.
+            if (mTransformers == null) mTransformers = new ArrayMap<>(2);
             assert transformer != null : "Requires non-null transformer";
             mTransformers.put(key, transformer);
             return this;
@@ -720,8 +728,14 @@
         return outList;
     }
 
-    private static Map<PropertyKey, ValueContainer> buildData(PropertyKey[] keys) {
-        Map<PropertyKey, ValueContainer> data = new HashMap<>();
+    // Threshold below which ArrayMap is used instead of HashMap to reduce memory overhead and
+    // avoid entry object allocations for typical small key sets (<= 32).
+    private static final int ARRAY_MAP_KEY_THRESHOLD = 32;
+
+    private static Map<PropertyKey, ValueContainer> buildData(Collection<PropertyKey> keys) {
+        int size = keys.size();
+        Map<PropertyKey, ValueContainer> data =
+                size <= ARRAY_MAP_KEY_THRESHOLD ? new ArrayMap<>(size) : new HashMap<>(size);
         for (PropertyKey key : keys) {
             if (data.containsKey(key)) {
                 throw new IllegalArgumentException("Duplicate key: " + key);
diff --git a/ui/android/java/src/org/chromium/ui/util/TokenHolder.java b/ui/android/java/src/org/chromium/ui/util/TokenHolder.java
index 597d785..7f0be85 100644
--- a/ui/android/java/src/org/chromium/ui/util/TokenHolder.java
+++ b/ui/android/java/src/org/chromium/ui/util/TokenHolder.java
@@ -4,9 +4,10 @@
 
 package org.chromium.ui.util;
 
+import android.util.ArraySet;
+
 import org.chromium.build.annotations.NullMarked;
 
-import java.util.HashSet;
 import java.util.Set;
 
 /**
@@ -20,7 +21,8 @@
 
     private int mNextToken;
 
-    private final Set<Integer> mAcquiredTokens = new HashSet<>();
+    // Normally there should not be very many tokens so an ArraySet should be fine.
+    private final Set<Integer> mAcquiredTokens = new ArraySet<>();
     private final Runnable mCallback;
 
     /**
diff --git a/ui/android/junit/src/org/chromium/ui/recyclerview/widget/ItemTouchHelper2UnitTest.java b/ui/android/junit/src/org/chromium/ui/recyclerview/widget/ItemTouchHelper2UnitTest.java
index c5e06bf..0aef595d 100644
--- a/ui/android/junit/src/org/chromium/ui/recyclerview/widget/ItemTouchHelper2UnitTest.java
+++ b/ui/android/junit/src/org/chromium/ui/recyclerview/widget/ItemTouchHelper2UnitTest.java
@@ -23,12 +23,10 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.base.test.util.Batch;
 
 /** Unit tests for {@link ItemTouchHelper2}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-@Batch(Batch.UNIT_TESTS)
 public class ItemTouchHelper2UnitTest {
 
     private Context mContext;
diff --git a/ui/android/junit/src/org/chromium/ui/util/TokenHolderTest.java b/ui/android/junit/src/org/chromium/ui/util/TokenHolderTest.java
index e3f2f63..710613e6 100644
--- a/ui/android/junit/src/org/chromium/ui/util/TokenHolderTest.java
+++ b/ui/android/junit/src/org/chromium/ui/util/TokenHolderTest.java
@@ -13,9 +13,13 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+import org.robolectric.annotation.Config;
 
-@RunWith(JUnit4.class)
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/** Unit tests for {@link TokenHolder}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
 public class TokenHolderTest {
     private final Runnable mCallback = mock(Runnable.class);
     private final TokenHolder mHolder = new TokenHolder(mCallback);
diff --git a/ui/android/junit/src/org/chromium/ui/widget/TextViewWithLeadingTest.java b/ui/android/junit/src/org/chromium/ui/widget/TextViewWithLeadingTest.java
index cda9690..78c80e3 100644
--- a/ui/android/junit/src/org/chromium/ui/widget/TextViewWithLeadingTest.java
+++ b/ui/android/junit/src/org/chromium/ui/widget/TextViewWithLeadingTest.java
@@ -22,7 +22,6 @@
 
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.ui.R;
@@ -31,7 +30,6 @@
 
 /** Unit tests for {@link TextViewWithLeading}. */
 @RunWith(BaseJUnit4ClassRunner.class)
-@Batch(Batch.PER_CLASS)
 public class TextViewWithLeadingTest {
     @ClassRule
     public static BaseActivityTestRule<BlankUiTestActivity> sActivityTestRule =
diff --git a/ui/base/interaction/BUILD.gn b/ui/base/interaction/BUILD.gn
index 2a5a0cb..f185132 100644
--- a/ui/base/interaction/BUILD.gn
+++ b/ui/base/interaction/BUILD.gn
@@ -49,6 +49,8 @@
   testonly = true
 
   sources = [
+    "element_state_observers.cc",
+    "element_state_observers.h",
     "element_test_util.cc",
     "element_test_util.h",
     "expect_call_in_scope.h",
diff --git a/ui/base/interaction/element_state_observers.cc b/ui/base/interaction/element_state_observers.cc
new file mode 100644
index 0000000..0f7981c2
--- /dev/null
+++ b/ui/base/interaction/element_state_observers.cc
@@ -0,0 +1,57 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/interaction/element_state_observers.h"
+
+#include "ui/base/interaction/state_observer.h"
+
+namespace ui::test::internal {
+
+DEFINE_STATE_IDENTIFIER_VALUE(::ui::test::internal::ElementCountStateObserver,
+                              kWaitForElementCountState);
+
+ElementCountStateObserver::ElementCountStateObserver(
+    ui::ElementIdentifier id,
+    ui::ElementContext context) {
+  auto* const tracker = ui::ElementTracker::GetElementTracker();
+
+  if (context) {
+    count_ = tracker->GetAllMatchingElements(id, context).size();
+    shown_subscription_ = tracker->AddElementShownCallback(
+        id, context,
+        base::BindRepeating(&ElementCountStateObserver::OnCountChanged,
+                            base::Unretained(this), true));
+    hidden_subscription_ = tracker->AddElementHiddenCallback(
+        id, context,
+        base::BindRepeating(&ElementCountStateObserver::OnCountChanged,
+                            base::Unretained(this), false));
+  } else {
+    count_ = tracker->GetAllMatchingElementsInAnyContext(id).size();
+    shown_subscription_ = tracker->AddElementShownInAnyContextCallback(
+        id, base::BindRepeating(&ElementCountStateObserver::OnCountChanged,
+                                base::Unretained(this), true));
+    hidden_subscription_ = tracker->AddElementHiddenInAnyContextCallback(
+        id, base::BindRepeating(&ElementCountStateObserver::OnCountChanged,
+                                base::Unretained(this), false));
+  }
+}
+
+ElementCountStateObserver::~ElementCountStateObserver() = default;
+
+size_t ElementCountStateObserver::GetStateObserverInitialState() const {
+  return count_;
+}
+
+void ElementCountStateObserver::OnCountChanged(bool added,
+                                               ui::TrackedElement*) {
+  if (added) {
+    ++count_;
+  } else {
+    CHECK_GT(count_, 0U);
+    --count_;
+  }
+  OnStateObserverStateChanged(count_);
+}
+
+}  // namespace ui::test::internal
diff --git a/ui/base/interaction/element_state_observers.h b/ui/base/interaction/element_state_observers.h
new file mode 100644
index 0000000..ed04815
--- /dev/null
+++ b/ui/base/interaction/element_state_observers.h
@@ -0,0 +1,39 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_INTERACTION_ELEMENT_STATE_OBSERVERS_H_
+#define UI_BASE_INTERACTION_ELEMENT_STATE_OBSERVERS_H_
+
+#include <optional>
+
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/state_observer.h"
+
+namespace ui::test::internal {
+
+// Observer that waits for a specific number of elements with `id` in `context`;
+// if `context` is null, the count of elements with `id` in all contexts are
+// observed.
+class ElementCountStateObserver : public StateObserver<size_t> {
+ public:
+  ElementCountStateObserver(ui::ElementIdentifier id,
+                            ui::ElementContext context);
+  ~ElementCountStateObserver() override;
+
+  size_t GetStateObserverInitialState() const override;
+
+ private:
+  void OnCountChanged(bool added, ui::TrackedElement*);
+
+  base::CallbackListSubscription shown_subscription_;
+  base::CallbackListSubscription hidden_subscription_;
+  size_t count_ = 0;
+};
+
+DECLARE_STATE_IDENTIFIER_VALUE(::ui::test::internal::ElementCountStateObserver,
+                               kWaitForElementCountState);
+
+}  // namespace ui::test::internal
+
+#endif  // UI_BASE_INTERACTION_ELEMENT_STATE_OBSERVERS_H_
diff --git a/ui/base/interaction/interactive_test.cc b/ui/base/interaction/interactive_test.cc
index 3dff7fc..6e71d14 100644
--- a/ui/base/interaction/interactive_test.cc
+++ b/ui/base/interaction/interactive_test.cc
@@ -20,11 +20,13 @@
 #include "base/test/scoped_run_loop_timeout.h"
 #include "third_party/abseil-cpp/absl/functional/overload.h"
 #include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_state_observers.h"
 #include "ui/base/interaction/element_tracker.h"
 #include "ui/base/interaction/interaction_sequence.h"
 #include "ui/base/interaction/interaction_test_util.h"
 #include "ui/base/interaction/interactive_test_definitions.h"
 #include "ui/base/interaction/interactive_test_internal.h"
+#include "ui/base/interaction/state_observer.h"
 
 namespace ui::test {
 
@@ -332,6 +334,26 @@
               internal::DescribeElement(element_to_check).c_str())));
 }
 
+InteractiveTestApi::MultiStep InteractiveTestApi::WaitForElementCount(
+    ElementIdentifier id,
+    size_t count) {
+  auto steps = Steps(
+      WithElement(
+          internal::kInteractiveTestPivotElementId,
+          [this, id](ui::InteractionSequence* seq, ui::TrackedElement* el) {
+            private_test_impl().AddStateObserver(
+                internal::kWaitForElementCountState.identifier(), el->context(),
+                std::make_unique<internal::ElementCountStateObserver>(
+                    id, seq->IsCurrentStepInAnyContextForTesting()
+                            ? ui::ElementContext()
+                            : el->context()));
+          }),
+      WaitForState(internal::kWaitForElementCountState, count),
+      StopObservingState(internal::kWaitForElementCountState));
+  AddDescriptionPrefix(steps, "WaitForElementCount");
+  return steps;
+}
+
 InteractionSequence::StepBuilder InteractiveTestApi::NameElement(
     std::string_view name,
     AbsoluteElementSpecifier spec) {
@@ -469,7 +491,7 @@
   return std::visit(
       absl::Overload{
           [](TrackedElement* el) {
-            CHECK(el) << "NameView(TrackedElement*): view must be set.";
+            CHECK(el) << "NameElement(TrackedElement*): element must be set.";
             return base::BindOnce(
                 [](const SafeElementReference& ref, TrackedElement*) {
                   LOG_IF(ERROR, !ref.get()) << "NameElement(TrackedElement*): "
diff --git a/ui/base/interaction/interactive_test.h b/ui/base/interaction/interactive_test.h
index 1d6ae4f..c4f66e0 100644
--- a/ui/base/interaction/interactive_test.h
+++ b/ui/base/interaction/interactive_test.h
@@ -311,6 +311,12 @@
   [[nodiscard]] static StepBuilder EnsurePresent(
       ElementSpecifier element_to_check);
 
+  // Waits until there are `count` visible elements with identifier `id`.
+  // If `InAnyContext()` is specified, the elements will be counted in any
+  // context.
+  [[nodiscard]] MultiStep WaitForElementCount(ElementIdentifier id,
+                                              size_t count);
+
   // Specifies an element not relative to any particular other element.
   using AbsoluteElementSpecifier = std::variant<
       // Specify an element that is known at the time the sequence is created.
@@ -923,7 +929,7 @@
          ui::TrackedElement* el) {
         TrackedElement* const result = std::move(find_callback).Run(el);
         if (!result) {
-          LOG(ERROR) << "NameElement(): No View found.";
+          LOG(ERROR) << "NameElement(): No element found.";
           seq->FailForTesting();
           return;
         }
diff --git a/ui/base/interaction/interactive_test_unittest.cc b/ui/base/interaction/interactive_test_unittest.cc
index f350161..210ed9c 100644
--- a/ui/base/interaction/interactive_test_unittest.cc
+++ b/ui/base/interaction/interactive_test_unittest.cc
@@ -791,6 +791,104 @@
   });
 }
 
+TEST_F(InteractiveTestTest, WaitForElementCount_FromBelow) {
+  TestElement e1(kTestId1, kTestContext1);
+  TestElement e2(kTestId1, kTestContext1);
+  TestElement e3(kTestId1, kTestContext1);
+
+  // These elements should be irrelevant.
+  TestElement e4(kTestId1, kTestContext2);
+  TestElement e5(kTestId2, kTestContext1);
+  e4.Show();
+  e5.Show();
+
+  bool expect_done = false;
+
+  QueueActions([&e1] { e1.Show(); }, [&e2] { e2.Show(); }, [&e2] { e2.Hide(); },
+               [&e2] { e2.Show(); },
+               [&e3, &expect_done] {
+                 expect_done = true;
+                 e3.Show();
+               });
+
+  RunTestSequenceInContext(kTestContext1, WaitForElementCount(kTestId1, 3U),
+                           Check([&expect_done] { return expect_done; }));
+}
+
+TEST_F(InteractiveTestTest, WaitForElementCount_FromAbove) {
+  TestElement e1(kTestId1, kTestContext1);
+  TestElement e2(kTestId1, kTestContext1);
+  TestElement e3(kTestId1, kTestContext1);
+  e1.Show();
+  e2.Show();
+  e3.Show();
+
+  // These elements should be irrelevant.
+  TestElement e4(kTestId1, kTestContext2);
+  TestElement e5(kTestId2, kTestContext1);
+  e4.Show();
+  e5.Show();
+
+  bool expect_done = false;
+
+  QueueActions([&e1] { e1.Hide(); }, [&e1] { e1.Show(); }, [&e2] { e2.Hide(); },
+               [&e1, &expect_done] {
+                 expect_done = true;
+                 e1.Hide();
+               });
+
+  RunTestSequenceInContext(kTestContext1, WaitForElementCount(kTestId1, 1U),
+                           Check([&expect_done] { return expect_done; }));
+}
+
+TEST_F(InteractiveTestTest, WaitForElementCount_InAnyContext_FromBelow) {
+  TestElement e1(kTestId1, kTestContext1);
+  TestElement e2(kTestId1, kTestContext2);
+  TestElement e3(kTestId1, kTestContext2);
+
+  // This element should be irrelevant.
+  TestElement e4(kTestId2, kTestContext1);
+  e4.Show();
+
+  bool expect_done = false;
+
+  QueueActions([&e1] { e1.Show(); }, [&e2] { e2.Show(); }, [&e2] { e2.Hide(); },
+               [&e2] { e2.Show(); },
+               [&e3, &expect_done] {
+                 expect_done = true;
+                 e3.Show();
+               });
+
+  RunTestSequenceInContext(kTestContext1,
+                           InAnyContext(WaitForElementCount(kTestId1, 3U)),
+                           Check([&expect_done] { return expect_done; }));
+}
+
+TEST_F(InteractiveTestTest, WaitForElementCount_InAnyContext_FromAbove) {
+  TestElement e1(kTestId1, kTestContext1);
+  TestElement e2(kTestId1, kTestContext2);
+  TestElement e3(kTestId1, kTestContext2);
+  e1.Show();
+  e2.Show();
+  e3.Show();
+
+  // This element should be irrelevant.
+  TestElement e4(kTestId2, kTestContext1);
+  e4.Show();
+
+  bool expect_done = false;
+
+  QueueActions([&e2] { e2.Hide(); }, [&e2] { e2.Show(); }, [&e1] { e1.Hide(); },
+               [&e2, &expect_done] {
+                 expect_done = true;
+                 e2.Hide();
+               });
+
+  RunTestSequenceInContext(kTestContext1,
+                           InAnyContext(WaitForElementCount(kTestId1, 1U)),
+                           Check([&expect_done] { return expect_done; }));
+}
+
 TEST_F(InteractiveTestTest, NameElementWithPointer) {
   UNCALLED_MOCK_CALLBACK(base::OnceCallback<void(TrackedElement*)>, cb);
 
diff --git a/ui/gtk/gtk_ui.cc b/ui/gtk/gtk_ui.cc
index 485fe8b..23025ffb 100644
--- a/ui/gtk/gtk_ui.cc
+++ b/ui/gtk/gtk_ui.cc
@@ -971,7 +971,7 @@
 
 void GtkUi::OnEnableAnimationsChanged(GtkSettings* settings,
                                       GtkParamSpec* param) {
-  NotifyAnimationsEnabledChanged();
+  gfx::Animation::UpdatePrefersReducedMotion();
 }
 
 void GtkUi::OnPrimaryPasteChanged(GtkSettings* settings, GtkParamSpec* param) {
diff --git a/ui/linux/linux_ui.cc b/ui/linux/linux_ui.cc
index c94906d..4c86d68 100644
--- a/ui/linux/linux_ui.cc
+++ b/ui/linux/linux_ui.cc
@@ -11,6 +11,7 @@
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
 #include "base/no_destructor.h"
+#include "build/build_config.h"
 #include "ui/linux/cursor_theme_manager_observer.h"
 #include "ui/linux/linux_ui_getter.h"
 #include "ui/linux/primary_paste_pref_observer.h"
@@ -21,26 +22,11 @@
 
 LinuxUi* g_linux_ui = nullptr;
 
-base::RepeatingClosureList& GetAnimationsEnabledCallbackList() {
-  static base::NoDestructor<base::RepeatingClosureList> s_callbacks;
-  return *s_callbacks;
-}
-
 }  // namespace
 
 // static
 LinuxUi* LinuxUi::SetInstance(LinuxUi* instance) {
-  LinuxUi* old_instance = std::exchange(g_linux_ui, instance);
-  if (old_instance != instance) {
-    GetAnimationsEnabledCallbackList().Notify();
-  }
-  return old_instance;
-}
-
-// static
-base::CallbackListSubscription LinuxUi::RegisterAnimationsEnabledCallback(
-    base::RepeatingClosure callback) {
-  return GetAnimationsEnabledCallbackList().Add(std::move(callback));
+  return std::exchange(g_linux_ui, instance);
 }
 
 // static
@@ -95,10 +81,6 @@
   primary_paste_observer_list_.RemoveObserver(observer);
 }
 
-void LinuxUi::NotifyAnimationsEnabledChanged() {
-  GetAnimationsEnabledCallbackList().Notify();
-}
-
 LinuxUi::FontSettings LinuxUi::GetDefaultFontDescription() {
   if (!default_font_settings_.has_value()) {
     InitializeFontSettings();
diff --git a/ui/linux/linux_ui.h b/ui/linux/linux_ui.h
index 6c150f2..511dcc9 100644
--- a/ui/linux/linux_ui.h
+++ b/ui/linux/linux_ui.h
@@ -10,11 +10,9 @@
 #include <string>
 #include <vector>
 
-#include "base/callback_list.h"
 #include "base/command_line.h"
 #include "base/component_export.h"
 #include "base/containers/flat_map.h"
-#include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr_exclusion.h"
 #include "base/observer_list.h"
 #include "base/scoped_observation_traits.h"
@@ -131,14 +129,6 @@
 
   void RemovePrimaryPastePrefObserver(PrimaryPastePrefObserver* observer);
 
-  // Registers a callback to be invoked when the LinuxUi instance's animation
-  // setting changes or when the active LinuxUi instance changes.
-  static base::CallbackListSubscription RegisterAnimationsEnabledCallback(
-      base::RepeatingClosure callback);
-
-  // Notifies registered callbacks that the animation setting has changed.
-  void NotifyAnimationsEnabledChanged();
-
   // Returns details about the default UI font.
   FontSettings GetDefaultFontDescription();
 
diff --git a/ui/qt/qt_ui.cc b/ui/qt/qt_ui.cc
index 3a037164..73b971d 100644
--- a/ui/qt/qt_ui.cc
+++ b/ui/qt/qt_ui.cc
@@ -604,8 +604,6 @@
   // Unlike GTK (which has settings signals), the Qt provider must be told to
   // re-derive the toolkit color scheme when the Qt theme changes.
   os_settings_provider_->OnThemeChanged();
-  // Animations enabled depends on the theme animation duration.
-  NotifyAnimationsEnabledChanged();
 }
 
 void QtUi::ScaleFactorMaybeChanged() {
diff --git a/ui/views/controls/scroll_view.cc b/ui/views/controls/scroll_view.cc
index 77a5759..1eef430 100644
--- a/ui/views/controls/scroll_view.cc
+++ b/ui/views/controls/scroll_view.cc
@@ -1623,6 +1623,18 @@
   return contents_viewport_;
 }
 
+bool ScrollView::GetUseContentsPreferredSize() const {
+  return use_contents_preferred_size_;
+}
+
+void ScrollView::SetUseContentsPreferredSize(bool use_contents_preferred_size) {
+  if (use_contents_preferred_size == use_contents_preferred_size_) {
+    return;
+  }
+  use_contents_preferred_size_ = use_contents_preferred_size;
+  OnPropertyChanged(&use_contents_preferred_size_, PropertyEffects::kLayout);
+}
+
 BEGIN_METADATA(ScrollView)
 ADD_READONLY_PROPERTY_METADATA(int, MinHeight)
 ADD_READONLY_PROPERTY_METADATA(int, MaxHeight)
@@ -1633,6 +1645,7 @@
 ADD_PROPERTY_METADATA(ScrollView::ScrollBarMode, HorizontalScrollBarMode)
 ADD_PROPERTY_METADATA(ScrollView::ScrollBarMode, VerticalScrollBarMode)
 ADD_PROPERTY_METADATA(bool, TreatAllScrollEventsAsHorizontal)
+ADD_PROPERTY_METADATA(bool, UseContentsPreferredSize)
 END_METADATA
 
 // VariableRowHeightScrollHelper ----------------------------------------------
diff --git a/ui/views/controls/scroll_view.h b/ui/views/controls/scroll_view.h
index 1e93efb..80796457 100644
--- a/ui/views/controls/scroll_view.h
+++ b/ui/views/controls/scroll_view.h
@@ -312,9 +312,8 @@
     return horiz_sb_->is_scrolling() || vert_sb_->is_scrolling();
   }
 
-  void SetUseContentsPreferredSize(bool use_contents_preferred_size) {
-    use_contents_preferred_size_ = use_contents_preferred_size;
-  }
+  bool GetUseContentsPreferredSize() const;
+  void SetUseContentsPreferredSize(bool use_contents_preferred_size);
 
  private:
   friend class test::ScrollViewTestApi;
diff --git a/ui/views/examples/json_view_builder_schema.md b/ui/views/examples/json_view_builder_schema.md
index b0998b1..d4a5e171 100644
--- a/ui/views/examples/json_view_builder_schema.md
+++ b/ui/views/examples/json_view_builder_schema.md
@@ -226,7 +226,7 @@
 | **`Slider`** | • `Value` (`number`): Position from `0.0` to `1.0`.<br>• `style` / `renderingstyle` (`string`): `kDefaultStyle`, `kMinimalStyle`. |
 | **`Throbber`** | • `Checked` (`boolean`): Show checkmark state.<br>• `running` / `isrunning` (`boolean`): Starts or stops animation. |
 | **`SmoothedThrobber`** | • `StartDelayMs` (`integer`): Delay before animating.<br>• `StopDelayMs` (`integer`): Delay before stopping.<br>• `running` (`boolean`): Start/stop animation. |
-| **`ScrollView`** | • **Children rule**: Must have exactly one child in `children` (installed as the scroll contents). |
+| **`ScrollView`** | • `UseContentsPreferredSize` (`boolean`): Uses the contents' preferred size when laying out if one exists.<br>• `HorizontalScrollBarMode` (`string`): `kDisabled`, `kHiddenButEnabled`, `kEnabled`.<br>• `VerticalScrollBarMode` (`string`): `kDisabled`, `kHiddenButEnabled`, `kEnabled`.<br>• `AllowKeyboardScrolling` (`boolean`): Enable arrow key scrolling.<br>• `DrawOverflowIndicator` (`boolean`): Draw overflow edge indicators.<br>• `HasFocusIndicator` (`boolean`): Show focus indicator.<br>• `TreatAllScrollEventsAsHorizontal` (`boolean`): Treat all scroll events as horizontal.<br>• **Children rule**: Must have exactly one child in `children` (installed as the scroll contents). |
 | **`TabbedPane`** | • `SelectedTabIndex` (`integer`): 0-based selected tab index.<br>• `DrawTabDivider` (`boolean`): Draw line divider below tab strip.<br>• **Children rule**: Each child represents a tab page and can specify `"title"` or `"TabTitle"`. |
 | **`TableView`** | • `TableType` (`string`): `TEXT_ONLY` or `ICON_AND_TEXT`.<br>• `SingleSelection` (`boolean`): Single row selection mode.<br>• `columns` (`array`): Column definitions (`id`, `title`, `alignment`, `width`, `percent`, `min_width`, `sortable`).<br>• `rows` / `data` (`array`): 2D array of row cell values `[["Cell 0,0", "Cell 0,1"], ...]`. |
 | **`BoxLayoutView`** / **`FlexLayoutView`** / **`TableLayoutView`** | • Host views that incorporate layout managers and expose layout properties directly on the view. |
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox_input.ts b/ui/webui/resources/cr_components/searchbox/searchbox_input.ts
index 61ef9326..75513b0 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox_input.ts
+++ b/ui/webui/resources/cr_components/searchbox/searchbox_input.ts
@@ -281,7 +281,14 @@
           metricsReporter.mark('CharTyped');
         }
       }
+      // The above code already updated the text and selection. Prevent default
+      // keydown handling muddling the updated state.
       e.preventDefault();
+      // The above code already identified this is a text-changing keydown event
+      // and fired 'searchbox-input-text-updated' to update state accordingly
+      // text update. Prevent event bubbling from triggering other custom
+      // keydown handlers treating this as a generic keydown event.
+      e.stopPropagation();
     }
   }
 
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox_match.ts b/ui/webui/resources/cr_components/searchbox/searchbox_match.ts
index 4a02961..3a48b2d8 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox_match.ts
+++ b/ui/webui/resources/cr_components/searchbox/searchbox_match.ts
@@ -365,6 +365,7 @@
     e.preventDefault();   // Prevents default browser action (navigation).
     e.stopPropagation();  // Prevents <iron-selector> from selecting the match.
 
+    this.fire('match-remove');
     this.pageHandler_.deleteAutocompleteMatch(
         this.matchIndex, this.match.destinationUrl);
   }
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox_mixin.ts b/ui/webui/resources/cr_components/searchbox/searchbox_mixin.ts
index 5c1b9ce..b739e9f 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox_mixin.ts
+++ b/ui/webui/resources/cr_components/searchbox/searchbox_mixin.ts
@@ -114,6 +114,16 @@
     override connectedCallback() {
       super.connectedCallback();
 
+      // On user interaction, freeze the current results to avoid result updates
+      // potentially erasing user changes like cursor position.
+      this.searchboxEventTracker_.add(this, 'input-mousedown', () => {
+        this.activeQueryId = -1;
+      });
+      // When deleting a match, unfreeze `activeQueryId` so post-deletion
+      // results are accepted.
+      this.searchboxEventTracker_.add(this, 'match-remove', () => {
+        this.activeQueryId = this.nextQueryId_ - 1;
+      });
       // Listen for 'keyup' on window to reliably catch Control key releases
       // even if the user clicks outside the searchbox while holding Control.
       this.searchboxEventTracker_.add(window, 'keyup', (e: Event) => {
@@ -390,13 +400,26 @@
         // listening for key presses. These stale results should never be shown.
         // They correspond to the potentially stale suggestion left in the
         // searchbox when blurred. That stale result may be navigated to by
-        // focusing and pressing 'Enter'.
+        // focusing and pressing 'Enter'. Reset `activeQueryId` to prevent an
+        // in-flight result from re-opening the popup.
+        this.activeQueryId = -1;
         this.pageHandler().stopAutocomplete(/*clearResult=*/ false);
       }
       this.pageHandler().onFocusChanged(false);
     }
 
     async onInputWrapperKeydown(e: KeyboardEvent) {
+      // On user interaction, freeze the current results to avoid result updates
+      // potentially erasing user changes like cursor position. Freezing on
+      // 'enter' would break the fast-enter navigations via
+      // `lastIgnoredEnterEvent_`. It'd cause the searchbox to discard the
+      // pending results the navigation is waiting for, causing the navigation
+      // to never occur. But this is a hack; comparing `e.key !=== 'Enter'` is
+      // only a semi-accurate heuristic for whether a navigation is about to
+      // occur.
+      if (e.key !== 'Enter') {
+        this.activeQueryId = -1;
+      }
       const modifier =
           isMac ? e.metaKey && !e.ctrlKey : e.ctrlKey && !e.metaKey;
       if (modifier && e.key === 'z') {
@@ -478,6 +501,8 @@
       if (e.key === 'Delete') {
         if (e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey) {
           if (this.selectedMatch && this.selectedMatch.supportsDeletion) {
+            // Unfreeze `activeQueryId` so post-deletion results are accepted.
+            this.activeQueryId = this.nextQueryId_ - 1;
             this.pageHandler().deleteAutocompleteMatch(
                 this.selectedMatchIndex, this.selectedMatch.destinationUrl);
             e.preventDefault();
@@ -525,6 +550,8 @@
           // because the matches are stale. Navigate to the default match (if
           // one exists) once the up-to-date matches arrive.
           this.lastIgnoredEnterEvent_ = e;
+          // Unfreeze `activeQueryId` so pending query results are accepted.
+          this.activeQueryId = this.nextQueryId_ - 1;
         }
         return;
       }
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html.ts b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html.ts
index 9afab34..67580a9f 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html.ts
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html.ts
@@ -42,7 +42,8 @@
   ${this.hasSearchText ? html`
     <cr-icon-button id="clearSearch" iron-icon="cr:cancel-filled"
         title="${this.clearLabel}" @click="${this.onClearSearchClick_}"
-        ?disabled="${this.disabled}"></cr-icon-button>` : ''}
+        ?disabled="${this.disabled}"></cr-icon-button>` :
+    html`<slot name="suffixElement"></slot>`}
 </div>`;
   // clang-format on
 }