diff --git a/DEPS b/DEPS
index 452cfd8..16a4acf9 100644
--- a/DEPS
+++ b/DEPS
@@ -248,7 +248,7 @@
   # luci-go CIPD package version.
   # Make sure the revision is uploaded by infra-packagers builder.
   # https://ci.chromium.org/p/infra-internal/g/infra-packagers/console
-  'luci_go': 'git_revision:74734688ffc9d2807cda0d1dc7752197480de29e',
+  'luci_go': 'git_revision:a9a10995e2889126ace91faf4052949e38c69d33',
 
   # This can be overridden, e.g. with custom_vars, to build clang from HEAD
   # instead of downloading the prebuilt pinned revision.
@@ -299,7 +299,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': '4c4da7a117aa9100f7a27e9f37a7e92b4bfcde60',
+  'src_internal_revision': 'f968a67f8ed26827ecdbf84d5cb239dc600692f8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
@@ -311,7 +311,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '432d1d1efdbd0e8d5788529fe193e0eb1e8456bf',
+  'angle_revision': '3b77a177ba09c418046d232e7f16dd402f10ef1c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -335,7 +335,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling googletest
   # and whatever else without interference from each other.
-  'googletest_revision': '59c924bc471cefea25a7cf7eb40b6c101b170b12',
+  'googletest_revision': '04ee1b4f2aefdffb0135d7cf2a2c519fe50dabe4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lighttpd
   # and whatever else without interference from each other.
@@ -375,7 +375,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '0f4bac59a38c6aa5ad9874778f822d76ea737d35',
+  'catapult_revision': '1591e813b66e04a5b9d9e8fc5d94e74ef4effaa1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
@@ -395,7 +395,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '6c841fc0da4e1eeec9f198fd14167572e1f6656a',
+  'devtools_frontend_revision': '503afe09a5c33d317399ceb4278bcd59b2f3de0b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -527,7 +527,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling llvm-libc
   # and whatever else without interference from each other.
-  'compiler_rt_revision': 'cf8cac9f64ff4ac6fd2041a699dd86f717c63586',
+  'compiler_rt_revision': '6558e6a3eacafd269527cf2595e0e722f117c1e6',
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
@@ -1152,7 +1152,7 @@
   },
 
   'src/chrome/release_scripts': {
-      'url': Var('chrome_git') + '/chrome/tools/release/scripts' + '@' + '6ad235a921b16b76e60316629f799e4fef593769',
+      'url': Var('chrome_git') + '/chrome/tools/release/scripts' + '@' + '38a00a22c91fb29e2b8f6e88fae3d151a52e8f21',
       'condition': 'checkout_chrome_release_scripts',
   },
 
@@ -1495,7 +1495,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'cfb3b184379b5491aafc36c95a2d8effd010eea2',
+    'b8e94f74cdf17fd4bad068bed25f841a4b566dce',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1987,7 +1987,7 @@
 
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e6596746dc95fe658b3a5f0924c10e322c7d8e22',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '354f6026f14f87466040e3e1878c599138a23bce',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -2949,7 +2949,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/linux-amd64',
-          'version': 'NtaId8DHFYM0HEPBE-dRD6zfLa9-6IjHXfsuAdzJzLEC',
+          'version': 'srGbc-O6bVQmBoT6VLvP66h6Be4A-nffx4UhixUJ9w0C',
         },
       ],
       'dep_type': 'cipd',
@@ -2959,7 +2959,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/windows-amd64',
-          'version': 'tRO9OhKyiYkium4C_kayNwnY6PIgcWtDksS3QxJwOXMC',
+          'version': 'zasiUhQIHGZ2e60_M6g41oz8mYgPta5JmOWV5LwFjGMC',
         },
       ],
       'dep_type': 'cipd',
@@ -2970,7 +2970,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-amd64',
-          'version': 'EpMC1Y5vZrdaOqYJF8868kIiqwwGIGE6ysHYLu7_6CkC',
+          'version': 'tZvHxy8AcDJhO9A-5UuZKL_-5vfE_wC-RBkElqWZpM4C',
         },
       ],
       'dep_type': 'cipd',
@@ -2981,7 +2981,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-arm64',
-          'version': 'iFehV3QPYIc0N4dnQVxK8HAHV_drYY-V7czzW0ipQdsC',
+          'version': '2br8hIWKHDAaOt77siYyRUclHBYUZX6dj8jtogL90nwC',
         },
       ],
       'dep_type': 'cipd',
@@ -4623,7 +4623,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        '28300a0a975163d53866dc59929e600f3a9917bc',
+        '976b8f41d35f356dd01e2dc99e317dbafb032477',
       'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/nonembedded/component_updater/aw_component_update_service_test.cc b/android_webview/nonembedded/component_updater/aw_component_update_service_test.cc
index e9fe3ef3..8626a167 100644
--- a/android_webview/nonembedded/component_updater/aw_component_update_service_test.cc
+++ b/android_webview/nonembedded/component_updater/aw_component_update_service_test.cc
@@ -103,6 +103,7 @@
              /* network_error= */ -2,
              /* header_etag= */ "",
              /* header_x_cup_server_proof= */ "",
+             /* header_cookie= */ "",
              /* x_header_retry_after_sec= */ 0ll);
   }
 
@@ -144,6 +145,7 @@
              /* network_error= */ -2,
              /* header_etag= */ "",
              /* header_x_cup_server_proof= */ "",
+             /* header_cookie= */ "",
              /* x_header_retry_after_sec= */ 0ll);
   }
 
@@ -200,6 +202,7 @@
              /* network_error= */ network_error,
              /* header_etag= */ "",
              /* header_x_cup_server_proof= */ "",
+             /* header_cookie= */ "",
              /* x_header_retry_after_sec= */ 0ll);
   }
 
diff --git a/android_webview/nonembedded/net/network_fetcher_task.cc b/android_webview/nonembedded/net/network_fetcher_task.cc
index 9e48d47..5d1f991 100644
--- a/android_webview/nonembedded/net/network_fetcher_task.cc
+++ b/android_webview/nonembedded/net/network_fetcher_task.cc
@@ -245,7 +245,8 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::move(post_request_complete_callback_)
       .Run(std::move(response_body), network_error, header_etag,
-           header_x_cup_server_proof, x_header_retry_after_sec);
+           header_x_cup_server_proof, /*header_cookie=*/"",
+           x_header_retry_after_sec);
 }
 
 }  // namespace android_webview
diff --git a/base/features.cc b/base/features.cc
index 79791ac..72db06b1 100644
--- a/base/features.cc
+++ b/base/features.cc
@@ -50,6 +50,19 @@
              "FastFilePathIsParent",
              FEATURE_ENABLED_BY_DEFAULT);
 
+// Use the Rust JSON parser. Enabled everywhere.
+BASE_FEATURE(kUseRustJsonParser,
+             "UseRustJsonParser",
+             FEATURE_ENABLED_BY_DEFAULT);
+
+// If true, use the Rust JSON parser in-thread; otherwise, it runs in a thread
+// pool.
+BASE_FEATURE_PARAM(bool,
+                   kUseRustJsonParserInCurrentSequence,
+                   &kUseRustJsonParser,
+                   "UseRustJsonParserInCurrentSequence",
+                   true);
+
 // Use non default low memory device threshold.
 // Value should be given via |LowMemoryDeviceThresholdMB|.
 #if BUILDFLAG(IS_ANDROID)
diff --git a/base/features.h b/base/features.h
index 0e4c48d..8b0dfea4 100644
--- a/base/features.h
+++ b/base/features.h
@@ -19,6 +19,8 @@
 
 BASE_EXPORT BASE_DECLARE_FEATURE(kFastFilePathIsParent);
 
+BASE_EXPORT BASE_DECLARE_FEATURE(kUseRustJsonParser);
+
 BASE_EXPORT BASE_DECLARE_FEATURE_PARAM(bool,
                                        kUseRustJsonParserInCurrentSequence);
 
diff --git a/base/json/json_reader.cc b/base/json/json_reader.cc
index 3fcccc9..f32622d 100644
--- a/base/json/json_reader.cc
+++ b/base/json/json_reader.cc
@@ -7,13 +7,13 @@
 #include <string_view>
 #include <utility>
 
+#include "base/features.h"
+#include "base/json/json_parser.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
 
-#if BUILDFLAG(IS_NACL)
-#include "base/json/json_parser.h"
-#else
+#if !BUILDFLAG(IS_NACL)
 #include "base/strings/string_view_rust.h"
 #include "third_party/rust/serde_json_lenient/v0_2/wrapper/functions.h"
 #include "third_party/rust/serde_json_lenient/v0_2/wrapper/lib.rs.h"
@@ -142,13 +142,17 @@
   return parser.Parse(json);
 #else   // BUILDFLAG(IS_NACL)
   SCOPED_UMA_HISTOGRAM_TIMER_MICROS(kSecurityJsonParsingTime);
-
-  JSONReader::Result result =
-      serde_json_lenient::DecodeJSONInRust(json, options, max_depth);
-  if (!result.has_value()) {
-    return std::nullopt;
+  if (UsingRust()) {
+    JSONReader::Result result =
+        serde_json_lenient::DecodeJSONInRust(json, options, max_depth);
+    if (!result.has_value()) {
+      return std::nullopt;
+    }
+    return std::move(*result);
+  } else {
+    internal::JSONParser parser(options, max_depth);
+    return parser.Parse(json);
   }
-  return std::move(*result);
 #endif  // BUILDFLAG(IS_NACL)
 }
 
@@ -192,9 +196,38 @@
   return std::move(*value);
 #else   // BUILDFLAG(IS_NACL)
   SCOPED_UMA_HISTOGRAM_TIMER_MICROS(kSecurityJsonParsingTime);
-  return serde_json_lenient::DecodeJSONInRust(json, options,
-                                              internal::kAbsoluteMaxDepth);
+  if (UsingRust()) {
+    return serde_json_lenient::DecodeJSONInRust(json, options,
+                                                internal::kAbsoluteMaxDepth);
+  } else {
+    internal::JSONParser parser(options);
+    auto value = parser.Parse(json);
+    if (!value) {
+      Error error;
+      error.message = parser.GetErrorMessage();
+      error.line = parser.error_line();
+      error.column = parser.error_column();
+      return base::unexpected(std::move(error));
+    }
+
+    return std::move(*value);
+  }
 #endif  // BUILDFLAG(IS_NACL)
 }
 
+// static
+bool JSONReader::UsingRust() {
+  // If features have not yet been enabled, we cannot check the feature, so fall
+  // back to the C++ parser. In practice, this seems to apply to
+  // `ReadPrefsFromDisk()`, which is parsing trusted JSON.
+  if (!base::FeatureList::GetInstance()) {
+    return false;
+  }
+#if BUILDFLAG(IS_NACL)
+  return false;
+#else
+  return base::FeatureList::IsEnabled(base::features::kUseRustJsonParser);
+#endif
+}
+
 }  // namespace base
diff --git a/base/json/json_reader.h b/base/json/json_reader.h
index 1b1c536..5349d3c 100644
--- a/base/json/json_reader.h
+++ b/base/json/json_reader.h
@@ -133,6 +133,9 @@
   static Result ReadAndReturnValueWithError(
       std::string_view json,
       int options = JSON_PARSE_CHROMIUM_EXTENSIONS);
+
+  // Determine whether the Rust parser is in use.
+  static bool UsingRust();
 };
 
 }  // namespace base
diff --git a/base/json/json_reader_unittest.cc b/base/json/json_reader_unittest.cc
index 79de5e0..ae2dca9 100644
--- a/base/json/json_reader_unittest.cc
+++ b/base/json/json_reader_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/base_paths.h"
 #include "base/containers/heap_array.h"
 #include "base/containers/span.h"
+#include "base/features.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/path_service.h"
@@ -26,6 +27,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/gmock_expected_support.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -51,20 +53,32 @@
 
 namespace base {
 
-TEST(JSONReaderTest, Whitespace) {
+class JSONReaderTest : public testing::TestWithParam<bool> {
+ public:
+  void SetUp() override {
+    feature_list_.InitWithFeatureState(base::features::kUseRustJsonParser,
+                                       using_rust_);
+  }
+
+ protected:
+  bool using_rust_ = GetParam();
+  base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_P(JSONReaderTest, Whitespace) {
   std::optional<Value> root = JSONReader::Read("   null   ");
   ASSERT_TRUE(root);
   EXPECT_TRUE(root->is_none());
 }
 
-TEST(JSONReaderTest, InvalidString) {
+TEST_P(JSONReaderTest, InvalidString) {
   // These are invalid because they do not represent a JSON value,
   // see https://tools.ietf.org/rfc/rfc8259.txt
   EXPECT_FALSE(JSONReader::Read(""));
   EXPECT_FALSE(JSONReader::Read("nu"));
 }
 
-TEST(JSONReaderTest, SimpleBool) {
+TEST_P(JSONReaderTest, SimpleBool) {
   base::HistogramTester histograms;
   std::optional<Value> root = JSONReader::Read("true  ");
   ASSERT_TRUE(root);
@@ -72,7 +86,7 @@
   histograms.ExpectTotalCount("Security.JSONParser.ParsingTime", 1);
 }
 
-TEST(JSONReaderTest, EmbeddedComments) {
+TEST_P(JSONReaderTest, EmbeddedComments) {
   std::optional<Value> root = JSONReader::Read("/* comment */null");
   ASSERT_TRUE(root);
   EXPECT_TRUE(root->is_none());
@@ -128,21 +142,21 @@
   EXPECT_FALSE(JSONReader::Read("/33"));
 }
 
-TEST(JSONReaderTest, Ints) {
+TEST_P(JSONReaderTest, Ints) {
   std::optional<Value> root = JSONReader::Read("43");
   ASSERT_TRUE(root);
   ASSERT_TRUE(root->is_int());
   EXPECT_EQ(43, root->GetInt());
 }
 
-TEST(JSONReaderTest, NonDecimalNumbers) {
+TEST_P(JSONReaderTest, NonDecimalNumbers) {
   // According to RFC 8259, oct, hex, and leading zeros are invalid JSON.
   EXPECT_FALSE(JSONReader::Read("043"));
   EXPECT_FALSE(JSONReader::Read("0x43"));
   EXPECT_FALSE(JSONReader::Read("00"));
 }
 
-TEST(JSONReaderTest, NumberZero) {
+TEST_P(JSONReaderTest, NumberZero) {
   // Test 0 (which needs to be special cased because of the leading zero
   // clause).
   std::optional<Value> root = JSONReader::Read("0");
@@ -151,7 +165,7 @@
   EXPECT_EQ(0, root->GetInt());
 }
 
-TEST(JSONReaderTest, LargeIntPromotion) {
+TEST_P(JSONReaderTest, LargeIntPromotion) {
   // Numbers that overflow ints should succeed, being internally promoted to
   // storage as doubles
   std::optional<Value> root = JSONReader::Read("2147483648");
@@ -164,7 +178,7 @@
   EXPECT_DOUBLE_EQ(-2147483649.0, root->GetDouble());
 }
 
-TEST(JSONReaderTest, LargerIntIsLossy) {
+TEST_P(JSONReaderTest, LargerIntIsLossy) {
   // Parse LONG_MAX as a JSON number (not a JSON string). The result of the
   // parse is a base::Value, either a (32-bit) int or a (64-bit) double.
   // LONG_MAX would overflow an int and can only be approximated by a double.
@@ -181,7 +195,7 @@
   EXPECT_EQ(std::string(etc808), StringPrintf("%f", root->GetDouble()));
 }
 
-TEST(JSONReaderTest, Doubles) {
+TEST_P(JSONReaderTest, Doubles) {
   std::optional<Value> root = JSONReader::Read("43.1");
   ASSERT_TRUE(root);
   EXPECT_TRUE(root->is_double());
@@ -225,14 +239,14 @@
   ASSERT_FALSE(value.has_value());
 }
 
-TEST(JSONReaderTest, FractionalNumbers) {
+TEST_P(JSONReaderTest, FractionalNumbers) {
   // Fractional parts must have a digit before and after the decimal point.
   EXPECT_FALSE(JSONReader::Read("1."));
   EXPECT_FALSE(JSONReader::Read(".1"));
   EXPECT_FALSE(JSONReader::Read("1.e10"));
 }
 
-TEST(JSONReaderTest, ExponentialNumbers) {
+TEST_P(JSONReaderTest, ExponentialNumbers) {
   // Exponent must have a digit following the 'e'.
   EXPECT_FALSE(JSONReader::Read("1e"));
   EXPECT_FALSE(JSONReader::Read("1E"));
@@ -240,7 +254,7 @@
   EXPECT_FALSE(JSONReader::Read("1e1.0"));
 }
 
-TEST(JSONReaderTest, InvalidInfNAN) {
+TEST_P(JSONReaderTest, InvalidInfNAN) {
   // The largest finite double is roughly 1.8e308.
   EXPECT_FALSE(JSONReader::Read("1e1000"));
   EXPECT_FALSE(JSONReader::Read("-1e1000"));
@@ -249,7 +263,7 @@
   EXPECT_FALSE(JSONReader::Read("inf"));
 }
 
-TEST(JSONReaderTest, InvalidNumbers) {
+TEST_P(JSONReaderTest, InvalidNumbers) {
   EXPECT_TRUE(JSONReader::Read("4.3"));
   EXPECT_FALSE(JSONReader::Read("4."));
   EXPECT_FALSE(JSONReader::Read("4.3.1"));
@@ -258,7 +272,7 @@
   EXPECT_FALSE(JSONReader::Read("42a"));
 }
 
-TEST(JSONReaderTest, Zeroes) {
+TEST_P(JSONReaderTest, Zeroes) {
   std::optional<Value> root = JSONReader::Read("0");
   ASSERT_TRUE(root);
   EXPECT_TRUE(root->is_int());
@@ -283,21 +297,21 @@
   EXPECT_TRUE(std::signbit(root->GetDouble()));
 }
 
-TEST(JSONReaderTest, SimpleString) {
+TEST_P(JSONReaderTest, SimpleString) {
   std::optional<Value> root = JSONReader::Read("\"hello world\"");
   ASSERT_TRUE(root);
   ASSERT_TRUE(root->is_string());
   EXPECT_EQ("hello world", root->GetString());
 }
 
-TEST(JSONReaderTest, EmptyString) {
+TEST_P(JSONReaderTest, EmptyString) {
   std::optional<Value> root = JSONReader::Read("\"\"");
   ASSERT_TRUE(root);
   ASSERT_TRUE(root->is_string());
   EXPECT_EQ("", root->GetString());
 }
 
-TEST(JSONReaderTest, BasicStringEscapes) {
+TEST_P(JSONReaderTest, BasicStringEscapes) {
   std::optional<Value> root =
       JSONReader::Read("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\"");
   ASSERT_TRUE(root);
@@ -305,7 +319,7 @@
   EXPECT_EQ(" \"\\/\b\f\n\r\t", root->GetString());
 }
 
-TEST(JSONReaderTest, UnicodeEscapes) {
+TEST_P(JSONReaderTest, UnicodeEscapes) {
   // Test hex and unicode escapes including the null character.
   std::optional<Value> root =
       JSONReader::Read("\"\\x41\\xFF\\x00\\u1234\\u0000\"");
@@ -322,7 +336,7 @@
   EXPECT_TRUE(JSONReader::Read("\"\\uD834\\uDD1E\""));  // U+1D11E
 }
 
-TEST(JSONReaderTest, InvalidStrings) {
+TEST_P(JSONReaderTest, InvalidStrings) {
   EXPECT_FALSE(JSONReader::Read("\"no closing quote"));
   EXPECT_FALSE(JSONReader::Read("\"\\z invalid escape char\""));
   EXPECT_FALSE(JSONReader::Read("\"\\xAQ invalid hex code\""));
@@ -331,7 +345,7 @@
   EXPECT_FALSE(JSONReader::Read("\"extra backslash at end of input\\\""));
 }
 
-TEST(JSONReaderTest, BasicArray) {
+TEST_P(JSONReaderTest, BasicArray) {
   std::optional<Value> root = JSONReader::Read("[true, false, null]");
   ASSERT_TRUE(root);
   Value::List* list = root->GetIfList();
@@ -345,7 +359,7 @@
   EXPECT_EQ(*list, *root2);
 }
 
-TEST(JSONReaderTest, EmptyArray) {
+TEST_P(JSONReaderTest, EmptyArray) {
   std::optional<Value> value = JSONReader::Read("[]");
   ASSERT_TRUE(value);
   Value::List* list = value->GetIfList();
@@ -353,7 +367,7 @@
   EXPECT_TRUE(list->empty());
 }
 
-TEST(JSONReaderTest, CompleteArray) {
+TEST_P(JSONReaderTest, CompleteArray) {
   std::optional<Value> value = JSONReader::Read("[\"a\", 3, 4.56, null]");
   ASSERT_TRUE(value);
   Value::List* list = value->GetIfList();
@@ -361,7 +375,7 @@
   EXPECT_EQ(4U, list->size());
 }
 
-TEST(JSONReaderTest, NestedArrays) {
+TEST_P(JSONReaderTest, NestedArrays) {
   std::optional<Value> value = JSONReader::Read(
       "[[true], [], {\"smell\": \"nice\",\"taste\": \"yummy\" }, [false, [], "
       "[null]], null]");
@@ -379,7 +393,7 @@
   EXPECT_EQ(*list, *root2);
 }
 
-TEST(JSONReaderTest, InvalidArrays) {
+TEST_P(JSONReaderTest, InvalidArrays) {
   // Missing close brace.
   EXPECT_FALSE(JSONReader::Read("[[true], [], [false, [], [null]], null"));
 
@@ -394,7 +408,7 @@
   EXPECT_FALSE(JSONReader::Read("[true,]"));
 }
 
-TEST(JSONReaderTest, ArrayTrailingComma) {
+TEST_P(JSONReaderTest, ArrayTrailingComma) {
   // Valid if we set |allow_trailing_comma| to true.
   std::optional<Value> value =
       JSONReader::Read("[true,]", JSON_ALLOW_TRAILING_COMMAS);
@@ -407,7 +421,7 @@
   EXPECT_TRUE(value1.GetBool());
 }
 
-TEST(JSONReaderTest, ArrayTrailingCommaNoEmptyElements) {
+TEST_P(JSONReaderTest, ArrayTrailingCommaNoEmptyElements) {
   // Don't allow empty elements, even if |allow_trailing_comma| is
   // true.
   EXPECT_FALSE(JSONReader::Read("[,]", JSON_ALLOW_TRAILING_COMMAS));
@@ -416,13 +430,13 @@
   EXPECT_FALSE(JSONReader::Read("[true,,false]", JSON_ALLOW_TRAILING_COMMAS));
 }
 
-TEST(JSONReaderTest, EmptyDictionary) {
+TEST_P(JSONReaderTest, EmptyDictionary) {
   std::optional<Value> dict_val = JSONReader::Read("{}");
   ASSERT_TRUE(dict_val);
   ASSERT_TRUE(dict_val->is_dict());
 }
 
-TEST(JSONReaderTest, CompleteDictionary) {
+TEST_P(JSONReaderTest, CompleteDictionary) {
   std::optional<Value> root1 = JSONReader::Read(
       "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\", \"bool\": "
       "false, \"more\": {} }");
@@ -481,7 +495,7 @@
   EXPECT_EQ(*root1_dict, *root2_dict);
 }
 
-TEST(JSONReaderTest, NestedDictionaries) {
+TEST_P(JSONReaderTest, NestedDictionaries) {
   std::optional<Value> root1 = JSONReader::Read(
       "{\"inner\":{\"array\":[true, 3, 4.56, null]},\"false\":false,\"d\":{}}");
   ASSERT_TRUE(root1);
@@ -506,7 +520,7 @@
   EXPECT_EQ(*root1_dict, *root2);
 }
 
-TEST(JSONReaderTest, DictionaryKeysWithPeriods) {
+TEST_P(JSONReaderTest, DictionaryKeysWithPeriods) {
   std::optional<Value> root =
       JSONReader::Read("{\"a.b\":3,\"c\":2,\"d.e.f\":{\"g.h.i.j\":1}}");
   ASSERT_TRUE(root);
@@ -538,7 +552,7 @@
   EXPECT_EQ(1, *integer_value);
 }
 
-TEST(JSONReaderTest, DuplicateKeys) {
+TEST_P(JSONReaderTest, DuplicateKeys) {
   std::optional<Value> root = JSONReader::Read("{\"x\":1,\"x\":2,\"y\":3}");
   ASSERT_TRUE(root);
   const Value::Dict* root_dict = root->GetIfDict();
@@ -549,7 +563,7 @@
   EXPECT_EQ(2, *integer_value);
 }
 
-TEST(JSONReaderTest, InvalidDictionaries) {
+TEST_P(JSONReaderTest, InvalidDictionaries) {
   // No closing brace.
   EXPECT_FALSE(JSONReader::Read("{\"a\": true"));
 
@@ -579,7 +593,7 @@
                                 JSON_ALLOW_TRAILING_COMMAS));
 }
 
-TEST(JSONReaderTest, StackOverflow) {
+TEST_P(JSONReaderTest, StackOverflow) {
   std::string evil(1000000, '[');
   evil.append(std::string(1000000, ']'));
   EXPECT_FALSE(JSONReader::Read(evil));
@@ -598,7 +612,7 @@
   EXPECT_EQ(5001U, list->size());
 }
 
-TEST(JSONReaderTest, UTF8Input) {
+TEST_P(JSONReaderTest, UTF8Input) {
   std::optional<Value> root = JSONReader::Read("\"\xe7\xbd\x91\xe9\xa1\xb5\"");
   ASSERT_TRUE(root);
   ASSERT_TRUE(root->is_string());
@@ -662,13 +676,13 @@
   }
 }
 
-TEST(JSONReaderTest, InvalidUTF8Input) {
+TEST_P(JSONReaderTest, InvalidUTF8Input) {
   EXPECT_FALSE(JSONReader::Read("\"345\xb0\xa1\xb0\xa2\""));
   EXPECT_FALSE(JSONReader::Read("\"123\xc0\x81\""));
   EXPECT_FALSE(JSONReader::Read("\"abc\xc0\xae\""));
 }
 
-TEST(JSONReaderTest, UTF16Escapes) {
+TEST_P(JSONReaderTest, UTF16Escapes) {
   std::optional<Value> root = JSONReader::Read("\"\\u20ac3,14\"");
   ASSERT_TRUE(root);
   ASSERT_TRUE(root->is_string());
@@ -683,7 +697,7 @@
   EXPECT_EQ("\xf0\x9f\x92\xa9\xf0\x9f\x91\xac", root->GetString());
 }
 
-TEST(JSONReaderTest, InvalidUTF16Escapes) {
+TEST_P(JSONReaderTest, InvalidUTF16Escapes) {
   const char* const cases[] = {
       "\"\\u123\"",          // Invalid scalar.
       "\"\\ud83d\"",         // Invalid scalar.
@@ -705,7 +719,7 @@
   }
 }
 
-TEST(JSONReaderTest, LiteralRoots) {
+TEST_P(JSONReaderTest, LiteralRoots) {
   std::optional<Value> root = JSONReader::Read("null");
   ASSERT_TRUE(root);
   EXPECT_TRUE(root->is_none());
@@ -726,7 +740,7 @@
   EXPECT_EQ("root", root->GetString());
 }
 
-TEST(JSONReaderTest, ReadFromFile) {
+TEST_P(JSONReaderTest, ReadFromFile) {
   FilePath path;
   ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &path));
   path = path.AppendASCII("json");
@@ -742,7 +756,7 @@
 
 // Tests that the root of a JSON object can be deleted safely while its
 // children outlive it.
-TEST(JSONReaderTest, StringOptimizations) {
+TEST_P(JSONReaderTest, StringOptimizations) {
   Value dict_literal_0;
   Value dict_literal_1;
   Value dict_string_0;
@@ -818,7 +832,7 @@
 // A smattering of invalid JSON designed to test specific portions of the
 // parser implementation against buffer overflow. Best run with DCHECKs so
 // that the one in NextChar fires.
-TEST(JSONReaderTest, InvalidSanity) {
+TEST_P(JSONReaderTest, InvalidSanity) {
   const auto kInvalidJson = std::to_array<const char*>({
       "/* test *",
       "{\"foo\"",
@@ -836,7 +850,7 @@
   }
 }
 
-TEST(JSONReaderTest, IllegalTrailingNull) {
+TEST_P(JSONReaderTest, IllegalTrailingNull) {
   const auto json = std::to_array<char>({'"', 'n', 'u', 'l', 'l', '"', '\0'});
   std::string json_string(json.data(), sizeof(json));
   auto root = JSONReader::ReadAndReturnValueWithError(json_string);
@@ -844,7 +858,7 @@
   EXPECT_NE("", root.error().message);
 }
 
-TEST(JSONReaderTest, ASCIIControlCodes) {
+TEST_P(JSONReaderTest, ASCIIControlCodes) {
   // A literal NUL byte or a literal new line, in a JSON string, should be
   // rejected. RFC 8259 section 7 says "the characters that MUST be escaped
   // [include]... the control characters (U+0000 through U+001F)".
@@ -869,13 +883,13 @@
   }
 }
 
-TEST(JSONReaderTest, MaxNesting) {
+TEST_P(JSONReaderTest, MaxNesting) {
   std::string json(R"({"outer": { "inner": {"foo": true}}})");
   EXPECT_FALSE(JSONReader::Read(json, JSON_PARSE_RFC, 3));
   EXPECT_TRUE(JSONReader::Read(json, JSON_PARSE_RFC, 4));
 }
 
-TEST(JSONReaderTest, Decode4ByteUtf8Char) {
+TEST_P(JSONReaderTest, Decode4ByteUtf8Char) {
   // kUtf8Data contains a 4 byte unicode character (a smiley!) that JSONReader
   // should be able to handle. The UTF-8 encoding of U+1F607 SMILING FACE WITH
   // HALO is "\xF0\x9F\x98\x87".
@@ -889,7 +903,7 @@
   EXPECT_EQ("\xF0\x9F\x98\x87", (*list)[0].GetString());
 }
 
-TEST(JSONReaderTest, DecodeUnicodeNonCharacter) {
+TEST_P(JSONReaderTest, DecodeUnicodeNonCharacter) {
   // Tests Unicode code points (encoded as escaped UTF-16) that are not valid
   // characters.
   EXPECT_TRUE(JSONReader::Read("[\"\\uFDD0\"]"));         // U+FDD0
@@ -931,13 +945,13 @@
   EXPECT_TRUE(JSONReader::Read("[\"\\uDBFF\\uDFFF\"]"));  // U+10FFFF
 }
 
-TEST(JSONReaderTest, DecodeNegativeEscapeSequence) {
+TEST_P(JSONReaderTest, DecodeNegativeEscapeSequence) {
   EXPECT_FALSE(JSONReader::Read("[\"\\x-A\"]"));
   EXPECT_FALSE(JSONReader::Read("[\"\\u-00A\"]"));
 }
 
 // Verifies invalid code points are replaced.
-TEST(JSONReaderTest, ReplaceInvalidCharacters) {
+TEST_P(JSONReaderTest, ReplaceInvalidCharacters) {
   // U+D800 is a lone high surrogate.
   const std::string invalid_high = "\"\xED\xA0\x80\"";
   std::optional<Value> value =
@@ -956,7 +970,7 @@
   EXPECT_EQ(U_FFFD U_FFFD U_FFFD, value->GetString());
 }
 
-TEST(JSONReaderTest, ReplaceInvalidUTF16EscapeSequence) {
+TEST_P(JSONReaderTest, ReplaceInvalidUTF16EscapeSequence) {
   // U+D800 is a lone high surrogate.
   const std::string invalid_high = "\"_\\uD800_\"";
   std::optional<Value> value =
@@ -973,7 +987,7 @@
   EXPECT_EQ("_" U_FFFD "_", value->GetString());
 }
 
-TEST(JSONReaderTest, InvalidUTF16HighSurrogates) {
+TEST_P(JSONReaderTest, InvalidUTF16HighSurrogates) {
   // U+dbaa is a high surrogate and expects a low surrogate, which U+001e is
   // not, so the entire surrogate pair should be replaced by
   // REPLACEMENT_CHARACTER.
@@ -990,7 +1004,7 @@
   ASSERT_FALSE(value);
 }
 
-TEST(JSONReaderTest, InvalidUTF16HighSurrogatesAndEscapes) {
+TEST_P(JSONReaderTest, InvalidUTF16HighSurrogatesAndEscapes) {
   // U+dbaa is a lone high surrogate, and should be replaced with
   // REPLACEMENT_CHARACTER and not affect interpretation of the following `\`
   // character as a part of the escape sequence `\"`.
@@ -1007,7 +1021,7 @@
   ASSERT_FALSE(value);
 }
 
-TEST(JSONReaderTest, ParseNumberErrors) {
+TEST_P(JSONReaderTest, ParseNumberErrors) {
   struct Cases {
     const char* input;
     bool parse_success;
@@ -1045,7 +1059,7 @@
   }
 }
 
-TEST(JSONReaderTest, UnterminatedInputs) {
+TEST_P(JSONReaderTest, UnterminatedInputs) {
   const auto kCases = std::to_array<const char*>({
       // clang-format off
       "/",
@@ -1077,7 +1091,7 @@
   }
 }
 
-TEST(JSONReaderTest, LineColumnCounting) {
+TEST_P(JSONReaderTest, LineColumnCounting) {
   struct Cases {
     const char* input;
     int error_line;
@@ -1151,7 +1165,7 @@
   }
 }
 
-TEST(JSONReaderTest, ChromiumExtensions) {
+TEST_P(JSONReaderTest, ChromiumExtensions) {
   // All of these cases should parse with JSON_PARSE_CHROMIUM_EXTENSIONS but
   // fail with JSON_PARSE_RFC.
   struct Cases {
@@ -1193,7 +1207,7 @@
 // For every control character, place it unescaped in a string and ensure that:
 // a) It doesn't parse with JSON_PARSE_RFC
 // b) It doesn't parse with JSON_PARSE_CHROMIUM_EXTENSIONS
-TEST(JSONReaderTest, UnescapedControls) {
+TEST_P(JSONReaderTest, UnescapedControls) {
   std::string input = "\"foo\"";
   // ECMA-404 (JSON standard) section 9: characters from 0x00 to 0x1f must be
   // escaped.
@@ -1209,7 +1223,11 @@
   }
 }
 
-TEST(JSONReaderTest, ReadingJsonIntoDictAndList) {
+TEST_P(JSONReaderTest, UsingRust) {
+  ASSERT_EQ(JSONReader::UsingRust(), using_rust_);
+}
+
+TEST_P(JSONReaderTest, ReadingJsonIntoDictAndList) {
   {
     std::optional<base::Value::List> list = JSONReader::ReadList("[1, 2, 3]");
     ASSERT_TRUE(list);
@@ -1238,4 +1256,11 @@
 
 FUZZ_TEST(JSONReaderTest, CanParseAnythingWithoutCrashing);
 
+INSTANTIATE_TEST_SUITE_P(All,
+                         JSONReaderTest,
+                         testing::Bool(),
+                         [](const testing::TestParamInfo<bool>& info) {
+                           return info.param ? "Rust" : "Cpp";
+                         });
+
 }  // namespace base
diff --git a/build/config/rust.gni b/build/config/rust.gni
index 80ff972..8c074fc 100644
--- a/build/config/rust.gni
+++ b/build/config/rust.gni
@@ -33,13 +33,6 @@
   #   (including `//base`, `//net`, etc.)
   enable_rust = build_with_chromium
 
-  # The CXX tool is in //third_party/rust which is not shared with downstream
-  # projects yet. So they need to copy the required dependencies and GN files
-  # into their project to enable CXX there.
-  #
-  # We do not support disabling this flag in Chromium code.
-  enable_rust_cxx = build_with_chromium
-
   # The chromium prelude crate provides the `chromium::import!` macro which
   # is needed to depend on first-party rust libraries. Third-party libraries
   # are specified with cargo_crate and do not get imported through this macro.
@@ -104,6 +97,16 @@
   force_rustc_color_output = false
 }
 
+declare_args() {
+  # The CXX tool is in //third_party/rust which is not shared with downstream
+  # projects yet. So they need to copy the required dependencies and GN files
+  # into their project to enable CXX there.
+  #
+  # Currently, cxx is needed to use Rust with an allocator since the Chromium
+  # allocator shim uses cxx
+  enable_rust_cxx = enable_rust
+}
+
 # Use the Rust toolchain built in-tree. When false, we use the prebuilt Rust
 # stdlibs that come with the specified custom toolchain.
 use_chromium_rust_toolchain = rust_sysroot_absolute == ""
diff --git a/chrome/VERSION b/chrome/VERSION
index 857b97cd..190627b 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=138
 MINOR=0
-BUILD=7154
+BUILD=7155
 PATCH=0
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 20e03027..a437c1e 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1192,6 +1192,7 @@
   chrome_java_sources += [
     "java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java",
     "java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialogBridge.java",
+    "java/src/org/chromium/chrome/browser/media/MediaCapturePickerHeadlessFragment.java",
     "java/src/org/chromium/chrome/browser/media/MediaCapturePickerItemProperties.java",
     "java/src/org/chromium/chrome/browser/media/MediaCapturePickerItemViewBinder.java",
   ]
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 5375fa9..362e7e9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -3471,10 +3471,16 @@
                     new MinimizeAppAndCloseTabBackPressHandler(
                             getActivityTabProvider(),
                             this::backShouldCloseTab,
+                            TabAssociatedApp::isOpenedFromExternalApp,
+                            this::closeTabUponMinimization,
                             this::sendToBackground);
             mBackPressManager.addHandler(
                     mMinimizeAppAndCloseTabBackPressHandler,
                     BackPressHandler.Type.MINIMIZE_APP_AND_CLOSE_TAB);
+            if (MinimizeAppAndCloseTabBackPressHandler.supportCloseTabUponMinimization()) {
+                mBackPressManager.addOnSystemNavigationObserver(
+                        mMinimizeAppAndCloseTabBackPressHandler);
+            }
         }
     }
 
@@ -3522,54 +3528,56 @@
     private void sendToBackground(@Nullable final Tab tabToClose) {
         Log.i(TAG, "sendToBackground(): " + tabToClose);
         moveTaskToBack(true);
-        if (tabToClose != null) {
-            // In the case of closing a tab upon minimization, don't allow the close action to
-            // happen until after our app is minimized to make sure we don't get a brief glimpse of
-            // the newly active tab before we exit Chrome.
-            //
-            // If the runnable doesn't run before the Activity dies, Chrome won't crash but the tab
-            // won't be closed (crbug.com/587565).
-            mHandler.postDelayed(
-                    () -> {
-                        if (mTabModelSelector == null
-                                || tabToClose.isClosing()
-                                || tabToClose.isDestroyed()) {
-                            return;
-                        }
+        if (tabToClose != null) closeTabUponMinimization(tabToClose);
+    }
 
-                        final TabModel currentModel = mTabModelSelector.getCurrentModel();
-                        final TabModel tabToCloseModel =
-                                mTabModelSelector.getModel(tabToClose.isIncognito());
-                        if (currentModel != tabToCloseModel) {
-                            // This seems improbable; however, crbug/1463397 suggests otherwise. If
-                            // this happens, remain on the current tab and close the tab in the
-                            // other model.
-                            tabToCloseModel
-                                    .getTabRemover()
-                                    .closeTabs(
-                                            TabClosureParams.closeTab(tabToClose)
-                                                    .uponExit(true)
-                                                    .allowUndo(false)
-                                                    .build(),
-                                            /* allowDialog= */ false);
-                            return;
-                        }
+    private void closeTabUponMinimization(@NonNull Tab tabToClose) {
+        // In the case of closing a tab upon minimization, don't allow the close action to
+        // happen until after our app is minimized to make sure we don't get a brief glimpse of
+        // the newly active tab before we exit Chrome.
+        //
+        // If the runnable doesn't run before the Activity dies, Chrome won't crash but the tab
+        // won't be closed (crbug.com/587565).
+        mHandler.postDelayed(
+                () -> {
+                    if (mTabModelSelector == null
+                            || tabToClose.isClosing()
+                            || tabToClose.isDestroyed()) {
+                        return;
+                    }
 
-                        Tab nextTab =
-                                currentModel.getNextTabIfClosed(
-                                        tabToClose.getId(), /* uponExit= */ true);
+                    final TabModel currentModel = mTabModelSelector.getCurrentModel();
+                    final TabModel tabToCloseModel =
+                            mTabModelSelector.getModel(tabToClose.isIncognito());
+                    if (currentModel != tabToCloseModel) {
+                        // This seems improbable; however, crbug/1463397 suggests otherwise. If
+                        // this happens, remain on the current tab and close the tab in the
+                        // other model.
                         tabToCloseModel
                                 .getTabRemover()
                                 .closeTabs(
                                         TabClosureParams.closeTab(tabToClose)
-                                                .recommendedNextTab(nextTab)
                                                 .uponExit(true)
                                                 .allowUndo(false)
                                                 .build(),
                                         /* allowDialog= */ false);
-                    },
-                    CLOSE_TAB_ON_MINIMIZE_DELAY_MS);
-        }
+                        return;
+                    }
+
+                    Tab nextTab =
+                            currentModel.getNextTabIfClosed(
+                                    tabToClose.getId(), /* uponExit= */ true);
+                    tabToCloseModel
+                            .getTabRemover()
+                            .closeTabs(
+                                    TabClosureParams.closeTab(tabToClose)
+                                            .recommendedNextTab(nextTab)
+                                            .uponExit(true)
+                                            .allowUndo(false)
+                                            .build(),
+                                    /* allowDialog= */ false);
+                },
+                CLOSE_TAB_ON_MINIMIZE_DELAY_MS);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java
index 5f18cda..db1a655 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java
@@ -37,6 +37,7 @@
 import org.chromium.ui.OverscrollAction;
 import org.chromium.ui.OverscrollRefreshHandler;
 import org.chromium.ui.base.BackGestureEventSwipeEdge;
+import org.chromium.ui.base.DeviceInput;
 import org.chromium.ui.base.WindowAndroid;
 
 import java.lang.annotation.Retention;
@@ -281,23 +282,26 @@
     public boolean start(
             @OverscrollAction int type, @BackGestureEventSwipeEdge int initiatingEdge) {
         mSwipeType = type;
-        if (type == OverscrollAction.PULL_TO_REFRESH) {
-            if (mSwipeRefreshLayout == null) initSwipeRefreshLayout(mTab.getContext());
-            attachSwipeRefreshLayoutIfNecessary();
-            return mSwipeRefreshLayout.start();
-        } else if (type == OverscrollAction.HISTORY_NAVIGATION) {
-            if (mNavigationCoordinator != null) {
-                mNavigationCoordinator.startGesture();
-                // Note: triggerUi returns true as long as the handler is in a valid state, i.e.
-                // even if the navigation direction doesn't have further history entries.
-                boolean navigable = mNavigationCoordinator.triggerUi(initiatingEdge);
-                return navigable;
-            }
-        } else if (type == OverscrollAction.PULL_FROM_BOTTOM_EDGE) {
-            if (mBrowserControls != null) {
-                recordEdgeToEdgeOverscrollFromBottom(mBrowserControls);
+        if (isRefreshOnOverscrollSupported()) {
+            if (type == OverscrollAction.PULL_TO_REFRESH) {
+                if (mSwipeRefreshLayout == null) initSwipeRefreshLayout(mTab.getContext());
+                attachSwipeRefreshLayoutIfNecessary();
+                return mSwipeRefreshLayout.start();
+            } else if (type == OverscrollAction.HISTORY_NAVIGATION) {
+                if (mNavigationCoordinator != null) {
+                    mNavigationCoordinator.startGesture();
+                    // Note: triggerUi returns true as long as the handler is in a valid state, i.e.
+                    // even if the navigation direction doesn't have further history entries.
+                    boolean navigable = mNavigationCoordinator.triggerUi(initiatingEdge);
+                    return navigable;
+                }
+            } else if (type == OverscrollAction.PULL_FROM_BOTTOM_EDGE) {
+                if (mBrowserControls != null) {
+                    recordEdgeToEdgeOverscrollFromBottom(mBrowserControls);
+                }
             }
         }
+
         mSwipeType = OverscrollAction.NONE;
         return false;
     }
@@ -419,4 +423,22 @@
                 sample,
                 BottomControlsStatus.NUM_TOTAL);
     }
+
+    /**
+     * Checks to see if page refresh on overscroll is supported Wrapped so we can stub behavior in
+     * tests.
+     *
+     * <p>Currently, overscroll to refresh is disabled if a precision pointer device is attached.
+     * For example, this will disable it for touch screen when a mouse is attaached. However
+     * long-term, the plan is to selectively enable for things such as touchscreen only.
+     *
+     * <p>TODO(crbug.com/412465463): enable overscroll refresh for touch even when precision pointer
+     * is attached
+     *
+     * @return true if page refresh on overscroll is supported.
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    boolean isRefreshOnOverscrollSupported() {
+        return !DeviceInput.supportsPrecisionPointer();
+    }
 }
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 304b3fa..3057cd7 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
@@ -29,6 +29,7 @@
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.FrameLayout;
+import android.window.OnBackInvokedDispatcher;
 
 import androidx.annotation.CallSuper;
 import androidx.annotation.NonNull;
@@ -1563,6 +1564,12 @@
         }
 
         if (mBackPressManager != null) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA
+                    && mBackPressManager.getOnSystemNavigationCallback() != null) {
+                getOnBackInvokedDispatcher()
+                        .unregisterOnBackInvokedCallback(
+                                mBackPressManager.getOnSystemNavigationCallback());
+            }
             mBackPressManager.destroy();
         }
 
@@ -2277,6 +2284,13 @@
     }
 
     private void initializeBackPressHandling() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA
+                && mBackPressManager.getOnSystemNavigationCallback() != null) {
+            getOnBackInvokedDispatcher()
+                    .registerOnBackInvokedCallback(
+                            OnBackInvokedDispatcher.PRIORITY_SYSTEM_NAVIGATION_OBSERVER,
+                            mBackPressManager.getOnSystemNavigationCallback());
+        }
         mBackPressManager.setIsGestureNavEnabledSupplier(
                 () -> UiUtils.isGestureNavigationMode(getWindow()));
         final Runnable callbackForLegacyTabStartupMetricsTracker =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
index 7b8c72f4..29c86a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
@@ -44,6 +44,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * Holds a hidden tab which may be used to preload pages before a CustomTabActivity is launched.
@@ -267,18 +268,11 @@
         try (TraceEvent e = TraceEvent.scoped("CustomTabsConnection.takeHiddenTab")) {
             if (intent.getBooleanExtra(IntentHandler.EXTRA_CCT_EARLY_NAV, false)) {
                 recordEarlyNavDebugMetric(session, ignoreFragments, url, intent);
-
-                // Work around Early Nav failures by always continuing with the early nav if it
-                // hasn't been cleaned up.
-                if (mSpeculation != null && mSpeculation.isEarlyNav) {
-                    HiddenTab hiddenTab = mSpeculation.hiddenTab;
-                    mSpeculation = null;
-                    return hiddenTab;
-                }
             }
 
-            if (mSpeculation == null || session == null) return null;
-            if (!session.equals(mSpeculation.session)) return null;
+            if (mSpeculation == null) return null;
+            // ~10% of CCT startups have no session, allow them to use Early Nav.
+            if (!Objects.equals(session, mSpeculation.session)) return null;
 
             HiddenTab hiddenTab = mSpeculation.hiddenTab;
             String speculatedUrl = hiddenTab.url;
@@ -313,7 +307,9 @@
         @EarlyNavFailureReason int failureReason = EarlyNavFailureReason.SUCCESS;
         if (mSpeculation == null) {
             failureReason = EarlyNavFailureReason.NO_SPECULATION;
-        } else if (session == null) {
+        } else if (!Objects.equals(session, mSpeculation.session)) {
+            // NO_SESSION redefined to be wrong session. This is just for short term debugging so
+            // it's fine.
             failureReason = EarlyNavFailureReason.NO_SESSION;
         } else {
             boolean isSessionValid = CustomTabsConnection.getInstance().isSessionValid(session);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java
index 5e0320b7..e9d5be3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java
@@ -12,12 +12,14 @@
 import android.widget.ListView;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.google.android.material.materialswitch.MaterialSwitch;
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.app.tabmodel.AllTabObserver;
+import org.chromium.chrome.browser.media.MediaCapturePickerHeadlessFragment.CaptureAction;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabLoadIfNeededCaller;
 import org.chromium.content_public.browser.WebContents;
@@ -41,21 +43,32 @@
     private final View mDialogView;
     private final LinearLayout mButtonsView;
     private final View mPositiveButton;
+    private final View mScreenButton;
     private final MaterialSwitch mAudioSwitch;
     private final ModelList mModelList = new ModelList();
     private final Map<Tab, TabItemState> mTabItemStateMap = new HashMap<>();
     @Nullable private TabItemState mLastSelectedTabItemState;
+    @Nullable private PropertyModel mPropertyModel;
     @Nullable private Delegate mDelegate;
 
     /** A delegate for handling returning the picker result. */
     interface Delegate {
         /**
-         * Called when the user has selected a tab to share, or when the dialog is cancelled.
+         * Called when the user has selected a tab to share.
          *
          * @param webContents The contents to share.
          * @param audioShare True if tab audio should be shared.
          */
-        void onFinishPicking(@Nullable WebContents webContents, boolean audioShare);
+        void onPickTab(@NonNull WebContents webContents, boolean audioShare);
+
+        /** Called when the user has selected a window to share. */
+        void onPickWindow();
+
+        /** Called when the user has selected a screen to share. */
+        void onPickScreen();
+
+        /** Called when the user has elected to not share anything. */
+        void onCancel();
     }
 
     private class TabItemState {
@@ -149,6 +162,7 @@
                                 .inflate(R.layout.media_capture_picker_button_row, null);
 
         mPositiveButton = mButtonsView.findViewById(R.id.positive_button);
+        mScreenButton = mButtonsView.findViewById(R.id.screen_button);
 
         // Share audio should be on by default.
         mAudioSwitch = mButtonsView.findViewById(R.id.media_capture_picker_audio_share_switch);
@@ -178,6 +192,30 @@
         removed.destroy();
     }
 
+    private void startAndroidCapturePrompt() {
+        var fragment = MediaCapturePickerHeadlessFragment.getInstanceForCurrentActivity();
+        fragment.startAndroidCapturePrompt(
+                result -> {
+                    switch (result) {
+                        case CaptureAction.CAPTURE_CANCELLED:
+                            mDelegate.onCancel();
+                            break;
+                        case CaptureAction.CAPTURE_WINDOW:
+                            mDelegate.onPickWindow();
+                            break;
+                        case CaptureAction.CAPTURE_SCREEN:
+                            mDelegate.onPickScreen();
+                            break;
+                        default:
+                            assert false;
+                    }
+
+                    mDelegate = null;
+                    mModalDialogManager.dismissDialog(
+                            mPropertyModel, DialogDismissalCause.ACTION_ON_DIALOG_COMPLETED);
+                });
+    }
+
     private void show() {
         var allTabObserver = new AllTabObserver(this);
 
@@ -192,10 +230,9 @@
                             var webContents = tab.getWebContents();
                             assert webContents != null;
 
-                            mDelegate.onFinishPicking(webContents, mAudioSwitch.isChecked());
+                            mDelegate.onPickTab(webContents, mAudioSwitch.isChecked());
                         } else {
-                            mDelegate.onFinishPicking(
-                                    /* webContents= */ null, /* audioShare= */ false);
+                            mDelegate.onCancel();
                         }
                         mDelegate = null;
                         mModalDialogManager.dismissDialog(
@@ -208,8 +245,7 @@
                     @Override
                     public void onDismiss(PropertyModel model, int dismissalCause) {
                         if (mDelegate != null) {
-                            mDelegate.onFinishPicking(
-                                    /* webContents= */ null, /* audioShare= */ false);
+                            mDelegate.onCancel();
                             mDelegate = null;
                         }
                         allTabObserver.destroy();
@@ -219,7 +255,8 @@
         Resources resources = mDialogView.getResources();
         var title = resources.getString(R.string.media_capture_picker_dialog_title, mAppName);
 
-        var propertyModel =
+        assert mPropertyModel == null;
+        mPropertyModel =
                 new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
                         .with(ModalDialogProperties.CONTROLLER, controller)
                         .with(ModalDialogProperties.CUSTOM_VIEW, mDialogView)
@@ -229,11 +266,14 @@
 
         mButtonsView
                 .findViewById(R.id.negative_button)
-                .setOnClickListener(view -> controller.onClick(propertyModel, ButtonType.NEGATIVE));
+                .setOnClickListener(
+                        view -> controller.onClick(mPropertyModel, ButtonType.NEGATIVE));
 
         mPositiveButton.setOnClickListener(
-                view -> controller.onClick(propertyModel, ButtonType.POSITIVE));
+                view -> controller.onClick(mPropertyModel, ButtonType.POSITIVE));
 
-        mModalDialogManager.showDialog(propertyModel, ModalDialogManager.ModalDialogType.TAB);
+        mScreenButton.setOnClickListener(view -> startAndroidCapturePrompt());
+
+        mModalDialogManager.showDialog(mPropertyModel, ModalDialogManager.ModalDialogType.TAB);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialogBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialogBridge.java
index b329d3c..a78499b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialogBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialogBridge.java
@@ -6,6 +6,8 @@
 
 import android.app.Activity;
 
+import androidx.annotation.NonNull;
+
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
@@ -15,7 +17,7 @@
 import org.chromium.ui.modaldialog.ModalDialogManagerHolder;
 
 /** Glue for the media capture picker dialog UI code and communication with the native backend. */
-public class MediaCapturePickerDialogBridge {
+public class MediaCapturePickerDialogBridge implements MediaCapturePickerDialog.Delegate {
     private long mNativeMediaCapturePickerDialogBridge;
 
     /**
@@ -55,14 +57,7 @@
                 ((ModalDialogManagerHolder) activity).getModalDialogManager(),
                 appName,
                 requestAudio,
-                (webContents, audioShare) -> {
-                    // We know `mNativeMediaCapturePickerDialogBridge` is non-zero because
-                    // `destroy` will only be called after the dialog is dismissed.
-                    assert mNativeMediaCapturePickerDialogBridge != 0;
-                    MediaCapturePickerDialogBridgeJni.get()
-                            .onResult(
-                                    mNativeMediaCapturePickerDialogBridge, webContents, audioShare);
-                });
+                this);
     }
 
     @CalledByNative
@@ -70,11 +65,50 @@
         mNativeMediaCapturePickerDialogBridge = 0;
     }
 
+    @Override
+    public void onPickTab(@NonNull WebContents webContents, boolean audioShare) {
+        // We know `mNativeMediaCapturePickerDialogBridge` is non-zero because
+        // `destroy` will only be called after the dialog is dismissed.
+        assert mNativeMediaCapturePickerDialogBridge != 0;
+        MediaCapturePickerDialogBridgeJni.get()
+                .onPickTab(mNativeMediaCapturePickerDialogBridge, webContents, audioShare);
+    }
+
+    @Override
+    public void onPickWindow() {
+        // We know `mNativeMediaCapturePickerDialogBridge` is non-zero because
+        // `destroy` will only be called after the dialog is dismissed.
+        assert mNativeMediaCapturePickerDialogBridge != 0;
+        MediaCapturePickerDialogBridgeJni.get().onPickWindow(mNativeMediaCapturePickerDialogBridge);
+    }
+
+    @Override
+    public void onPickScreen() {
+        // We know `mNativeMediaCapturePickerDialogBridge` is non-zero because
+        // `destroy` will only be called after the dialog is dismissed.
+        assert mNativeMediaCapturePickerDialogBridge != 0;
+        MediaCapturePickerDialogBridgeJni.get().onPickScreen(mNativeMediaCapturePickerDialogBridge);
+    }
+
+    @Override
+    public void onCancel() {
+        // We know `mNativeMediaCapturePickerDialogBridge` is non-zero because
+        // `destroy` will only be called after the dialog is dismissed.
+        assert mNativeMediaCapturePickerDialogBridge != 0;
+        MediaCapturePickerDialogBridgeJni.get().onCancel(mNativeMediaCapturePickerDialogBridge);
+    }
+
     @NativeMethods
     interface Natives {
-        void onResult(
+        void onPickTab(
                 long nativeMediaCapturePickerDialogBridge,
                 WebContents webContents,
                 boolean audioShare);
+
+        void onPickWindow(long nativeMediaCapturePickerDialogBridge);
+
+        void onPickScreen(long nativeMediaCapturePickerDialogBridge);
+
+        void onCancel(long nativeMediaCapturePickerDialogBridge);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerHeadlessFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerHeadlessFragment.java
new file mode 100644
index 0000000..8b72a251
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerHeadlessFragment.java
@@ -0,0 +1,107 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.media;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.media.projection.MediaProjectionManager;
+import android.os.Bundle;
+
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.Callback;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Headless fragment used to invoke the MediaProjection API.
+ *
+ * <p>This is created on a per-Activity basis.
+ */
+public class MediaCapturePickerHeadlessFragment extends Fragment {
+    private static final String TAG = "MediaCapturePickerHeadlessFragment";
+
+    @Retention(RetentionPolicy.SOURCE)
+    @Target(ElementType.TYPE_USE)
+    @IntDef({
+        CaptureAction.CAPTURE_CANCELLED,
+        CaptureAction.CAPTURE_WINDOW,
+        CaptureAction.CAPTURE_SCREEN
+    })
+    @interface CaptureAction {
+        int CAPTURE_CANCELLED = 0;
+        int CAPTURE_WINDOW = 1;
+        int CAPTURE_SCREEN = 2;
+    }
+
+    private MediaProjectionManager mMediaProjectionManager;
+    private ActivityResultLauncher<Intent> mLauncher;
+    private Callback<@CaptureAction Integer> mNextCallback;
+
+    public static MediaCapturePickerHeadlessFragment getInstanceForCurrentActivity() {
+        var activity = (FragmentActivity) ApplicationStatus.getLastTrackedFocusedActivity();
+        if (activity == null) {
+            return null;
+        }
+        FragmentManager fragmentManager = activity.getSupportFragmentManager();
+        var fragment =
+                (MediaCapturePickerHeadlessFragment)
+                        fragmentManager.findFragmentByTag(MediaCapturePickerHeadlessFragment.TAG);
+
+        if (fragment == null) {
+            fragment = new MediaCapturePickerHeadlessFragment();
+            fragmentManager
+                    .beginTransaction()
+                    .add(fragment, MediaCapturePickerHeadlessFragment.TAG)
+                    .commitNowAllowingStateLoss();
+        }
+        return fragment;
+    }
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mMediaProjectionManager =
+                (MediaProjectionManager)
+                        requireActivity().getSystemService(Context.MEDIA_PROJECTION_SERVICE);
+
+        mLauncher =
+                registerForActivityResult(
+                        new ActivityResultContracts.StartActivityForResult(),
+                        result -> {
+                            assert mNextCallback != null;
+                            if (result.getResultCode() == Activity.RESULT_OK
+                                    && result.getData() != null) {
+                                // There is currently no way to differentiate between window and
+                                // screen sharing.
+                                mNextCallback.onResult(CaptureAction.CAPTURE_WINDOW);
+                            } else {
+                                mNextCallback.onResult(CaptureAction.CAPTURE_CANCELLED);
+                            }
+                            mNextCallback = null;
+                        });
+
+        // This fragment has no UI so we can retain it.
+        setRetainInstance(true);
+    }
+
+    public void startAndroidCapturePrompt(Callback<@CaptureAction Integer> callback) {
+        assert mNextCallback == null;
+        mNextCallback = callback;
+        mLauncher.launch(mMediaProjectionManager.createScreenCaptureIntent());
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 2d3c59e..a0a3aa3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -137,6 +137,7 @@
 import org.chromium.chrome.browser.toolbar.bottom.BottomControlsContentDelegate;
 import org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator;
 import org.chromium.chrome.browser.toolbar.bottom.ScrollingBottomViewResourceFrameLayout;
+import org.chromium.chrome.browser.toolbar.extensions.ExtensionToolbarManager;
 import org.chromium.chrome.browser.toolbar.home_button.HomeButtonCoordinator;
 import org.chromium.chrome.browser.toolbar.load_progress.LoadProgressCoordinator;
 import org.chromium.chrome.browser.toolbar.menu_button.MenuButtonCoordinator;
@@ -302,6 +303,8 @@
     private HomeButtonCoordinator mHomeButtonCoordinator;
     private ToggleTabStackButtonCoordinator mTabSwitcherButtonCoordinator;
     private @Nullable BackButtonCoordinator mBackButtonCoordinator;
+    private @Nullable ExtensionToolbarManager mExtensionToolbarManager;
+
     private BrowserStateBrowserControlsVisibilityDelegate mControlsVisibilityDelegate;
     private int mFullscreenFocusToken = TokenHolder.INVALID_TOKEN;
     private int mFullscreenFindInPageToken = TokenHolder.INVALID_TOKEN;
@@ -1592,6 +1595,14 @@
         mWindowAndroid.setProgressBarConfigProvider(mProgressBarConfigProvider);
         initializeToolbarPositionController();
 
+        ViewStub extensionToolbarStub =
+                controlContainer.findViewById(R.id.extension_toolbar_container_stub);
+        if (extensionToolbarStub != null) {
+            mExtensionToolbarManager =
+                    ExtensionToolbarManager.maybeCreate(
+                            mActivity, extensionToolbarStub, profileSupplier, tabProvider);
+        }
+
         TraceEvent.end("ToolbarManager.ToolbarManager");
     }
 
@@ -2308,6 +2319,11 @@
             mTabSwitcherButtonCoordinator = null;
         }
 
+        if (mExtensionToolbarManager != null) {
+            mExtensionToolbarManager.destroy();
+            mExtensionToolbarManager = null;
+        }
+
         if (mCallbackController != null) {
             mCallbackController.destroy();
             mCallbackController = null;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/SwipeRefreshHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/SwipeRefreshHandlerTest.java
index 8076639..5bc3bfa 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/SwipeRefreshHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/SwipeRefreshHandlerTest.java
@@ -5,10 +5,13 @@
 package org.chromium.chrome.browser;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 
@@ -63,6 +66,9 @@
     private OnRefreshListener mOnRefreshListener;
     private OnResetListener mOnResetListener;
     private SwipeRefreshLayout mSwipeRefreshLayout;
+
+    private SwipeRefreshHandler mHandler;
+
     private final SwipeRefreshHandler.SwipeRefreshLayoutCreator mSwipeRefreshLayoutCreator =
             context -> {
                 mSwipeRefreshLayout = mock();
@@ -87,14 +93,18 @@
         when(mTab.getContext()).thenReturn(activityTestRule.getActivity());
         when(mTab.getUserDataHost()).thenReturn(new UserDataHost());
         when(mTab.getContentView()).thenReturn(mock());
+
+        // Limited use of spy() so we can test actual object, while changing some behaviors
+        // dynamically (such as whether mouse is attached or not)
+        mHandler = spy(SwipeRefreshHandler.from(mTab, mSwipeRefreshLayoutCreator));
+        mHandler.initWebContents(mock()); // Needed to enable the overscroll refresh handler.
+        doReturn(true).when(mHandler).isRefreshOnOverscrollSupported(); // Default no mouse/touchpad
     }
 
     @Test
     @SmallTest
     public void testAccessibilityAnnouncement() {
-        var handler = SwipeRefreshHandler.from(mTab, mSwipeRefreshLayoutCreator);
-        handler.initWebContents(mock()); // Needed to enable the overscroll refresh handler.
-        triggerRefresh(handler);
+        triggerRefresh(mHandler);
 
         InOrder orderVerifier = inOrder(mSwipeRefreshLayout);
         orderVerifier
@@ -104,17 +114,25 @@
                 .verify(mSwipeRefreshLayout, times(1))
                 .setContentDescription(sAccessibilitySwipeRefreshString);
 
-        reset(handler);
+        reset(mHandler);
 
         orderVerifier.verify(mSwipeRefreshLayout, times(1)).setContentDescription(null);
     }
 
+    /** Ensures that we do not trigger refresh if precision pointing device is attached */
+    @Test
+    @SmallTest
+    public void testOverscrollButNoRefresh() {
+        doReturn(false).when(mHandler).isRefreshOnOverscrollSupported(); // pointer device attached
+        triggerRefresh(mHandler);
+        // When refresh is NOT triggered, then refresh layout is NOT created
+        assertNull(mSwipeRefreshLayout);
+    }
+
     @Test
     @SmallTest
     public void testAccessibilityAnnouncement_swipingASecondTime() {
-        var handler = SwipeRefreshHandler.from(mTab, mSwipeRefreshLayoutCreator);
-        handler.initWebContents(mock()); // Needed to enable the overscroll refresh handler.
-        triggerRefresh(handler);
+        triggerRefresh(mHandler);
 
         var firstSwipeRefreshLayout = mSwipeRefreshLayout;
 
@@ -126,11 +144,11 @@
                 .verify(firstSwipeRefreshLayout, times(1))
                 .setContentDescription(sAccessibilitySwipeRefreshString);
 
-        reset(handler);
+        reset(mHandler);
 
         orderVerifier.verify(mSwipeRefreshLayout, times(1)).setContentDescription(null);
 
-        triggerRefresh(handler);
+        triggerRefresh(mHandler);
 
         var secondSwipeRefreshLayout = mSwipeRefreshLayout;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
index 9f4b2a2..0e52e01 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
@@ -95,6 +95,9 @@
 import org.chromium.content_public.browser.test.util.TestTouchUtils;
 import org.chromium.content_public.common.ContentFeatures;
 import org.chromium.net.test.EmbeddedTestServer;
+import org.chromium.printing.Printable;
+import org.chromium.printing.PrintingController;
+import org.chromium.printing.PrintingControllerImpl;
 import org.chromium.ui.base.Clipboard;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.mojom.MenuSourceType;
@@ -118,6 +121,7 @@
 
     @Mock private TabContextMenuItemDelegate mItemDelegate;
     @Mock private ShareDelegate mShareDelegate;
+    @Mock private PrintingController mPrintingController;
     @Mock private DataProtectionBridge.Natives mDataProtectionBridgeMock;
 
     @ClassRule
@@ -1192,6 +1196,31 @@
                 chromeExtrasCaptor.getValue().saveLastUsed());
     }
 
+    @Test
+    @MediumTest
+    @Restriction(DeviceFormFactor.DESKTOP)
+    @EnableFeatures({ChromeFeatureList.CONTEXT_MENU_EMPTY_SPACE})
+    public void testPrintPage() throws Exception {
+        Tab tab = sDownloadTestRule.getActivity().getActivityTab();
+        ThreadUtils.runOnUiThreadBlocking(
+                // Set printing controller to use the mock instance.
+                () -> {
+                    PrintingControllerImpl.setInstanceForTesting(mPrintingController);
+                });
+
+        ContextMenuUtils.selectContextMenuItemFromRightClick(
+                InstrumentationRegistry.getInstrumentation(),
+                sDownloadTestRule.getActivity(),
+                tab,
+                "testEmptySpace",
+                R.id.contextmenu_print_page);
+
+        // Check that the started print job has the same title as the current tab.
+        ArgumentCaptor<Printable> printableCaptor = ArgumentCaptor.forClass(Printable.class);
+        verify(mPrintingController).startPrint(printableCaptor.capture(), any());
+        Assert.assertEquals(tab.getTitle(), printableCaptor.getValue().getTitle());
+    }
+
     // TODO(benwgold): Add more test coverage for histogram recording of other context menu types.
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/SettingsActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/SettingsActivityTest.java
index cac57b705..89aec25 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/SettingsActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/SettingsActivityTest.java
@@ -116,5 +116,10 @@
                 SettingsActivity.class, Stage.CREATED, () -> activity.startActivity(intent3));
     }
 
-    public static class TestFragment extends Fragment implements SettingsFragment {}
+    public static class TestFragment extends Fragment implements SettingsFragment {
+        @Override
+        public @AnimationType int getAnimationType() {
+            return AnimationType.PROPERTY;
+        }
+    }
 }
diff --git a/chrome/app/app-Info.plist b/chrome/app/app-Info.plist
index d834534c..9190c5e 100644
--- a/chrome/app/app-Info.plist
+++ b/chrome/app/app-Info.plist
@@ -246,8 +246,6 @@
 	<dict>
 		<key>MallocNanoZone</key>
 		<string>0</string>
-		<key>MallocProbGuard</key>
-		<string>0</string>
 	</dict>
 	<key>LSFileQuarantineEnabled</key>
 	<true/>
diff --git a/chrome/app/helper-Info.plist b/chrome/app/helper-Info.plist
index 764c3d9..7e1bbe0 100644
--- a/chrome/app/helper-Info.plist
+++ b/chrome/app/helper-Info.plist
@@ -22,8 +22,6 @@
 	<dict>
 		<key>MallocNanoZone</key>
 		<string>0</string>
-		<key>MallocProbGuard</key>
-		<string>0</string>
 	</dict>
 	<key>LSFileQuarantineEnabled</key>
 	<true/>
diff --git a/chrome/app_shim/app_mode-Info.plist b/chrome/app_shim/app_mode-Info.plist
index 5e502f74..9ac68981 100644
--- a/chrome/app_shim/app_mode-Info.plist
+++ b/chrome/app_shim/app_mode-Info.plist
@@ -30,8 +30,6 @@
        <dict>
               <key>MallocNanoZone</key>
               <string>0</string>
-              <key>MallocProbGuard</key>
-              <string>0</string>
        </dict>
        <key>LSMinimumSystemVersion</key>
        <string>${CHROMIUM_MIN_SYSTEM_VERSION}</string>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 6ac1a70b..f002e3a 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -5867,6 +5867,12 @@
      flag_descriptions::kGridTabSwitcherUpdateDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kGridTabSwitcherUpdate)},
 
+    // Predictive back gesture
+    {"allow-tab-closing-upon-minimization",
+     flag_descriptions::kAllowTabClosingUponMinimizationName,
+     flag_descriptions::kAllowTabClosingUponMinimizationDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kAllowTabClosingUponMinimization)},
+
 #endif  // BUILDFLAG(IS_ANDROID)
     {"disallow-doc-written-script-loads",
      flag_descriptions::kDisallowDocWrittenScriptsUiName,
@@ -12257,6 +12263,11 @@
      flag_descriptions::kGridTabSwitcherSurfaceColorUpdateDescription,
      kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kGridTabSwitcherSurfaceColorUpdate)},
+
+    // Contextual Page Action button.
+    {"cpa-spec-update", flag_descriptions::kCpaSpecUpdateName,
+     flag_descriptions::kCpaSpecUpdateDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kCpaSpecUpdate)},
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/android/omnibox/autocomplete_controller_android.cc b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
index d1e5811..8f4cd7a3 100644
--- a/chrome/browser/android/omnibox/autocomplete_controller_android.cc
+++ b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
@@ -45,6 +45,7 @@
 #include "components/omnibox/browser/actions/omnibox_answer_action.h"
 #include "components/omnibox/browser/autocomplete_classifier.h"
 #include "components/omnibox/browser/autocomplete_controller_emitter.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_grouper_sections.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
@@ -284,7 +285,9 @@
 }
 
 void AutocompleteControllerAndroid::Stop(JNIEnv* env, bool clear_results) {
-  autocomplete_controller_->Stop(clear_results);
+  autocomplete_controller_->Stop(clear_results
+                                     ? AutocompleteStopReason::kClobbered
+                                     : AutocompleteStopReason::kInteraction);
 }
 
 void AutocompleteControllerAndroid::ResetSession(JNIEnv* env) {
diff --git a/chrome/browser/ash/app_list/search/omnibox/omnibox_provider.cc b/chrome/browser/ash/app_list/search/omnibox/omnibox_provider.cc
index 8242e98..67cff74 100644
--- a/chrome/browser/ash/app_list/search/omnibox/omnibox_provider.cc
+++ b/chrome/browser/ash/app_list/search/omnibox/omnibox_provider.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "components/favicon/core/favicon_service.h"
 #include "components/omnibox/browser/autocomplete_classifier.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/prefs/pref_service.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
@@ -73,7 +74,7 @@
   last_query_ = query;
   last_tokenized_query_.emplace(query, TokenizedString::Mode::kCamelCase);
 
-  controller_->Stop(false);
+  controller_->Stop(AutocompleteStopReason::kInteraction);
   query_finished_ = false;
   // The new page classification value(CHROMEOS_APP_LIST) is introduced
   // to differentiate the suggest requests initiated by ChromeOS app_list from
@@ -91,7 +92,7 @@
   last_tokenized_query_.reset();
   query_finished_ = false;
 
-  controller_->Stop(true);
+  controller_->Stop(AutocompleteStopReason::kClobbered);
 }
 
 ash::AppListSearchResultType OmniboxProvider::ResultType() const {
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_observer.cc b/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
index c992fe3c..376c3cf 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
@@ -475,6 +475,8 @@
       return mojom::NamedDomKey::kAudioVolumeDown;
     case ui::DomKey::AUDIO_VOLUME_UP:
       return mojom::NamedDomKey::kAudioVolumeUp;
+    case ui::DomKey::NUM_LOCK:
+      return mojom::NamedDomKey::kNumLock;
     default:
       return std::nullopt;
   }
diff --git a/chrome/browser/autocomplete/keyword_extensions_delegate_impl.cc b/chrome/browser/autocomplete/keyword_extensions_delegate_impl.cc
index 07edcb8..044a2da 100644
--- a/chrome/browser/autocomplete/keyword_extensions_delegate_impl.cc
+++ b/chrome/browser/autocomplete/keyword_extensions_delegate_impl.cc
@@ -54,18 +54,19 @@
       base::UTF16ToUTF8(suggestion_text));
 }
 
-void  KeywordExtensionsDelegateImpl::IncrementInputId() {
+void KeywordExtensionsDelegateImpl::IncrementInputId() {
   current_input_id_ = ++global_input_uid_;
 }
 
 bool KeywordExtensionsDelegateImpl::IsEnabledExtension(
     const std::string& extension_id) {
   const extensions::Extension* extension =
-      extensions::ExtensionRegistry::Get(
-          profile_)->enabled_extensions().GetByID(extension_id);
+      extensions::ExtensionRegistry::Get(profile_)
+          ->enabled_extensions()
+          .GetByID(extension_id);
   return extension &&
-      (!profile_->IsOffTheRecord() ||
-       extensions::util::IsIncognitoEnabled(extension_id, profile_));
+         (!profile_->IsOffTheRecord() ||
+          extensions::util::IsIncognitoEnabled(extension_id, profile_));
 }
 
 bool KeywordExtensionsDelegateImpl::Start(
diff --git a/chrome/browser/back_press/android/BUILD.gn b/chrome/browser/back_press/android/BUILD.gn
index 669fcd85..95e1c625 100644
--- a/chrome/browser/back_press/android/BUILD.gn
+++ b/chrome/browser/back_press/android/BUILD.gn
@@ -67,6 +67,7 @@
     "//components/browser_ui/widget/android:java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/androidx:androidx_activity_activity_java",
+    "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/hamcrest:hamcrest_java",
     "//third_party/junit",
diff --git a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManager.java b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManager.java
index d6b74f3..5f208cb 100644
--- a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManager.java
+++ b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManager.java
@@ -7,14 +7,18 @@
 import static org.chromium.build.NullUtil.assumeNonNull;
 
 import android.annotation.SuppressLint;
+import android.os.Build;
 import android.util.SparseIntArray;
+import android.window.OnBackInvokedCallback;
 
 import androidx.activity.BackEventCompat;
 import androidx.activity.OnBackPressedCallback;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Callback;
 import org.chromium.base.CallbackUtils;
+import org.chromium.base.ObserverList;
 import org.chromium.base.lifetime.Destroyable;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.Supplier;
@@ -24,6 +28,7 @@
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler.BackPressResult;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler.Type;
+import org.chromium.components.browser_ui.widget.gesture.OnSystemNavigationObserver;
 
 /**
  * A central manager class to handle the back gesture. Every component/feature which is going to
@@ -166,10 +171,13 @@
     private boolean mHasSystemBackArm;
 
     private final @Nullable Callback<Boolean>[] mObserverCallbacks = new Callback[Type.NUM_TYPES];
+    private @Nullable OnBackInvokedCallback mOnSystemNavigationCallback;
     private Runnable mFallbackOnBackPressed;
     private int mLastCalledHandlerType = -1;
     private @Nullable Runnable mOnBackPressed;
     private Supplier<Boolean> mIsGestureNavEnabledSupplier = () -> false;
+    private final ObserverList<OnSystemNavigationObserver> mOnSystemNavigationObservers =
+            new ObserverList<>();
 
     /**
      * Record when the back press is consumed by a certain feature.
@@ -210,6 +218,11 @@
     public BackPressManager() {
         mFallbackOnBackPressed = CallbackUtils.emptyRunnable();
         mUseSystemBack = MinimizeAppAndCloseTabBackPressHandler.shouldUseSystemBack();
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
+            createOnSystemNavigationCallback();
+        }
+
         backPressStateChanged();
     }
 
@@ -274,6 +287,18 @@
         return mCallback;
     }
 
+    /**
+     * Callback when the back press is not consumed by any feature {@link BackPressHandler} and this
+     * back press will be consumed by the Android OS. In this case, Clank is minimized by the OS.
+     *
+     * @return The callback registered by OS to observe system navigation events; return null if the
+     *     current OS does not support this feature.
+     */
+    @Nullable
+    public OnBackInvokedCallback getOnSystemNavigationCallback() {
+        return mOnSystemNavigationCallback;
+    }
+
     /*
      * @param fallbackOnBackPressed Callback executed when a handler claims to intercept back press
      *         but no handler succeeds.
@@ -305,6 +330,27 @@
         mIsGestureNavEnabledSupplier = supplier;
     }
 
+    /**
+     * Add a new observer to observe the system navigation event. See details at {@link
+     * OnSystemNavigationObserver}. All registered observers will be called and any two observers
+     * should be mutually exclusive.
+     *
+     * @param observer The observer of system navigation to add.
+     */
+    public void addOnSystemNavigationObserver(OnSystemNavigationObserver observer) {
+        mOnSystemNavigationObservers.addObserver(observer);
+    }
+
+    /**
+     * Remove a new observer to observe the system navigation event. See details at {@link
+     * OnSystemNavigationObserver}.
+     *
+     * @param observer The observer of system navigation to remove.
+     */
+    public void removeOnSystemNavigationObserver(OnSystemNavigationObserver observer) {
+        mOnSystemNavigationObservers.removeObserver(observer);
+    }
+
     private void backPressStateChanged() {
         boolean intercept = shouldInterceptBackPress();
         if (mHasSystemBackArm) {
@@ -365,6 +411,22 @@
         assert !failed : "Callback is enabled but no handler consumed back gesture.";
     }
 
+    private void onSystemNavigationInternal() {
+        mOnSystemNavigationObservers.forEach(OnSystemNavigationObserver::onSystemNavigation);
+    }
+
+    @VisibleForTesting
+    @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
+    void createOnSystemNavigationCallback() {
+        mOnSystemNavigationCallback =
+                new OnBackInvokedCallback() {
+                    @Override
+                    public void onBackInvoked() {
+                        onSystemNavigationInternal();
+                    }
+                };
+    }
+
     @Override
     public void destroy() {
         for (int i = 0; i < mHandlers.length; i++) {
@@ -372,6 +434,7 @@
                 removeHandler(i);
             }
         }
+        mOnSystemNavigationObservers.clear();
     }
 
     @VisibleForTesting
diff --git a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerUnitTest.java b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerUnitTest.java
index 32fa8f1..989fdfe 100644
--- a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerUnitTest.java
+++ b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerUnitTest.java
@@ -5,9 +5,11 @@
 package org.chromium.chrome.browser.back_press;
 
 import android.os.Build;
+import android.window.OnBackInvokedCallback;
 
 import androidx.activity.BackEventCompat;
 import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -19,6 +21,7 @@
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 
 import java.util.concurrent.TimeoutException;
@@ -473,6 +476,22 @@
         callbackHelper.waitForCallback("Callback should be called again", 1);
     }
 
+    @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
+    @Test
+    @MinAndroidSdkLevel(Build.VERSION_CODES.TIRAMISU)
+    public void testOnSystemNavigationObserver() {
+        BackPressManager manager = new BackPressManager();
+        manager.createOnSystemNavigationCallback();
+        OnBackInvokedCallback callback = manager.getOnSystemNavigationCallback();
+        CallbackHelper callbackHelper = new CallbackHelper();
+
+        manager.addOnSystemNavigationObserver(callbackHelper::notifyCalled);
+        manager.addOnSystemNavigationObserver(callbackHelper::notifyCalled);
+
+        callback.onBackInvoked();
+        Assert.assertEquals("All observers should be called", 2, callbackHelper.getCallCount());
+    }
+
     private int getHandlerCount(BackPressManager manager) {
         int count = 0;
         for (BackPressHandler handler : manager.getHandlersForTesting()) {
diff --git a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandler.java b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandler.java
index 5d8f37f..913f6677 100644
--- a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandler.java
+++ b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandler.java
@@ -18,10 +18,11 @@
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabAssociatedApp;
 import org.chromium.chrome.browser.ui.native_page.NativePage;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
+import org.chromium.components.browser_ui.widget.gesture.OnSystemNavigationObserver;
 import org.chromium.content_public.browser.WebContents;
 
 import java.util.function.Predicate;
@@ -31,7 +32,8 @@
  * to manually minimize app and close tab if necessary.
  */
 @NullMarked
-public class MinimizeAppAndCloseTabBackPressHandler implements BackPressHandler, Destroyable {
+public class MinimizeAppAndCloseTabBackPressHandler
+        implements BackPressHandler, OnSystemNavigationObserver, Destroyable {
     static final String HISTOGRAM = "Android.BackPress.MinimizeAppAndCloseTab";
     static final String HISTOGRAM_CUSTOM_TAB_SAME_TASK =
             "Android.BackPress.MinimizeAppAndCloseTab.CustomTab.SameTask";
@@ -47,7 +49,9 @@
     private final ObservableSupplierImpl<Boolean> mNonSystemBackPressSupplier =
             new ObservableSupplierImpl<>();
     private final Predicate<Tab> mBackShouldCloseTab;
+    private final Predicate<Tab> mMinimizationShouldCloseTab;
     private final Callback<@Nullable Tab> mSendToBackground;
+    private final Callback<Tab> mCloseTabUponMinimization;
     private final Callback<Tab> mOnTabChanged = this::onTabChanged;
     private final ObservableSupplier<Tab> mActivityTabSupplier;
     private final boolean mUseSystemBack;
@@ -67,8 +71,17 @@
         int NUM_TYPES = 3;
     }
 
+    /** Whether the feature of closing tab during minimization is supported. */
+    public static boolean supportCloseTabUponMinimization() {
+        boolean isAtLeastB =
+                (sVersionForTesting == null ? VERSION.SDK_INT : sVersionForTesting)
+                        >= VERSION_CODES.BAKLAVA;
+        return isAtLeastB && ChromeFeatureList.sAllowTabClosingUponMinimization.isEnabled();
+    }
+
     /**
      * Record metrics of how back press is finally consumed by the app.
+     *
      * @param type The action we do when back press is consumed.
      */
     public static void record(@MinimizeAppAndCloseTabType int type) {
@@ -92,15 +105,20 @@
 
     /**
      * @param activityTabSupplier Supplier giving the current interact-able tab.
-     * @param backShouldCloseTab Test whether the current tab should be closed on back press.
+     * @param backShouldCloseTab Test whether the back press should be intercepted to close tab.
+     * @param minimizationShouldCloseTab Test whether the tab should be closed during minimization.
+     * @param closeTabUponMinimization Callback triggered during minimization to close tab.
      * @param sendToBackground Callback when app should be sent to background on back press.
-     * @param callbackOnBackPress Callback when back press is handled.
      */
     public MinimizeAppAndCloseTabBackPressHandler(
             ObservableSupplier<Tab> activityTabSupplier,
             Predicate<Tab> backShouldCloseTab,
+            Predicate<Tab> minimizationShouldCloseTab,
+            Callback<Tab> closeTabUponMinimization,
             Callback<@Nullable Tab> sendToBackground) {
         mBackShouldCloseTab = backShouldCloseTab;
+        mMinimizationShouldCloseTab = minimizationShouldCloseTab;
+        mCloseTabUponMinimization = closeTabUponMinimization;
         mSendToBackground = sendToBackground;
         mActivityTabSupplier = activityTabSupplier;
         mUseSystemBack = shouldUseSystemBack();
@@ -132,6 +150,8 @@
             }
         }
 
+        if (supportCloseTabUponMinimization()) assert !minimizeApp : "Should be minimized by OS";
+
         if (minimizeApp) {
             record(
                     shouldCloseTab
@@ -150,13 +170,19 @@
         return BackPressResult.SUCCESS;
     }
 
-    private Pair<Boolean, Boolean> determineBackPressAction(Tab currentTab) {
+    @Override
+    public void onSystemNavigation() {
+        Tab currentTab = mActivityTabSupplier.get();
+        if (currentTab != null && mMinimizationShouldCloseTab.test(currentTab)) {
+            mCloseTabUponMinimization.onResult(currentTab);
+        }
+    }
+
+    private Pair<Boolean, Boolean> determineBackPressAction(@Nullable Tab currentTab) {
         boolean minimizeApp;
         boolean shouldCloseTab;
 
         if (currentTab == null) {
-            assert !mUseSystemBack
-                    : "Should be disabled when there is no valid tab and back press is consumed.";
             minimizeApp = true;
             shouldCloseTab = false;
         } else {
@@ -164,9 +190,8 @@
 
             // Minimize the app if either:
             // - we decided not to close the tab
-            // - we decided to close the tab, but it was opened by an external app, so we will go
-            //   exit Chrome on top of closing the tab
-            minimizeApp = !shouldCloseTab || TabAssociatedApp.isOpenedFromExternalApp(currentTab);
+            // - we decided to close the tab, but this can be closed during minimization.
+            minimizeApp = !shouldCloseTab || mMinimizationShouldCloseTab.test(currentTab);
         }
 
         return new Pair(minimizeApp, shouldCloseTab);
@@ -183,7 +208,13 @@
     }
 
     private void onTabChanged(Tab tab) {
-        mSystemBackPressSupplier.set(tab != null && mBackShouldCloseTab.test(tab));
+        if (supportCloseTabUponMinimization()) {
+            Pair<Boolean, Boolean> backPressAction = determineBackPressAction(tab);
+            boolean minimizeApp = backPressAction.first;
+            mSystemBackPressSupplier.set(!minimizeApp);
+        } else {
+            mSystemBackPressSupplier.set(tab != null && mBackShouldCloseTab.test(tab));
+        }
     }
 
     static boolean shouldUseSystemBack() {
diff --git a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandlerUnitTest.java b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandlerUnitTest.java
index 8921d2e..ae284072 100644
--- a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandlerUnitTest.java
+++ b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandlerUnitTest.java
@@ -11,7 +11,6 @@
 import androidx.activity.BackEventCompat;
 import androidx.test.filters.SmallTest;
 
-import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -29,8 +28,10 @@
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.Features;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.back_press.MinimizeAppAndCloseTabBackPressHandler.MinimizeAppAndCloseTabType;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabAssociatedApp;
 import org.chromium.chrome.browser.tab.TabLaunchType;
@@ -50,6 +51,9 @@
 
     @Mock private Predicate<Tab> mShouldCloseTab;
 
+    @Mock private Predicate<Tab> mMinimizationShouldCloseTab;
+    @Mock private Callback<Tab> mCloseTabUponMinimization;
+
     @Mock private Tab mTab;
 
     private MinimizeAppAndCloseTabBackPressHandler mHandler;
@@ -68,9 +72,7 @@
                         MinimizeAppAndCloseTabBackPressHandler.HISTOGRAM,
                         MinimizeAppAndCloseTabType.MINIMIZE_APP_AND_CLOSE_TAB);
         Mockito.when(mShouldCloseTab.test(mTab)).thenReturn(true);
-        UserDataHost userDataHost = ThreadUtils.runOnUiThreadBlocking(() -> new UserDataHost());
-        Mockito.when(mTab.getUserDataHost()).thenReturn(userDataHost);
-        ThreadUtils.runOnUiThreadBlocking(() -> TabAssociatedApp.from(mTab));
+        Mockito.when(mMinimizationShouldCloseTab.test(mTab)).thenReturn(true);
         Mockito.when(mTab.getLaunchType()).thenReturn(TabLaunchType.FROM_EXTERNAL_APP);
         ThreadUtils.runOnUiThreadBlocking(() -> mActivityTabSupplier.set(mTab));
         Assert.assertTrue(mHandler.getHandleBackPressChangedSupplier().get());
@@ -153,38 +155,34 @@
 
     @Test
     @SmallTest
-    public void testMinimizeApp_NoValidTab_SystemBack() {
-        createBackPressHandler(true);
+    @Features.EnableFeatures({ChromeFeatureList.ALLOW_TAB_CLOSING_UPON_MINIMIZATION})
+    public void testCloseTabDuringMinimization() {
+        createBackPressHandler(true, true);
+        Mockito.when(mShouldCloseTab.test(mTab)).thenReturn(true);
+        Mockito.when(mMinimizationShouldCloseTab.test(mTab)).thenReturn(true);
+        Mockito.when(mTab.getLaunchType()).thenReturn(TabLaunchType.FROM_EXTERNAL_APP);
+        ThreadUtils.runOnUiThreadBlocking(() -> mActivityTabSupplier.set(mTab));
+        Assert.assertFalse(mHandler.getHandleBackPressChangedSupplier().get());
 
-        var histogram =
-                HistogramWatcher.newBuilder()
-                        .expectNoRecords(MinimizeAppAndCloseTabBackPressHandler.HISTOGRAM)
-                        .build();
-        ThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    mActivityTabSupplier.set(null);
-                });
-
-        Assert.assertFalse(
-                "Back press should be handled by OS.",
-                mHandler.getHandleBackPressChangedSupplier().get());
-
-        thrown.expect(Matchers.instanceOf(AssertionError.class));
-        thrown.expectMessage(
-                "Should be disabled when there is no valid tab and back press is consumed.");
-
-        mHandler.handleBackPress();
-        histogram.assertExpected();
-        verify(mSendToBackground, Mockito.never()).onResult(Mockito.any());
-        verify(mShouldCloseTab, Mockito.never()).test(Mockito.any());
+        ThreadUtils.runOnUiThreadBlocking(mHandler::onSystemNavigation);
+        verify(
+                        mCloseTabUponMinimization,
+                        Mockito.description("Tab should be closed during minimizing the app."))
+                .onResult(mTab);
     }
 
     private void createBackPressHandler() {
-        createBackPressHandler(false);
+        createBackPressHandler(false, false);
     }
 
     private void createBackPressHandler(boolean systemBack) {
-        if (systemBack) {
+        createBackPressHandler(systemBack, false);
+    }
+
+    private void createBackPressHandler(boolean systemBack, boolean systemMinimize) {
+        if (systemMinimize) {
+            MinimizeAppAndCloseTabBackPressHandler.setVersionForTesting(VERSION_CODES.BAKLAVA);
+        } else if (systemBack) {
             MinimizeAppAndCloseTabBackPressHandler.setVersionForTesting(VERSION_CODES.TIRAMISU);
         }
         ThreadUtils.runOnUiThreadBlocking(
@@ -195,6 +193,10 @@
                 ThreadUtils.runOnUiThreadBlocking(
                         () ->
                                 new MinimizeAppAndCloseTabBackPressHandler(
-                                        mActivityTabSupplier, mShouldCloseTab, mSendToBackground));
+                                        mActivityTabSupplier,
+                                        mShouldCloseTab,
+                                        mMinimizationShouldCloseTab,
+                                        mCloseTabUponMinimization,
+                                        mSendToBackground));
     }
 }
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_service.cc b/chrome/browser/contextual_cueing/contextual_cueing_service.cc
index 42951fe..9970a85d 100644
--- a/chrome/browser/contextual_cueing/contextual_cueing_service.cc
+++ b/chrome/browser/contextual_cueing/contextual_cueing_service.cc
@@ -294,22 +294,31 @@
     return;
   }
 
-  // Remote suggestions generation.
   ZeroStateSuggestionsPageData* page_data =
       ZeroStateSuggestionsPageData::GetOrCreateForPage(
           web_contents->GetPrimaryPage());
-  page_data->FetchSuggestions(is_fre, std::move(callback));
+  page_data->FetchSuggestions(
+      is_fre, base::BindOnce(&ContextualCueingService::OnSuggestionsReceived,
+                             weak_ptr_factory_.GetWeakPtr(),
+                             base::TimeTicks::Now(), std::move(callback)));
 #else
   std::move(callback).Run(std::nullopt);
 #endif
 }
 
 void ContextualCueingService::OnSuggestionsReceived(
-    content::WebContents* web_contents,
+    base::TimeTicks fetch_begin_time,
     GlicSuggestionsCallback callback,
     std::optional<std::vector<std::string>> suggestions) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  base::UmaHistogramTimes(suggestions
+                              ? "ContextualCueing.GlicSuggestions."
+                                "SuggestionsFetchLatency.ValidSuggestions"
+                              : "ContextualCueing.GlicSuggestions."
+                                "SuggestionsFetchLatency.EmptySuggestions",
+                          base::TimeTicks::Now() - fetch_begin_time);
+
   std::move(callback).Run(suggestions);
 }
 
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_service.h b/chrome/browser/contextual_cueing/contextual_cueing_service.h
index 892c1c51..6150ec1 100644
--- a/chrome/browser/contextual_cueing/contextual_cueing_service.h
+++ b/chrome/browser/contextual_cueing/contextual_cueing_service.h
@@ -101,7 +101,7 @@
   // Called when suggestions are received. Cleans up after suggestions
   // generation.
   void OnSuggestionsReceived(
-      content::WebContents* web_contents,
+      base::TimeTicks fetch_begin_time,
       GlicSuggestionsCallback callback,
       std::optional<std::vector<std::string>> suggestions);
 
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_service_browsertest.cc b/chrome/browser/contextual_cueing/contextual_cueing_service_browsertest.cc
index 565d13e..1d10714 100644
--- a/chrome/browser/contextual_cueing/contextual_cueing_service_browsertest.cc
+++ b/chrome/browser/contextual_cueing/contextual_cueing_service_browsertest.cc
@@ -14,6 +14,8 @@
 #include "chrome/browser/contextual_cueing/contextual_cueing_service_factory.h"
 #include "chrome/browser/extensions/keyed_services/browser_context_keyed_service_factories.h"
 #include "chrome/browser/optimization_guide/browser_test_util.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/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/ui/browser.h"
@@ -22,6 +24,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/optimization_guide/proto/contextual_cueing_metadata.pb.h"
 #include "components/prefs/pref_service.h"
 #include "components/search_engines/template_url_service.h"
 #include "content/public/test/browser_test.h"
@@ -34,6 +37,10 @@
 
 namespace contextual_cueing {
 
+using ::testing::_;
+using ::testing::An;
+using ::testing::WithArgs;
+
 class ContextualCueingServiceBrowserTest : public InProcessBrowserTest {
  public:
   ContextualCueingServiceBrowserTest() = default;
@@ -308,6 +315,131 @@
       "ZeroStateSuggestions",
       0);
 }
+
+class ContextualCueingServiceBrowserTestZSSHistogram
+    : public ContextualCueingServiceBrowserTest {
+ public:
+  ContextualCueingServiceBrowserTestZSSHistogram() {
+    scoped_feature_list_.InitAndEnableFeature(kGlicZeroStateSuggestions);
+  }
+
+  void SetUpOnMainThread() override {
+    browser()->profile()->GetPrefs()->SetBoolean(
+        glic::prefs::kGlicTabContextEnabled, true);
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        optimization_guide::switches::
+            kDisableCheckingUserPermissionsForTesting);
+  }
+
+  void PostRunTestOnMainThread() override {
+    mock_optimization_guide_keyed_service_ = nullptr;
+    InProcessBrowserTest::PostRunTestOnMainThread();
+  }
+
+  void SetUpBrowserContextKeyedServices(
+      content::BrowserContext* browser_context) override {
+    mock_optimization_guide_keyed_service_ =
+        static_cast<testing::NiceMock<MockOptimizationGuideKeyedService>*>(
+            OptimizationGuideKeyedServiceFactory::GetInstance()
+                ->SetTestingFactoryAndUse(
+                    browser_context,
+                    base::BindRepeating([](content::BrowserContext* context)
+                                            -> std::unique_ptr<KeyedService> {
+                      return std::make_unique<testing::NiceMock<
+                          MockOptimizationGuideKeyedService>>();
+                    })));
+  }
+
+  MockOptimizationGuideKeyedService& mock_optimization_guide_keyed_service() {
+    return *mock_optimization_guide_keyed_service_;
+  }
+
+  void SetUpHints(bool allow_contextual) {
+    ON_CALL(mock_optimization_guide_keyed_service(),
+            CanApplyOptimization(
+                _, optimization_guide::proto::GLIC_ZERO_STATE_SUGGESTIONS,
+                An<optimization_guide::OptimizationGuideDecisionCallback>()))
+        .WillByDefault(WithArgs<2>(
+            [=](optimization_guide::OptimizationGuideDecisionCallback
+                    callback) {
+              optimization_guide::proto::GlicZeroStateSuggestionsMetadata
+                  metadata;
+              metadata.set_contextual_suggestions_eligible(allow_contextual);
+              if (allow_contextual) {
+                metadata.mutable_contextual_suggestions()->Add("suggestion");
+              }
+
+              optimization_guide::OptimizationMetadata og_metadata;
+              og_metadata.SetAnyMetadataForTesting(metadata);
+              std::move(callback).Run(
+                  optimization_guide::OptimizationGuideDecision::kTrue,
+                  og_metadata);
+            }));
+  }
+
+ protected:
+  raw_ptr<testing::NiceMock<MockOptimizationGuideKeyedService>>
+      mock_optimization_guide_keyed_service_;
+};
+
+IN_PROC_BROWSER_TEST_F(ContextualCueingServiceBrowserTestZSSHistogram,
+                       LogsLatencyForValidSuggestions) {
+  base::HistogramTester histogram_tester;
+  SetUpHints(/*allow_contextual=*/true);
+
+  auto* service =
+      ContextualCueingServiceFactory::GetForProfile(browser()->profile());
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL("/optimization_guide/zss_page.html")));
+
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future;
+  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  service->GetContextualGlicZeroStateSuggestions(web_contents, /*is_fre=*/true,
+                                                 future.GetCallback());
+  ASSERT_TRUE(future.Wait());
+
+  histogram_tester.ExpectTotalCount(
+      "ContextualCueing.GlicSuggestions.SuggestionsFetchLatency."
+      "ValidSuggestions",
+      1);
+  histogram_tester.ExpectTotalCount(
+      "ContextualCueing.GlicSuggestions.SuggestionsFetchLatency."
+      "EmptySuggestions",
+      0);
+}
+
+IN_PROC_BROWSER_TEST_F(ContextualCueingServiceBrowserTestZSSHistogram,
+                       LogsLatencyForEmptySuggestions) {
+  base::HistogramTester histogram_tester;
+  SetUpHints(/*allow_contextual=*/false);
+
+  auto* service =
+      ContextualCueingServiceFactory::GetForProfile(browser()->profile());
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL("/optimization_guide/zss_page.html")));
+
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future;
+  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  service->GetContextualGlicZeroStateSuggestions(web_contents, /*is_fre=*/true,
+                                                 future.GetCallback());
+  ASSERT_TRUE(future.Wait());
+
+  histogram_tester.ExpectTotalCount(
+      "ContextualCueing.GlicSuggestions.SuggestionsFetchLatency."
+      "EmptySuggestions",
+      1);
+  histogram_tester.ExpectTotalCount(
+      "ContextualCueing.GlicSuggestions.SuggestionsFetchLatency."
+      "ValidSuggestions",
+      0);
+}
+
 #endif
 
 class ContextualCueingServiceBrowserTestCCFlag
diff --git a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
index 010f5f3..248d4088 100644
--- a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
+++ b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
@@ -92,7 +92,8 @@
                 new ToolbarCoordinator(
                         mActivity,
                         this,
-                        mListCoordinator,
+                        /* listActionDelegate= */ mListCoordinator,
+                        /* listContentView= */ mListCoordinator.getView(),
                         mSelectionDelegate,
                         config.isSeparateActivity,
                         tracker);
diff --git a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/toolbar/DownloadHomeToolbar.java b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/toolbar/DownloadHomeToolbar.java
index cebd99a8..a0b7a11 100644
--- a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/toolbar/DownloadHomeToolbar.java
+++ b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/toolbar/DownloadHomeToolbar.java
@@ -11,6 +11,7 @@
 
 import org.chromium.base.BuildInfo;
 import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.build.annotations.Initializer;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.download.home.list.ListItem;
@@ -24,6 +25,7 @@
 @NullMarked
 public class DownloadHomeToolbar extends SelectableListToolbar<ListItem> {
     private @Nullable UiConfig mUiConfig;
+    private View mListContentView;
 
     public DownloadHomeToolbar(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -96,4 +98,21 @@
             }
         }
     }
+
+    // SelectableListToolbar implementation.
+    @Override
+    protected boolean handleEnterKeyPress() {
+        return getMenu().performIdentifierAction(R.id.search_menu_id, 0);
+    }
+
+    @Override
+    protected View getNextFocusForward() {
+        // Move focus to list content view.
+        return mListContentView;
+    }
+
+    @Initializer
+    void setListContentView(View listContentView) {
+        mListContentView = listContentView;
+    }
 }
diff --git a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
index 14f287d..b20f436 100644
--- a/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
+++ b/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/toolbar/ToolbarCoordinator.java
@@ -93,6 +93,7 @@
             Context context,
             ToolbarActionDelegate delegate,
             ToolbarListActionDelegate listActionDelegate,
+            View listContentView,
             SelectionDelegate<ListItem> selectionDelegate,
             boolean hasCloseButton,
             Tracker tracker) {
@@ -112,6 +113,8 @@
                 R.id.selection_mode_menu_group,
                 hasCloseButton);
         mToolbar.setOnMenuItemClickListener(this::onMenuItemClick);
+        mToolbar.setFocusable(true);
+        mToolbar.setListContentView(listContentView);
 
         // TODO(crbug.com/41412009): Pass the visible group to the toolbar during initialization.
         mToolbar.initializeSearchView(
diff --git a/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle_unittest.cc b/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle_unittest.cc
index 0cda658..c98894ce 100644
--- a/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle_unittest.cc
+++ b/chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/base64.h"
 #include "base/base64url.h"
+#include "base/features.h"
 #include "base/json/json_writer.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
@@ -723,6 +724,23 @@
       OidcInterceptionResult::kInvalidUrlOrTokens);
 }
 
+TEST_P(OidcAuthResponseCaptureNavigationThrottleTest, DataDecoderFailure) {
+  // Disable the Rust JSON parser, as it is in-process and cannot crash.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatureState(base::features::kUseRustJsonParser, false);
+  in_process_data_decoder_.SimulateJsonParserCrash(/*drop=*/true);
+
+  std::string redirection_url =
+      BuildStandardResponseUrl(/*oidc_state=*/std::string());
+
+  auto* oidc_interceptor = GetMockOidcInterceptor();
+  RunThrottleAndExpectNoOidcInterception(oidc_interceptor, redirection_url,
+                                         NavigationThrottle::DEFER);
+  CheckFunnelAndResultHistogram(
+      OidcInterceptionFunnelStep::kValidRedirectionCaptured,
+      OidcInterceptionResult::kInvalidUrlOrTokens);
+}
+
 TEST_P(OidcAuthResponseCaptureNavigationThrottleTest, NoServiceForGuestMode) {
   TestNoServiceForInvalidProfile(profile_manager()->CreateGuestProfile());
 }
diff --git a/chrome/browser/extensions/ai_language_model_browsertest.cc b/chrome/browser/extensions/ai_language_model_browsertest.cc
index 15fcb50..c75e3a6 100644
--- a/chrome/browser/extensions/ai_language_model_browsertest.cc
+++ b/chrome/browser/extensions/ai_language_model_browsertest.cc
@@ -79,6 +79,11 @@
           );
           chrome.test.succeed();
         },
+        function verifyLanguageModel() {
+          const expectLanguageModel = %s;
+          chrome.test.assertEq(expectLanguageModel, !!self.LanguageModel);
+          chrome.test.succeed();
+        },
       ]);
     )JS";
 
@@ -240,11 +245,12 @@
        IsExtensionParticipatingInOriginTrial(GetParam())) &&
       IsExtensionPermissionRequested(GetParam()) &&
       !IsPromptAPIForExtensionKillSwitchTriggered(GetParam());
-  test_dir.WriteFile(
-      FILE_PATH_LITERAL("sw.js"),
-      base::StringPrintf(kServiceWorkerScript,
-                         base::ToString(is_self_ai_accessible),
-                         base::ToString(is_chrome_ai_accessible)));
+  bool is_global_accessible = is_self_ai_accessible || is_chrome_ai_accessible;
+  test_dir.WriteFile(FILE_PATH_LITERAL("sw.js"),
+                     base::StringPrintf(kServiceWorkerScript,
+                                        base::ToString(is_self_ai_accessible),
+                                        base::ToString(is_chrome_ai_accessible),
+                                        base::ToString(is_global_accessible)));
   ResultCatcher result_catcher;
   const Extension* extension = LoadExtension(test_dir.UnpackedPath());
   ASSERT_TRUE(extension);
diff --git a/chrome/browser/extensions/api/declarative_net_request/action_tracker_unittest.cc b/chrome/browser/extensions/api/declarative_net_request/action_tracker_unittest.cc
index b5fe446f..0535f39 100644
--- a/chrome/browser/extensions/api/declarative_net_request/action_tracker_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/action_tracker_unittest.cc
@@ -23,6 +23,8 @@
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
 #include "url/gurl.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace extensions {
 namespace declarative_net_request {
 
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc
index 5bd746d47..5ec7e11 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc
@@ -62,6 +62,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace extensions {
 namespace declarative_net_request {
 namespace {
diff --git a/chrome/browser/extensions/api/declarative_net_request/dnr_test_base.cc b/chrome/browser/extensions/api/declarative_net_request/dnr_test_base.cc
index bc34460..f7287af 100644
--- a/chrome/browser/extensions/api/declarative_net_request/dnr_test_base.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/dnr_test_base.cc
@@ -6,6 +6,8 @@
 
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace extensions {
 namespace declarative_net_request {
 
diff --git a/chrome/browser/extensions/api/declarative_net_request/dnr_test_base.h b/chrome/browser/extensions/api/declarative_net_request/dnr_test_base.h
index 778dda12..8bf533b 100644
--- a/chrome/browser/extensions/api/declarative_net_request/dnr_test_base.h
+++ b/chrome/browser/extensions/api/declarative_net_request/dnr_test_base.h
@@ -11,6 +11,8 @@
 #include "extensions/browser/api/declarative_net_request/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace extensions {
 class ChromeTestExtensionLoader;
 
diff --git a/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc b/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
index 7f63e1b..c2dcbfa 100644
--- a/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
@@ -42,6 +42,8 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace extensions::declarative_net_request {
 
 namespace dnr_api = api::declarative_net_request;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index e17f127..e49ba08f7 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -169,6 +169,11 @@
     "expiry_milestone": 140
   },
   {
+    "name": "allow-tab-closing-upon-minimization",
+    "owners": ["lazzzis@google.com", "twellington@chromium.org", "clank-app-team@google.com"],
+    "expiry_milestone": 142
+  },
+  {
     "name": "allow-user-installed-chrome-apps",
     "owners": ["//apps/DEPRECATION_OWNERS"],
     "expiry_milestone": 169
@@ -1735,6 +1740,11 @@
     "expiry_milestone": 136
   },
   {
+    "name": "cpa-spec-update",
+    "owners": [ "eleanorlee@google.com", "wenyufu@chromium.org" ],
+    "expiry_milestone": 145
+  },
+  {
     "name": "cpe-automatic-passkey-upgrade",
     "owners": ["sugoi@chromium.org", "bling-transactions-eng@google.com"],
     "expiry_milestone": 145
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index fdca4d929d..8344a1ae 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -96,6 +96,12 @@
 #endif
 
 #if BUILDFLAG(IS_ANDROID)
+const char kAllowTabClosingUponMinimizationName[] =
+    "Allow tab to be closed during minimization";
+const char kAllowTabClosingUponMinimizationDescription[] =
+    "Utilize Android 16's new API to allow tab to be closed during minimization"
+    " triggered by back press.";
+
 const char kAndroidAdaptiveFrameRateName[] =
     "Android Adaptive Refresh Rate features";
 const char kAndroidAdaptiveFrameRateDescription[] =
@@ -4681,6 +4687,11 @@
 const char kConvertTrackpadEventsToMouseDescription[] =
     "Convert trackpad events to mouse events to improve gesture support";
 
+const char kCpaSpecUpdateName[] = "CpaSpecUpdate";
+const char kCpaSpecUpdateDescription[] =
+    "Updates the Cpa button animation and changes the shape of the checked "
+    "state button for stateful CPAs.";
+
 const char kDeprecatedExternalPickerFunctionName[] =
     "Use deprecated External Picker method";
 const char kDeprecatedExternalPickerFunctionDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 482f34bd..13b32f5 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -79,6 +79,9 @@
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
         //
 #if BUILDFLAG(IS_ANDROID)
+extern const char kAllowTabClosingUponMinimizationName[];
+extern const char kAllowTabClosingUponMinimizationDescription[];
+
 extern const char kAndroidAdaptiveFrameRateName[];
 extern const char kAndroidAdaptiveFrameRateDescription[];
 #endif
@@ -2739,6 +2742,9 @@
 extern const char kConvertTrackpadEventsToMouseName[];
 extern const char kConvertTrackpadEventsToMouseDescription[];
 
+extern const char kCpaSpecUpdateName[];
+extern const char kCpaSpecUpdateDescription[];
+
 extern const char kDeprecatedExternalPickerFunctionName[];
 extern const char kDeprecatedExternalPickerFunctionDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 3bdc9cfb..e641b56 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -182,6 +182,7 @@
     &kAdaptiveButtonInTopToolbarCustomizationV2,
     &kAdaptiveButtonInTopToolbarPageSummary,
     &kAllowNewIncognitoTabIntents,
+    &kAllowTabClosingUponMinimization,
     &kAndroidAppIntegration,
     &kAndroidAppIntegrationV2,
     &kNewTabPageCustomization,
@@ -269,6 +270,7 @@
     &kContextualSearchDisableOnlineDetection,
     &kContextualSearchSuppressShortView,
     &kControlsVisibilityFromNavigations,
+    &kCpaSpecUpdate,
     &kCrossDeviceTabPaneAndroid,
     &kDeviceAuthenticatorAndroidx,
     &kDisableCompositedProgressBar,
@@ -468,6 +470,10 @@
              "AllowNewIncognitoTabIntents",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+BASE_FEATURE(kAllowTabClosingUponMinimization,
+             "AllowTabClosingUponMinimization",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kDisableCompositedProgressBar,
              "DisableCompositedProgressBar",
              base::FEATURE_DISABLED_BY_DEFAULT);
@@ -531,7 +537,7 @@
 
 BASE_FEATURE(kAndroidNativePagesInNewTab,
              "AndroidNativePagesInNewTab",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kAndroidNoVisibleHintForDifferentTLD,
              "AndroidNoVisibleHintForDifferentTLD",
@@ -845,6 +851,10 @@
              "ControlsVisibilityFromNavigations",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+BASE_FEATURE(kCpaSpecUpdate,
+             "CpaSpecUpdate",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kCrossDeviceTabPaneAndroid,
              "CrossDeviceTabPaneAndroid",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index bdae2b6..61ea261 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -18,6 +18,7 @@
 BASE_DECLARE_FEATURE(kAdaptiveButtonInTopToolbarCustomizationV2);
 BASE_DECLARE_FEATURE(kAdaptiveButtonInTopToolbarPageSummary);
 BASE_DECLARE_FEATURE(kAllowNewIncognitoTabIntents);
+BASE_DECLARE_FEATURE(kAllowTabClosingUponMinimization);
 BASE_DECLARE_FEATURE(kAndroidAppIntegration);
 BASE_DECLARE_FEATURE(kAndroidAppIntegrationV2);
 BASE_DECLARE_FEATURE(kNewTabPageCustomization);
@@ -107,6 +108,7 @@
 BASE_DECLARE_FEATURE(kContextualSearchDisableOnlineDetection);
 BASE_DECLARE_FEATURE(kContextualSearchSuppressShortView);
 BASE_DECLARE_FEATURE(kControlsVisibilityFromNavigations);
+BASE_DECLARE_FEATURE(kCpaSpecUpdate);
 BASE_DECLARE_FEATURE(kCrossDeviceTabPaneAndroid);
 BASE_DECLARE_FEATURE(kDeviceAuthenticatorAndroidx);
 BASE_DECLARE_FEATURE(kDisableCompositedProgressBar);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index a30de65e..ac506bf4 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -159,6 +159,8 @@
     public static final String ADAPTIVE_BUTTON_IN_TOP_TOOLBAR_PAGE_SUMMARY =
             "AdaptiveButtonInTopToolbarPageSummary";
     public static final String ALLOW_NEW_INCOGNITO_TAB_INTENTS = "AllowNewIncognitoTabIntents";
+    public static final String ALLOW_TAB_CLOSING_UPON_MINIMIZATION =
+            "AllowTabClosingUponMinimization";
     public static final String ALWAYS_BLOCK_3PCS_INCOGNITO = "AlwaysBlock3pcsIncognito";
     public static final String ANDROID_APPEARANCE_SETTINGS = "AndroidAppearanceSettings";
     public static final String ANDROID_APP_INTEGRATION = "AndroidAppIntegration";
@@ -325,6 +327,7 @@
     public static final String CONTROLS_VISIBILITY_FROM_NAVIGATIONS =
             "ControlsVisibilityFromNavigations";
     public static final String CORMORANT = "Cormorant";
+    public static final String CPA_SPEC_UPDATE = "CpaSpecUpdate";
     public static final String CROSS_DEVICE_TAB_PANE_ANDROID = "CrossDeviceTabPaneAndroid";
     public static final String DARKEN_WEBSITES_CHECKBOX_IN_THEMES_SETTING =
             "DarkenWebsitesCheckboxInThemesSetting";
@@ -611,6 +614,8 @@
     public static final String XSURFACE_METRICS_REPORTING = "XsurfaceMetricsReporting";
 
     /* Alphabetical: */
+    public static final CachedFlag sAllowTabClosingUponMinimization =
+            newCachedFlag(ALLOW_TAB_CLOSING_UPON_MINIMIZATION, false);
     public static final CachedFlag sAndroidAppIntegration =
             newCachedFlag(ANDROID_APP_INTEGRATION, true);
     public static final CachedFlag sAndroidAppIntegrationModule =
@@ -708,6 +713,7 @@
                     /* defaultValueInTests= */ true);
     public static final CachedFlag sCommandLineOnNonRooted =
             newCachedFlag(COMMAND_LINE_ON_NON_ROOTED, false);
+    public static final CachedFlag sCpaSpecUpdate = newCachedFlag(CPA_SPEC_UPDATE, false);
     public static final CachedFlag sCrossDeviceTabPaneAndroid =
             newCachedFlag(CROSS_DEVICE_TAB_PANE_ANDROID, false);
     public static final CachedFlag sDisableInstanceLimit =
@@ -849,6 +855,7 @@
 
     public static final List<CachedFlag> sFlagsCachedFullBrowser =
             List.of(
+                    sAllowTabClosingUponMinimization,
                     sAndroidAppIntegration,
                     sAndroidAppIntegrationModule,
                     sAndroidAppIntegrationMultiDataSource,
@@ -894,6 +901,7 @@
                     sClankStartupLatencyInjection,
                     sCollectAndroidFrameTimelineMetrics,
                     sCommandLineOnNonRooted,
+                    sCpaSpecUpdate,
                     sCrossDeviceTabPaneAndroid,
                     sDisableInstanceLimit,
                     sDisableListTabSwitcher,
@@ -974,7 +982,7 @@
     public static final MutableFlagWithSafeDefault sAndroidDumpOnScrollWithoutResource =
             newMutableFlagWithSafeDefault(ANDROID_DUMP_ON_SCROLL_WITHOUT_RESOURCE, false);
     public static final MutableFlagWithSafeDefault sAndroidNativePagesInNewTab =
-            newMutableFlagWithSafeDefault(ANDROID_NATIVE_PAGES_IN_NEW_TAB, false);
+            newMutableFlagWithSafeDefault(ANDROID_NATIVE_PAGES_IN_NEW_TAB, true);
     public static final MutableFlagWithSafeDefault sAndroidTabDeclutter =
             newMutableFlagWithSafeDefault(ANDROID_TAB_DECLUTTER, true);
     public static final MutableFlagWithSafeDefault sAndroidTabDeclutterArchiveAllButActiveTab =
diff --git a/chrome/browser/glic/widget/glic_window_controller.cc b/chrome/browser/glic/widget/glic_window_controller.cc
index 8f4ef27..010d93b 100644
--- a/chrome/browser/glic/widget/glic_window_controller.cc
+++ b/chrome/browser/glic/widget/glic_window_controller.cc
@@ -263,7 +263,7 @@
         glic_window_controller_->ShouldStartDrag(initial_press_loc_,
                                                  mouse_location)) {
       glic_window_controller_->HandleWindowDragWithOffset(
-          mouse_location.OffsetFromOrigin());
+          initial_press_loc_.OffsetFromOrigin());
     }
   }
 
@@ -1126,7 +1126,7 @@
       GetGlicWidget()->SetZOrderLevel(ui::ZOrderLevel::kFloatingWindow);
     }
     GetGlicWidget()->RunMoveLoop(
-        GetClampedMouseDragOffset(mouse_offset), move_loop_source,
+        mouse_offset, move_loop_source,
         views::Widget::MoveLoopEscapeBehavior::kDontHide);
     in_move_loop_ = false;
     scoped_glic_button_indicator_.reset();
@@ -1134,6 +1134,7 @@
     // request.
     glic_window_animator_->MaybeAnimateToTargetSize();
 
+    AdjustPositionIfNeeded();
     SaveWidgetPosition();
 
     if (!AlwaysDetached()) {
@@ -1150,16 +1151,22 @@
   return panel_state_;
 }
 
-gfx::Vector2d GlicWindowController::GetClampedMouseDragOffset(
-    const gfx::Vector2d& mouse_offset) {
-  static const int kMinimumDragOffset = 10;
-  const int max_x = GetGlicView()->width() - kMinimumDragOffset;
-  const int max_y = GlicWidget::GetInitialSize().height() - kMinimumDragOffset;
-  CHECK_GT(max_x, kMinimumDragOffset);
-  CHECK_GT(max_y, kMinimumDragOffset);
+void GlicWindowController::AdjustPositionIfNeeded() {
+  // Always have at least `kMinimumVisible` px visible from glic window in
+  // both vertical and horizontal directions.
+  constexpr int kMinimumVisible = 40;
+  const auto widget_size = GetGlicWidget()->GetSize();
+  const int horizontal_buffer = widget_size.width() - kMinimumVisible;
+  const int vertical_buffer = widget_size.height() - kMinimumVisible;
 
-  return {std::clamp(mouse_offset.x(), kMinimumDragOffset, max_x),
-          std::clamp(mouse_offset.y(), kMinimumDragOffset, max_y)};
+  // Adjust bounds of visible area screen to allow part of glic to go off
+  // screen.
+  auto workarea = GetGlicWidget()->GetWorkAreaBoundsInScreen();
+  workarea.Outset(gfx::Outsets::VH(vertical_buffer, horizontal_buffer));
+
+  auto rect = GetGlicWidget()->GetRestoredBounds();
+  rect.AdjustToFit(workarea);
+  GetGlicWidget()->SetBounds(rect);
 }
 
 void GlicWindowController::OnDragComplete() {
diff --git a/chrome/browser/glic/widget/glic_window_controller.h b/chrome/browser/glic/widget/glic_window_controller.h
index af6c6e5..1b485bbe 100644
--- a/chrome/browser/glic/widget/glic_window_controller.h
+++ b/chrome/browser/glic/widget/glic_window_controller.h
@@ -365,8 +365,8 @@
   // TODO(crbug.com/410629338): Reimplement attachment.
   void AttachToBrowser(Browser& browser, AttachChangeReason reason);
 
-  // Clamp the mouse drag offsets to keep glic within the visible region.
-  gfx::Vector2d GetClampedMouseDragOffset(const gfx::Vector2d& mouse_offset);
+  // Keep part of glic window within the visible region.
+  void AdjustPositionIfNeeded();
 
   // Handles end-of-drag:
   //  - If glic is within attachment distance of a browser window's glic button,
diff --git a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/SelectLanguageFragment.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/SelectLanguageFragment.java
index 7a9d620..e0c7226 100644
--- a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/SelectLanguageFragment.java
+++ b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/settings/SelectLanguageFragment.java
@@ -202,4 +202,9 @@
     public void setProfile(Profile profile) {
         mProfile = profile;
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/browser/media/android/java/res/layout/media_capture_picker_button_row.xml b/chrome/browser/media/android/java/res/layout/media_capture_picker_button_row.xml
index f64711b..ba22dabf 100644
--- a/chrome/browser/media/android/java/res/layout/media_capture_picker_button_row.xml
+++ b/chrome/browser/media/android/java/res/layout/media_capture_picker_button_row.xml
@@ -53,6 +53,15 @@
     </LinearLayout>
 
 
+    <org.chromium.ui.widget.ButtonCompat
+    android:id="@+id/screen_button"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_marginHorizontal="@dimen/dialog_padding_sides"
+    android:layout_gravity="end"
+    android:text="@string/media_capture_picker_dialog_window_or_screen_text"
+    style="@style/TextButton" />
+
     <org.chromium.components.browser_ui.widget.DualControlLayout
         android:id="@+id/button_bar"
         android:layout_width="match_parent"
diff --git a/chrome/browser/media/android/media_capture_picker_dialog_bridge.cc b/chrome/browser/media/android/media_capture_picker_dialog_bridge.cc
index ab6b4500..d28edc8 100644
--- a/chrome/browser/media/android/media_capture_picker_dialog_bridge.cc
+++ b/chrome/browser/media/android/media_capture_picker_dialog_bridge.cc
@@ -52,26 +52,35 @@
       request_audio);
 }
 
-void MediaCapturePickerDialogBridge::OnResult(
+void MediaCapturePickerDialogBridge::OnPickTab(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& java_web_contents,
     bool audio_share) {
-  content::DesktopMediaID desktop_media_id;
-
-  // If no web contents was selected to capture, return a null DesktopMediaID.
-  // TODO(crbug.com/352186941): Implement for window and screen sharing.
   content::WebContents* web_contents =
       content::WebContents::FromJavaWebContents(java_web_contents);
-  if (web_contents) {
-    desktop_media_id = content::DesktopMediaID(
-        content::DesktopMediaID::TYPE_WEB_CONTENTS,
-        content::DesktopMediaID::kNullId,
-        content::WebContentsMediaCaptureId(
-            web_contents->GetPrimaryMainFrame()
-                ->GetProcess()
-                ->GetDeprecatedID(),
-            web_contents->GetPrimaryMainFrame()->GetRoutingID()));
-    desktop_media_id.audio_share = audio_share;
-  }
+  CHECK(web_contents);
+  auto desktop_media_id = content::DesktopMediaID(
+      content::DesktopMediaID::TYPE_WEB_CONTENTS,
+      content::DesktopMediaID::kNullId,
+      content::WebContentsMediaCaptureId(
+          web_contents->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID(),
+          web_contents->GetPrimaryMainFrame()->GetRoutingID()));
+  desktop_media_id.audio_share = audio_share;
   std::move(callback_).Run(desktop_media_id);
 }
+
+void MediaCapturePickerDialogBridge::OnPickWindow(JNIEnv* env) {
+  auto desktop_media_id = content::DesktopMediaID(
+      content::DesktopMediaID::TYPE_WINDOW, content::DesktopMediaID::kNullId);
+  std::move(callback_).Run(desktop_media_id);
+}
+
+void MediaCapturePickerDialogBridge::OnPickScreen(JNIEnv* env) {
+  auto desktop_media_id = content::DesktopMediaID(
+      content::DesktopMediaID::TYPE_SCREEN, content::DesktopMediaID::kNullId);
+  std::move(callback_).Run(desktop_media_id);
+}
+
+void MediaCapturePickerDialogBridge::OnCancel(JNIEnv* env) {
+  std::move(callback_).Run({});
+}
diff --git a/chrome/browser/media/android/media_capture_picker_dialog_bridge.h b/chrome/browser/media/android/media_capture_picker_dialog_bridge.h
index a3d3dbc..4f0df251 100644
--- a/chrome/browser/media/android/media_capture_picker_dialog_bridge.h
+++ b/chrome/browser/media/android/media_capture_picker_dialog_bridge.h
@@ -41,9 +41,18 @@
             MediaCapturePickerDialogCallback callback);
 
   // Called from Java via JNI when the dialog resolves.
-  void OnResult(JNIEnv* env,
-                const base::android::JavaParamRef<jobject>& java_web_contents,
-                bool audio_share);
+  void OnPickTab(JNIEnv* env,
+                 const base::android::JavaParamRef<jobject>& java_web_contents,
+                 bool audio_share);
+
+  // Called from Java via JNI when the dialog resolves.
+  void OnPickWindow(JNIEnv* env);
+
+  // Called from Java via JNI when the dialog resolves.
+  void OnPickScreen(JNIEnv* env);
+
+  // Called from Java via JNI when the dialog resolves.
+  void OnCancel(JNIEnv* env);
 
  private:
   MediaCapturePickerDialogCallback callback_;
diff --git a/chrome/browser/media/media_engagement_preloaded_list.cc b/chrome/browser/media/media_engagement_preloaded_list.cc
index 9f775e0..bc08f85 100644
--- a/chrome/browser/media/media_engagement_preloaded_list.cc
+++ b/chrome/browser/media/media_engagement_preloaded_list.cc
@@ -127,5 +127,5 @@
 MediaEngagementPreloadedList::CheckStringIsPresent(
     const std::string& input) const {
   return static_cast<MediaEngagementPreloadedList::DafsaResult>(
-      net::LookupStringInFixedSet(dafsa_, input.c_str(), input.size()));
+      net::LookupStringInFixedSet(dafsa_, input));
 }
diff --git a/chrome/browser/predictors/loading_predictor.cc b/chrome/browser/predictors/loading_predictor.cc
index 46bf870b9..02ec770 100644
--- a/chrome/browser/predictors/loading_predictor.cc
+++ b/chrome/browser/predictors/loading_predictor.cc
@@ -151,8 +151,7 @@
     HintOrigin origin,
     bool preconnectable,
     std::optional<PreconnectPrediction> preconnect_prediction) {
-  if (shutdown_)
-    return true;
+  CHECK(!shutdown_);
 
   TRACE_EVENT("loading", "LoadingPredictor::PrepareForPageLoad");
 
@@ -290,14 +289,11 @@
   shutdown_ = true;
 }
 
-bool LoadingPredictor::OnNavigationStarted(
-    NavigationId navigation_id,
-    ukm::SourceId ukm_source_id,
-    const std::optional<url::Origin>& initiator_origin,
-    const GURL& main_frame_url,
-    base::TimeTicks creation_time) {
-  if (shutdown_)
-    return true;
+void LoadingPredictor::OnNavigationStarted(NavigationId navigation_id,
+                                           ukm::SourceId ukm_source_id,
+                                           const GURL& main_frame_url,
+                                           base::TimeTicks creation_time) {
+  CHECK(!shutdown_);
 
   TRACE_EVENT("loading", "LoadingPredictor::OnNavigationStarted");
 
@@ -307,8 +303,6 @@
   active_navigations_.emplace(navigation_id,
                               NavigationInfo{main_frame_url, creation_time});
   active_urls_to_navigations_[main_frame_url].insert(navigation_id);
-  return PrepareForPageLoad(initiator_origin, main_frame_url,
-                            HintOrigin::NAVIGATION);
 }
 
 void LoadingPredictor::OnNavigationFinished(NavigationId navigation_id,
diff --git a/chrome/browser/predictors/loading_predictor.h b/chrome/browser/predictors/loading_predictor.h
index e3394d4..20f7230 100644
--- a/chrome/browser/predictors/loading_predictor.h
+++ b/chrome/browser/predictors/loading_predictor.h
@@ -92,13 +92,12 @@
 
   // KeyedService:
   void Shutdown() override;
+  bool WasShutdown() { return shutdown_; }
 
   // OnNavigationStarted is invoked when navigation |navigation_id| with
-  // |main_frame_url| has started navigating. It returns whether any actions
-  // were taken, such as preconnecting to known resource hosts, at that time.
-  bool OnNavigationStarted(NavigationId navigation_id,
+  // |main_frame_url| has started navigating.
+  void OnNavigationStarted(NavigationId navigation_id,
                            ukm::SourceId ukm_source_id,
-                           const std::optional<url::Origin>& initiator_origin,
                            const GURL& main_frame_url,
                            base::TimeTicks creation_time);
   void OnNavigationFinished(NavigationId navigation_id,
diff --git a/chrome/browser/predictors/loading_predictor_tab_helper.cc b/chrome/browser/predictors/loading_predictor_tab_helper.cc
index 3d59544..e18fbad 100644
--- a/chrome/browser/predictors/loading_predictor_tab_helper.cc
+++ b/chrome/browser/predictors/loading_predictor_tab_helper.cc
@@ -357,8 +357,9 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   TRACE_EVENT("navigation", "LoadingPredictorTabHelper::DidStartNavigation");
 
-  if (!predictor_)
+  if (!predictor_ || predictor_->WasShutdown()) {
     return;
+  }
 
   MaybeSetLCPPNavigationHint(*navigation_handle, *predictor_);
 
@@ -369,44 +370,71 @@
     return;
   }
 
+  const bool should_consult_optimization_guide = ShouldConsultOptimizationGuide(
+      navigation_handle->GetURL(), web_contents());
+
   PageData& page_data = PageData::CreateForNavigationHandle(*navigation_handle);
   page_data.predictor_ = predictor_;
 
-  page_data.has_local_preconnect_predictions_for_current_navigation_ =
-      predictor_->OnNavigationStarted(
-          page_data.navigation_id_,
-          ukm::ConvertToSourceId(navigation_handle->GetNavigationId(),
-                                 ukm::SourceIdType::NAVIGATION_ID),
-          navigation_handle->GetInitiatorOrigin(), navigation_handle->GetURL(),
-          navigation_handle->NavigationStart());
-  if (page_data.has_local_preconnect_predictions_for_current_navigation_ &&
-      !features::ShouldAlwaysRetrieveOptimizationGuidePredictions()) {
+  predictor_->OnNavigationStarted(
+      page_data.navigation_id_,
+      ukm::ConvertToSourceId(navigation_handle->GetNavigationId(),
+                             ukm::SourceIdType::NAVIGATION_ID),
+      navigation_handle->GetURL(), navigation_handle->NavigationStart());
+
+  if (base::FeatureList::IsEnabled(
+          blink::features::kLCPPPrefetchSubresourceAsync)) {
+    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &LoadingPredictorTabHelper::PrepareForPageLoad,
+            weak_ptr_factory_.GetWeakPtr(), base::WrapRefCounted(&page_data),
+            navigation_handle->GetInitiatorOrigin(),
+            navigation_handle->GetURL(), should_consult_optimization_guide));
+  } else {
+    PrepareForPageLoad(base::WrapRefCounted(&page_data),
+                       navigation_handle->GetInitiatorOrigin(),
+                       navigation_handle->GetURL(),
+                       should_consult_optimization_guide);
+  }
+}
+
+void LoadingPredictorTabHelper::PrepareForPageLoad(
+    scoped_refptr<PageData> page_data,
+    const std::optional<url::Origin> initiator_origin,
+    const GURL main_frame_url,
+    bool should_consult_optimization_guide) {
+  TRACE_EVENT("navigation", "LoadingPredictorTabHelper::PrepareForPageLoad.");
+  is_prepare_for_pageload_called_for_testing_ = true;
+  if (!predictor_ || predictor_->WasShutdown()) {
     return;
   }
+  page_data->has_local_preconnect_predictions_for_current_navigation_ =
+      predictor_->PrepareForPageLoad(initiator_origin, main_frame_url,
+                                     HintOrigin::NAVIGATION);
 
-  if (!optimization_guide_decider_)
-    return;
-
-  if (!ShouldConsultOptimizationGuide(navigation_handle->GetURL(),
-                                      web_contents())) {
+  if ((page_data->has_local_preconnect_predictions_for_current_navigation_ &&
+       !features::ShouldAlwaysRetrieveOptimizationGuidePredictions()) ||
+      !optimization_guide_decider_ || !should_consult_optimization_guide) {
     return;
   }
 
   TRACE_EVENT("navigation",
-              "LoadingPredictorTabHelper::DidStartNavigation."
+              "LoadingPredictorTabHelper::PrepareForPageLoad."
               "OptimizationGuidePrediction");
-
-  page_data.last_optimization_guide_prediction_ = OptimizationGuidePrediction();
-  page_data.last_optimization_guide_prediction_->decision =
+  page_data->last_optimization_guide_prediction_ =
+      OptimizationGuidePrediction();
+  page_data->last_optimization_guide_prediction_->decision =
       optimization_guide::OptimizationGuideDecision::kUnknown;
 
   optimization_guide_decider_->CanApplyOptimization(
-      navigation_handle->GetURL(), optimization_guide::proto::LOADING_PREDICTOR,
+      main_frame_url, optimization_guide::proto::LOADING_PREDICTOR,
       base::BindOnce(
           &LoadingPredictorTabHelper::OnOptimizationGuideDecision,
-          weak_ptr_factory_.GetWeakPtr(), base::WrapRefCounted(&page_data),
-          navigation_handle->GetInitiatorOrigin(), navigation_handle->GetURL(),
-          !page_data.has_local_preconnect_predictions_for_current_navigation_));
+          weak_ptr_factory_.GetWeakPtr(), std::move(page_data),
+          initiator_origin, main_frame_url,
+          !page_data
+               ->has_local_preconnect_predictions_for_current_navigation_));
 }
 
 void LoadingPredictorTabHelper::DidRedirectNavigation(
diff --git a/chrome/browser/predictors/loading_predictor_tab_helper.h b/chrome/browser/predictors/loading_predictor_tab_helper.h
index dd0778d..7403980 100644
--- a/chrome/browser/predictors/loading_predictor_tab_helper.h
+++ b/chrome/browser/predictors/loading_predictor_tab_helper.h
@@ -70,6 +70,10 @@
     predictor_ = predictor;
   }
 
+  bool IsPrepareForPageloadCalledForTesting() const {
+    return is_prepare_for_pageload_called_for_testing_;
+  }
+
  private:
   // The PageData stores the state needed for each page. It is primarily owned
   // by the DocumentPageDataHolder or NavigationPageDataHolder, depending on
@@ -158,6 +162,16 @@
       optimization_guide::OptimizationGuideDecision decision,
       const optimization_guide::OptimizationMetadata& metadata);
 
+  // Calls LoadingPredictor::PrepareForPageLoad doing prefetch and/or
+  // preconnect. This is called asynchronously after main resource fetching if
+  // kLCPPPrefetchSubresourceAsync is enabled.
+  void PrepareForPageLoad(scoped_refptr<PageData> page_data,
+                          const std::optional<url::Origin> initiator_origin,
+                          const GURL main_frame_url,
+                          bool should_consult_optimization_guide);
+
+  bool is_prepare_for_pageload_called_for_testing_ = false;
+
   // Owned by profile.
   base::WeakPtr<LoadingPredictor> predictor_;
 
diff --git a/chrome/browser/predictors/loading_predictor_tab_helper_unittest.cc b/chrome/browser/predictors/loading_predictor_tab_helper_unittest.cc
index 117a9b4..8a7bfdd 100644
--- a/chrome/browser/predictors/loading_predictor_tab_helper_unittest.cc
+++ b/chrome/browser/predictors/loading_predictor_tab_helper_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/run_loop.h"
 #include "base/test/gmock_callback_support.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/run_until.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
@@ -64,8 +65,16 @@
     const LoadingPredictorConfig& config)
     : LoadingDataCollector(nullptr, nullptr, config) {}
 
-class LoadingPredictorTabHelperTest : public ChromeRenderViewHostTestHarness {
+class LoadingPredictorTabHelperTest : public ChromeRenderViewHostTestHarness,
+                                      public testing::WithParamInterface<bool> {
  public:
+  LoadingPredictorTabHelperTest() {
+    if (GetParam()) {
+      scoped_feature_list_.InitAndEnableFeature(
+          blink::features::kLCPPPrefetchSubresourceAsync);
+    }
+  }
+
   void SetUp() override;
   void TearDown() override;
 
@@ -83,6 +92,8 @@
       mock_optimization_guide_keyed_service_;
   // Owned by |web_contents()|.
   raw_ptr<LoadingPredictorTabHelper, DanglingUntriaged> tab_helper_;
+
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 void LoadingPredictorTabHelperTest::SetUp() {
@@ -159,18 +170,20 @@
   // events dispatched by NavigationSimulator.
   navigation->SetKeepLoading(true);
   navigation->Start();
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return tab_helper_->IsPrepareForPageloadCalledForTesting(); }));
   navigation->Commit();
 }
 
 // Tests that a main frame navigation is correctly recorded by the
 // LoadingDataCollector.
-TEST_F(LoadingPredictorTabHelperTest, MainFrameNavigation) {
+TEST_P(LoadingPredictorTabHelperTest, MainFrameNavigation) {
   NavigateAndCommitInMainFrameAndVerifyMetrics("http://test.org");
 }
 
 // Tests that an old and new navigation ids are correctly set if a navigation
 // has redirects.
-TEST_F(LoadingPredictorTabHelperTest, MainFrameNavigationWithRedirects) {
+TEST_P(LoadingPredictorTabHelperTest, MainFrameNavigationWithRedirects) {
   GURL main_frame_url("http://test.org");
   auto navigation = content::NavigationSimulator::CreateRendererInitiated(
       main_frame_url, main_rfh());
@@ -197,7 +210,7 @@
 }
 
 // Tests that a subframe navigation is not recorded.
-TEST_F(LoadingPredictorTabHelperTest, SubframeNavigation) {
+TEST_P(LoadingPredictorTabHelperTest, SubframeNavigation) {
   // We need to have a committed main frame navigation before navigating in sub
   // frames.
   NavigateAndCommitInMainFrameAndVerifyMetrics("http://test.org");
@@ -209,7 +222,7 @@
 }
 
 // Tests that a failed navigation is correctly recorded.
-TEST_F(LoadingPredictorTabHelperTest, MainFrameNavigationFailed) {
+TEST_P(LoadingPredictorTabHelperTest, MainFrameNavigationFailed) {
   GURL url("http://test.org");
   auto navigation =
       content::NavigationSimulator::CreateRendererInitiated(url, main_rfh());
@@ -234,7 +247,7 @@
 }
 
 // Tests that a same document navigation is not recorded.
-TEST_F(LoadingPredictorTabHelperTest, MainFrameNavigationSameDocument) {
+TEST_P(LoadingPredictorTabHelperTest, MainFrameNavigationSameDocument) {
   NavigateAndCommitInMainFrameAndVerifyMetrics("http://test.org");
 
   // Same document navigation shouldn't be recorded.
@@ -245,7 +258,7 @@
 
 // Tests that document on load completed is recorded with correct navigation
 // id.
-TEST_F(LoadingPredictorTabHelperTest, DocumentOnLoadCompleted) {
+TEST_P(LoadingPredictorTabHelperTest, DocumentOnLoadCompleted) {
   NavigateAndCommitInMainFrameAndVerifyMetrics("http://test.org");
 
   // Adding subframe navigation to ensure that the committed main frame url will
@@ -259,7 +272,7 @@
 }
 
 // Tests that a resource load is correctly recorded.
-TEST_F(LoadingPredictorTabHelperTest, ResourceLoadComplete) {
+TEST_P(LoadingPredictorTabHelperTest, ResourceLoadComplete) {
   NavigateAndCommitInMainFrameAndVerifyMetrics("http://test.org");
 
   auto resource_load_info = CreateResourceLoadInfo(
@@ -271,7 +284,7 @@
 }
 
 // Tests that a resource loaded in a subframe is not recorded.
-TEST_F(LoadingPredictorTabHelperTest, ResourceLoadCompleteInSubFrame) {
+TEST_P(LoadingPredictorTabHelperTest, ResourceLoadCompleteInSubFrame) {
   NavigateAndCommitInMainFrameAndVerifyMetrics("http://test.org");
 
   auto* subframe =
@@ -288,7 +301,7 @@
 }
 
 // Tests that a resource load from the memory cache is correctly recorded.
-TEST_F(LoadingPredictorTabHelperTest, LoadResourceFromMemoryCache) {
+TEST_P(LoadingPredictorTabHelperTest, LoadResourceFromMemoryCache) {
   NavigateAndCommitInMainFrameAndVerifyMetrics("http://test.org");
 
   auto resource_load_info = CreateResourceLoadInfo(
@@ -303,6 +316,10 @@
       network::mojom::RequestDestination::kScript);
 }
 
+INSTANTIATE_TEST_SUITE_P(LCPPPrefetchSubresourceAsyncFlag,
+                         LoadingPredictorTabHelperTest,
+                         testing::Bool());
+
 class LoadingPredictorTabHelperOptimizationGuideDeciderTest
     : public LoadingPredictorTabHelperTest {
  public:
@@ -328,7 +345,7 @@
 
 // Tests that document on load completed is recorded with correct navigation
 // id and that optimization guide is not consulted when from same-origin.
-TEST_F(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
+TEST_P(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
        DocumentOnLoadCompletedOptimizationGuideSameOrigin) {
   NavigateAndCommitInMainFrameAndVerifyMetrics("http://test.org");
 
@@ -350,7 +367,7 @@
 }
 
 // Tests that document on load completed is recorded.
-TEST_F(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
+TEST_P(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
        DocumentOnLoadCompletedOptimizationGuide) {
   base::HistogramTester histogram_tester;
 
@@ -379,7 +396,7 @@
 
 // Tests that page destruction is recorded with the correct navigation id and
 // optimization guide prediction.
-TEST_F(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
+TEST_P(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
        PageDestroyedOptimizationGuide) {
   base::HistogramTester histogram_tester;
 
@@ -422,7 +439,7 @@
 
 // Tests that predictions are recorded correctly when they come after the
 // navigation commits.
-TEST_F(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
+TEST_P(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
        PageDestroyedOptimizationGuidePredictionComesAfterCommit) {
   base::HistogramTester histogram_tester;
 
@@ -472,7 +489,7 @@
 
 // Tests that predictions are recorded correctly when they arrive after a
 // redirect.
-TEST_F(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
+TEST_P(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
        PageDestroyedOptimizationGuidePredictionArrivedAfterRedirect) {
   base::HistogramTester histogram_tester;
 
@@ -541,7 +558,7 @@
 
 // Tests that page destruction is recorded with correct navigation id and
 // optimization guide prediction when the prediction has not arrived.
-TEST_F(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
+TEST_P(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
        PageDestroyedOptimizationGuidePredictionHasNotArrived) {
   base::HistogramTester histogram_tester;
 
@@ -566,7 +583,7 @@
 // Tests that page destroyed is recorded with correct navigation id and
 // optimization guide prediction and does not crash if callback comes after
 // everything has been recorded.
-TEST_F(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
+TEST_P(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
        PageDestroyedOptimizationGuidePredictionComesAfterPageDestroyed) {
   base::HistogramTester histogram_tester;
 
@@ -615,7 +632,7 @@
 
 // Tests that page destruction is recorded with correct navigation and
 // optimization guide prediction with no prediction..
-TEST_F(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
+TEST_P(LoadingPredictorTabHelperOptimizationGuideDeciderTest,
        PageDestroyedOptimizationGuidePredictionArrivedNoPrediction) {
   base::HistogramTester histogram_tester;
 
@@ -648,7 +665,7 @@
 
 // Tests that page destruction is recorded with correct navigation id and
 // optimization guide prediction with no prediction..
-TEST_F(
+TEST_P(
     LoadingPredictorTabHelperOptimizationGuideDeciderTest,
     PageDestroyedOptimizationGuidePredictionArrivedNoLoadingPredictorMetadata) {
   base::HistogramTester histogram_tester;
@@ -683,6 +700,10 @@
               RecordPageDestroyed(_, optimization_guide_prediction));
 }
 
+INSTANTIATE_TEST_SUITE_P(LCPPPrefetchSubresourceAsyncFlag,
+                         LoadingPredictorTabHelperOptimizationGuideDeciderTest,
+                         testing::Bool());
+
 class LoadingPredictorTabHelperOptimizationGuideDeciderWithPrefetchTest
     : public LoadingPredictorTabHelperOptimizationGuideDeciderTest {
  public:
@@ -703,7 +724,7 @@
 
 // Tests that page destruction is recorded with correct navigation id and
 // optimization guide prediction.
-TEST_F(LoadingPredictorTabHelperOptimizationGuideDeciderWithPrefetchTest,
+TEST_P(LoadingPredictorTabHelperOptimizationGuideDeciderWithPrefetchTest,
        PageDestroyedOptimizationGuide) {
   base::HistogramTester histogram_tester;
 
@@ -757,6 +778,11 @@
   EXPECT_CALL(*mock_collector_, RecordPageDestroyed(_, prediction));
 }
 
+INSTANTIATE_TEST_SUITE_P(
+    LCPPPrefetchSubresourceAsyncFlag,
+    LoadingPredictorTabHelperOptimizationGuideDeciderWithPrefetchTest,
+    testing::Bool());
+
 class TestLoadingDataCollector : public LoadingDataCollector {
  public:
   explicit TestLoadingDataCollector(const LoadingPredictorConfig& config);
@@ -826,7 +852,7 @@
 }
 
 // Tests that a resource load is correctly recorded with the correct priority.
-TEST_F(LoadingPredictorTabHelperTestCollectorTest, ResourceLoadComplete) {
+TEST_P(LoadingPredictorTabHelperTestCollectorTest, ResourceLoadComplete) {
   NavigateAndCommitInFrame("http://test.org", main_rfh());
 
   // Set expected priority to HIGHEST and load a HIGHEST priority resource.
@@ -846,6 +872,10 @@
   EXPECT_EQ(2u, test_collector_->count_resource_loads_completed());
 }
 
+INSTANTIATE_TEST_SUITE_P(LCPPPrefetchSubresourceAsyncFlag,
+                         LoadingPredictorTabHelperTestCollectorTest,
+                         testing::Bool());
+
 class LoadingPredictorTabHelperTestCollectorFencedFramesTest
     : public LoadingPredictorTabHelperTestCollectorTest {
  public:
@@ -859,7 +889,7 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-TEST_F(LoadingPredictorTabHelperTestCollectorFencedFramesTest,
+TEST_P(LoadingPredictorTabHelperTestCollectorFencedFramesTest,
        DoNotRecordResourceLoadComplete) {
   NavigateAndCommitInFrame("http://test.org", main_rfh());
   content::RenderFrameHost* fenced_frame_root =
@@ -888,4 +918,8 @@
   EXPECT_EQ(1u, test_collector_->count_resource_loads_completed());
 }
 
+INSTANTIATE_TEST_SUITE_P(LCPPPrefetchSubresourceAsyncFlag,
+                         LoadingPredictorTabHelperTestCollectorFencedFramesTest,
+                         testing::Bool());
+
 }  // namespace predictors
diff --git a/chrome/browser/predictors/loading_predictor_unittest.cc b/chrome/browser/predictors/loading_predictor_unittest.cc
index 846ba4aa..2421b52 100644
--- a/chrome/browser/predictors/loading_predictor_unittest.cc
+++ b/chrome/browser/predictors/loading_predictor_unittest.cc
@@ -183,16 +183,12 @@
 
 TEST_F(LoadingPredictorTest, TestOnNavigationStarted) {
   // Should return true if there are predictions.
-  auto navigation_id = GetNextId();
-  EXPECT_TRUE(predictor_->OnNavigationStarted(
-      navigation_id, ukm::SourceId(), /*initiator_origin=*/std::nullopt,
-      GURL(kUrl), base::TimeTicks::Now()));
+  EXPECT_TRUE(predictor_->PrepareForPageLoad(
+      /*initiator_origin=*/std::nullopt, GURL(kUrl), HintOrigin::NAVIGATION));
 
   // Should return false since there are no predictions.
-  auto navigation_id2 = GetNextId();
-  EXPECT_FALSE(predictor_->OnNavigationStarted(
-      navigation_id2, ukm::SourceId(), /*initiator_origin=*/std::nullopt,
-      GURL(kUrl3), base::TimeTicks::Now()));
+  EXPECT_FALSE(predictor_->PrepareForPageLoad(
+      /*initiator_origin=*/std::nullopt, GURL(kUrl3), HintOrigin::NAVIGATION));
 }
 
 TEST_F(LoadingPredictorTest, TestMainFrameResponseCancelsHint) {
@@ -216,9 +212,10 @@
 
   auto navigation_id = GetNextId();
 
-  predictor_->OnNavigationStarted(navigation_id, ukm::SourceId(),
-                                  /*initiator_origin=*/std::nullopt, url,
+  predictor_->OnNavigationStarted(navigation_id, ukm::SourceId(), url,
                                   base::TimeTicks::Now());
+  predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, url,
+                                 HintOrigin::EXTERNAL);
   EXPECT_NE(active_navigations.find(navigation_id), active_navigations.end());
   EXPECT_FALSE(active_hints.empty());
   EXPECT_NE(active_urls_to_navigations.find(url),
@@ -230,9 +227,10 @@
   EXPECT_TRUE(active_urls_to_navigations.empty());
 
   // With redirects.
-  predictor_->OnNavigationStarted(navigation_id, ukm::SourceId(),
-                                  /*initiator_origin=*/std::nullopt, url,
+  predictor_->OnNavigationStarted(navigation_id, ukm::SourceId(), url,
                                   base::TimeTicks::Now());
+  predictor_->PrepareForPageLoad(/*initiator_origin=*/std::nullopt, url,
+                                 HintOrigin::EXTERNAL);
   EXPECT_NE(active_navigations.find(navigation_id), active_navigations.end());
   EXPECT_FALSE(active_hints.empty());
   EXPECT_NE(active_urls_to_navigations.find(url),
@@ -263,7 +261,6 @@
   auto navigation_id = GetNextId();
 
   predictor_->OnNavigationStarted(navigation_id, ukm::SourceId(),
-                                  /*initiator_origin=*/std::nullopt,
                                   GURL(url.spec()), base::TimeTicks::Now());
   EXPECT_NE(active_navigations.find(navigation_id), active_navigations.end());
   it = active_hints.find(url);
diff --git a/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragment.java b/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragment.java
index 6ec70e5..80ffcbd 100644
--- a/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragment.java
+++ b/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragment.java
@@ -163,9 +163,14 @@
     }
 
     @Override
+    public void onStart() {
+        super.onStart();
+        updateButtonVisibility();
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
-        updateButtonVisibility();
         mHandleBackPressChangedSupplier.set(shouldHandleBackPress());
     }
 
@@ -312,4 +317,9 @@
     public void setProfile(Profile profile) {
         mProfile = profile;
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/browser/resources/ash/settings/OWNERS b/chrome/browser/resources/ash/settings/OWNERS
index 6866620..c0978e5 100644
--- a/chrome/browser/resources/ash/settings/OWNERS
+++ b/chrome/browser/resources/ash/settings/OWNERS
@@ -13,6 +13,7 @@
 # user so that our GWSQ is prioritized instead.
 wesokuhara@google.com #{LAST_RESORT_SUGGESTION}
 xiaohuic@chromium.org #{LAST_RESORT_SUGGESTION}
+moteva@google.com #{LAST_RESORT_SUGGESTION}
 
 # Subdir OWNERS can approve icon changes related to their subdir.
 per-file os_settings_icons.html=file://chrome/browser/resources/ash/settings/crostini_page/OWNERS
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/accessibility_common_loader.ts b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/accessibility_common_loader.ts
index 0b661cb0..36e927b95 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/accessibility_common_loader.ts
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/mv3/accessibility_common_loader.ts
@@ -5,7 +5,7 @@
 import '/common/testing/test_import_manager.js';
 
 import {Flags} from '/common/flags.js';
-import {InstanceChecker} from '/common/mv2/instance_checker.js';
+import {InstanceChecker} from '/common/mv3/instance_checker.js';
 import {TestImportManager} from '/common/testing/test_import_manager.js';
 
 import {Autoclick} from './autoclick/autoclick.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common_manifest.json.jinja2 b/chrome/browser/resources/chromeos/accessibility/accessibility_common_manifest.json.jinja2
index 44a86f5..37b946a 100644
--- a/chrome/browser/resources/chromeos/accessibility/accessibility_common_manifest.json.jinja2
+++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common_manifest.json.jinja2
@@ -6,7 +6,7 @@
   "manifest_version": 3,
   "minimum_chrome_version": "93",
   "background": {
-    "service_worker": "accessibility_common/mv3/accessibility_common_loader.ts",
+    "service_worker": "accessibility_common/mv3/accessibility_common_loader.js",
     "type": "module"
   },
   "content_security_policy": {
diff --git a/chrome/browser/resources/omnibox/omnibox_output_column_widths.css b/chrome/browser/resources/omnibox/omnibox_output_column_widths.css
index beff9fa..49de272 100644
--- a/chrome/browser/resources/omnibox/omnibox_output_column_widths.css
+++ b/chrome/browser/resources/omnibox/omnibox_output_column_widths.css
@@ -9,9 +9,11 @@
 .header-relevance {
   width: 100%;
 }
+
 .header-image-and-icon {
   width: 85px;
 }
+
 .header-contents-and-description {
   width: 480%;
 }
diff --git a/chrome/browser/resources/side_panel/customize_chrome/app.html.ts b/chrome/browser/resources/side_panel/customize_chrome/app.html.ts
index 6504139..b3019bb 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/app.html.ts
+++ b/chrome/browser/resources/side_panel/customize_chrome/app.html.ts
@@ -33,14 +33,14 @@
       </sp-heading>
       <cr-icon icon="cr:chevron-right" slot="suffix-icon"></cr-icon>
     </cr-button>
-     ${this.isSourceTabFirstPartyNtp_() ? html`<hr class="sp-cards-separator">
+     ${this.isSourceTabFirstPartyNtp_ ? html`<hr class="sp-cards-separator">
     <div id="shortcuts" class="section sp-card">
       <sp-heading hide-back-button>
         <h2 slot="heading">$i18n{shortcutsHeader}</h2>
       </sp-heading>
       <customize-chrome-shortcuts></customize-chrome-shortcuts>
     </div>`: ''}
-    ${(this.modulesEnabled_ && this.isSourceTabFirstPartyNtp_()) ? html`
+    ${(this.modulesEnabled_ && this.isSourceTabFirstPartyNtp_) ? html`
       <hr class="sp-cards-separator">
       <div id="modules" class="section sp-card">
         <sp-heading hide-back-button>
@@ -77,7 +77,7 @@
         </div>
       </div>
     ` : ''}
-    ${(this.footerEnabled_ && this.isSourceTabExtension_()) ? html`
+    ${this.footerEnabled_ ? html`
       <hr class="sp-cards-separator">
       <div id="footer" class="section sp-card">
         <sp-heading hide-back-button>
diff --git a/chrome/browser/resources/side_panel/customize_chrome/app.ts b/chrome/browser/resources/side_panel/customize_chrome/app.ts
index 4fbb739a..ea99b2d 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/app.ts
+++ b/chrome/browser/resources/side_panel/customize_chrome/app.ts
@@ -29,7 +29,7 @@
 import type {CategoriesElement} from './categories.js';
 import {CustomizeChromeImpression, recordCustomizeChromeImpression} from './common.js';
 import type {BackgroundCollection, CustomizeChromePageHandlerInterface} from './customize_chrome.mojom-webui.js';
-import {ChromeWebStoreCategory, ChromeWebStoreCollection, CustomizeChromeSection, NewTabPageType} from './customize_chrome.mojom-webui.js';
+import {ChromeWebStoreCategory, ChromeWebStoreCollection, CustomizeChromeSection} from './customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from './customize_chrome_api_proxy.js';
 import type {ThemesElement} from './themes.js';
 
@@ -82,7 +82,7 @@
       extensionsCardEnabled_: {type: Boolean},
       footerEnabled_: {type: Boolean},
       wallpaperSearchEnabled_: {type: Boolean},
-      newTabPageType_: {type: NewTabPageType},
+      isSourceTabFirstPartyNtp_: {type: Boolean},
       showEditTheme_: {type: Boolean},
     };
   }
@@ -106,8 +106,7 @@
       loadTimeData.getBoolean('footerEnabled');
   protected accessor wallpaperSearchEnabled_: boolean =
       loadTimeData.getBoolean('wallpaperSearchEnabled');
-  protected accessor newTabPageType_: NewTabPageType =
-      NewTabPageType.kFirstPartyWebUI;
+  protected accessor isSourceTabFirstPartyNtp_: boolean = true;
   protected accessor showEditTheme_: boolean = true;
   private scrollToSectionListenerId_: number|null = null;
   private attachedTabStateUpdatedId_: number|null = null;
@@ -143,16 +142,17 @@
     this.attachedTabStateUpdatedId_ =
         CustomizeChromeApiProxy.getInstance()
             .callbackRouter.attachedTabStateUpdated.addListener(
-                (newTabPageType: NewTabPageType) => {
-                  if (this.newTabPageType_ === newTabPageType) {
+                (isSourceTabFirstPartyNtp: boolean) => {
+                  if (this.isSourceTabFirstPartyNtp_ ===
+                      isSourceTabFirstPartyNtp) {
                     return;
                   }
 
-                  this.newTabPageType_ = newTabPageType;
+                  this.isSourceTabFirstPartyNtp_ = isSourceTabFirstPartyNtp;
 
                   // Since some pages aren't supported in non first party mode,
                   // change the section back to the overview.
-                  if (!this.isSourceTabFirstPartyNtp_() &&
+                  if (!this.isSourceTabFirstPartyNtp_ &&
                       !this.pageSupportedOnNonFirstPartyNtps()) {
                     this.page_ = CustomizeChromePage.OVERVIEW;
                   }
@@ -207,14 +207,6 @@
         this.setThemeEditableId_);
   }
 
-  protected isSourceTabFirstPartyNtp_(): boolean {
-    return this.newTabPageType_ === NewTabPageType.kFirstPartyWebUI;
-  }
-
-  protected isSourceTabExtension_(): boolean {
-    return this.newTabPageType_ === NewTabPageType.kExtension;
-  }
-
   protected async onBackClick_() {
     switch (this.page_) {
       case CustomizeChromePage.CATEGORIES:
diff --git a/chrome/browser/screen_ai/optical_character_recognizer_browsertest.cc b/chrome/browser/screen_ai/optical_character_recognizer_browsertest.cc
index 3991abe05..7b0bf949 100644
--- a/chrome/browser/screen_ai/optical_character_recognizer_browsertest.cc
+++ b/chrome/browser/screen_ai/optical_character_recognizer_browsertest.cc
@@ -38,6 +38,10 @@
 
 namespace {
 
+// LINT.IfChange(kServiceIdleCheckingDelay)
+constexpr base::TimeDelta kServiceIdleCheckingDelay = base::Seconds(3);
+// LINT.ThenChange(//services/screen_ai/screen_ai_service_impl.cc:kIdleCheckingDelay)
+
 using ::testing::ElementsAre;
 using ::testing::Field;
 using ::testing::IsEmpty;
@@ -88,14 +92,13 @@
     std::move(callback).Run();
     return;
   }
-  router->ShutDownIfNoClientsForTesting();
 
   // Wait more...
   base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&WaitForDisconnecting, router, std::move(callback),
                      remaining_tries - 1),
-      base::Milliseconds(200));
+      kServiceIdleCheckingDelay);
 }
 
 // bool: PDF OCR service enabled.
@@ -497,9 +500,9 @@
     ASSERT_TRUE(router->IsProcessRunningForTesting());
   }
 
-  // Trigger service shut down and wait for disconnecting.
+  // Wait for shut down due to being idle.
   base::test::TestFuture<void> future;
-  WaitForDisconnecting(router, future.GetCallback(), /*remaining_tries=*/10);
+  WaitForDisconnecting(router, future.GetCallback(), /*remaining_tries=*/2);
   ASSERT_TRUE(future.Wait());
   ASSERT_FALSE(router->IsProcessRunningForTesting());
 
diff --git a/chrome/browser/screen_ai/screen_ai_service_router.h b/chrome/browser/screen_ai/screen_ai_service_router.h
index 04f7ab1..90dd422 100644
--- a/chrome/browser/screen_ai/screen_ai_service_router.h
+++ b/chrome/browser/screen_ai/screen_ai_service_router.h
@@ -69,12 +69,6 @@
   // Returns true if sandboxed process is running.
   bool IsProcessRunningForTesting();
 
-  void ShutDownIfNoClientsForTesting() {
-    if (screen_ai_service_factory_.is_bound()) {
-      screen_ai_service_factory_->ShutDownIfNoClients();
-    }
-  }
-
  private:
   friend class ScreenAIServiceRouterFactory;
   friend class ScreenAIServiceShutdownHandlerTest;
diff --git a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/TestStandaloneFragment.java b/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/TestStandaloneFragment.java
index 13effc6c..2a8574b 100644
--- a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/TestStandaloneFragment.java
+++ b/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/TestStandaloneFragment.java
@@ -53,4 +53,9 @@
     public ObservableSupplierImpl<Boolean> getHandleBackPressChangedSupplier() {
         return mBackPressStateSupplier;
     }
+
+    @Override
+    public @AnimationType int getAnimationType() {
+        return AnimationType.PROPERTY;
+    }
 }
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index c9201e6..bcb98c7 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -2305,6 +2305,10 @@
           desc="Text for the dialog showing a list of tabs for capture to ask user if tab audio should be shared.">
           Also share tab audio
         </message>
+        <message name="IDS_MEDIA_CAPTURE_PICKER_DIALOG_WINDOW_OR_SCREEN_TEXT"
+          desc="Text for the dialog showing a list of tabs for capture to ask user whether to share a window or screen instead of a tab.">
+          Share window or screen instead
+        </message>
       </if>
 
       <!-- Download open dialog -->
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MEDIA_CAPTURE_PICKER_DIALOG_WINDOW_OR_SCREEN_TEXT.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MEDIA_CAPTURE_PICKER_DIALOG_WINDOW_OR_SCREEN_TEXT.png.sha1
new file mode 100644
index 0000000..8397a9e
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MEDIA_CAPTURE_PICKER_DIALOG_WINDOW_OR_SCREEN_TEXT.png.sha1
@@ -0,0 +1 @@
+fd9be039a93799c2ba95db39bc3fe69ee77a76cf
\ No newline at end of file
diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn
index 2bfb4248..0ef0e9d 100644
--- a/chrome/browser/ui/android/toolbar/BUILD.gn
+++ b/chrome/browser/ui/android/toolbar/BUILD.gn
@@ -60,6 +60,7 @@
     "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java",
     "java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewResourceFrameLayout.java",
     "java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewSceneLayer.java",
+    "java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionToolbarManager.java",
     "java/src/org/chromium/chrome/browser/toolbar/home_button/HomeButton.java",
     "java/src/org/chromium/chrome/browser/toolbar/home_button/HomeButtonCoordinator.java",
     "java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressCoordinator.java",
@@ -127,6 +128,7 @@
   deps = [
     ":java_resources",
     "//base:base_java",
+    "//base:service_loader_java",
     "//build/android:build_java",
     "//cc:cc_java",
     "//chrome/android/features/tab_ui/public:java",
@@ -316,6 +318,7 @@
     "java/res/layout/address_bar_preference.xml",
     "java/res/layout/bottom_control_container.xml",
     "java/res/layout/control_container.xml",
+    "java/res/layout/extension_toolbar_container.xml",
     "java/res/layout/menu_button.xml",
     "java/res/layout/navigation_popup_item.xml",
     "java/res/layout/optional_button_layout.xml",
diff --git a/chrome/browser/ui/android/toolbar/java/res/layout/extension_toolbar_container.xml b/chrome/browser/ui/android/toolbar/java/res/layout/extension_toolbar_container.xml
new file mode 100644
index 0000000..7a7aea82
--- /dev/null
+++ b/chrome/browser/ui/android/toolbar/java/res/layout/extension_toolbar_container.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+
+<!-- TODO(crbug.com/385984462): Replace this with a real implementation. -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent" />
diff --git a/chrome/browser/ui/android/toolbar/java/res/layout/toolbar_tablet.xml b/chrome/browser/ui/android/toolbar/java/res/layout/toolbar_tablet.xml
index 04125b9..633a77eae 100644
--- a/chrome/browser/ui/android/toolbar/java/res/layout/toolbar_tablet.xml
+++ b/chrome/browser/ui/android/toolbar/java/res/layout/toolbar_tablet.xml
@@ -98,6 +98,13 @@
             app:menuMaxWidth="@dimen/tab_switcher_menu_width"
             app:menuVerticalOverlapAnchor="false" />
 
+        <ViewStub
+            android:id="@+id/extension_toolbar_container_stub"
+            android:inflatedId="@+id/extension_toolbar_container"
+            android:layout="@layout/extension_toolbar_container"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent" />
+
         <include layout="@layout/menu_button"/>
     </LinearLayout>
 </org.chromium.chrome.browser.toolbar.top.ToolbarTablet>
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionToolbarManager.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionToolbarManager.java
new file mode 100644
index 0000000..5b4915a
--- /dev/null
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionToolbarManager.java
@@ -0,0 +1,43 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.toolbar.extensions;
+
+import android.content.Context;
+import android.view.ViewStub;
+
+import org.chromium.base.ServiceLoaderUtil;
+import org.chromium.base.lifetime.Destroyable;
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.tab.Tab;
+
+/** Provides extension-related buttons for {@link ToolbarManager}. */
+@NullMarked
+public interface ExtensionToolbarManager extends Destroyable {
+    /** Initializes the manager. */
+    public void initialize(
+            Context context,
+            ViewStub extensionToolbarStub,
+            ObservableSupplier<Profile> profileSupplier,
+            ObservableSupplier<Tab> currentTabSupplier);
+
+    /** Instantiates the implementation of {@link ExtensionToolbarManager} if it is available. */
+    @Nullable
+    public static ExtensionToolbarManager maybeCreate(
+            Context context,
+            ViewStub extensionToolbarStub,
+            ObservableSupplier<Profile> profileSupplier,
+            ObservableSupplier<Tab> currentTabSupplier) {
+        ExtensionToolbarManager manager =
+                ServiceLoaderUtil.maybeCreate(ExtensionToolbarManager.class);
+        if (manager == null) {
+            return null;
+        }
+        manager.initialize(context, extensionToolbarStub, profileSupplier, currentTabSupplier);
+        return manager;
+    }
+}
diff --git a/chrome/browser/ui/browser_window_state.cc b/chrome/browser/ui/browser_window_state.cc
index ce77dbe..074dc56b 100644
--- a/chrome/browser/ui/browser_window_state.cc
+++ b/chrome/browser/ui/browser_window_state.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "chrome/browser/ui/browser_window_state.h"
 
diff --git a/chrome/browser/ui/certificate_dialogs.cc b/chrome/browser/ui/certificate_dialogs.cc
index 691bda4..9b418d2 100644
--- a/chrome/browser/ui/certificate_dialogs.cc
+++ b/chrome/browser/ui/certificate_dialogs.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "chrome/browser/ui/certificate_dialogs.h"
 
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index 30676b0..624d05fc 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
 
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model_states.cc b/chrome/browser/ui/content_settings/content_setting_image_model_states.cc
index 9c0a64f..443a2ea 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model_states.cc
+++ b/chrome/browser/ui/content_settings/content_setting_image_model_states.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "chrome/browser/ui/content_settings/content_setting_image_model_states.h"
 
diff --git a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
index 078ac7b..7c292b9 100644
--- a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
+++ b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "chrome/browser/ui/lens/lens_overlay_untrusted_ui.h"
 
diff --git a/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc b/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc
index 6323702..9bb7b6c1 100644
--- a/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc
+++ b/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc
@@ -4,10 +4,6 @@
 
 #include "chrome/browser/ui/lens/lens_side_panel_untrusted_ui.h"
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "base/strings/strcat.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc b/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc
index 591ec115..43a2a5e 100644
--- a/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc
+++ b/chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop.h"
 
diff --git a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
index dfa86b4..9214c67 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
@@ -1,11 +1,6 @@
 // Copyright 2012 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/models/list_selection_model.h"
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 
@@ -68,6 +63,7 @@
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/models/list_selection_model.h"
 
 using content::WebContents;
 
diff --git a/chrome/browser/ui/task_manager/task_manager_table_model.cc b/chrome/browser/ui/task_manager/task_manager_table_model.cc
index 311207e..d8caa1da 100644
--- a/chrome/browser/ui/task_manager/task_manager_table_model.cc
+++ b/chrome/browser/ui/task_manager/task_manager_table_model.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "chrome/browser/ui/task_manager/task_manager_table_model.h"
 
diff --git a/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc b/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc
index f4e590f..2d99db4 100644
--- a/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc
+++ b/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc
@@ -390,7 +390,8 @@
           ShowSignInAndSyncUi(profile);
           break;
         case collaboration::SyncStatus::kSyncWithoutTabGroup:
-          chrome::ShowSettingsSubPage(browser_, chrome::kSyncSetupSubPage);
+          chrome::ShowSettingsSubPage(browser_,
+                                      chrome::kSyncSetupAdvancedSubPage);
           break;
         case collaboration::SyncStatus::kSyncEnabled:
           NOTREACHED();
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_view_views_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_view_views_browsertest.cc
index ea9f3b2..c994f1d 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_view_views_browsertest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_view_views_browsertest.cc
@@ -30,6 +30,7 @@
 #include "chrome/test/base/search_test_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/omnibox/browser/actions/tab_switch_action.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_result.h"
 #include "components/omnibox/browser/fake_autocomplete_provider.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
@@ -646,7 +647,8 @@
       ax::mojom::IntAttribute::kPopupForId));
 
   // Check accessibility of list box while it's closed.
-  controller()->autocomplete_controller()->Stop(true);
+  controller()->autocomplete_controller()->Stop(
+      AutocompleteStopReason::kClobbered);
   popup_view()->UpdatePopupAppearance();
   popup_node_data_while_open = ui::AXNodeData();
   popup_view()->GetViewAccessibility().GetAccessibleNodeData(
@@ -892,7 +894,8 @@
 
   // Check accessibility when popup is closed.
   ax_node_data_omnibox = ui::AXNodeData();
-  controller()->autocomplete_controller()->Stop(true);
+  controller()->autocomplete_controller()->Stop(
+      AutocompleteStopReason::kClobbered);
   omnibox_view()->GetViewAccessibility().GetAccessibleNodeData(
       &ax_node_data_omnibox);
   EXPECT_FALSE(popup_view()->IsOpen());
@@ -924,7 +927,8 @@
 
   // Check accessibility when popup is closed.
   ax_node_data_omnibox = ui::AXNodeData();
-  controller()->autocomplete_controller()->Stop(true);
+  controller()->autocomplete_controller()->Stop(
+      AutocompleteStopReason::kClobbered);
   omnibox_view()->GetViewAccessibility().GetAccessibleNodeData(
       &ax_node_data_omnibox);
   EXPECT_FALSE(popup_view()->IsOpen());
diff --git a/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.cc b/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.cc
index d2d2759..5075ce8 100644
--- a/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.cc
+++ b/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.cc
@@ -15,7 +15,6 @@
 #include "extensions/browser/disable_reason.h"
 #include "extensions/browser/extension_registrar.h"
 #include "extensions/browser/extension_system.h"
-#include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 
 namespace customize_chrome {
@@ -56,19 +55,4 @@
                          {extensions::disable_reason::DISABLE_USER_ACTION});
 }
 
-bool IsExtensionNtp(const GURL& url, Profile* profile) {
-  if (!url.SchemeIs(extensions::kExtensionScheme)) {
-    return false;
-  }
-
-  const extensions::Extension* extension_managing_ntp =
-      extensions::GetExtensionOverridingNewTabPage(profile);
-
-  if (!extension_managing_ntp) {
-    return false;
-  }
-
-  return extension_managing_ntp->id() == url.host();
-}
-
 }  // namespace customize_chrome
diff --git a/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.h b/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.h
index 2dcdde48..fdb5725 100644
--- a/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.h
+++ b/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.h
@@ -5,9 +5,6 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_CUSTOMIZE_CHROME_CUSTOMIZE_CHROME_UTILS_H_
 #define CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_CUSTOMIZE_CHROME_CUSTOMIZE_CHROME_UTILS_H_
 
-#include "chrome/browser/profiles/profile.h"
-#include "url/gurl.h"
-
 namespace content {
 class BrowserContext;
 }
@@ -22,9 +19,6 @@
 void MaybeDisableExtensionOverridingNtp(
     content::BrowserContext* browser_context);
 
-// Returns whether `url` belongs to an extension NTP.
-bool IsExtensionNtp(const GURL& url, Profile* profile);
-
 }  // namespace customize_chrome
 
 #endif  // CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_CUSTOMIZE_CHROME_CUSTOMIZE_CHROME_UTILS_H_
diff --git a/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.cc b/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.cc
index d10e30d9..5254f69b 100644
--- a/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.cc
+++ b/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
-#include "chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_web_ui_view.h"
 #include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h"
@@ -98,11 +97,26 @@
 }
 
 bool SidePanelControllerViews::ShouldEnableEditTheme(const GURL& url) const {
-  Profile* const profile =
-      Profile::FromBrowserContext(tab_->GetContents()->GetBrowserContext());
   return NewTabPageUI::IsNewTabPageOrigin(url) ||
          (base::FeatureList::IsEnabled(ntp_features::kNtpFooter) &&
-          customize_chrome::IsExtensionNtp(url, profile));
+          IsExtensionNtp(url));
+}
+
+bool SidePanelControllerViews::IsExtensionNtp(const GURL& url) const {
+  if (!url.SchemeIs(extensions::kExtensionScheme)) {
+    return false;
+  }
+
+  Profile* const profile =
+      Profile::FromBrowserContext(tab_->GetContents()->GetBrowserContext());
+  const extensions::Extension* extension_managing_ntp =
+      extensions::GetExtensionOverridingNewTabPage(profile);
+
+  if (!extension_managing_ntp) {
+    return false;
+  }
+
+  return extension_managing_ntp->id() == url.host();
 }
 
 void SidePanelControllerViews::DidFinishNavigation(
@@ -120,7 +134,8 @@
   if (CanShowOnURL(url)) {
     CreateAndRegisterEntry();
     if (customize_chrome_ui_) {
-      customize_chrome_ui_->AttachedTabStateUpdated(url);
+      customize_chrome_ui_->AttachedTabStateUpdated(
+          NewTabPageUI::IsNewTabPageOrigin(url));
       customize_chrome_ui_->UpdateThemeEditable(ShouldEnableEditTheme(url));
     }
   } else {
@@ -226,7 +241,8 @@
     entry = tab_->GetContents()->GetController().GetVisibleEntry();
   }
   const GURL& url = entry->GetURL();
-  customize_chrome_ui_->AttachedTabStateUpdated(url);
+  customize_chrome_ui_->AttachedTabStateUpdated(
+      NewTabPageUI::IsNewTabPageOrigin(url));
   customize_chrome_ui_->UpdateThemeEditable(ShouldEnableEditTheme(url));
 
   return customize_chrome_web_view;
diff --git a/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.h b/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.h
index 9842ad05..7ec206b1 100644
--- a/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.h
+++ b/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.h
@@ -66,6 +66,9 @@
   // Returns true for 1P NTP or extension NTP, otherwise returns false.
   bool ShouldEnableEditTheme(const GURL& url) const;
 
+  // Helper function to check if the URL belongs to an extension NTP.
+  bool IsExtensionNtp(const GURL& url) const;
+
   // Generates the view for the SidePanel contents. This is the WebUI for the
   // SidePanel. Used by the SidepanelRegistry to create the view.
   std::unique_ptr<views::View> CreateCustomizeChromeWebView(
diff --git a/chrome/browser/ui/webui/cr_components/history/history_util.cc b/chrome/browser/ui/webui/cr_components/history/history_util.cc
index 17ad4a9..801d755 100644
--- a/chrome/browser/ui/webui/cr_components/history/history_util.cc
+++ b/chrome/browser/ui/webui/cr_components/history/history_util.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "chrome/browser/ui/webui/cr_components/history/history_util.h"
 
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom
index 8996d03..e23bca0 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom
@@ -111,17 +111,6 @@
   kShopping,
 };
 
-// Types of New Tabs that CustomizeChromePage can attach to.
-enum NewTabPageType {
-  kFirstPartyWebUI,
-  kThirdPartyWebUI,
-  kThirdPartyRemote,
-  kExtension,
-  kIncognito,
-  kGuestMode,
-  kNone,
-};
-
 // Used by the WebUI page to bootstrap bidirectional communication.
 interface CustomizeChromePageHandlerFactory {
   // The WebUI calls this method when the page is first initialized.
@@ -237,7 +226,7 @@
   // |CustomizeChromePageHandler.UpdateScrollToSection()|.
   ScrollToSection(CustomizeChromeSection section);
   // Sets Information about the tab that is attached to the CustomizeChromePage.
-  AttachedTabStateUpdated(NewTabPageType ntp_type);
+  AttachedTabStateUpdated(bool is_source_tab_first_party_ntp);
   // Sets the name of the system that manages the new tab page if there is one.
   // If not, empty string should be provided.
   NtpManagedByNameUpdated(string ntp_managed_by_name);
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_interactive_uitest.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_interactive_uitest.cc
index fa4acd6..13bc5ed 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_interactive_uitest.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_interactive_uitest.cc
@@ -5,7 +5,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
-#include "chrome/browser/extensions/install_verifier.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
@@ -68,12 +67,12 @@
 
     extensions::ChromeTestExtensionLoader extension_loader(profile);
     extension_loader.set_ignore_manifest_warnings(true);
-    const extensions::Extension* extension =
-        extension_loader.LoadExtension(extension_dir.Pack()).get();
-    ASSERT_TRUE(extension);
+    // TODO(temao) Not blocking the test, but note that LoadExtension()
+    // occasionally returns null.
+    extension_loader.LoadExtension(extension_dir.Pack()).get();
   }
 
-  void OpenNewTabPage() {
+  void OpenExtensionNewTabPage() {
     chrome::NewTab(browser());
     content::WebContents* web_contents =
         browser()->tab_strip_model()->GetActiveWebContents();
@@ -85,7 +84,6 @@
 
  protected:
   base::test::ScopedFeatureList scoped_feature_list_;
-  extensions::ScopedInstallVerifierBypassForTest install_verifier_bypass_;
 };
 }  // namespace
 
@@ -100,7 +98,8 @@
   InstallExtension(browser()->profile());
   RunTestSequence(
       // 2. Open extension new tab page.
-      Do(base::BindLambdaForTesting([&, this]() { OpenNewTabPage(); })),
+      Do(base::BindLambdaForTesting(
+          [&, this]() { OpenExtensionNewTabPage(); })),
       // 3. Open customize chrome side panel.
       OpenCustomizeChromeSidePanel(kLocalCustomizeChromeElementId),
       // 4. Check edit theme is enabled in customize chrome side panel.
@@ -109,38 +108,3 @@
             WaitForElementToRender(kLocalCustomizeChromeElementId,
                                    kEditThemeButton)));
 }
-
-IN_PROC_BROWSER_TEST_F(CustomizeChromeInteractiveTest,
-                       ShowsFooterSectionForExtensionNtp) {
-  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kLocalCustomizeChromeElementId);
-  const DeepQuery kFooterSection = {"customize-chrome-app", "#footer",
-                                    "customize-chrome-footer",
-                                    "#showToggleContainer"};
-  // 1. Load extension that overrides NTP.
-  InstallExtension(browser()->profile());
-  RunTestSequence(
-      // 2. Open extension new tab page.
-      Do(base::BindLambdaForTesting([&, this]() { OpenNewTabPage(); })),
-      // 3. Open customize chrome side panel.
-      OpenCustomizeChromeSidePanel(kLocalCustomizeChromeElementId),
-      // 4. Check that the footer section exists.
-      Steps(
-          WaitForElementExists(kLocalCustomizeChromeElementId, kFooterSection),
-          WaitForElementToRender(kLocalCustomizeChromeElementId,
-                                 kFooterSection)));
-}
-
-IN_PROC_BROWSER_TEST_F(CustomizeChromeInteractiveTest,
-                       FooterSectionNotShownForNonExtensionNtp) {
-  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kLocalCustomizeChromeElementId);
-  const DeepQuery kFooterSection = {"customize-chrome-app", "#footer",
-                                    "customize-chrome-footer",
-                                    "#showToggleContainer"};
-  RunTestSequence(
-      // 1. Open non-extension new tab page.
-      Do(base::BindLambdaForTesting([&, this]() { OpenNewTabPage(); })),
-      // 2. Open customize chrome side panel.
-      OpenCustomizeChromeSidePanel(kLocalCustomizeChromeElementId),
-      // 3. Check that the footer section does not exist.
-      EnsureNotPresent(kLocalCustomizeChromeElementId, kFooterSection));
-}
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
index 981d88d2..01fcf2f3 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
@@ -29,10 +29,7 @@
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/search/ntp_user_data_types.h"
 #include "chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.h"
-#include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h"
 #include "chrome/browser/ui/webui/new_tab_page/ntp_pref_names.h"
-#include "chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.h"
-#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
 #include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_section.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
@@ -186,9 +183,10 @@
   page_->ScrollToSection(mojo_section);
 }
 
-void CustomizeChromePageHandler::AttachedTabStateUpdated(const GURL& url) {
-  last_source_url_ = url;
-  page_->AttachedTabStateUpdated(GetNewTabPageType(url));
+void CustomizeChromePageHandler::AttachedTabStateUpdated(
+    bool is_source_tab_first_party_ntp) {
+  last_is_source_tab_first_party_ntp_ = is_source_tab_first_party_ntp;
+  page_->AttachedTabStateUpdated(is_source_tab_first_party_ntp);
 }
 
 bool CustomizeChromePageHandler::IsNtpManagedByThirdPartySearchEngine() const {
@@ -541,7 +539,7 @@
 }
 
 void CustomizeChromePageHandler::UpdateAttachedTabState() {
-  AttachedTabStateUpdated(last_source_url_);
+  AttachedTabStateUpdated(last_is_source_tab_first_party_ntp_);
 }
 
 void CustomizeChromePageHandler::UpdateNtpManagedByName() {
@@ -728,22 +726,3 @@
   LogEvent(NTP_BACKGROUND_UPLOAD_CANCEL);
   std::move(choose_local_custom_background_callback_).Run(false);
 }
-
-side_panel::mojom::NewTabPageType CustomizeChromePageHandler::GetNewTabPageType(
-    const GURL& url) {
-  if (NewTabPageUI::IsNewTabPageOrigin(url)) {
-    return side_panel::mojom::NewTabPageType::kFirstPartyWebUI;
-  } else if (customize_chrome::IsExtensionNtp(url, profile_)) {
-    return side_panel::mojom::NewTabPageType::kExtension;
-  } else if (NewTabPageThirdPartyUI::IsNewTabPageOrigin(url)) {
-    return side_panel::mojom::NewTabPageType::kThirdPartyWebUI;
-  } else if (IsNtpManagedByThirdPartySearchEngine()) {
-    return side_panel::mojom::NewTabPageType::kThirdPartyRemote;
-  } else if (NewTabUI::IsNewTab(url)) {
-    return profile_->IsGuestSession()
-               ? side_panel::mojom::NewTabPageType::kGuestMode
-               : side_panel::mojom::NewTabPageType::kIncognito;
-  }
-
-  return side_panel::mojom::NewTabPageType::kNone;
-}
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h
index 460c8dd..a128141 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h
@@ -21,7 +21,6 @@
 #include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom.h"
 #include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_section.h"
 #include "chrome/common/search/ntp_logging_events.h"
-#include "chrome/common/webui_url_constants.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/search_engines/template_url_service_observer.h"
 #include "components/themes/ntp_background_service.h"
@@ -91,7 +90,7 @@
   void ScrollToSection(CustomizeChromeSection section);
 
   // Passes AttachedTabStateUpdated calls to the CustomizeChromePage.
-  void AttachedTabStateUpdated(const GURL& url);
+  void AttachedTabStateUpdated(bool is_source_tab_first_party_ntp);
 
   // Helper method to determine if the search engine is overriding the first
   // party NTP.
@@ -146,9 +145,6 @@
 
   std::u16string GetManagingThirdPartyName() const;
 
-  // Returns the type of New Tab Page the SidePanel is attached to.
-  side_panel::mojom::NewTabPageType GetNewTabPageType(const GURL& url);
-
   // ui::NativeThemeObserver:
   void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
 
@@ -194,7 +190,7 @@
 
   // Caches the attached tab state provided to the handler, in cases where the
   // value needs to be requeried by the page.
-  GURL last_source_url_{GURL(chrome::kChromeUINewTabPageURL)};
+  bool last_is_source_tab_first_party_ntp_ = true;
 
   PrefChangeRegistrar pref_change_registrar_;
   base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver>
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
index f0b26493..43e2f3b 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
@@ -169,9 +169,7 @@
   MOCK_METHOD(void,
               ScrollToSection,
               (side_panel::mojom::CustomizeChromeSection));
-  MOCK_METHOD(void,
-              AttachedTabStateUpdated,
-              (side_panel::mojom::NewTabPageType));
+  MOCK_METHOD(void, AttachedTabStateUpdated, (bool));
   MOCK_METHOD(void, NtpManagedByNameUpdated, (const std::string&));
   MOCK_METHOD(void, SetFooterSettings, (bool visible));
 
@@ -812,38 +810,18 @@
   EXPECT_EQ(side_panel::mojom::CustomizeChromeSection::kAppearance, section);
 }
 
-// Ensures that url's are correctly mapped to their NewTabPage type.
-// Does not include test for `side_panel::mojom::NewTabPageType::kExtension`
-// See `CustomizeChromeInteractiveTest.FooterSectionForExtensionNtp` for
-// confirming Customize Chrome shows the footer section for Extension NTPs.
 TEST_F(CustomizeChromePageHandlerTest, AttachedTabStateUpdated) {
-  std::vector<std::pair<side_panel::mojom::NewTabPageType, GURL>>
-      ntp_types_and_urls = {
-          {side_panel::mojom::NewTabPageType::kNone,
-           GURL("https://www.google.com/")},
-          {side_panel::mojom::NewTabPageType::kFirstPartyWebUI,
-           GURL(chrome::kChromeUINewTabPageURL)},
-          {side_panel::mojom::NewTabPageType::kThirdPartyWebUI,
-           GURL(chrome::kChromeUINewTabPageThirdPartyURL)},
-          {side_panel::mojom::NewTabPageType::kIncognito,
-           GURL(chrome::kChromeUINewTabURL)},
-          {side_panel::mojom::NewTabPageType::kGuestMode,
-           GURL(chrome::kChromeUINewTabURL)}};
+  bool kIsSourceTabFirstPartyNtpValue = false;
 
-  for (const auto& ntp_type_and_url : ntp_types_and_urls) {
-    if (ntp_type_and_url.first ==
-        side_panel::mojom::NewTabPageType::kGuestMode) {
-      profile().SetGuestSession(true);
-    }
+  bool isSourceTabFirstPartyNtp;
+  EXPECT_CALL(mock_page_, AttachedTabStateUpdated)
+      .Times(1)
+      .WillOnce(SaveArg<0>(&isSourceTabFirstPartyNtp));
 
-    side_panel::mojom::NewTabPageType source_tab;
-    EXPECT_CALL(mock_page_, AttachedTabStateUpdated)
-        .Times(1)
-        .WillOnce(SaveArg<0>(&source_tab));
-    handler().AttachedTabStateUpdated(ntp_type_and_url.second);
-    mock_page_.FlushForTesting();
-    EXPECT_EQ(ntp_type_and_url.first, source_tab);
-  }
+  handler().AttachedTabStateUpdated(kIsSourceTabFirstPartyNtpValue);
+  mock_page_.FlushForTesting();
+
+  EXPECT_EQ(kIsSourceTabFirstPartyNtpValue, isSourceTabFirstPartyNtp);
 }
 
 TEST_F(CustomizeChromePageHandlerTest, ScrollToUnspecifiedSection) {
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc
index 05502f4..1c9e0c1 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc
@@ -305,11 +305,13 @@
   }
 }
 
-void CustomizeChromeUI::AttachedTabStateUpdated(const GURL& url) {
+void CustomizeChromeUI::AttachedTabStateUpdated(
+    bool is_source_tab_first_party_ntp) {
   if (customize_chrome_page_handler_) {
-    customize_chrome_page_handler_->AttachedTabStateUpdated(url);
+    customize_chrome_page_handler_->AttachedTabStateUpdated(
+        is_source_tab_first_party_ntp);
   } else {
-    source_tab_url_ = url;
+    is_source_tab_first_party_ntp_ = is_source_tab_first_party_ntp;
   }
 }
 
@@ -407,8 +409,10 @@
     customize_chrome_page_handler_->ScrollToSection(*section_);
     section_.reset();
   }
-  if (!source_tab_url_.is_empty()) {
-    customize_chrome_page_handler_->AttachedTabStateUpdated(source_tab_url_);
+  if (is_source_tab_first_party_ntp_.has_value()) {
+    customize_chrome_page_handler_->AttachedTabStateUpdated(
+        is_source_tab_first_party_ntp_.value());
+    is_source_tab_first_party_ntp_.reset();
   }
   if (is_theme_editable_.has_value()) {
     customize_chrome_page_handler_->UpdateThemeEditable(
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h
index 33bc34fe..8445464 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h
@@ -83,7 +83,7 @@
   void ScrollToSection(CustomizeChromeSection section);
 
   // Passthrough that calls the CustomizeChromePage's AttachedTabStateUpdated.
-  void AttachedTabStateUpdated(const GURL& url);
+  void AttachedTabStateUpdated(bool is_attached_tab_first_party_ntp);
 
   // Passthrough that calls to CustomizeChromePage's UpdateThemeEditable.
   void UpdateThemeEditable(bool is_theme_editable);
@@ -187,7 +187,7 @@
   // Caches a request to scroll to a section in case the request happens before
   // the front-end is ready to receive the request.
   std::optional<CustomizeChromeSection> section_;
-  GURL source_tab_url_;
+  std::optional<bool> is_source_tab_first_party_ntp_;
   std::optional<bool> is_theme_editable_;
 
   std::unique_ptr<user_education::HelpBubbleHandler> help_bubble_handler_;
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 2121e42..7a3178a5 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1746044609-24b97d12ffd181c7270a89ff64582030f8202794-15f70a334873425aa887d4cdb216796f676c015a.profdata
+chrome-android64-main-1746073150-e92a3ea778a2815127221f459aba3e74187a47c6-be45dde8005e8554979ffe8c2068f7f97f9536b6.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 82e6ec5..e389d034 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1746013960-7f7f0468d166b5e0c336ff95294ca554bc904703-85c17e9abc4ed443ef6794a715b5e9430d7ab21c.profdata
+chrome-linux-main-1746057556-0fdc03a59cb9b581cdcbda430bb7355493197536-388545bb627f929d012e37faf58450a138d69a77.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index c5b90f4..a3e1b2fa 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1746050330-29bcdbb49f3e79fe3a8f9df22753151932f89e38-f56a6acf56996a56ef31a581d16665e62070b42d.profdata
+chrome-mac-arm-main-1746071557-3183843248cde74d232190341032509f8140f87a-0eebbc236620fd680660c61b3a2febfaa7267c3b.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 667b77d..ecf607e 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1746035971-4213c9308da5133e6bb3567981d16a60b087804b-b23738f45a2bc90b4fca6b0754754deaa3612f40.profdata
+chrome-win-arm64-main-1746057556-111f17ef8fee0aa54dc18d6c9f3717d66fcfd919-388545bb627f929d012e37faf58450a138d69a77.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 091f2aa..3b3c8a8 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1746025195-882f8772ccd05493ec80fcf6548955fc1f9bb170-ab50c042d650061c65e5a9bceae223f7647b0df9.profdata
+chrome-win32-main-1746035971-e8c9b3ee9a88f2d39d106757119693bc42b9ff66-b23738f45a2bc90b4fca6b0754754deaa3612f40.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index d336a639..1befe76 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1746013960-ece2f36b9cef2b73a3d83dd17d4b877bea06bf92-85c17e9abc4ed443ef6794a715b5e9430d7ab21c.profdata
+chrome-win64-main-1746035971-081f85400de8f1a46812aecdb6bc4a2dcd104407-b23738f45a2bc90b4fca6b0754754deaa3612f40.profdata
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index af2bad43..d2e895d 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -652,6 +652,7 @@
 inline constexpr char kSignOutSubPage[] = "signOut";
 inline constexpr char kSiteDetailsSubpage[] = "content/siteDetails";
 inline constexpr char kSyncSetupSubPage[] = "syncSetup";
+inline constexpr char kSyncSetupAdvancedSubPage[] = "syncSetup/advanced";
 inline constexpr char kTriggeredResetProfileSettingsSubPage[] =
     "triggeredResetProfileSettings";
 
diff --git a/chrome/release_scripts b/chrome/release_scripts
index 6ad235a..38a00a2 160000
--- a/chrome/release_scripts
+++ b/chrome/release_scripts
@@ -1 +1 @@
-Subproject commit 6ad235a921b16b76e60316629f799e4fef593769
+Subproject commit 38a00a22c91fb29e2b8f6e88fae3d151a52e8f21
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 839ff13..3b762714 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -1709,6 +1709,7 @@
 #if !BUILDFLAG(IS_ANDROID)
     blink::WebRuntimeFeatures::EnableWebHIDOnServiceWorkers(true);
 #endif  // !BUILDFLAG(IS_ANDROID)
+    blink::WebRuntimeFeatures::EnableAIPromptAPIForWorkers(true);
     blink::WebRuntimeFeatures::EnableAIRewriterAPIForWorkers(true);
     blink::WebRuntimeFeatures::EnableAISummarizationAPIForWorkers(true);
     blink::WebRuntimeFeatures::EnableAIWriterAPIForWorkers(true);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 4afda06..117e1d5 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -8898,6 +8898,11 @@
   # Unit tests that run on Win/Mac/Linux and Android desktop.
   if (enable_extensions_core) {
     sources += [
+      "../browser/extensions/api/declarative_net_request/action_tracker_unittest.cc",
+      "../browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc",
+      "../browser/extensions/api/declarative_net_request/dnr_test_base.cc",
+      "../browser/extensions/api/declarative_net_request/dnr_test_base.h",
+      "../browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc",
       "../browser/extensions/api/webstore_private/extension_install_status_unittest.cc",
       "../browser/extensions/api/webstore_private/webstore_private_unittest.cc",
       "../browser/extensions/chrome_content_verifier_unittest.cc",
@@ -9007,11 +9012,6 @@
       "../browser/extensions/api/declarative_content/declarative_content_css_condition_tracker_unittest.cc",
       "../browser/extensions/api/declarative_content/declarative_content_is_bookmarked_condition_tracker_unittest.cc",
       "../browser/extensions/api/declarative_content/declarative_content_page_url_condition_tracker_unittest.cc",
-      "../browser/extensions/api/declarative_net_request/action_tracker_unittest.cc",
-      "../browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc",
-      "../browser/extensions/api/declarative_net_request/dnr_test_base.cc",
-      "../browser/extensions/api/declarative_net_request/dnr_test_base.h",
-      "../browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc",
       "../browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc",
       "../browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc",
       "../browser/extensions/api/developer_private/developer_private_api_unittest.cc",
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts
index 64f2981..6d06f6f 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts
@@ -7,7 +7,7 @@
 import type {AppElement} from 'chrome://customize-chrome-side-panel.top-chrome/app.js';
 import {CustomizeChromeImpression} from 'chrome://customize-chrome-side-panel.top-chrome/common.js';
 import type {BackgroundCollection, CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
-import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote, CustomizeChromeSection, NewTabPageType} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote, CustomizeChromeSection} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome_api_proxy.js';
 import {CustomizeToolbarClientCallbackRouter, CustomizeToolbarHandlerRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_toolbar.mojom-webui.js';
 import type {CustomizeToolbarHandlerInterface} from 'chrome://customize-chrome-side-panel.top-chrome/customize_toolbar.mojom-webui.js';
@@ -230,7 +230,7 @@
     });
   });
 
-  test('source tab type should update the cards', async () => {
+  test('isSourceTabFirstPartyNtp should update the cards', async () => {
     const idsControlledByIsSourceTabFirstPartyNtp = [
       '#shortcuts',
       '#modules',
@@ -249,29 +249,19 @@
       '#buttonContainer',
     ];
 
-    const newTabPageTypes = [
-      NewTabPageType.kFirstPartyWebUI,
-      NewTabPageType.kThirdPartyWebUI,
-      NewTabPageType.kThirdPartyRemote,
-      NewTabPageType.kExtension,
-      NewTabPageType.kIncognito,
-      NewTabPageType.kGuestMode,
-      NewTabPageType.kNone,
-    ];
-
-    const checkIdsVisibility = (sourceTabType: NewTabPageType) => {
+    const checkIdsVisibility = (isSourceTabFirstPartyNtp: boolean) => {
       idsControlledByIsSourceTabFirstPartyNtp.forEach(
           id => assertEquals(
-              sourceTabType === NewTabPageType.kFirstPartyWebUI,
+              isSourceTabFirstPartyNtp,
               !!customizeChromeApp.shadowRoot.querySelector(id)));
       idsNotControlledByIsSourceTabFirstPartyNtp.forEach(
           id => assertTrue(!!customizeChromeApp.shadowRoot.querySelector(id)));
     };
 
-    await newTabPageTypes.forEach(async t => {
-      callbackRouter.attachedTabStateUpdated(t);
+    await[true, false].forEach(async b => {
+      callbackRouter.attachedTabStateUpdated(b);
       await microtasksFinished();
-      checkIdsVisibility(t);
+      checkIdsVisibility(b);
     });
   });
 
@@ -311,7 +301,7 @@
               'selected'));
           assertEquals(customizeChromeApp, document.activeElement);
 
-          callbackRouter.attachedTabStateUpdated(NewTabPageType.kExtension);
+          callbackRouter.attachedTabStateUpdated(false);
           callbackRouter.setThemeEditable(false);
           await microtasksFinished();
 
@@ -336,7 +326,7 @@
               customizeChromeApp.shadowRoot.querySelector('#toolbarPage')!
                   .classList.contains('selected'));
 
-          callbackRouter.attachedTabStateUpdated(NewTabPageType.kExtension);
+          callbackRouter.attachedTabStateUpdated(false);
           await microtasksFinished();
 
           // Current page should now be toolbar.
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts
index b2dc801b..ae64285 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts
@@ -7,7 +7,7 @@
 import type {AppearanceElement} from 'chrome://customize-chrome-side-panel.top-chrome/appearance.js';
 import {CustomizeChromeAction} from 'chrome://customize-chrome-side-panel.top-chrome/common.js';
 import type {CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
-import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote, NewTabPageType} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome_api_proxy.js';
 import type {ManagedDialogElement} from 'chrome://resources/cr_components/managed_dialog/managed_dialog.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
@@ -619,7 +619,7 @@
     });
   });
 
-  test('source tab type should update the content', async () => {
+  test('isSourceTabFirstPartyNtp should update the content', async () => {
     const idsControlledByIsSourceTabFirstPartyNtp = [
       '#editButtonsContainer',
       '#themeSnapshot',
@@ -637,29 +637,19 @@
       '#editThemeIcon',
     ];
 
-    const newTabPageTypes = [
-      NewTabPageType.kFirstPartyWebUI,
-      NewTabPageType.kThirdPartyWebUI,
-      NewTabPageType.kThirdPartyRemote,
-      NewTabPageType.kExtension,
-      NewTabPageType.kIncognito,
-      NewTabPageType.kGuestMode,
-      NewTabPageType.kNone,
-    ];
-
-    const checkIdsVisibility = (sourceTabType: NewTabPageType) => {
+    const checkIdsVisibility = (isSourceTabFirstPartyNtp: boolean) => {
       idsControlledByIsSourceTabFirstPartyNtp.forEach(
           id => assertEquals(
-              sourceTabType === NewTabPageType.kFirstPartyWebUI,
+              isSourceTabFirstPartyNtp,
               !!appearanceElement.shadowRoot.querySelector(id)));
       idsNotControlledByIsSourceTabFirstPartyNtp.forEach(
           id => assertTrue(!!appearanceElement.shadowRoot.querySelector(id)));
     };
 
-    await newTabPageTypes.forEach(async t => {
-      callbackRouterRemote.attachedTabStateUpdated(t);
+    await[true, false].forEach(async b => {
+      callbackRouterRemote.attachedTabStateUpdated(b);
       await microtasksFinished();
-      checkIdsVisibility(t);
+      checkIdsVisibility(b);
     });
   });
 });
diff --git a/chrome/updater/app/app_net_worker.cc b/chrome/updater/app/app_net_worker.cc
index e2d3022..5664d48 100644
--- a/chrome/updater/app/app_net_worker.cc
+++ b/chrome/updater/app/app_net_worker.cc
@@ -62,10 +62,11 @@
                          int32_t net_error,
                          const std::string& header_etag,
                          const std::string& header_x_cup_server_proof,
+                         const std::string& header_cookie,
                          int64_t xheader_retry_after_sec) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     observer_->OnRequestComplete(*response_body, net_error, header_etag,
-                                 header_x_cup_server_proof,
+                                 header_x_cup_server_proof, header_cookie,
                                  xheader_retry_after_sec);
   }
 
@@ -172,7 +173,8 @@
       base::MakeRefCounted<PostRequestObserverWrapper>(std::move(callback));
   if (fetcher_ || file_fetcher_) {
     LOG(ERROR) << "Each service instance can do only one fetch request.";
-    wrapper->OnRequestComplete(nullptr, kErrorMojoRequestRejected, {}, {}, -1);
+    wrapper->OnRequestComplete(nullptr, kErrorMojoRequestRejected, {}, {}, {},
+                               -1);
     std::move(on_complete_callback_).Run(kErrorMojoRequestRejected);
     return;
   }
@@ -195,10 +197,11 @@
              std::optional<std::string> response_body, int32_t net_error,
              const std::string& header_etag,
              const std::string& header_x_cup_server_proof,
+             const std::string& header_cookie,
              int64_t xheader_retry_after_sec) {
             wrapper->OnRequestComplete(std::move(response_body), net_error,
                                        header_etag, header_x_cup_server_proof,
-                                       xheader_retry_after_sec);
+                                       header_cookie, xheader_retry_after_sec);
             std::move(callback).Run(net_error);
           },
           wrapper, std::move(on_complete_callback_)));
diff --git a/chrome/updater/app/app_net_worker_unittest.cc b/chrome/updater/app/app_net_worker_unittest.cc
index 96f10cf..5ef1ae3 100644
--- a/chrome/updater/app/app_net_worker_unittest.cc
+++ b/chrome/updater/app/app_net_worker_unittest.cc
@@ -99,6 +99,8 @@
         http_response->AddCustomHeader(
             update_client::NetworkFetcher::kHeaderXCupServerProof,
             "cup-server-proof-xyz");
+        http_response->AddCustomHeader(
+            update_client::NetworkFetcher::kHeaderCookie, "cookie-for-testing");
         http_response->AddCustomHeader("SomeOtherHeader", "foo-bar");
         return http_response;
       }));
@@ -120,11 +122,13 @@
               [&](std::optional<std::string> response_body, int32_t net_error,
                   const std::string& header_etag,
                   const std::string& header_x_cup_server_proof,
+                  const std::string& header_cookie,
                   int64_t xheader_retry_after_sec) {
                 EXPECT_EQ(net_error, 0);
                 EXPECT_EQ(*response_body, "hello world!");
                 EXPECT_EQ(header_etag, "etag-for-test");
                 EXPECT_EQ(header_x_cup_server_proof, "cup-server-proof-xyz");
+                EXPECT_EQ(header_cookie, "cookie-for-testing");
                 run_loop.Quit();
               })));
   run_loop.Run();
@@ -190,6 +194,7 @@
               [&](std::optional<std::string> response_body, int32_t net_error,
                   const std::string& header_etag,
                   const std::string& header_x_cup_server_proof,
+                  const std::string& header_cookie,
                   int64_t xheader_retry_after_sec) {
                 EXPECT_NE(net_error, 0);
                 run_loop.Quit();
diff --git a/chrome/updater/device_management/dm_client.cc b/chrome/updater/device_management/dm_client.cc
index f98b612..062fe0a7 100644
--- a/chrome/updater/device_management/dm_client.cc
+++ b/chrome/updater/device_management/dm_client.cc
@@ -162,6 +162,7 @@
                          int net_error,
                          const std::string& header_etag,
                          const std::string& header_x_cup_server_proof,
+                         const std::string& header_cookie,
                          int64_t xheader_retry_after_sec);
 
   std::unique_ptr<DMClient::Configurator> config_;
@@ -275,6 +276,7 @@
                                 int net_error,
                                 const std::string& header_etag,
                                 const std::string& header_x_cup_server_proof,
+                                const std::string& header_cookie,
                                 int64_t xheader_retry_after_sec) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   VLOG(2) << __func__;
diff --git a/chrome/updater/net/fallback_net_fetcher.cc b/chrome/updater/net/fallback_net_fetcher.cc
index 466dcf7..02315dc7 100644
--- a/chrome/updater/net/fallback_net_fetcher.cc
+++ b/chrome/updater/net/fallback_net_fetcher.cc
@@ -75,6 +75,7 @@
     int net_error,
     const std::string& header_etag,
     const std::string& header_x_cup_server_proof,
+    const std::string& header_cookie,
     int64_t xheader_retry_after_sec) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   const int should_fallback = net_error || (http_status_code_ != 200);
@@ -89,7 +90,7 @@
   }
   std::move(post_request_complete_callback)
       .Run(std::move(response_body), net_error, header_etag,
-           header_x_cup_server_proof, xheader_retry_after_sec);
+           header_x_cup_server_proof, header_cookie, xheader_retry_after_sec);
 }
 
 base::OnceClosure FallbackNetFetcher::DownloadToFile(
diff --git a/chrome/updater/net/fallback_net_fetcher.h b/chrome/updater/net/fallback_net_fetcher.h
index 93b773b..015ec1a8 100644
--- a/chrome/updater/net/fallback_net_fetcher.h
+++ b/chrome/updater/net/fallback_net_fetcher.h
@@ -74,6 +74,7 @@
       int net_error,
       const std::string& header_etag,
       const std::string& header_x_cup_server_proof,
+      const std::string& header_cookie,
       int64_t xheader_retry_after_sec);
 
   void DownloadToFileDone(
diff --git a/chrome/updater/net/fallback_net_fetcher_unittest.cc b/chrome/updater/net/fallback_net_fetcher_unittest.cc
index 2aa1d127..7aa2bcf 100644
--- a/chrome/updater/net/fallback_net_fetcher_unittest.cc
+++ b/chrome/updater/net/fallback_net_fetcher_unittest.cc
@@ -77,8 +77,8 @@
           [](base::OnceCallback<int(void)> error_supplier,
              update_client::NetworkFetcher::PostRequestCompleteCallback
                  callback) {
-            std::move(callback).Run(std::nullopt,
-                                    std::move(error_supplier).Run(), {}, {}, 0);
+            std::move(callback).Run(
+                std::nullopt, std::move(error_supplier).Run(), {}, {}, {}, 0);
           },
           std::move(error_supplier)),
       base::BindOnce(
@@ -95,7 +95,7 @@
       base::BindOnce(
           [](update_client::NetworkFetcher::PostRequestCompleteCallback
                  callback) {
-            std::move(callback).Run(std::nullopt, 0, {}, {}, 0);
+            std::move(callback).Run(std::nullopt, 0, {}, {}, {}, 0);
           }),
       base::BindOnce(
           [](base::OnceCallback<int(void)> error_supplier,
@@ -121,11 +121,11 @@
                        ran2 = true;
                        return 0;
                      })))
-      .PostRequest(
-          {}, {}, {}, {}, base::DoNothing(), base::DoNothing(),
-          base::BindLambdaForTesting([&](std::optional<std::string>, int,
-                                         const std::string&, const std::string&,
-                                         int64_t) { called_back = true; }));
+      .PostRequest({}, {}, {}, {}, base::DoNothing(), base::DoNothing(),
+                   base::BindLambdaForTesting(
+                       [&](std::optional<std::string>, int, const std::string&,
+                           const std::string&, const std::string&,
+                           int64_t) { called_back = true; }));
   EXPECT_TRUE(ran1);
   EXPECT_FALSE(ran2);
   EXPECT_TRUE(called_back);
@@ -163,11 +163,11 @@
                        ran2 = true;
                        return 0;
                      })))
-      .PostRequest(
-          {}, {}, {}, {}, base::DoNothing(), base::DoNothing(),
-          base::BindLambdaForTesting([&](std::optional<std::string>, int,
-                                         const std::string&, const std::string&,
-                                         int64_t) { called_back = true; }));
+      .PostRequest({}, {}, {}, {}, base::DoNothing(), base::DoNothing(),
+                   base::BindLambdaForTesting(
+                       [&](std::optional<std::string>, int, const std::string&,
+                           const std::string&, const std::string&,
+                           int64_t) { called_back = true; }));
   EXPECT_TRUE(ran1);
   EXPECT_TRUE(ran2);
   EXPECT_TRUE(called_back);
@@ -201,11 +201,11 @@
                        return 1;
                      })),
                      nullptr)
-      .PostRequest(
-          {}, {}, {}, {}, base::DoNothing(), base::DoNothing(),
-          base::BindLambdaForTesting([&](std::optional<std::string>, int,
-                                         const std::string&, const std::string&,
-                                         int64_t) { called_back = true; }));
+      .PostRequest({}, {}, {}, {}, base::DoNothing(), base::DoNothing(),
+                   base::BindLambdaForTesting(
+                       [&](std::optional<std::string>, int, const std::string&,
+                           const std::string&, const std::string&,
+                           int64_t) { called_back = true; }));
   EXPECT_TRUE(ran1);
   EXPECT_TRUE(called_back);
 }
diff --git a/chrome/updater/net/fetcher_callback_adapter.cc b/chrome/updater/net/fetcher_callback_adapter.cc
index 9686409..e04b8c6 100644
--- a/chrome/updater/net/fetcher_callback_adapter.cc
+++ b/chrome/updater/net/fetcher_callback_adapter.cc
@@ -58,12 +58,15 @@
       int32_t net_error,
       const std::string& header_etag,
       const std::string& header_x_cup_server_proof,
+      const std::string& header_cookie,
       std::optional<uint64_t> xheader_retry_after_sec) override {
     CHECK(post_request_complete_callback_)
         << __func__ << " is called without a valid callback. Was " << __func__
         << " called mulitple times?";
+
     std::move(post_request_complete_callback_)
         .Run(response_body, net_error, header_etag, header_x_cup_server_proof,
+             header_cookie,
              xheader_retry_after_sec
                  ? ToSignedIntegral(*xheader_retry_after_sec)
                  : -1);
diff --git a/chrome/updater/net/mac/mojom/updater_fetcher.mojom b/chrome/updater/net/mac/mojom/updater_fetcher.mojom
index 606d5a3e..6de8f40 100644
--- a/chrome/updater/net/mac/mojom/updater_fetcher.mojom
+++ b/chrome/updater/net/mac/mojom/updater_fetcher.mojom
@@ -28,6 +28,7 @@
         int32 net_error,
         string header_etag,
         string header_x_cup_server_proof,
+        string header_cookie,
         uint64? xheader_retry_after_sec);
 };
 
diff --git a/chrome/updater/net/network_fetcher_linux.cc b/chrome/updater/net/network_fetcher_linux.cc
index 7821b905e..e0a867cf 100644
--- a/chrome/updater/net/network_fetcher_linux.cc
+++ b/chrome/updater/net/network_fetcher_linux.cc
@@ -211,6 +211,8 @@
                          update_client::NetworkFetcher::kHeaderEtag),
           GetHeaderValue(response_headers,
                          update_client::NetworkFetcher::kHeaderXCupServerProof),
+          GetHeaderValue(response_headers,
+                         update_client::NetworkFetcher::kHeaderCookie),
           x_retry_after));
 
   curl_slist_free_all(headers);
diff --git a/chrome/updater/net/network_fetcher_mac.mm b/chrome/updater/net/network_fetcher_mac.mm
index e7d8ba6..68cef73 100644
--- a/chrome/updater/net/network_fetcher_mac.mm
+++ b/chrome/updater/net/network_fetcher_mac.mm
@@ -176,6 +176,13 @@
   if ([headers objectForKey:headerXCupServerProof]) {
     cupServerProof = [headers objectForKey:headerXCupServerProof];
   }
+  NSString* headerCookie =
+      base::SysUTF8ToNSString(update_client::NetworkFetcher::kHeaderCookie);
+  NSString* cookie = @"";
+  if ([headers objectForKey:headerCookie]) {
+    cookie = [headers objectForKey:headerCookie];
+  }
+
   int64_t retryAfterResult = -1;
   NSString* xRetryAfter = [headers
       objectForKey:base::SysUTF8ToNSString(
@@ -191,7 +198,8 @@
           std::string(reinterpret_cast<const char*>([_downloadedData bytes]),
                       [_downloadedData length]),
           error.code, base::SysNSStringToUTF8(etag),
-          base::SysNSStringToUTF8(cupServerProof), retryAfterResult));
+          base::SysNSStringToUTF8(cupServerProof),
+          base::SysNSStringToUTF8(cookie), retryAfterResult));
 }
 
 @end
@@ -638,7 +646,7 @@
   if (const int dial_result = DialFetchService(); dial_result != kErrorOk) {
     LOG(ERROR) << "Failed to dial the fetch service: " << dial_result;
     std::move(post_request_complete_callback)
-        .Run(nullptr, dial_result, {}, {}, -1);
+        .Run(nullptr, dial_result, {}, {}, {}, -1);
     return;
   }
 
diff --git a/chrome/updater/net/network_fetcher_win.cc b/chrome/updater/net/network_fetcher_win.cc
index b3f8abe2..df74bd5b 100644
--- a/chrome/updater/net/network_fetcher_win.cc
+++ b/chrome/updater/net/network_fetcher_win.cc
@@ -154,6 +154,7 @@
   // this is best effort only.
   std::wstring x_cup_server_proof;
   std::wstring etag;
+  std::wstring cookie;
   int x_retry_after_sec = -1;
   winhttp_network_fetcher_->QueryHeaderString(
       base::SysUTF8ToWide(
@@ -161,6 +162,9 @@
       &x_cup_server_proof);
   winhttp_network_fetcher_->QueryHeaderString(
       base::SysUTF8ToWide(update_client::NetworkFetcher::kHeaderEtag), &etag);
+  winhttp_network_fetcher_->QueryHeaderString(
+      base::SysUTF8ToWide(update_client::NetworkFetcher::kHeaderCookie),
+      &cookie);
   winhttp_network_fetcher_->QueryHeaderInt(
       base::SysUTF8ToWide(update_client::NetworkFetcher::kHeaderXRetryAfter),
       &x_retry_after_sec);
@@ -168,7 +172,8 @@
   std::move(post_request_complete_callback_)
       .Run(winhttp_network_fetcher_->GetResponseBody(),
            winhttp_network_fetcher_->GetNetError(), base::SysWideToUTF8(etag),
-           base::SysWideToUTF8(x_cup_server_proof), x_retry_after_sec);
+           base::SysWideToUTF8(x_cup_server_proof), base::SysWideToUTF8(cookie),
+           x_retry_after_sec);
 }
 
 void NetworkFetcher::DownloadToFileComplete(int /*response_code*/) {
diff --git a/chrome/updater/net/network_unittest.cc b/chrome/updater/net/network_unittest.cc
index 6ee6bb9..43f71f2 100644
--- a/chrome/updater/net/network_unittest.cc
+++ b/chrome/updater/net/network_unittest.cc
@@ -66,11 +66,13 @@
                                    int net_error,
                                    const std::string& header_etag,
                                    const std::string& header_x_cup_server_proof,
+                                   const std::string& header_cookie,
                                    int64_t xheader_retry_after_sec) {
     EXPECT_EQ(*response_body, expected_body);
     EXPECT_EQ(net_error, 0);
     EXPECT_EQ(header_etag, "Wfhw789h");
     EXPECT_EQ(header_x_cup_server_proof, "server-proof");
+    EXPECT_EQ(header_cookie, "cookie");
     EXPECT_EQ(xheader_retry_after_sec, 67);
     PostRequestCompleted();
   }
@@ -95,6 +97,7 @@
       http_response->AddCustomHeader("x-retry-after", "67");
       http_response->AddCustomHeader("etag", "Wfhw789h");
       http_response->AddCustomHeader("x-cup-server-proof", "server-proof");
+      http_response->AddCustomHeader("Cookie", "cookie");
     } else if (request.method == net::test_server::HttpMethod::METHOD_GET) {
       http_response->set_content("hello");
       http_response->set_content_type("application/octet-stream");
diff --git a/chromeos/ash/services/ime/public/mojom/input_method.mojom b/chromeos/ash/services/ime/public/mojom/input_method.mojom
index 1395430a..b53f2f9d62 100644
--- a/chromeos/ash/services/ime/public/mojom/input_method.mojom
+++ b/chromeos/ash/services/ime/public/mojom/input_method.mojom
@@ -6,7 +6,7 @@
 // the Chromium repo. This file should be updated first, before syncing in the
 // other repos.
 
-// Next MinVersion: 25
+// Next MinVersion: 26
 
 module ash.ime.mojom;
 
@@ -25,7 +25,7 @@
 // If a key is not listed here, then it means that the Chrome OS first-party
 // IMEs do not handle it and it is safe for the OS to handle the key itself.
 //
-// Next ordinal: 41
+// Next ordinal: 42
 [Stable, Extensible, RenamedFrom="chromeos.ime.mojom.NamedDomKey"]
 enum NamedDomKey {
   [Default] kOther = 0,
@@ -75,6 +75,7 @@
   [MinVersion=23] kAudioVolumeDown = 38,
   [MinVersion=23] kAudioVolumeUp = 39,
   [MinVersion=24] kDelete = 40,
+  [MinVersion=25] kNumLock = 41,
 };
 
 // Represents a key pressed by the user, taking into account aspects like
diff --git a/clank b/clank
index cfb3b18..b8e94f7 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit cfb3b184379b5491aafc36c95a2d8effd010eea2
+Subproject commit b8e94f74cdf17fd4bad068bed25f841a4b566dce
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index f2ec66ae..7618a839 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -498,6 +498,8 @@
     "payments/payments_request_details.h",
     "payments/payments_requests/create_bnpl_payment_instrument_request.cc",
     "payments/payments_requests/create_bnpl_payment_instrument_request.h",
+    "payments/payments_requests/create_card_request.cc",
+    "payments/payments_requests/create_card_request.h",
     "payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request.cc",
     "payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request.h",
     "payments/payments_requests/get_bnpl_payment_instrument_for_fetching_vcn_request.cc",
@@ -1386,6 +1388,7 @@
     "payments/payments_data_cleaner_unittest.cc",
     "payments/payments_network_interface_unittest.cc",
     "payments/payments_requests/create_bnpl_payment_instrument_request_unittest.cc",
+    "payments/payments_requests/create_card_request_unittest.cc",
     "payments/payments_requests/get_bnpl_payment_instrument_for_fetching_url_request_unittest.cc",
     "payments/payments_requests/get_bnpl_payment_instrument_for_fetching_vcn_request_unittest.cc",
     "payments/payments_requests/get_card_upload_details_request_unittest.cc",
diff --git a/components/autofill/core/browser/payments/payments_request_details.h b/components/autofill/core/browser/payments/payments_request_details.h
index 0efd0d2..9f2c0bc 100644
--- a/components/autofill/core/browser/payments/payments_request_details.h
+++ b/components/autofill/core/browser/payments/payments_request_details.h
@@ -300,38 +300,6 @@
   LegalMessageLines issuer_legal_message;
 };
 
-// A collection of the information required to make a credit card upload
-// request.
-struct UploadCardRequestDetails {
-  UploadCardRequestDetails();
-  UploadCardRequestDetails(const UploadCardRequestDetails& other);
-  ~UploadCardRequestDetails();
-
-  int64_t billing_customer_number = 0;
-  int detected_values;
-  CreditCard card;
-  std::u16string cvc;
-  std::vector<AutofillProfile> profiles;
-  std::u16string context_token;
-  std::string risk_data;
-  std::string app_locale;
-  std::vector<ClientBehaviorConstants> client_behavior_signals;
-};
-
-// A collection of information required to make an IBAN upload request.
-struct UploadIbanRequestDetails {
-  UploadIbanRequestDetails();
-  UploadIbanRequestDetails(const UploadIbanRequestDetails& other);
-  ~UploadIbanRequestDetails();
-
-  std::string app_locale;
-  int64_t billing_customer_number = 0;
-  std::u16string context_token;
-  std::u16string value;
-  std::u16string nickname;
-  std::string risk_data;
-};
-
 // An enum set in the GetCardUploadDetailsRequest indicating the source of the
 // request when uploading a card to Google Payments. It should stay consistent
 // with the same enum in Google Payments server code.
@@ -350,6 +318,40 @@
   UPSTREAM_SAVE_AND_FILL,
 };
 
+// A collection of the information required to make a credit card upload
+// request.
+struct UploadCardRequestDetails {
+  UploadCardRequestDetails();
+  UploadCardRequestDetails(const UploadCardRequestDetails& other);
+  ~UploadCardRequestDetails();
+
+  int64_t billing_customer_number = 0;
+  int detected_values;
+  CreditCard card;
+  std::u16string cvc;
+  std::vector<AutofillProfile> profiles;
+  std::u16string context_token;
+  std::string risk_data;
+  std::string app_locale;
+  std::vector<ClientBehaviorConstants> client_behavior_signals;
+  UploadCardSource upload_card_source =
+      UploadCardSource::UNKNOWN_UPLOAD_CARD_SOURCE;
+};
+
+// A collection of information required to make an IBAN upload request.
+struct UploadIbanRequestDetails {
+  UploadIbanRequestDetails();
+  UploadIbanRequestDetails(const UploadIbanRequestDetails& other);
+  ~UploadIbanRequestDetails();
+
+  std::string app_locale;
+  int64_t billing_customer_number = 0;
+  std::u16string context_token;
+  std::u16string value;
+  std::u16string nickname;
+  std::string risk_data;
+};
+
 // A collection of information received in the response for an
 // UploadCardRequest.
 struct UploadCardResponseDetails {
diff --git a/components/autofill/core/browser/payments/payments_requests/create_card_request.cc b/components/autofill/core/browser/payments/payments_requests/create_card_request.cc
new file mode 100644
index 0000000..206e69d346
--- /dev/null
+++ b/components/autofill/core/browser/payments/payments_requests/create_card_request.cc
@@ -0,0 +1,168 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/payments/payments_requests/create_card_request.h"
+
+#include "base/json/json_writer.h"
+#include "base/strings/escape.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace autofill::payments {
+
+namespace {
+const char kCreateCardRequestPath[] =
+    "payments/apis-secure/chromepaymentsservice/createpaymentinstrument"
+    "?s7e_suffix=chromewallet";
+const char kCreateCardRequestFormat[] =
+    "requestContentType=application/json; charset=utf-8&request=%s"
+    "&s7e_21_pan=%s&s7e_13_cvc=%s";
+const char kCreateCardRequestFormatWithoutCvc[] =
+    "requestContentType=application/json; charset=utf-8&request=%s"
+    "&s7e_21_pan=%s";
+}  // namespace
+
+CreateCardRequest::CreateCardRequest(
+    const UploadCardRequestDetails& request_details,
+    base::OnceCallback<void(PaymentsAutofillClient::PaymentsRpcResult,
+                            const std::string&)> callback)
+    : request_details_(request_details), callback_(std::move(callback)) {}
+
+CreateCardRequest::~CreateCardRequest() = default;
+
+std::string CreateCardRequest::GetRequestUrlPath() {
+  return kCreateCardRequestPath;
+}
+
+std::string CreateCardRequest::GetRequestContentType() {
+  return "application/x-www-form-urlencoded";
+}
+
+std::string CreateCardRequest::GetRequestContent() {
+  base::Value::Dict request_dict;
+
+  const std::string& app_locale = request_details_.app_locale;
+  base::Value::Dict context;
+  context.Set("language_code", app_locale);
+  context.Set("billable_service", kUploadPaymentMethodBillableServiceNumber);
+  if (request_details_.billing_customer_number != 0) {
+    context.Set("customer_context",
+                BuildCustomerContextDictionary(
+                    request_details_.billing_customer_number));
+  }
+  request_dict.Set("context", std::move(context));
+
+  request_dict.Set(
+      "chrome_user_context",
+      BuildChromeUserContext(request_details_.client_behavior_signals));
+
+  request_dict.Set("context_token", request_details_.context_token);
+
+  if (request_details_.card.HasNonEmptyValidNickname()) {
+    request_dict.Set("nickname", request_details_.card.nickname());
+  }
+
+  request_dict.Set("risk_data_encoded",
+                   BuildRiskDictionary(request_details_.risk_data));
+
+  base::Value::Dict card_info;
+  card_info.Set("pan", "__param:s7e_21_pan");
+  if (!request_details_.cvc.empty()) {
+    card_info.Set("cvc", "__param:s7e_13_cvc");
+  }
+  int value = 0;
+  const std::u16string exp_month =
+      request_details_.card.GetInfo(CREDIT_CARD_EXP_MONTH, app_locale);
+  const std::u16string exp_year =
+      request_details_.card.GetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, app_locale);
+  if (base::StringToInt(exp_month, &value)) {
+    card_info.Set("expiration_month", value);
+  }
+  if (base::StringToInt(exp_year, &value)) {
+    card_info.Set("expiration_year", value);
+  }
+  SetStringIfNotEmpty(request_details_.card, CREDIT_CARD_NAME_FULL, app_locale,
+                      "cardholder_name", card_info);
+  // When this is invoked, caller is guaranteed to send only one unique address,
+  // if not an empty list.
+  CHECK_LE(request_details_.profiles.size(), 1U);
+  if (!request_details_.profiles.empty()) {
+    card_info.Set(
+        "address",
+        BuildAddressDictionary(request_details_.profiles[0], app_locale, true));
+  }
+
+  switch (request_details_.upload_card_source) {
+    case UploadCardSource::UNKNOWN_UPLOAD_CARD_SOURCE:
+      card_info.Set("upload_card_source", "UNKNOWN_UPLOAD_CARD_SOURCE");
+      break;
+    case UploadCardSource::UPSTREAM_SAVE_AND_FILL:
+      card_info.Set("upload_card_source", "UPSTREAM_SAVE_AND_FILL");
+      break;
+    default:
+      // This class has not been integrated with other Upstream flows yet.
+      NOTREACHED();
+  }
+  request_dict.Set("card_info", std::move(card_info));
+
+  const std::u16string pan =
+      request_details_.card.GetInfo(CREDIT_CARD_NUMBER, app_locale);
+  std::string json_request;
+  base::JSONWriter::Write(request_dict, &json_request);
+  std::string request_content;
+  if (request_details_.cvc.empty()) {
+    request_content = base::StringPrintf(
+        kCreateCardRequestFormatWithoutCvc,
+        base::EscapeUrlEncodedData(json_request, true).c_str(),
+        base::EscapeUrlEncodedData(base::UTF16ToASCII(pan), true).c_str());
+  } else {
+    request_content = base::StringPrintf(
+        kCreateCardRequestFormat,
+        base::EscapeUrlEncodedData(json_request, true).c_str(),
+        base::EscapeUrlEncodedData(base::UTF16ToASCII(pan), true).c_str(),
+        base::EscapeUrlEncodedData(base::UTF16ToASCII(request_details_.cvc),
+                                   true)
+            .c_str());
+  }
+
+  DVLOG(3) << "createcard request body: " << request_content;
+  return request_content;
+}
+
+void CreateCardRequest::ParseResponse(const base::Value::Dict& response) {
+  if (const base::Value::Dict* card_info = response.FindDict("card_info")) {
+    contains_card_info_ = true;
+    if (const std::string* instrument_id =
+            card_info->FindString("instrument_id")) {
+      instrument_id_ = std::move(*instrument_id);
+    }
+  }
+}
+
+bool CreateCardRequest::IsResponseComplete() {
+  return contains_card_info_;
+}
+
+void CreateCardRequest::RespondToDelegate(
+    PaymentsAutofillClient::PaymentsRpcResult result) {
+  std::move(callback_).Run(result, instrument_id_);
+}
+
+std::string CreateCardRequest::GetHistogramName() const {
+  return "CreateCardRequest";
+}
+
+std::optional<base::TimeDelta> CreateCardRequest::GetTimeout() const {
+  if (!base::FeatureList::IsEnabled(
+          features::kAutofillUploadCardRequestTimeout)) {
+    return std::nullopt;
+  }
+  return base::Milliseconds(
+      features::kAutofillUploadCardRequestTimeoutMilliseconds.Get());
+}
+
+std::string CreateCardRequest::GetInstrumentIdForTesting() const {
+  return instrument_id_;
+}
+
+}  // namespace autofill::payments
diff --git a/components/autofill/core/browser/payments/payments_requests/create_card_request.h b/components/autofill/core/browser/payments/payments_requests/create_card_request.h
new file mode 100644
index 0000000..0b1e475
--- /dev/null
+++ b/components/autofill/core/browser/payments/payments_requests/create_card_request.h
@@ -0,0 +1,47 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_CREATE_CARD_REQUEST_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_CREATE_CARD_REQUEST_H_
+
+#include "components/autofill/core/browser/payments/payments_request_details.h"
+#include "components/autofill/core/browser/payments/payments_requests/payments_request.h"
+
+namespace autofill::payments {
+
+class CreateCardRequest : public PaymentsRequest {
+ public:
+  CreateCardRequest(
+      const UploadCardRequestDetails& request_details,
+      base::OnceCallback<void(PaymentsAutofillClient::PaymentsRpcResult,
+                              const std::string&)> callback);
+  CreateCardRequest(const CreateCardRequest&) = delete;
+  CreateCardRequest& operator=(const CreateCardRequest&) = delete;
+  ~CreateCardRequest() override;
+
+  // PaymentsRequest:
+  std::string GetRequestUrlPath() override;
+  std::string GetRequestContentType() override;
+  std::string GetRequestContent() override;
+  void ParseResponse(const base::Value::Dict& response) override;
+  bool IsResponseComplete() override;
+  void RespondToDelegate(
+      PaymentsAutofillClient::PaymentsRpcResult result) override;
+  std::string GetHistogramName() const override;
+  std::optional<base::TimeDelta> GetTimeout() const override;
+
+  std::string GetInstrumentIdForTesting() const;
+
+ private:
+  bool contains_card_info_ = false;
+  const UploadCardRequestDetails request_details_;
+  std::string instrument_id_;
+  base::OnceCallback<void(PaymentsAutofillClient::PaymentsRpcResult,
+                          const std::string&)>
+      callback_;
+};
+
+}  // namespace autofill::payments
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_PAYMENTS_REQUESTS_CREATE_CARD_REQUEST_H_
diff --git a/components/autofill/core/browser/payments/payments_requests/create_card_request_unittest.cc b/components/autofill/core/browser/payments/payments_requests/create_card_request_unittest.cc
new file mode 100644
index 0000000..47076f9
--- /dev/null
+++ b/components/autofill/core/browser/payments/payments_requests/create_card_request_unittest.cc
@@ -0,0 +1,210 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/core/browser/payments/payments_requests/create_card_request.h"
+
+#include "base/functional/callback_helpers.h"
+#include "base/json/json_writer.h"
+#include "base/strings/escape.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/values_test_util.h"
+#include "components/autofill/core/browser/payments/payments_request_details.h"
+#include "components/autofill/core/browser/payments/test/autofill_payments_test_utils.h"
+#include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill::payments {
+
+namespace {
+
+struct CreateCardOptions {
+  CreateCardOptions& with_cvc_in_request(bool b) {
+    include_cvc = b;
+    return *this;
+  }
+
+  CreateCardOptions& with_nickname_in_request(bool b) {
+    include_nickname = b;
+    return *this;
+  }
+
+  CreateCardOptions& with_address_in_request(bool b) {
+    include_address = b;
+    return *this;
+  }
+
+  bool include_cvc = true;
+  bool include_nickname = true;
+  bool include_address = true;
+};
+
+std::unique_ptr<CreateCardRequest> BuildCreateCardRequest(
+    const CreateCardOptions& option = CreateCardOptions()) {
+  UploadCardRequestDetails request_details;
+  request_details.billing_customer_number = 111122223333;
+  request_details.card = test::GetCreditCard();
+  if (option.include_cvc) {
+    request_details.cvc = u"123";
+  }
+  if (option.include_nickname) {
+    request_details.card.SetNickname(u"some nickname");
+  }
+  request_details.client_behavior_signals = {
+      ClientBehaviorConstants::kOfferingToSaveCvc};
+  request_details.context_token = u"some context token";
+  request_details.risk_data = "some risk data";
+  request_details.app_locale = "en";
+  if (option.include_address) {
+    request_details.profiles.emplace_back(
+        test::GetFullProfile(AddressCountryCode("US")));
+  }
+  request_details.upload_card_source = UploadCardSource::UPSTREAM_SAVE_AND_FILL;
+
+  return std::make_unique<CreateCardRequest>(request_details,
+                                             base::DoNothing());
+}
+
+TEST(CreateCardRequestTest, GetRequestUrlPath) {
+  std::unique_ptr<CreateCardRequest> request = BuildCreateCardRequest();
+
+  EXPECT_EQ(request->GetRequestUrlPath(),
+            "payments/apis-secure/chromepaymentsservice/"
+            "createpaymentinstrument?s7e_suffix=chromewallet");
+}
+
+TEST(CreateCardRequestTest, GetRequestContent_ContainsExpectedData) {
+  std::unique_ptr<CreateCardRequest> request = BuildCreateCardRequest();
+  base::Value::Dict address =
+      base::Value::Dict()
+          .Set("phone_number", "16502111111")
+          .Set("postal_address",
+               base::Value::Dict()
+                   .Set("address_line", base::Value::List()
+                                            .Append("666 Erebus St.")
+                                            .Append("Apt 8"))
+                   .Set("administrative_area_name", "CA")
+                   .Set("country_name_code", "US")
+                   .Set("locality_name", "Elysium")
+                   .Set("postal_code_number", "91111")
+                   .Set("recipient_name", "John H. Doe"));
+  int exp_month, exp_year;
+  base::StringToInt(test::NextMonth(), &exp_month);
+  base::StringToInt(test::NextYear(), &exp_year);
+  base::Value::Dict json_dict =
+      base::Value::Dict()
+          .Set("context",
+               base::Value::Dict()
+                   .Set("billable_service",
+                        payments::kUploadPaymentMethodBillableServiceNumber)
+                   .Set("customer_context",
+                        PaymentsRequest::BuildCustomerContextDictionary(
+                            111122223333))
+                   .Set("language_code", "en"))
+          .Set("chrome_user_context",
+               base::Value::Dict().Set(
+                   "client_behavior_signals",
+                   base::Value::List().Append(static_cast<int>(
+                       ClientBehaviorConstants::kOfferingToSaveCvc))))
+          .Set("context_token", "some context token")
+          .Set("risk_data_encoded",
+               PaymentsRequest::BuildRiskDictionary("some risk data"))
+          .Set("nickname", "some nickname")
+          .Set("card_info",
+               base::Value::Dict()
+                   .Set("pan", "__param:s7e_21_pan")
+                   .Set("cvc", "__param:s7e_13_cvc")
+                   .Set("expiration_month", exp_month)
+                   .Set("expiration_year", exp_year)
+                   .Set("cardholder_name", "Test User")
+                   .Set("address", std::move(address))
+                   .Set("upload_card_source", "UPSTREAM_SAVE_AND_FILL"));
+  std::string expected_json_content;
+  base::JSONWriter::Write(json_dict, &expected_json_content);
+
+  std::string expected_request_content = base::StringPrintf(
+      "requestContentType=application/json; charset=utf-8&request=%s"
+      "&s7e_21_pan=%s&s7e_13_cvc=%s",
+      base::EscapeUrlEncodedData(expected_json_content, true).c_str(),
+      base::EscapeUrlEncodedData(base::UTF16ToASCII(u"4111111111111111"), true)
+          .c_str(),
+      base::EscapeUrlEncodedData(base::UTF16ToASCII(u"123"), true).c_str());
+
+  EXPECT_EQ(request->GetRequestContent(), expected_request_content);
+}
+
+TEST(CreateCardRequestTest, NoCvcInRequestIfNotProvided) {
+  std::unique_ptr<CreateCardRequest> request =
+      BuildCreateCardRequest(CreateCardOptions().with_cvc_in_request(false));
+
+  EXPECT_TRUE(!request->GetRequestContent().empty());
+  // Verify that the encrypted_cvc and s7e_13_cvc parameters were not included
+  // in the request.
+  EXPECT_TRUE(request->GetRequestContent().find("encrypted_cvc") ==
+              std::string::npos);
+  EXPECT_TRUE(request->GetRequestContent().find("__param:s7e_13_cvc") ==
+              std::string::npos);
+  EXPECT_TRUE(request->GetRequestContent().find("&s7e_13_cvc=") ==
+              std::string::npos);
+}
+
+TEST(CreateCardRequestTest, NoNicknameInRequestIfNicknameNotProvided) {
+  std::unique_ptr<CreateCardRequest> request = BuildCreateCardRequest(
+      CreateCardOptions().with_nickname_in_request(false));
+
+  // Card nickname was not set.
+  EXPECT_TRUE(request->GetRequestContent().find("nickname") ==
+              std::string::npos);
+}
+
+TEST(CreateCardRequestTest, NoAddressInRequestIfAddressNotProvided) {
+  std::unique_ptr<CreateCardRequest> request = BuildCreateCardRequest(
+      CreateCardOptions().with_address_in_request(false));
+
+  // Address was not set.
+  EXPECT_TRUE(request->GetRequestContent().find("address") ==
+              std::string::npos);
+}
+
+TEST(CreateCardRequestTest, GetTimeOutIfFlagDisabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      features::kAutofillUploadCardRequestTimeout);
+  std::unique_ptr<CreateCardRequest> request = BuildCreateCardRequest();
+  EXPECT_FALSE(request->GetTimeout().has_value());
+}
+
+TEST(CreateCardRequestTest, GetTimeOut) {
+  base::FieldTrialParams params;
+  params["autofill_upload_card_request_timeout_milliseconds"] = "6000";
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeatureWithParameters(
+      features::kAutofillUploadCardRequestTimeout, params);
+  std::unique_ptr<CreateCardRequest> request = BuildCreateCardRequest();
+
+  EXPECT_EQ(*request->GetTimeout(), base::Milliseconds(6000));
+}
+
+TEST(CreateCardRequestTest, ParseResponse) {
+  std::unique_ptr<CreateCardRequest> request = BuildCreateCardRequest();
+
+  base::Value::Dict response = base::Value::Dict().Set(
+      "card_info", base::Value::Dict().Set("instrument_id", "11223344"));
+
+  request->ParseResponse(response);
+
+  EXPECT_EQ(request->GetInstrumentIdForTesting(), "11223344");
+  EXPECT_TRUE(request->IsResponseComplete());
+}
+
+TEST(CreateCardRequestTest, ParseResponse_MissingCardInfo) {
+  std::unique_ptr<CreateCardRequest> request = BuildCreateCardRequest();
+
+  request->ParseResponse(base::Value::Dict());
+
+  EXPECT_FALSE(request->IsResponseComplete());
+}
+
+}  // namespace
+
+}  // namespace autofill::payments
diff --git a/components/bookmarks/browser/bookmark_model_unittest.cc b/components/bookmarks/browser/bookmark_model_unittest.cc
index c9173f3c..8b23124 100644
--- a/components/bookmarks/browser/bookmark_model_unittest.cc
+++ b/components/bookmarks/browser/bookmark_model_unittest.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/bookmarks/browser/bookmark_model.h"
 
diff --git a/components/breadcrumbs/core/breadcrumb_persistent_storage_manager.cc b/components/breadcrumbs/core/breadcrumb_persistent_storage_manager.cc
index 38b01cf..7eea636 100644
--- a/components/breadcrumbs/core/breadcrumb_persistent_storage_manager.cc
+++ b/components/breadcrumbs/core/breadcrumb_persistent_storage_manager.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/breadcrumbs/core/breadcrumb_persistent_storage_manager.h"
 
diff --git a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/AsyncNotificationManagerProxyImpl.java b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/AsyncNotificationManagerProxyImpl.java
index 7e8db98..11465898 100644
--- a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/AsyncNotificationManagerProxyImpl.java
+++ b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/AsyncNotificationManagerProxyImpl.java
@@ -17,7 +17,6 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.NullUnmarked;
 import org.chromium.build.annotations.Nullable;
-import org.chromium.components.browser_ui.notifications.NotificationProxyUtils.NotificationEvent;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -177,15 +176,9 @@
         AsyncTask.SERIAL_EXECUTOR.execute(
                 () -> {
                     try (TraceEvent te = TraceEvent.scoped(eventName)) {
-                        NotificationProxyUtils.recordNotificationEventHistogram(
-                                NotificationEvent.NO_CALLBACK_START);
                         runnable.run();
-                        NotificationProxyUtils.recordNotificationEventHistogram(
-                                NotificationEvent.NO_CALLBACK_SUCCESS);
                     } catch (Exception e) {
                         Log.e(TAG, "unable to run a runnable.", e);
-                        NotificationProxyUtils.recordNotificationEventHistogram(
-                                NotificationEvent.NO_CALLBACK_FAILED);
                     }
                 });
     }
@@ -202,8 +195,6 @@
             @Override
             protected @Nullable T doInBackground() {
                 try (TraceEvent te = TraceEvent.scoped(eventName)) {
-                    NotificationProxyUtils.recordNotificationEventHistogram(
-                            NotificationEvent.HAS_CALLBACK_START);
                     return callable.call();
                 } catch (Exception e) {
                     Log.e(TAG, "Unable to call method.", e);
@@ -213,10 +204,6 @@
 
             @Override
             protected void onPostExecute(@Nullable T result) {
-                NotificationProxyUtils.recordNotificationEventHistogram(
-                        result == null
-                                ? NotificationEvent.HAS_CALLBACK_FAILED
-                                : NotificationEvent.HAS_CALLBACK_SUCCESS);
                 callback.onResult(result);
             }
         }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
diff --git a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/NotificationManagerProxyImpl.java b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/NotificationManagerProxyImpl.java
index 280b91b..3dc7e0354 100644
--- a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/NotificationManagerProxyImpl.java
+++ b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/NotificationManagerProxyImpl.java
@@ -22,7 +22,6 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.NullUnmarked;
 import org.chromium.build.annotations.Nullable;
-import org.chromium.components.browser_ui.notifications.NotificationProxyUtils.NotificationEvent;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -253,15 +252,9 @@
     /** Helper method to run an runnable inside a scoped event. */
     private void runRunnable(@Nullable TraceEvent scopedEvent, Runnable runnable) {
         try (scopedEvent) {
-            NotificationProxyUtils.recordNotificationEventHistogram(
-                    NotificationEvent.NO_CALLBACK_START);
             runnable.run();
-            NotificationProxyUtils.recordNotificationEventHistogram(
-                    NotificationEvent.NO_CALLBACK_SUCCESS);
         } catch (Exception e) {
             Log.e(TAG, "unable to run a runnable.", e);
-            NotificationProxyUtils.recordNotificationEventHistogram(
-                    NotificationEvent.NO_CALLBACK_FAILED);
         }
     }
 
@@ -276,18 +269,12 @@
             Callback<T> callback,
             T defaultValue) {
         T result;
-        @NotificationEvent int event;
         try (scopedEvent) {
-            NotificationProxyUtils.recordNotificationEventHistogram(
-                    NotificationEvent.HAS_CALLBACK_START);
             result = callable.call();
-            event = NotificationEvent.HAS_CALLBACK_SUCCESS;
         } catch (Exception e) {
             Log.e(TAG, "Unable to call method.", e);
-            event = NotificationEvent.HAS_CALLBACK_FAILED;
             result = defaultValue;
         }
-        NotificationProxyUtils.recordNotificationEventHistogram(event);
         T finalResult = result;
         PostTask.postTask(TaskTraits.UI_DEFAULT, () -> callback.onResult(finalResult));
     }
diff --git a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/NotificationProxyUtils.java b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/NotificationProxyUtils.java
index 2084d8e..409228f 100644
--- a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/NotificationProxyUtils.java
+++ b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/NotificationProxyUtils.java
@@ -6,18 +6,13 @@
 
 import android.os.Build;
 
-import androidx.annotation.IntDef;
 import androidx.core.app.NotificationManagerCompat;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ResettersForTesting;
-import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /**
  * Base interface for NofificationManagerProxy that only supports simple functionalities. Remove
  * this once AsyncNofificationManagerProxy is set to default.
@@ -27,32 +22,6 @@
     private static @Nullable Boolean sAreNotificationsEnabled;
     private static @Nullable Boolean sAreNotificationsEnabledForTest;
 
-    /** Defines the notification event */
-    @IntDef({
-        NotificationEvent.NO_CALLBACK_START,
-        NotificationEvent.NO_CALLBACK_SUCCESS,
-        NotificationEvent.NO_CALLBACK_FAILED,
-        NotificationEvent.HAS_CALLBACK_START,
-        NotificationEvent.HAS_CALLBACK_SUCCESS,
-        NotificationEvent.HAS_CALLBACK_FAILED,
-        NotificationEvent.COUNT
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface NotificationEvent {
-        int NO_CALLBACK_START = 0; /* A notification call without a callback started */
-        int NO_CALLBACK_SUCCESS = 1; /* A notification call without a callback succeeded */
-        int NO_CALLBACK_FAILED = 2; /* A notification call without a callback failed */
-        int HAS_CALLBACK_START = 3; /* A notification call with a callback started */
-        int HAS_CALLBACK_SUCCESS = 4; /* A notification call with a callback succeeded */
-        int HAS_CALLBACK_FAILED = 5; /* A notification call with a callback failed */
-        int COUNT = 6;
-    }
-
-    public static void recordNotificationEventHistogram(@NotificationEvent int event) {
-        RecordHistogram.recordEnumeratedHistogram(
-                "Notifications.Android.NotificationEvent", event, NotificationEvent.COUNT);
-    }
-
     /** Returns whether notifications are enabled for the app. */
     public static boolean areNotificationsEnabled() {
         if (sAreNotificationsEnabledForTest != null) {
diff --git a/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/EmbeddableSettingsPage.java b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/EmbeddableSettingsPage.java
index 809c4c1..322cb5b8 100644
--- a/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/EmbeddableSettingsPage.java
+++ b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/EmbeddableSettingsPage.java
@@ -30,4 +30,11 @@
      * <p>The activity will observe changes to this value and update the UI as necessary.
      */
     ObservableSupplier<String> getPageTitle();
+
+    @Override
+    default @AnimationType int getAnimationType() {
+        // TODO(crbug.com/404074032): Each leaf subclass should override this method to use
+        // PROPERTY animation with auditing.
+        return AnimationType.TWEEN;
+    }
 }
diff --git a/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsFragment.java b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsFragment.java
index 992751c..6fb44b83 100644
--- a/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsFragment.java
+++ b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsFragment.java
@@ -33,9 +33,5 @@
     }
 
     /** Returns animation type to be used for fragment transition. */
-    default @AnimationType int getAnimationType() {
-        // TODO(crbug.com/404074032): Each leaf subclass should override this method to use
-        // PROPERTY animation with auditing.
-        return AnimationType.TWEEN;
-    }
+    public @AnimationType int getAnimationType();
 }
diff --git a/components/browser_ui/widget/android/BUILD.gn b/components/browser_ui/widget/android/BUILD.gn
index 8eff26d..5030bd1 100644
--- a/components/browser_ui/widget/android/BUILD.gn
+++ b/components/browser_ui/widget/android/BUILD.gn
@@ -74,6 +74,7 @@
     "java/src/org/chromium/components/browser_ui/widget/dragreorder/DragStateDelegate.java",
     "java/src/org/chromium/components/browser_ui/widget/dragreorder/DragUtils.java",
     "java/src/org/chromium/components/browser_ui/widget/gesture/BackPressHandler.java",
+    "java/src/org/chromium/components/browser_ui/widget/gesture/OnSystemNavigationObserver.java",
     "java/src/org/chromium/components/browser_ui/widget/gesture/SwipeGestureListener.java",
     "java/src/org/chromium/components/browser_ui/widget/highlight/PulseDrawable.java",
     "java/src/org/chromium/components/browser_ui/widget/highlight/PulseInterpolator.java",
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/gesture/OnSystemNavigationObserver.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/gesture/OnSystemNavigationObserver.java
new file mode 100644
index 0000000..57998e8
--- /dev/null
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/gesture/OnSystemNavigationObserver.java
@@ -0,0 +1,22 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.browser_ui.widget.gesture;
+
+import org.chromium.build.annotations.NullMarked;
+
+/** Observer of the system navigation. */
+@NullMarked
+public interface OnSystemNavigationObserver {
+    /**
+     * Invoked when a back navigation event is handled by the Android OS itself, rather than by a
+     * specific in-app feature (see {@link BackPressHandler}). This typically occurs when the app is
+     * about to minimize or close. This callback is intended primary for logging and recording
+     * purposes and must not be used for any heavy task.
+     *
+     * <p>All registered observers will be called and so any two observers should be mutually
+     * exclusive.
+     */
+    void onSystemNavigation();
+}
diff --git a/components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc b/components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc
index 6a8232b9..0b31cb6 100644
--- a/components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc
+++ b/components/gcm_driver/crypto/gcm_message_cryptographer_unittest.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/gcm_driver/crypto/gcm_message_cryptographer.h"
 
diff --git a/components/gwp_asan/client/lightweight_detector/poison_metadata_recorder_unittest.cc b/components/gwp_asan/client/lightweight_detector/poison_metadata_recorder_unittest.cc
index 1925b0e..fdb1b9c3 100644
--- a/components/gwp_asan/client/lightweight_detector/poison_metadata_recorder_unittest.cc
+++ b/components/gwp_asan/client/lightweight_detector/poison_metadata_recorder_unittest.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/gwp_asan/client/lightweight_detector/poison_metadata_recorder.h"
 
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index 7ccab5d..216c56c 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -161,6 +161,7 @@
     "autocomplete_controller_emitter.h",
     "autocomplete_controller_metrics.cc",
     "autocomplete_controller_metrics.h",
+    "autocomplete_enums.h",
     "autocomplete_grouper_groups.cc",
     "autocomplete_grouper_groups.h",
     "autocomplete_grouper_sections.cc",
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index c9fc066d..dc8eefa 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -45,6 +45,7 @@
 #include "components/omnibox/browser/actions/omnibox_action_in_suggest.h"
 #include "components/omnibox/browser/actions/omnibox_answer_action.h"
 #include "components/omnibox/browser/actions/omnibox_pedal_provider.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_type.h"
@@ -356,7 +357,7 @@
 }  // namespace
 
 AutocompleteController::OldResult::OldResult(UpdateType update_type,
-                                             AutocompleteInput input,
+                                             const AutocompleteInput& input,
                                              AutocompleteResult* result) {
   if (result->default_match()) {
     last_default_match = *result->default_match();
@@ -612,15 +613,11 @@
 AutocompleteController::~AutocompleteController() {
   base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
       this);
-
-  // The providers may have tasks outstanding that hold refs to them.  We need
-  // to ensure they won't call us back if they outlive us.  (Practically,
-  // calling Stop() should also cancel those tasks and make it so that we hold
-  // the only refs.)  We also don't want to bother notifying anyone of our
-  // result changes here, because the notification observer is in the midst of
-  // shutdown too, so we don't ask Stop() to clear `internal_result_` (and
-  // notify).
-  Stop(false);
+  // Must stop providers because they may have unowned tasks that continue to
+  // run or hold refs to them; e.g. remote requests or DB reads. Don't bother
+  // notifying observers or using `kClobbered` to clear provider caches and
+  // state, since the observers and providers are being destroyed too.
+  Stop(AutocompleteStopReason::kInteraction);
 }
 
 void AutocompleteController::AddObserver(Observer* observer) {
@@ -772,7 +769,7 @@
 
     // Avoid starting a prefetch request if a non-prefetch request is in
     // progress. Though explicitly discouraged as per documentation in
-    // AutocompleteProvider::StartPrefetch(), a provider may still cancel its
+    // `AutocompleteProvider::StartPrefetch()`, a provider may still cancel its
     // in-flight non-prefetch request when a prefetch request is started. This
     // may cause the provider to never get a chance to notify the controller of
     // its status; resulting in the controller to remain in an invalid state.
@@ -785,8 +782,7 @@
   }
 }
 
-void AutocompleteController::Stop(bool clear_result,
-                                  bool due_to_user_inactivity) {
+void AutocompleteController::Stop(AutocompleteStopReason stop_reason) {
   // Must be called before `expire_timer_.Stop()`, modifying `done_`, or
   // modifying `AutocompleteProvider::done_` below. If the current request has
   // not completed, and therefore has not been logged yet, will log it now.
@@ -797,17 +793,16 @@
   for (const auto& provider : providers_) {
     if (!ShouldRunProvider(provider.get()))
       continue;
-    provider->Stop(clear_result, due_to_user_inactivity);
+    provider->Stop(stop_reason);
   }
 
-  UpdateResult(UpdateType::kStop);
-
   // Cancel any pending requests that may update the results. Otherwise, e.g.,
   // the user's suggestion selection may be reset.
+  UpdateResult(UpdateType::kStop);
   CancelNotifyChangedRequest();
 
   const bool non_empty_result = !internal_result_.empty();
-  if (clear_result) {
+  if (stop_reason == AutocompleteStopReason::kClobbered) {
     internal_result_.Reset();
     if (non_empty_result) {
       // Pass `notify_default_match` as false to clear only the popup and not
@@ -2128,7 +2123,7 @@
 }
 
 void AutocompleteController::OnStopTimerTriggered() {
-  Stop(false, true);
+  Stop(AutocompleteStopReason::kInactivity);
   for (Observer& obs : observers_) {
     obs.OnAutocompleteStopTimerTriggered(input_);
   }
diff --git a/components/omnibox/browser/autocomplete_controller.h b/components/omnibox/browser/autocomplete_controller.h
index 9423d8e..8eba8dd 100644
--- a/components/omnibox/browser/autocomplete_controller.h
+++ b/components/omnibox/browser/autocomplete_controller.h
@@ -23,6 +23,7 @@
 #include "base/trace_event/memory_dump_provider.h"
 #include "build/build_config.h"
 #include "components/omnibox/browser/autocomplete_controller_metrics.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
@@ -206,12 +207,11 @@
   // Made virtual for mocking in tests.
   virtual void StartPrefetch(const AutocompleteInput& input);
 
-  // Cancels the current query, ensuring there will be no future notifications
-  // fired.  If new matches have come in since the most recent notification was
-  // fired, they will be discarded. If `clear_result` is true, the controller
-  // will also erase the result set. `due_to_user_inactivity` means this call
-  // was triggered by a user's idleness, i.e., not an explicit user action.
-  void Stop(bool clear_result, bool due_to_user_inactivity = false);
+  // Cancels the current query, ensuring most future updates won't fire
+  // notifications. If new matches have come in since the most recent
+  // notification was fired, they may be discarded. See
+  // `AutocompleteProvider::Stop()` & `AutocompleteStopReason`.
+  void Stop(AutocompleteStopReason stop_reason);
 
   // Asks the relevant provider to delete |match|, and ensures observers are
   // notified of resulting changes immediately.  This should only be called when
@@ -400,7 +400,7 @@
   // `UpdateResult()`'s helper methods.
   struct OldResult {
     OldResult(UpdateType update_type,
-              AutocompleteInput input,
+              const AutocompleteInput& input,
               AutocompleteResult* result);
     ~OldResult();
 
diff --git a/components/omnibox/browser/autocomplete_controller_metrics_unittest.cc b/components/omnibox/browser/autocomplete_controller_metrics_unittest.cc
index 70cdb4e..7d39bf4 100644
--- a/components/omnibox/browser/autocomplete_controller_metrics_unittest.cc
+++ b/components/omnibox/browser/autocomplete_controller_metrics_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "components/omnibox/browser/autocomplete_controller.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_result.h"
 #include "components/omnibox/browser/fake_autocomplete_controller.h"
 #include "components/omnibox/browser/fake_autocomplete_provider.h"
@@ -116,7 +117,7 @@
   // Simulates `AutocompleteController::Stop()`.
   void SimulateOnStop() {
     task_environment_.FastForwardBy(base::Milliseconds(1));
-    controller_.Stop(false);
+    controller_.Stop(AutocompleteStopReason::kInteraction);
   }
 
   // Convenience function to be called before/after EXPECT'ing histograms to
@@ -132,7 +133,7 @@
   // when the controller is interrupted in which case metrics are expected to be
   // logged. Does not check provider or cross stability metrics.
   void StopAndExpectNoSuggestionFinalizationMetrics() {
-    controller_.Stop(false, false);
+    controller_.Stop(AutocompleteStopReason::kClobbered);
     ExpectNoSuggestionFinalizationMetrics();
   }
 
diff --git a/components/omnibox/browser/autocomplete_controller_unittest.cc b/components/omnibox/browser/autocomplete_controller_unittest.cc
index c46c4e5..c5ab43f 100644
--- a/components/omnibox/browser/autocomplete_controller_unittest.cc
+++ b/components/omnibox/browser/autocomplete_controller_unittest.cc
@@ -19,6 +19,7 @@
 #include "build/build_config.h"
 #include "components/omnibox/browser/actions/omnibox_answer_action.h"
 #include "components/omnibox/browser/autocomplete_controller.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_test_util.h"
 #include "components/omnibox/browser/autocomplete_match_type.h"
@@ -740,7 +741,7 @@
   }
   {
     SCOPED_TRACE("Stop with clear_result=false is called due to user idleness");
-    controller_.Stop(/*clear_result=*/false);
+    controller_.Stop(AutocompleteStopReason::kInteraction);
     // Stop with clear_result=false does not clear the internal result set and
     // does not notify `OnResultChanged()`.
     EXPECT_FALSE(controller_.internal_result_.empty());
@@ -799,7 +800,7 @@
   }
   {
     SCOPED_TRACE("Stop with clear_result=true is called due to popup closing");
-    controller_.Stop(/*clear_result=*/true);
+    controller_.Stop(AutocompleteStopReason::kClobbered);
     // Stop with clear_result=true clears the internal result set and notifies
     // `OnResultChanged()`.
     EXPECT_TRUE(controller_.internal_result_.empty());
@@ -1875,34 +1876,34 @@
 
   {
     SCOPED_TRACE(
-        "Stop with clear_result=false and no pending changes should not notify"
+        "Stop with `kInteraction` and no pending changes should not notify "
         "`OnResultChanged()` - there's no change to notify of.");
     controller_.SimulateAutocompletePass(true, false, matches);
-    controller_.Stop(false);
+    controller_.Stop(AutocompleteStopReason::kInteraction);
     controller_.ExpectStopAfter(0, true);
     EXPECT_FALSE(controller_.published_result_.empty());
     controller_.ExpectNoNotificationOrStop();
   }
   {
     SCOPED_TRACE(
-        "Stop with clear_result=false and pending changes should not notify"
-        "`OnResultChanged()` - the last pending change should be abandoned to "
-        "avoid changes as the user's e.g. down arrowing..");
+        "Stop with `kInteraction` and pending changes should not notify "
+        "`OnResultChanged()` - the last pending change should be "
+        "abandoned to avoid changes as the user's e.g. down arrowing.");
     controller_.SimulateAutocompletePass(true, false, matches);
     controller_.SimulateAutocompletePass(false, false, matches);
-    controller_.Stop(false);
+    controller_.Stop(AutocompleteStopReason::kInteraction);
     EXPECT_FALSE(controller_.published_result_.empty());
     controller_.ExpectStopAfter(0, true);
     controller_.ExpectNoNotificationOrStop();
   }
   {
     SCOPED_TRACE(
-        "Stop with clear_result=true and no pending notifications should "
-        "notify `OnResultChanged()` - observers should know the results were "
+        "Stop with `kClobbered` and no pending notifications should notify "
+        "`OnResultChanged()` - observers should know the results were "
         "cleared.");
     controller_.SimulateAutocompletePass(true, false, matches);
     controller_.observer_->last_default_match_changed = true;
-    controller_.Stop(true);
+    controller_.Stop(AutocompleteStopReason::kClobbered);
     EXPECT_TRUE(controller_.published_result_.empty());
     controller_.ExpectOnResultChanged(
         0, AutocompleteController::UpdateType::kStop);
@@ -1911,13 +1912,13 @@
   }
   {
     SCOPED_TRACE(
-        "Stop with clear_result=true and pending notifications should notify "
-        "`OnResultChanged()` - observers should know the results were "
+        "Stop with `kClobbered` and pending notifications should notify "
+        "`OnResultChanged()` - observers should know the results were cleared."
         "cleared.");
     controller_.SimulateAutocompletePass(true, false, matches);
     controller_.SimulateAutocompletePass(false, false, matches);
     controller_.observer_->last_default_match_changed = true;
-    controller_.Stop(true);
+    controller_.Stop(AutocompleteStopReason::kClobbered);
     EXPECT_TRUE(controller_.published_result_.empty());
     controller_.ExpectOnResultChanged(
         0, AutocompleteController::UpdateType::kStop);
@@ -2445,7 +2446,7 @@
   AutocompleteMatch match1 = CreateSearchMatch("match1", true, 1300);
   match1.actions.push_back(answer_action);
 
-  controller_.Stop(true);
+  controller_.Stop(AutocompleteStopReason::kClobbered);
   EXPECT_THAT(controller_.SimulateAutocompletePass(
                   /*sync=*/true, /*done=*/true,
                   {match1, CreateSearchMatch("match2", true, 1200),
@@ -2561,11 +2562,11 @@
   controller_.AttachActions();
 
   // The takeover action should be for the contextual search action, not pedals.
-  EXPECT_TRUE(controller_.internal_result_.match_at(0)->takeover_action);
+  ASSERT_TRUE(controller_.internal_result_.match_at(0)->takeover_action);
   EXPECT_EQ(
       OmniboxActionId::CONTEXTUAL_SEARCH_FULFILLMENT,
       controller_.internal_result_.match_at(0)->takeover_action->ActionId());
-  EXPECT_TRUE(controller_.internal_result_.match_at(1)->takeover_action);
+  ASSERT_TRUE(controller_.internal_result_.match_at(1)->takeover_action);
   EXPECT_EQ(
       OmniboxActionId::CONTEXTUAL_SEARCH_FULFILLMENT,
       controller_.internal_result_.match_at(1)->takeover_action->ActionId());
@@ -2619,11 +2620,11 @@
   EXPECT_FALSE(controller_.internal_result_.match_at(0)->takeover_action);
   EXPECT_FALSE(controller_.internal_result_.match_at(3)->takeover_action);
 
-  EXPECT_TRUE(controller_.internal_result_.match_at(1)->takeover_action);
+  ASSERT_TRUE(controller_.internal_result_.match_at(1)->takeover_action);
   EXPECT_EQ(
       OmniboxActionId::CONTEXTUAL_SEARCH_FULFILLMENT,
       controller_.internal_result_.match_at(1)->takeover_action->ActionId());
-  EXPECT_TRUE(controller_.internal_result_.match_at(2)->takeover_action);
+  ASSERT_TRUE(controller_.internal_result_.match_at(2)->takeover_action);
   EXPECT_EQ(
       OmniboxActionId::CONTEXTUAL_SEARCH_FULFILLMENT,
       controller_.internal_result_.match_at(2)->takeover_action->ActionId());
diff --git a/components/omnibox/browser/autocomplete_enums.h b/components/omnibox/browser/autocomplete_enums.h
new file mode 100644
index 0000000..e88bdfc
--- /dev/null
+++ b/components/omnibox/browser/autocomplete_enums.h
@@ -0,0 +1,38 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OMNIBOX_BROWSER_AUTOCOMPLETE_ENUMS_H_
+#define COMPONENTS_OMNIBOX_BROWSER_AUTOCOMPLETE_ENUMS_H_
+
+// The reason `AutocompleteController::Stop()` or `AutocompleteProvider::Stop()`
+// were called.
+enum class AutocompleteStopReason {
+
+  // - Called when: Significant change occurred and the existing state is no
+  //   longer useful.
+  // - Action taken: Stop all providers and clear results.
+  // - Example calls: The omnibox popup is closed or when some providers
+  //   `Start()`.
+  kClobbered,
+
+  // - Called when: Omnibox was interacted with in a way that requires its state
+  //   to be frozen.
+  // - Action taken: Stop all providers and keep results.
+  // - Example calls: Entered/left keyword mode, arrowed down through
+  //   suggestions, arrowed left through text, match deleted , or when some
+  //   providers `Start()`.
+  kInteraction,
+
+  // - Called when: User hasn't interacted with the omnibox for a while. See
+  //   `AutocompleteController::stop_timer_duration_` comment.
+  // - Action taken: Stop most providers and keep results. Some providers are
+  //   allowed to continue if there's a good reason the user is likely to want
+  //   even long-delayed asynchronous results, e.g. the user has explicitly
+  //   invoked a keyword extension and the extension is still processing the
+  //   request.
+  // - Example calls: `stop_timer_` triggers.
+  kInactivity,
+};
+
+#endif  // COMPONENTS_OMNIBOX_BROWSER_AUTOCOMPLETE_ENUMS_H_
diff --git a/components/omnibox/browser/autocomplete_input.cc b/components/omnibox/browser/autocomplete_input.cc
index d0026474..5534ab7b 100644
--- a/components/omnibox/browser/autocomplete_input.cc
+++ b/components/omnibox/browser/autocomplete_input.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/omnibox/browser/autocomplete_input.h"
 
diff --git a/components/omnibox/browser/autocomplete_provider.cc b/components/omnibox/browser/autocomplete_provider.cc
index 1b4d7e3b5..c9bf366 100644
--- a/components/omnibox/browser/autocomplete_provider.cc
+++ b/components/omnibox/browser/autocomplete_provider.cc
@@ -18,6 +18,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/memory_usage_estimator.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_i18n.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
@@ -138,10 +139,9 @@
   DCHECK(!input.omit_asynchronous_matches());
 }
 
-void AutocompleteProvider::Stop(bool clear_cached_results,
-                                bool due_to_user_inactivity) {
+void AutocompleteProvider::Stop(AutocompleteStopReason stop_reason) {
   done_ = true;
-  if (clear_cached_results) {
+  if (stop_reason == AutocompleteStopReason::kClobbered) {
     matches_.clear();
     suggestion_groups_map_.clear();
   }
@@ -239,7 +239,9 @@
 }
 
 AutocompleteProvider::~AutocompleteProvider() {
-  Stop(false, false);
+  // Don't bother using `kClobbered` to clear caches and state, since those will
+  // be destroyed with the provider.
+  Stop(AutocompleteStopReason::kInteraction);
 }
 
 // static
diff --git a/components/omnibox/browser/autocomplete_provider.h b/components/omnibox/browser/autocomplete_provider.h
index d3d3db6..ae24c62 100644
--- a/components/omnibox/browser/autocomplete_provider.h
+++ b/components/omnibox/browser/autocomplete_provider.h
@@ -15,6 +15,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/in_memory_url_index_types.h"
 #include "components/omnibox/browser/suggestion_group_util.h"
@@ -249,25 +250,12 @@
   // AutocompleteController::Start().
   virtual void Start(const AutocompleteInput& input, bool minimal_changes) = 0;
 
-  // Advises the provider to stop processing.  This may be called even if the
-  // provider is already done.  If the provider caches any results, it should
-  // clear the cache based on the value of `clear_cached_results`.  Normally,
-  // once this is called, the provider should not send more notifications to
-  // the controller.
-  //
-  // If `user_inactivity_timer` is true, Stop() is being called because it's
-  // been a long time since the user started the current query, and returning
-  // further asynchronous results would normally just be disruptive.  Most
-  // providers should still stop processing in this case, but continuing is
-  // legal if there's a good reason the user is likely to want even long-
-  // delayed asynchronous results, e.g. the user has explicitly invoked a
-  // keyword extension and the extension is still processing the request.
-  //
-  // The default implementation sets `done_` to true and clears `matches_` if
-  // `clear_cached_results` is true. Overridden functions must call
-  // `AutocompleteProvider::Stop()` with the same arguments passed to the
-  // function.
-  virtual void Stop(bool clear_cached_results, bool due_to_user_inactivity);
+  // Advises the provider to stop processing. This may be called even if the
+  // provider is already done. Normally, once this is called, the provider
+  // should not send more notifications to the controller. Overridden functions
+  // must call `AutocompleteProvider::Stop()` with the same `stop_reason` passed
+  // to the function.
+  virtual void Stop(AutocompleteStopReason stop_reason);
 
   // Returns the enum equivalent to the name of this provider.
   // TODO(derat): Make metrics use AutocompleteProvider::Type directly, or at
diff --git a/components/omnibox/browser/bookmark_provider_unittest.cc b/components/omnibox/browser/bookmark_provider_unittest.cc
index b1e8ff4..5823e7a 100644
--- a/components/omnibox/browser/bookmark_provider_unittest.cc
+++ b/components/omnibox/browser/bookmark_provider_unittest.cc
@@ -8,10 +8,6 @@
 
 #include "components/query_parser/query_parser.h"
 #include "third_party/omnibox_proto/groups.pb.h"
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include <stddef.h>
 
diff --git a/components/omnibox/browser/calculator_provider.cc b/components/omnibox/browser/calculator_provider.cc
index f8894e5..0d0a348 100644
--- a/components/omnibox/browser/calculator_provider.cc
+++ b/components/omnibox/browser/calculator_provider.cc
@@ -64,11 +64,6 @@
   }
 }
 
-void CalculatorProvider::Stop(bool clear_cached_results,
-                              bool due_to_user_inactivity) {
-  done_ = true;
-}
-
 void CalculatorProvider::DeleteMatch(const AutocompleteMatch& match) {
   auto it = std::ranges::find_if(Cache(), [&](const auto& cached) {
     return cached.match.destination_url == match.destination_url;
diff --git a/components/omnibox/browser/calculator_provider.h b/components/omnibox/browser/calculator_provider.h
index 3902597..b9abbc5f 100644
--- a/components/omnibox/browser/calculator_provider.h
+++ b/components/omnibox/browser/calculator_provider.h
@@ -48,7 +48,6 @@
 
   // AutocompleteProvider:
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
   void DeleteMatch(const AutocompleteMatch& match) override;
 
   // AutocompleteProviderListener:
diff --git a/components/omnibox/browser/clipboard_provider.cc b/components/omnibox/browser/clipboard_provider.cc
index a6320c3..7f7f11b 100644
--- a/components/omnibox/browser/clipboard_provider.cc
+++ b/components/omnibox/browser/clipboard_provider.cc
@@ -21,6 +21,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/thread_pool.h"
 #include "build/build_config.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
@@ -198,10 +199,8 @@
   }
 }
 
-void ClipboardProvider::Stop(bool clear_cached_results,
-                             bool due_to_user_inactivity) {
-  AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
-
+void ClipboardProvider::Stop(AutocompleteStopReason stop_reason) {
+  AutocompleteProvider::Stop(stop_reason);
   callback_weak_ptr_factory_.InvalidateWeakPtrs();
 }
 
diff --git a/components/omnibox/browser/clipboard_provider.h b/components/omnibox/browser/clipboard_provider.h
index ea1b2cf..9daf7ebf 100644
--- a/components/omnibox/browser/clipboard_provider.h
+++ b/components/omnibox/browser/clipboard_provider.h
@@ -7,6 +7,7 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 
 class AutocompleteProviderClient;
@@ -49,7 +50,7 @@
 
   // AutocompleteProvider implementation.
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
+  void Stop(AutocompleteStopReason stop_reason) override;
   void DeleteMatch(const AutocompleteMatch& match) override;
   void AddProviderInfo(ProvidersInfo* provider_info) const override;
 
diff --git a/components/omnibox/browser/contextual_search_provider.cc b/components/omnibox/browser/contextual_search_provider.cc
index e02e01d5..72d4d97 100644
--- a/components/omnibox/browser/contextual_search_provider.cc
+++ b/components/omnibox/browser/contextual_search_provider.cc
@@ -23,6 +23,7 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "components/omnibox/browser/actions/contextual_search_action.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_classification.h"
@@ -92,7 +93,7 @@
   TRACE_EVENT0("omnibox", "ContextualSearchProvider::Start");
   // Clear the cached results to remove the page search action matches. Also,
   // matches the behavior of the `ZeroSuggestProvider`.
-  Stop(/*clear_cached_results=*/true, /*due_to_user_inactivity=*/false);
+  Stop(AutocompleteStopReason::kClobbered);
 
   if (client()->IsOffTheRecord()) {
     done_ = true;
@@ -128,17 +129,16 @@
   StartSuggestRequest(std::move(input));
 }
 
-void ContextualSearchProvider::Stop(bool clear_cached_results,
-                                    bool due_to_user_inactivity) {
-  if (!due_to_user_inactivity) {
-    // Stop the pending request if the sotp is not due to user inactivity. If
-    // it is due to user inactivity, the request will continue so the
-    // suggestions can be shown when they are ready.
-    AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
-    lens_suggest_inputs_subscription_ = {};
-    loader_.reset();
-    input_keyword_.clear();
+void ContextualSearchProvider::Stop(AutocompleteStopReason stop_reason) {
+  // If the stop is due to user inactivity, the request will continue so the
+  // suggestions can be shown when they are ready.
+  if (stop_reason == AutocompleteStopReason::kInactivity) {
+    return;
   }
+  AutocompleteProvider::Stop(stop_reason);
+  lens_suggest_inputs_subscription_ = {};
+  loader_.reset();
+  input_keyword_.clear();
 }
 
 void ContextualSearchProvider::AddProviderInfo(
diff --git a/components/omnibox/browser/contextual_search_provider.h b/components/omnibox/browser/contextual_search_provider.h
index eafd703..9b9fab31 100644
--- a/components/omnibox/browser/contextual_search_provider.h
+++ b/components/omnibox/browser/contextual_search_provider.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/callback_list.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/base_search_provider.h"
 
@@ -34,7 +35,7 @@
 
   // AutocompleteProvider:
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
+  void Stop(AutocompleteStopReason stop_reason) override;
   void AddProviderInfo(ProvidersInfo* provider_info) const override;
 
  protected:
diff --git a/components/omnibox/browser/document_provider.cc b/components/omnibox/browser/document_provider.cc
index 38f1973..134d80e 100644
--- a/components/omnibox/browser/document_provider.cc
+++ b/components/omnibox/browser/document_provider.cc
@@ -31,6 +31,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_classification.h"
@@ -478,8 +479,7 @@
 void DocumentProvider::Start(const AutocompleteInput& input,
                              bool minimal_changes) {
   TRACE_EVENT0("omnibox", "DocumentProvider::Start");
-  Stop(true, false);
-
+  Stop(AutocompleteStopReason::kClobbered);
   // Perform various checks - feature is enabled, user is allowed to use the
   // feature, we're not under backoff, etc.
   if (!IsDocumentProviderAllowed(input))
@@ -517,10 +517,9 @@
               base::Unretained(this) /* this owns SimpleURLLoader */));
 }
 
-void DocumentProvider::Stop(bool clear_cached_results,
-                            bool due_to_user_inactivity) {
+void DocumentProvider::Stop(AutocompleteStopReason stop_reason) {
   TRACE_EVENT0("omnibox", "DocumentProvider::Stop");
-  AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
+  AutocompleteProvider::Stop(stop_reason);
 
   debouncer_->CancelRequest();
 
diff --git a/components/omnibox/browser/document_provider.h b/components/omnibox/browser/document_provider.h
index a99ac841..e683c47 100644
--- a/components/omnibox/browser/document_provider.h
+++ b/components/omnibox/browser/document_provider.h
@@ -20,6 +20,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
 #include "components/history/core/browser/history_types.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/autocomplete_provider_debouncer.h"
 #include "third_party/metrics_proto/omnibox_event.pb.h"
@@ -48,7 +49,7 @@
 
   // AutocompleteProvider:
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
+  void Stop(AutocompleteStopReason stop_reason) override;
   void DeleteMatch(const AutocompleteMatch& match) override;
   void AddProviderInfo(ProvidersInfo* provider_info) const override;
 
diff --git a/components/omnibox/browser/document_provider_unittest.cc b/components/omnibox/browser/document_provider_unittest.cc
index 1eadf43..85501d6 100644
--- a/components/omnibox/browser/document_provider_unittest.cc
+++ b/components/omnibox/browser/document_provider_unittest.cc
@@ -24,6 +24,7 @@
 #include "base/values.h"
 #include "build/blink_buildflags.h"
 #include "build/build_config.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_match_type.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
@@ -1042,7 +1043,7 @@
   {
     SCOPED_TRACE("Case: Stop() before Run().");
     base::HistogramTester histogram_tester;
-    provider_->Stop(false, false);
+    provider_->Stop(AutocompleteStopReason::kClobbered);
     histogram_tester.ExpectTotalCount("Omnibox.DocumentSuggest.Requests", 0);
     histogram_tester.ExpectTotalCount("Omnibox.DocumentSuggest.TotalTime", 0);
     histogram_tester.ExpectTotalCount(
@@ -1060,7 +1061,7 @@
     SCOPED_TRACE("Case: Stop() before request.");
     base::HistogramTester histogram_tester;
     provider_->time_run_invoked_ = base::TimeTicks::Now();
-    provider_->Stop(false, false);
+    provider_->Stop(AutocompleteStopReason::kClobbered);
     histogram_tester.ExpectTotalCount("Omnibox.DocumentSuggest.Requests", 0);
     histogram_tester.ExpectTotalCount("Omnibox.DocumentSuggest.TotalTime", 1);
     histogram_tester.ExpectTotalCount(
@@ -1082,7 +1083,7 @@
         network::SimpleURLLoader::Create(
             std::make_unique<network::ResourceRequest>(),
             net::DefineNetworkTrafficAnnotation("test", "test")));
-    provider_->Stop(false, false);
+    provider_->Stop(AutocompleteStopReason::kClobbered);
     histogram_tester.ExpectTotalCount("Omnibox.DocumentSuggest.Requests", 2);
     histogram_tester.ExpectBucketCount("Omnibox.DocumentSuggest.Requests", 1,
                                        1);
diff --git a/components/omnibox/browser/enterprise_search_aggregator_provider.cc b/components/omnibox/browser/enterprise_search_aggregator_provider.cc
index 34a38f8..11b1d42 100644
--- a/components/omnibox/browser/enterprise_search_aggregator_provider.cc
+++ b/components/omnibox/browser/enterprise_search_aggregator_provider.cc
@@ -28,6 +28,7 @@
 #include "base/time/time.h"
 #include "base/types/expected.h"
 #include "base/values.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_classification.h"
@@ -437,8 +438,7 @@
                                                bool minimal_changes) {
   // Don't clear matches. Keep showing old matches until a new response comes.
   // This avoids flickering.
-  Stop(/*clear_cached_results=*/false,
-       /*due_to_user_inactivity=*/false);
+  Stop(AutocompleteStopReason::kInteraction);
 
   if (!IsProviderAllowed(input)) {
     // Clear old matches if provider is not allowed.
@@ -480,24 +480,25 @@
       &EnterpriseSearchAggregatorProvider::Run, base::Unretained(this)));
 }
 
-void EnterpriseSearchAggregatorProvider::Stop(bool clear_cached_results,
-                                              bool due_to_user_inactivity) {
-  // Ignore the stop timer since this provider is expected to take longer than
-  // 1500ms (the stop timer gets triggered due to user inactivity).
-  if (!due_to_user_inactivity) {
-    AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
-    debouncer_->CancelRequest();
+void EnterpriseSearchAggregatorProvider::Stop(
+    AutocompleteStopReason stop_reason) {
+  // Ignore the stop timer since this provider is expected to sometimes take
+  // longer than 1500ms.
+  if (stop_reason == AutocompleteStopReason::kInactivity) {
+    return;
+  }
+  AutocompleteProvider::Stop(stop_reason);
+  debouncer_->CancelRequest();
 
-    if (auto* remote_suggestions_service = client_->GetRemoteSuggestionsService(
-            /*create_if_necessary=*/false)) {
-      remote_suggestions_service
-          ->StopCreatingEnterpriseSearchAggregatorSuggestionsRequest();
-    }
+  if (auto* remote_suggestions_service = client_->GetRemoteSuggestionsService(
+          /*create_if_necessary=*/false)) {
+    remote_suggestions_service
+        ->StopCreatingEnterpriseSearchAggregatorSuggestionsRequest();
+  }
 
-    if (loader_) {
-      LogResponseTime(true);
-      loader_.reset();
-    }
+  if (loader_) {
+    LogResponseTime(true);
+    loader_.reset();
   }
 }
 
diff --git a/components/omnibox/browser/enterprise_search_aggregator_provider.h b/components/omnibox/browser/enterprise_search_aggregator_provider.h
index bb8d6559..ace7108 100644
--- a/components/omnibox/browser/enterprise_search_aggregator_provider.h
+++ b/components/omnibox/browser/enterprise_search_aggregator_provider.h
@@ -16,6 +16,7 @@
 #include "base/time/time.h"
 #include "base/types/expected.h"
 #include "base/values.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
@@ -48,7 +49,7 @@
 
   // AutocompleteProvider:
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
+  void Stop(AutocompleteStopReason stop_reason) override;
 
  private:
   friend class FakeEnterpriseSearchAggregatorProvider;
diff --git a/components/omnibox/browser/enterprise_search_aggregator_provider_unittest.cc b/components/omnibox/browser/enterprise_search_aggregator_provider_unittest.cc
index 9e5c8cb8..92168c1 100644
--- a/components/omnibox/browser/enterprise_search_aggregator_provider_unittest.cc
+++ b/components/omnibox/browser/enterprise_search_aggregator_provider_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/time/time.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_type.h"
@@ -1669,7 +1670,7 @@
   {
     SCOPED_TRACE("Case: Stop() before Run().");
     base::HistogramTester histogram_tester;
-    provider_->Stop(false, false);
+    provider_->Stop(AutocompleteStopReason::kClobbered);
     histogram_tester.ExpectTotalCount(
         "Omnibox.SuggestRequestsSent.ResponseTime2.RequestState."
         "EnterpriseSearchAggregatorSuggest.Interrupted",
@@ -1687,7 +1688,7 @@
     provider_->RequestStarted(network::SimpleURLLoader::Create(
         std::make_unique<network::ResourceRequest>(),
         net::DefineNetworkTrafficAnnotation("test", "test")));
-    provider_->Stop(false, false);
+    provider_->Stop(AutocompleteStopReason::kClobbered);
     histogram_tester.ExpectTotalCount(
         "Omnibox.SuggestRequestsSent.ResponseTime2.RequestState."
         "EnterpriseSearchAggregatorSuggest.Interrupted",
diff --git a/components/omnibox/browser/featured_search_provider_unittest.cc b/components/omnibox/browser/featured_search_provider_unittest.cc
index cd7cb89..450f5df6 100644
--- a/components/omnibox/browser/featured_search_provider_unittest.cc
+++ b/components/omnibox/browser/featured_search_provider_unittest.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/omnibox/browser/featured_search_provider.h"
 
diff --git a/components/omnibox/browser/history_cluster_provider.cc b/components/omnibox/browser/history_cluster_provider.cc
index 1d834289..2f4d955f 100644
--- a/components/omnibox/browser/history_cluster_provider.cc
+++ b/components/omnibox/browser/history_cluster_provider.cc
@@ -13,15 +13,15 @@
 #include "components/history_clusters/core/url_constants.h"
 #include "components/omnibox/browser/actions/history_clusters_action.h"
 #include "components/omnibox/browser/autocomplete_controller.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_classification.h"
 #include "components/omnibox/browser/autocomplete_match_type.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
-#include "third_party/metrics_proto/omnibox_event.pb.h"
-
 #include "components/omnibox/browser/autocomplete_provider_client.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
 #include "components/strings/grit/components_strings.h"
+#include "third_party/metrics_proto/omnibox_event.pb.h"
 #include "ui/base/l10n/l10n_util.h"
 
 HistoryClusterProvider::HistoryClusterProvider(
@@ -63,8 +63,7 @@
 
 void HistoryClusterProvider::Start(const AutocompleteInput& input,
                                    bool minimal_changes) {
-  Stop(true, false);
-
+  Stop(AutocompleteStopReason::kClobbered);
   if (input.omit_asynchronous_matches())
     return;
 
diff --git a/components/omnibox/browser/history_embeddings_provider.cc b/components/omnibox/browser/history_embeddings_provider.cc
index 85117cd..28b6f062 100644
--- a/components/omnibox/browser/history_embeddings_provider.cc
+++ b/components/omnibox/browser/history_embeddings_provider.cc
@@ -16,6 +16,7 @@
 #include "components/history_clusters/core/history_clusters_util.h"
 #include "components/history_embeddings/history_embeddings_features.h"
 #include "components/history_embeddings/history_embeddings_service.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_classification.h"
@@ -97,16 +98,15 @@
                           weak_factory_.GetWeakPtr()));
 }
 
-void HistoryEmbeddingsProvider::Stop(bool clear_cached_results,
-                                     bool due_to_user_inactivity) {
+void HistoryEmbeddingsProvider::Stop(AutocompleteStopReason stop_reason) {
   // TODO(crbug.com/364303536): Ignore the stop timer since we know answers take
-  //   longer than 1500ms to generate. This inadvertently also ignores stops
-  //   caused by user action. A real fix is for providers to inform the
-  //   controller that they expect a slow response and the controller to
-  //   accommodate it by updating its stop, debounce, and cache timers'
-  //   behaviors.
-  if (!due_to_user_inactivity && !done_) {
-    done_ = true;
+  //   longer than 1500ms to generate.
+  if (stop_reason == AutocompleteStopReason::kInactivity) {
+    return;
+  }
+
+  // Erase the abandoned placeholder answer.
+  if (!done_) {
     size_t erased_count = std::erase_if(matches_, [&](const auto& match) {
       return match.type == AutocompleteMatchType::HISTORY_EMBEDDINGS_ANSWER;
     });
@@ -115,6 +115,8 @@
       NotifyListeners(!matches_.empty());
   }
 
+  AutocompleteProvider::Stop(stop_reason);
+
   // TODO(b/333770460): Once `HistoryEmbeddingsService` has a stop API, we
   //   should call it here.
 }
diff --git a/components/omnibox/browser/history_embeddings_provider.h b/components/omnibox/browser/history_embeddings_provider.h
index c663255..523696a 100644
--- a/components/omnibox/browser/history_embeddings_provider.h
+++ b/components/omnibox/browser/history_embeddings_provider.h
@@ -10,6 +10,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "components/history_embeddings/history_embeddings_service.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/history_provider.h"
 
@@ -23,7 +24,7 @@
 
   // AutocompleteProvider:
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
+  void Stop(AutocompleteStopReason stop_reason) override;
 
  private:
   friend class FakeHistoryEmbeddingsProvider;
diff --git a/components/omnibox/browser/history_embeddings_provider_unittest.cc b/components/omnibox/browser/history_embeddings_provider_unittest.cc
index 50b45f2..a658d38 100644
--- a/components/omnibox/browser/history_embeddings_provider_unittest.cc
+++ b/components/omnibox/browser/history_embeddings_provider_unittest.cc
@@ -25,6 +25,7 @@
 #include "components/history_embeddings/history_embeddings_features.h"
 #include "components/history_embeddings/history_embeddings_service.h"
 #include "components/history_embeddings/mock_history_embeddings_service.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
@@ -360,7 +361,7 @@
   history_embeddings_provider_->Start(CreateAutocompleteInput(u"1 1 1"), false);
   EXPECT_TRUE(last_update_matches_.empty());
 
-  history_embeddings_provider_->Stop(false, false);
+  history_embeddings_provider_->Stop(AutocompleteStopReason::kClobbered);
 
   // Results returned after `Stop()` should be discarded.
   std::move(search_callbacks_[0]).Run("1 1 1", u"1");
@@ -372,10 +373,10 @@
 
   // TODO(crbug.com/364303536) Temporarily allow history embeddings provider to
   //   ignore `Stop()`.
-  history_embeddings_provider_->Stop(false, true);
+  history_embeddings_provider_->Stop(AutocompleteStopReason::kInactivity);
   EXPECT_FALSE(history_embeddings_provider_->done_);
 
-  history_embeddings_provider_->Stop(false, false);
+  history_embeddings_provider_->Stop(AutocompleteStopReason::kClobbered);
   EXPECT_TRUE(history_embeddings_provider_->done_);
 }
 
diff --git a/components/omnibox/browser/history_fuzzy_provider_unittest.cc b/components/omnibox/browser/history_fuzzy_provider_unittest.cc
index 8bf62afe..abc5f96d 100644
--- a/components/omnibox/browser/history_fuzzy_provider_unittest.cc
+++ b/components/omnibox/browser/history_fuzzy_provider_unittest.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/omnibox/browser/history_fuzzy_provider.h"
 
diff --git a/components/omnibox/browser/history_quick_provider_performance_unittest.cc b/components/omnibox/browser/history_quick_provider_performance_unittest.cc
index a2cbc0af..2e94eba 100644
--- a/components/omnibox/browser/history_quick_provider_performance_unittest.cc
+++ b/components/omnibox/browser/history_quick_provider_performance_unittest.cc
@@ -4,10 +4,6 @@
 
 #include <array>
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include <algorithm>
 #include <memory>
diff --git a/components/omnibox/browser/history_url_provider.cc b/components/omnibox/browser/history_url_provider.cc
index 4175bc9..299dd6ba 100644
--- a/components/omnibox/browser/history_url_provider.cc
+++ b/components/omnibox/browser/history_url_provider.cc
@@ -29,6 +29,7 @@
 #include "components/history/core/browser/history_database.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_types.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_classification.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
@@ -423,8 +424,7 @@
   // re-run the query from scratch and ignore `minimal_changes`.
 
   // Cancel any in-progress query.
-  Stop(true, false);
-
+  Stop(AutocompleteStopReason::kClobbered);
   if (input.IsZeroSuggest() ||
       (input.type() == metrics::OmniboxInputType::EMPTY)) {
     return;
@@ -543,10 +543,8 @@
   }
 }
 
-void HistoryURLProvider::Stop(bool clear_cached_results,
-                              bool due_to_user_inactivity) {
-  AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
-
+void HistoryURLProvider::Stop(AutocompleteStopReason stop_reason) {
+  AutocompleteProvider::Stop(stop_reason);
   if (params_)
     params_->cancel_flag.Set();
 }
diff --git a/components/omnibox/browser/history_url_provider.h b/components/omnibox/browser/history_url_provider.h
index 9d60570..ab94125bb 100644
--- a/components/omnibox/browser/history_url_provider.h
+++ b/components/omnibox/browser/history_url_provider.h
@@ -17,6 +17,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/atomic_flag.h"
 #include "base/threading/thread_checker.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/history_match.h"
 #include "components/omnibox/browser/history_provider.h"
@@ -211,7 +212,7 @@
 
   // HistoryProvider:
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
+  void Stop(AutocompleteStopReason stop_reason) override;
 
   // Estimates dynamic memory usage.
   // See base/trace_event/memory_usage_estimator.h for more info.
diff --git a/components/omnibox/browser/history_url_provider_unittest.cc b/components/omnibox/browser/history_url_provider_unittest.cc
index 155a308..3e93e06 100644
--- a/components/omnibox/browser/history_url_provider_unittest.cc
+++ b/components/omnibox/browser/history_url_provider_unittest.cc
@@ -28,6 +28,7 @@
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/url_database.h"
 #include "components/history/core/test/history_service_test_util.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
@@ -1167,9 +1168,9 @@
       // clang-format on
   };
   for (size_t i = 0; i < std::size(test_cases); ++i) {
-    SCOPED_TRACE(testing::Message() << "Index " << i << " input: "
-                                    << test_cases[i].input << ", trim_http: "
-                                    << test_cases[i].trim_http);
+    SCOPED_TRACE(testing::Message()
+                 << "Index " << i << " input: " << test_cases[i].input
+                 << ", trim_http: " << test_cases[i].trim_http);
 
     AutocompleteInput input(ASCIIToUTF16(test_cases[i].input),
                             metrics::OmniboxEventProto::BLANK,
@@ -1184,8 +1185,9 @@
       EXPECT_EQ(test_cases[i].offsets[match_index],
                 match.contents_class[match_index].offset);
       EXPECT_EQ(ACMatchClassification::URL |
-                (match_index == test_cases[i].match_classification_index ?
-                 ACMatchClassification::MATCH : 0),
+                    (match_index == test_cases[i].match_classification_index
+                         ? ACMatchClassification::MATCH
+                         : 0),
                 match.contents_class[match_index].style);
     }
     EXPECT_EQ(npos, test_cases[i].offsets[match.contents_class.size()]);
@@ -1408,7 +1410,7 @@
           metrics::OmniboxEventProto_KeywordModeEntryMethod_TAB);
     }
 
-    provider_->Stop(true, false);
+    provider_->Stop(AutocompleteStopReason::kClobbered);
     provider_->Start(input, false);
     if (!provider_->done()) {
       base::RunLoop loop{base::RunLoop::Type::kNestableTasksAllowed};
diff --git a/components/omnibox/browser/in_memory_url_index_unittest.cc b/components/omnibox/browser/in_memory_url_index_unittest.cc
index ca9af00..0757a93 100644
--- a/components/omnibox/browser/in_memory_url_index_unittest.cc
+++ b/components/omnibox/browser/in_memory_url_index_unittest.cc
@@ -4,10 +4,6 @@
 
 #include <array>
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/omnibox/browser/in_memory_url_index.h"
 
diff --git a/components/omnibox/browser/keyword_provider.cc b/components/omnibox/browser/keyword_provider.cc
index f9943de..5cfe125 100644
--- a/components/omnibox/browser/keyword_provider.cc
+++ b/components/omnibox/browser/keyword_provider.cc
@@ -15,6 +15,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
@@ -320,15 +321,16 @@
   }
 }
 
-void KeywordProvider::Stop(bool clear_cached_results,
-                           bool due_to_user_inactivity) {
-  AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
+void KeywordProvider::Stop(AutocompleteStopReason stop_reason) {
+  AutocompleteProvider::Stop(stop_reason);
 
   // Only end an extension's request if the user did something to explicitly
   // cancel it; mere inactivity shouldn't terminate long-running extension
   // operations since the user likely explicitly requested them.
-  if (extensions_delegate_ && !due_to_user_inactivity)
+  if (extensions_delegate_ &&
+      stop_reason != AutocompleteStopReason::kInactivity) {
     extensions_delegate_->MaybeEndExtensionKeywordMode();
+  }
 }
 
 KeywordProvider::~KeywordProvider() = default;
diff --git a/components/omnibox/browser/keyword_provider.h b/components/omnibox/browser/keyword_provider.h
index 5eacf2c..7bc29c1f 100644
--- a/components/omnibox/browser/keyword_provider.h
+++ b/components/omnibox/browser/keyword_provider.h
@@ -19,6 +19,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/memory/raw_ptr.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/keyword_extensions_delegate.h"
@@ -73,9 +74,7 @@
   // AutocompleteProvider:
   void DeleteMatch(const AutocompleteMatch& match) override;
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
-
-  bool done() const { return done_; }
+  void Stop(AutocompleteStopReason stop_reason) override;
 
  private:
   friend class KeywordExtensionsDelegateImpl;
diff --git a/components/omnibox/browser/local_history_zero_suggest_provider.cc b/components/omnibox/browser/local_history_zero_suggest_provider.cc
index 5f595b9..bf4a103 100644
--- a/components/omnibox/browser/local_history_zero_suggest_provider.cc
+++ b/components/omnibox/browser/local_history_zero_suggest_provider.cc
@@ -26,6 +26,7 @@
 #include "components/history/core/browser/keyword_search_term.h"
 #include "components/history/core/browser/keyword_search_term_util.h"
 #include "components/history/core/browser/url_database.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_classification.h"
@@ -104,8 +105,7 @@
 void LocalHistoryZeroSuggestProvider::Start(const AutocompleteInput& input,
                                             bool minimal_changes) {
   TRACE_EVENT0("omnibox", "LocalHistoryZeroSuggestProvider::Start");
-  Stop(true, false);
-
+  Stop(AutocompleteStopReason::kClobbered);
   if (!AllowLocalHistoryZeroSuggestSuggestions(client_, input)) {
     return;
   }
diff --git a/components/omnibox/browser/most_visited_sites_provider.cc b/components/omnibox/browser/most_visited_sites_provider.cc
index 8ec28e3..6915ef0 100644
--- a/components/omnibox/browser/most_visited_sites_provider.cc
+++ b/components/omnibox/browser/most_visited_sites_provider.cc
@@ -17,6 +17,7 @@
 #include "base/trace_event/trace_event.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/top_sites.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_classification.h"
@@ -293,8 +294,7 @@
 
 void MostVisitedSitesProvider::Start(const AutocompleteInput& input,
                                      bool minimal_changes) {
-  Stop(true, false);
-
+  Stop(AutocompleteStopReason::kClobbered);
   if (!AllowMostVisitedSitesSuggestions(client_, input)) {
     return;
   }
@@ -360,9 +360,8 @@
   }
 }
 
-void MostVisitedSitesProvider::Stop(bool clear_cached_results,
-                                    bool due_to_user_inactivity) {
-  AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
+void MostVisitedSitesProvider::Stop(AutocompleteStopReason stop_reason) {
+  AutocompleteProvider::Stop(stop_reason);
   request_weak_ptr_factory_.InvalidateWeakPtrs();
   cancelable_task_tracker_.TryCancelAll();
   debouncer_->CancelRequest();
diff --git a/components/omnibox/browser/most_visited_sites_provider.h b/components/omnibox/browser/most_visited_sites_provider.h
index 6569599..d1ee32d5 100644
--- a/components/omnibox/browser/most_visited_sites_provider.h
+++ b/components/omnibox/browser/most_visited_sites_provider.h
@@ -12,6 +12,7 @@
 #include "base/task/cancelable_task_tracker.h"
 #include "base/timer/elapsed_timer.h"
 #include "components/history/core/browser/history_types.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
@@ -33,7 +34,7 @@
   // AutocompleteProvider:
   void StartPrefetch(const AutocompleteInput& input) override;
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
+  void Stop(AutocompleteStopReason stop_reason) override;
   void DeleteMatch(const AutocompleteMatch& match) override;
   void DeleteMatchElement(const AutocompleteMatch& match,
                           size_t element) override;
diff --git a/components/omnibox/browser/most_visited_sites_provider_unittest.cc b/components/omnibox/browser/most_visited_sites_provider_unittest.cc
index d96f641..4a32087 100644
--- a/components/omnibox/browser/most_visited_sites_provider_unittest.cc
+++ b/components/omnibox/browser/most_visited_sites_provider_unittest.cc
@@ -14,6 +14,7 @@
 #include "components/history/core/browser/features.h"
 #include "components/history/core/browser/top_sites.h"
 #include "components/history/core/browser/top_sites_impl.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
 #include "components/omnibox/browser/fake_autocomplete_provider_client.h"
 #include "components/omnibox/browser/test_scheme_classifier.h"
@@ -351,11 +352,11 @@
   EXPECT_TRUE(top_sites_->EmitURLs(test_data));
   CheckMatchesEquivalentTo(test_data, ExpectedUiType::kAggregateMatch);
   EXPECT_EQ(1, provider_update_count_);
-  provider_->Stop(false, false);
+  provider_->Stop(AutocompleteStopReason::kClobbered);
 
   // Observe that subsequent request does not return stale data.
   provider_->Start(input, true);
-  provider_->Stop(false, false);
+  provider_->Stop(AutocompleteStopReason::kClobbered);
   // Since this provider's async logic is still in-flight (`EmitURLs()` has not
   // been called yet), we should not be reporting anything from past runs.
   EXPECT_EQ(0ul, NumMostVisitedMatches());
@@ -372,7 +373,7 @@
   EXPECT_EQ(1, provider_update_count_);
 
   provider_->Start(input, true);
-  provider_->Stop(false, false);
+  provider_->Stop(AutocompleteStopReason::kClobbered);
   provider_->Start(input, true);
 
   // Stale results (reported for the first of the two Start() requests) should
@@ -385,7 +386,7 @@
   EXPECT_TRUE(top_sites_->EmitURLs(test_data));
   CheckMatchesEquivalentTo(test_data, ExpectedUiType::kAggregateMatch);
   EXPECT_EQ(2, provider_update_count_);
-  provider_->Stop(false, false);
+  provider_->Stop(AutocompleteStopReason::kClobbered);
 }
 
 TEST_F(MostVisitedSitesProviderTest, TestMostVisitedNavigateToSearchPage) {
diff --git a/components/omnibox/browser/omnibox_controller.cc b/components/omnibox/browser/omnibox_controller.cc
index 420d0ea..590fec3 100644
--- a/components/omnibox/browser/omnibox_controller.cc
+++ b/components/omnibox/browser/omnibox_controller.cc
@@ -10,6 +10,7 @@
 #include "base/trace_event/trace_event.h"
 #include "components/omnibox/browser/autocomplete_classifier.h"
 #include "components/omnibox/browser/autocomplete_controller_emitter.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_type.h"
 #include "components/omnibox/browser/omnibox_client.h"
@@ -74,7 +75,9 @@
 
 void OmniboxController::StopAutocomplete(bool clear_result) const {
   TRACE_EVENT0("omnibox", "OmniboxController::StopAutocomplete");
-  autocomplete_controller_->Stop(clear_result);
+  autocomplete_controller_->Stop(clear_result
+                                     ? AutocompleteStopReason::kClobbered
+                                     : AutocompleteStopReason::kInteraction);
 }
 
 void OmniboxController::StartZeroSuggestPrefetch() {
diff --git a/components/omnibox/browser/on_device_head_provider.cc b/components/omnibox/browser/on_device_head_provider.cc
index 543d59d..fe439a9 100644
--- a/components/omnibox/browser/on_device_head_provider.cc
+++ b/components/omnibox/browser/on_device_head_provider.cc
@@ -18,6 +18,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "base/trace_event/trace_event.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
 #include "components/omnibox/browser/base_search_provider.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
@@ -152,7 +153,8 @@
   TRACE_EVENT0("omnibox", "OnDeviceHeadProvider::Start");
 
   // Cancel any in-progress request.
-  Stop(!minimal_changes, false);
+  Stop(minimal_changes ? AutocompleteStopReason::kInteraction
+                       : AutocompleteStopReason::kClobbered);
 
   if (!IsOnDeviceHeadProviderAllowed(input)) {
     matches_.clear();
@@ -180,10 +182,8 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(params)));
 }
 
-void OnDeviceHeadProvider::Stop(bool clear_cached_results,
-                                bool due_to_user_inactivity) {
-  AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
-
+void OnDeviceHeadProvider::Stop(AutocompleteStopReason stop_reason) {
+  AutocompleteProvider::Stop(stop_reason);
   // Increase the request_id so that any in-progress requests will become
   // obsolete.
   on_device_search_request_id_ =
diff --git a/components/omnibox/browser/on_device_head_provider.h b/components/omnibox/browser/on_device_head_provider.h
index ed3e4c7..022440b3 100644
--- a/components/omnibox/browser/on_device_head_provider.h
+++ b/components/omnibox/browser/on_device_head_provider.h
@@ -11,6 +11,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/task/sequenced_task_runner.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
 #include "components/omnibox/browser/on_device_head_model.h"
@@ -38,7 +39,7 @@
                                       AutocompleteProviderListener* listener);
 
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
+  void Stop(AutocompleteStopReason stop_reason) override;
   void AddProviderInfo(ProvidersInfo* provider_info) const override;
 
   AutocompleteProviderClient* client() { return client_; }
diff --git a/components/omnibox/browser/search_provider.cc b/components/omnibox/browser/search_provider.cc
index bfcf964..8eab016 100644
--- a/components/omnibox/browser/search_provider.cc
+++ b/components/omnibox/browser/search_provider.cc
@@ -32,6 +32,7 @@
 #include "components/history/core/browser/keyword_search_term.h"
 #include "components/history/core/browser/keyword_search_term_util.h"
 #include "components/lens/lens_features.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
@@ -232,7 +233,7 @@
   if (base::FeatureList::IsEnabled(omnibox::kAblateSearchProviderWarmup) &&
       (input.IsZeroSuggest() ||
        input.type() == metrics::OmniboxInputType::EMPTY)) {
-    Stop(true, false);
+    Stop(AutocompleteStopReason::kClobbered);
     return;
   }
 
@@ -255,7 +256,7 @@
 
   if (!default_provider && !keyword_provider) {
     // No valid providers.
-    Stop(true, false);
+    Stop(AutocompleteStopReason::kClobbered);
     return;
   }
 
@@ -269,7 +270,7 @@
       !providers_.equal(default_provider_keyword, keyword_provider_keyword)) {
     // Cancel any in-flight suggest requests.
     if (!done_)
-      Stop(false, false);
+      Stop(AutocompleteStopReason::kInteraction);
   }
 
   providers_.set(default_provider_keyword, keyword_provider_keyword);
@@ -290,7 +291,7 @@
       match.allowed_to_be_default_match = true;
       matches_.push_back(match);
     }
-    Stop(true, false);
+    Stop(AutocompleteStopReason::kClobbered);
     return;
   }
 
@@ -320,12 +321,10 @@
   UpdateMatches();
 }
 
-void SearchProvider::Stop(bool clear_cached_results,
-                          bool due_to_user_inactivity) {
-  AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
-
+void SearchProvider::Stop(AutocompleteStopReason stop_reason) {
+  AutocompleteProvider::Stop(stop_reason);
   StopSuggest();
-  if (clear_cached_results)
+  if (stop_reason == AutocompleteStopReason::kClobbered)
     ClearAllResults();
 }
 
diff --git a/components/omnibox/browser/search_provider.h b/components/omnibox/browser/search_provider.h
index a2d9702..faef1c03 100644
--- a/components/omnibox/browser/search_provider.h
+++ b/components/omnibox/browser/search_provider.h
@@ -22,6 +22,7 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "components/omnibox/browser/answers_cache.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/base_search_provider.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
@@ -131,8 +132,8 @@
     // by this class.
     bool equal(const std::u16string& default_provider,
                const std::u16string& keyword_provider) const {
-      return (default_provider == default_provider_) &&
-          (keyword_provider == keyword_provider_);
+      return default_provider == default_provider_ &&
+             keyword_provider == keyword_provider_;
     }
 
     // Resets the cached providers.
@@ -172,8 +173,7 @@
 
   // AutocompleteProvider:
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results,
-            bool due_to_user_inactivity) override;
+  void Stop(AutocompleteStopReason stop_reason) override;
 
   // BaseSearchProvider:
   bool ShouldAppendExtraParams(
@@ -424,7 +424,7 @@
   GURL top_navigation_suggestion_;
 
   // Answers prefetch management.
-  AnswersCache answers_cache_;  // Cache for last answers seen.
+  AnswersCache answers_cache_;      // Cache for last answers seen.
   AnswersQueryData prefetch_data_;  // Data to use for query prefetching.
 
   base::ScopedObservation<TemplateURLService, TemplateURLServiceObserver>
diff --git a/components/omnibox/browser/shortcuts_database_unittest.cc b/components/omnibox/browser/shortcuts_database_unittest.cc
index f39fc5c5..48b4ae85 100644
--- a/components/omnibox/browser/shortcuts_database_unittest.cc
+++ b/components/omnibox/browser/shortcuts_database_unittest.cc
@@ -4,10 +4,6 @@
 
 #include <array>
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/omnibox/browser/shortcuts_database.h"
 
diff --git a/components/omnibox/browser/tab_group_provider.cc b/components/omnibox/browser/tab_group_provider.cc
index 9450da11..0543f91 100644
--- a/components/omnibox/browser/tab_group_provider.cc
+++ b/components/omnibox/browser/tab_group_provider.cc
@@ -16,6 +16,7 @@
 #if BUILDFLAG(IS_ANDROID)
 #include "components/browser_ui/util/android/url_constants.h"
 #endif
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_classification.h"
@@ -82,7 +83,7 @@
 // TODO(crbug.com/412433887): Make the TabGroupProvider async.
 void TabGroupProvider::Start(const AutocompleteInput& input,
                              bool minimal_changes) {
-  Stop(true, false);
+  Stop(AutocompleteStopReason::kClobbered);
   if (input.current_page_classification() !=
       ::metrics::OmniboxEventProto::ANDROID_HUB) {
     return;
diff --git a/components/omnibox/browser/unscoped_extension_provider.cc b/components/omnibox/browser/unscoped_extension_provider.cc
index 4d7dfcd..f48c5ca 100644
--- a/components/omnibox/browser/unscoped_extension_provider.cc
+++ b/components/omnibox/browser/unscoped_extension_provider.cc
@@ -8,6 +8,7 @@
 
 #include "base/check_is_test.h"
 #include "base/memory/raw_ptr.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
@@ -33,8 +34,8 @@
   // matches and suggestion group information and increment the current request
   // ID to discard any suggestions that may be incoming later with a stale
   // request ID.
-  Stop(/*clear_cached_results=*/!minimal_changes,
-       /*due_to_user_inactivity=*/false);
+  Stop(minimal_changes ? AutocompleteStopReason::kInteraction
+                       : AutocompleteStopReason::kClobbered);
 
   // Unscoped mode input should not be redirected to an extension in incognito.
   if (client_->IsOffTheRecord()) {
@@ -82,14 +83,14 @@
   delegate_->Start(input, minimal_changes, unscoped_extensions);
 }
 
-void UnscopedExtensionProvider::Stop(bool clear_cached_results,
-                                     bool due_to_user_inactivity) {
+void UnscopedExtensionProvider::Stop(AutocompleteStopReason stop_reason) {
   // Ignore the stop timer since extension suggestions might take longer than
   // 1500ms to generate (the stop timer gets triggered due to user inactivity).
-  if (!due_to_user_inactivity) {
-    AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
-    delegate_->Stop(clear_cached_results);
+  if (stop_reason == AutocompleteStopReason::kInactivity) {
+    return;
   }
+  AutocompleteProvider::Stop(stop_reason);
+  delegate_->Stop(stop_reason == AutocompleteStopReason::kClobbered);
 }
 
 void UnscopedExtensionProvider::DeleteMatch(const AutocompleteMatch& match) {
diff --git a/components/omnibox/browser/unscoped_extension_provider.h b/components/omnibox/browser/unscoped_extension_provider.h
index d4231b7..ead3999 100644
--- a/components/omnibox/browser/unscoped_extension_provider.h
+++ b/components/omnibox/browser/unscoped_extension_provider.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/memory/raw_ptr.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/omnibox_suggestions_watcher.h"
@@ -32,7 +33,7 @@
 
   // AutocompleteProvider:
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
+  void Stop(AutocompleteStopReason stop_reason) override;
   void DeleteMatch(const AutocompleteMatch& match) override;
 
   // Used by UnscopedExtensionProviderDelegateImpl.
@@ -40,7 +41,6 @@
   void AddToSuggestionGroupsMap(omnibox::GroupId group_id,
                                 omnibox::GroupConfig group_config);
   void set_done(bool done) { done_ = done; }
-  bool done() const { return done_; }
   ACMatches* matches() { return &matches_; }
 
  private:
diff --git a/components/omnibox/browser/voice_suggest_provider.cc b/components/omnibox/browser/voice_suggest_provider.cc
index 1f6481e1..f50075ea 100644
--- a/components/omnibox/browser/voice_suggest_provider.cc
+++ b/components/omnibox/browser/voice_suggest_provider.cc
@@ -6,10 +6,9 @@
 
 #include <string>
 
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
-#include "components/omnibox/browser/autocomplete_match_classification.h"
-#include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
 #include "third_party/omnibox_proto/navigational_intent.pb.h"
 #include "third_party/omnibox_proto/types.pb.h"
@@ -79,11 +78,9 @@
 
 void VoiceSuggestProvider::RecordDeletionResult(bool success) {}
 
-void VoiceSuggestProvider::Stop(bool clear_cached_results,
-                                bool due_to_user_inactivity) {
-  AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
-
-  if (clear_cached_results) {
+void VoiceSuggestProvider::Stop(AutocompleteStopReason stop_reason) {
+  AutocompleteProvider::Stop(stop_reason);
+  if (stop_reason == AutocompleteStopReason::kClobbered) {
     ClearCache();
   }
 }
diff --git a/components/omnibox/browser/voice_suggest_provider.h b/components/omnibox/browser/voice_suggest_provider.h
index 1eb6290..4a7536a0 100644
--- a/components/omnibox/browser/voice_suggest_provider.h
+++ b/components/omnibox/browser/voice_suggest_provider.h
@@ -11,6 +11,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/memory/raw_ptr.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
 #include "components/omnibox/browser/base_search_provider.h"
@@ -22,7 +23,7 @@
   explicit VoiceSuggestProvider(AutocompleteProviderClient* client);
 
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
+  void Stop(AutocompleteStopReason stop_reason) override;
 
   // Adds voice suggestion to the list of reported AutocompleteMatches.
   // The voice suggestion is next converted to a proper Search suggestion
diff --git a/components/omnibox/browser/voice_suggest_provider_unittest.cc b/components/omnibox/browser/voice_suggest_provider_unittest.cc
index 1462707..9763c10 100644
--- a/components/omnibox/browser/voice_suggest_provider_unittest.cc
+++ b/components/omnibox/browser/voice_suggest_provider_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/fake_autocomplete_provider_client.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -19,7 +20,6 @@
   VoiceSuggestProviderTest& operator=(const VoiceSuggestProviderTest&) = delete;
 
   void SetUp() override;
-  void TearDown() override;
 
  protected:
   base::test::TaskEnvironment environment_;
@@ -35,10 +35,6 @@
       TestSchemeClassifier());
 }
 
-void VoiceSuggestProviderTest::TearDown() {
-  provider_->Stop(true, true);
-}
-
 TEST_F(VoiceSuggestProviderTest, ServesNoSuggestionsByDefault) {
   provider_->Start(*input_, false);
   EXPECT_TRUE(provider_->matches().empty());
@@ -74,7 +70,7 @@
   provider_->AddVoiceSuggestion(u"Bob", 0.5f);
   provider_->AddVoiceSuggestion(u"Carol", 0.4f);
   provider_->Start(*input_, false);
-  provider_->Stop(true, false);
+  provider_->Stop(AutocompleteStopReason::kClobbered);
   provider_->Start(*input_, false);
 
   EXPECT_TRUE(provider_->matches().empty());
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc
index e8b449f..0c8bc96 100644
--- a/components/omnibox/browser/zero_suggest_provider.cc
+++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -20,6 +20,7 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_classification.h"
@@ -327,13 +328,15 @@
   match.contents = url_formatter::FormatUrl(navigation.url(), format_types,
                                             base::UnescapeRule::SPACES, nullptr,
                                             nullptr, nullptr);
-  match.contents_class = ClassifyTermMatches({}, match.contents.length(), 0,
+  match.contents_class = ClassifyTermMatches({}, match.contents.length(),
+                                             ACMatchClassification::NONE,
                                              ACMatchClassification::URL);
 
   match.description =
       AutocompleteMatch::SanitizeString(navigation.description());
   match.description_class = ClassifyTermMatches({}, match.description.length(),
-                                                0, ACMatchClassification::NONE);
+                                                ACMatchClassification::NONE,
+                                                ACMatchClassification::NONE);
   match.suggest_type = navigation.suggest_type();
   for (const int subtype : navigation.subtypes()) {
     match.subtypes.insert(SuggestSubtypeForNumber(subtype));
@@ -471,7 +474,7 @@
 void ZeroSuggestProvider::Start(const AutocompleteInput& input,
                                 bool minimal_changes) {
   TRACE_EVENT0("omnibox", "ZeroSuggestProvider::Start");
-  Stop(true, false);
+  Stop(AutocompleteStopReason::kClobbered);
 
   auto [result_type, eligible] = GetResultTypeAndEligibility(client(), input);
   LogOmniboxZeroSuggestEligibility(result_type, eligible);
@@ -532,9 +535,8 @@
                                /*is_prefetch=*/false);
 }
 
-void ZeroSuggestProvider::Stop(bool clear_cached_results,
-                               bool due_to_user_inactivity) {
-  AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
+void ZeroSuggestProvider::Stop(AutocompleteStopReason stop_reason) {
+  AutocompleteProvider::Stop(stop_reason);
 
   if (base::FeatureList::IsEnabled(omnibox::kZeroSuggestPrefetchDebouncing)) {
     debouncer_->CancelRequest();
@@ -548,7 +550,7 @@
   }
   result_type_running_ = ResultType::kNone;
 
-  if (clear_cached_results) {
+  if (stop_reason == AutocompleteStopReason::kClobbered) {
     experiment_stats_v2s_.clear();
     gws_event_id_hashes_.clear();
   }
diff --git a/components/omnibox/browser/zero_suggest_provider.h b/components/omnibox/browser/zero_suggest_provider.h
index e09407e0..a3a5dde 100644
--- a/components/omnibox/browser/zero_suggest_provider.h
+++ b/components/omnibox/browser/zero_suggest_provider.h
@@ -13,6 +13,7 @@
 #include <string>
 
 #include "base/gtest_prod_util.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_provider_debouncer.h"
 #include "components/omnibox/browser/base_search_provider.h"
 
@@ -72,8 +73,7 @@
   // AutocompleteProvider:
   void StartPrefetch(const AutocompleteInput& input) override;
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results,
-            bool due_to_user_inactivity) override;
+  void Stop(AutocompleteStopReason stop_reason) override;
   void DeleteMatch(const AutocompleteMatch& match) override;
   void AddProviderInfo(ProvidersInfo* provider_info) const override;
 
diff --git a/components/omnibox/browser/zero_suggest_verbatim_match_provider.cc b/components/omnibox/browser/zero_suggest_verbatim_match_provider.cc
index 5ae5bda..c732fbc 100644
--- a/components/omnibox/browser/zero_suggest_verbatim_match_provider.cc
+++ b/components/omnibox/browser/zero_suggest_verbatim_match_provider.cc
@@ -12,6 +12,7 @@
 #include "components/history/core/browser/history_types.h"
 #include "components/history/core/browser/url_database.h"
 #include "components/history/core/browser/url_row.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_match_classification.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
@@ -54,7 +55,7 @@
 
 void ZeroSuggestVerbatimMatchProvider::Start(const AutocompleteInput& input,
                                              bool minimal_changes) {
-  Stop(true, false);
+  Stop(AutocompleteStopReason::kClobbered);
   if (!IsVerbatimMatchEligible(input.current_page_classification()))
     return;
 
@@ -100,9 +101,9 @@
       &task_tracker_);
 }
 
-void ZeroSuggestVerbatimMatchProvider::Stop(bool clear_cached_results,
-                                            bool due_to_user_inactivity) {
-  AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
+void ZeroSuggestVerbatimMatchProvider::Stop(
+    AutocompleteStopReason stop_reason) {
+  AutocompleteProvider::Stop(stop_reason);
   request_weak_ptr_factory_.InvalidateWeakPtrs();
 }
 
diff --git a/components/omnibox/browser/zero_suggest_verbatim_match_provider.h b/components/omnibox/browser/zero_suggest_verbatim_match_provider.h
index 826e905..d4a48b4e 100644
--- a/components/omnibox/browser/zero_suggest_verbatim_match_provider.h
+++ b/components/omnibox/browser/zero_suggest_verbatim_match_provider.h
@@ -7,6 +7,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "components/history/core/browser/history_types.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/history_url_provider.h"
@@ -27,7 +28,7 @@
 
   // AutocompleteProvider:
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
-  void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
+  void Stop(AutocompleteStopReason stop_reason) override;
 
  private:
   void OnPageTitleRetrieved(const AutocompleteInput& input,
diff --git a/components/omnibox/browser/zero_suggest_verbatim_match_provider_unittest.cc b/components/omnibox/browser/zero_suggest_verbatim_match_provider_unittest.cc
index 776eb95..d78f614 100644
--- a/components/omnibox/browser/zero_suggest_verbatim_match_provider_unittest.cc
+++ b/components/omnibox/browser/zero_suggest_verbatim_match_provider_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/test/task_environment.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_types.h"
+#include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/fake_autocomplete_provider_client.h"
 #include "components/omnibox/browser/mock_autocomplete_provider_client.h"
 #include "components/omnibox/browser/test_scheme_classifier.h"
@@ -456,7 +457,7 @@
   }
 
   // Cancel action.
-  provider_->Stop(false, false);
+  provider_->Stop(AutocompleteStopReason::kInteraction);
 
   {
     // Resolve history service.
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index 28300a0..976b8f4 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit 28300a0a975163d53866dc59929e600f3a9917bc
+Subproject commit 976b8f41d35f356dd01e2dc99e317dbafb032477
diff --git a/components/os_crypt/sync/kwallet_dbus.cc b/components/os_crypt/sync/kwallet_dbus.cc
index 7e4f428..20cf5519 100644
--- a/components/os_crypt/sync/kwallet_dbus.cc
+++ b/components/os_crypt/sync/kwallet_dbus.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/os_crypt/sync/kwallet_dbus.h"
 
diff --git a/components/os_crypt/sync/kwallet_dbus_unittest.cc b/components/os_crypt/sync/kwallet_dbus_unittest.cc
index 8d69d4b..4ae8e8b 100644
--- a/components/os_crypt/sync/kwallet_dbus_unittest.cc
+++ b/components/os_crypt/sync/kwallet_dbus_unittest.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/os_crypt/sync/kwallet_dbus.h"
 
diff --git a/components/page_load_metrics/common/page_load_metrics.mojom b/components/page_load_metrics/common/page_load_metrics.mojom
index 6dddf5c..31c7dfe 100644
--- a/components/page_load_metrics/common/page_load_metrics.mojom
+++ b/components/page_load_metrics/common/page_load_metrics.mojom
@@ -447,6 +447,11 @@
   // Routing API, from navigation start till onload event.
   uint32 matched_race_network_and_fetch_router_source_count = 0;
 
+  // Total number of sub-resources which were matched to the
+  // `RouterSourceEnum.race-network-and-cache` in ServiceWorker Static
+  // Routing API, from navigation start till onload event.
+  uint32 matched_race_network_and_cache_router_source_count = 0;
+
   // Total router evaluation time of ServiceWorker Static Routing API for
   // sub-resources.
   mojo_base.mojom.TimeDelta total_router_evaluation_time_for_subresources;
diff --git a/components/page_load_metrics/common/page_load_metrics_mojom_traits.cc b/components/page_load_metrics/common/page_load_metrics_mojom_traits.cc
index d00ca978..b873cbdd 100644
--- a/components/page_load_metrics/common/page_load_metrics_mojom_traits.cc
+++ b/components/page_load_metrics/common/page_load_metrics_mojom_traits.cc
@@ -65,6 +65,8 @@
       data.matched_network_router_source_count();
   out->matched_race_network_and_fetch_router_source_count =
       data.matched_race_network_and_fetch_router_source_count();
+  out->matched_race_network_and_cache_router_source_count =
+      data.matched_race_network_and_cache_router_source_count();
   if (!data.ReadTotalRouterEvaluationTimeForSubresources(
           &out->total_router_evaluation_time_for_subresources)) {
     return false;
diff --git a/components/page_load_metrics/common/page_load_metrics_mojom_traits.h b/components/page_load_metrics/common/page_load_metrics_mojom_traits.h
index 46fff08..601bd58b 100644
--- a/components/page_load_metrics/common/page_load_metrics_mojom_traits.h
+++ b/components/page_load_metrics/common/page_load_metrics_mojom_traits.h
@@ -179,6 +179,11 @@
     return d.matched_race_network_and_fetch_router_source_count;
   }
 
+  static uint32_t matched_race_network_and_cache_router_source_count(
+      const blink::ServiceWorkerSubresourceLoadMetrics& d) {
+    return d.matched_race_network_and_cache_router_source_count;
+  }
+
   static base::TimeDelta total_router_evaluation_time_for_subresources(
       const blink::ServiceWorkerSubresourceLoadMetrics& d) {
     return d.total_router_evaluation_time_for_subresources;
diff --git a/components/paint_preview/common/serialized_recording.cc b/components/paint_preview/common/serialized_recording.cc
index c007349..2e65a31 100644
--- a/components/paint_preview/common/serialized_recording.cc
+++ b/components/paint_preview/common/serialized_recording.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/paint_preview/common/serialized_recording.h"
 
diff --git a/components/policy/core/common/cloud/resource_cache.cc b/components/policy/core/common/cloud/resource_cache.cc
index e2aeb05a..08e3296c 100644
--- a/components/policy/core/common/cloud/resource_cache.cc
+++ b/components/policy/core/common/cloud/resource_cache.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/policy/core/common/cloud/resource_cache.h"
 
diff --git a/components/policy/core/common/policy_proto_decoders_unittest.cc b/components/policy/core/common/policy_proto_decoders_unittest.cc
index 9d3b619..8fab36c 100644
--- a/components/policy/core/common/policy_proto_decoders_unittest.cc
+++ b/components/policy/core/common/policy_proto_decoders_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/policy/core/common/policy_proto_decoders.h"
 
+#include "base/json/json_reader.h"
 #include "base/strings/string_number_conversions.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/test/policy_builder.h"
@@ -195,8 +196,10 @@
   expected_policy_map_.Set(key::kManagedBookmarks, POLICY_LEVEL_MANDATORY,
                            POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
                            base::Value(invalidDummyJson), nullptr);
-  const std::u16string kExpectedMessage =
-      u"EOF while parsing an object at line 3 column 2";
+  std::u16string kExpectedMessage =
+      base::JSONReader::UsingRust()
+          ? u"EOF while parsing an object at line 3 column 2"
+          : u"Line: 3, column: 3, Syntax error.";
   expected_policy_map_.AddMessage(
       key::kManagedBookmarks, PolicyMap::MessageType::kError,
       IDS_POLICY_PROTO_PARSING_ERROR, {kExpectedMessage});
diff --git a/components/policy/core/common/policy_service_impl.cc b/components/policy/core/common/policy_service_impl.cc
index 9ca5608..93fb9a88 100644
--- a/components/policy/core/common/policy_service_impl.cc
+++ b/components/policy/core/common/policy_service_impl.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/policy/core/common/policy_service_impl.h"
 
diff --git a/components/policy/core/common/schema_registry.cc b/components/policy/core/common/schema_registry.cc
index c0e9585d..670895f 100644
--- a/components/policy/core/common/schema_registry.cc
+++ b/components/policy/core/common/schema_registry.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/policy/core/common/schema_registry.h"
 
diff --git a/components/power_metrics/energy_metrics_provider_linux.cc b/components/power_metrics/energy_metrics_provider_linux.cc
index 219ad14..716bce9 100644
--- a/components/power_metrics/energy_metrics_provider_linux.cc
+++ b/components/power_metrics/energy_metrics_provider_linux.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/power_metrics/energy_metrics_provider_linux.h"
 
diff --git a/components/services/storage/indexed_db/scopes/varint_coding.cc b/components/services/storage/indexed_db/scopes/varint_coding.cc
index d957520a..8b0ef3d 100644
--- a/components/services/storage/indexed_db/scopes/varint_coding.cc
+++ b/components/services/storage/indexed_db/scopes/varint_coding.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/services/storage/indexed_db/scopes/varint_coding.h"
 
diff --git a/components/services/storage/indexed_db/scopes/varint_coding_unittest.cc b/components/services/storage/indexed_db/scopes/varint_coding_unittest.cc
index fe925e75..6dfb1c6 100644
--- a/components/services/storage/indexed_db/scopes/varint_coding_unittest.cc
+++ b/components/services/storage/indexed_db/scopes/varint_coding_unittest.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/services/storage/indexed_db/scopes/varint_coding.h"
 
diff --git a/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc b/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc
index 00306ff..4a6601275 100644
--- a/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc
+++ b/components/sync_bookmarks/bookmark_model_observer_impl_unittest.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/sync_bookmarks/bookmark_model_observer_impl.h"
 
diff --git a/components/update_client/net/network_impl.cc b/components/update_client/net/network_impl.cc
index 9789398b..01c38e6 100644
--- a/components/update_client/net/network_impl.cc
+++ b/components/update_client/net/network_impl.cc
@@ -150,6 +150,7 @@
                      GetStringHeader(simple_url_loader.get(), kHeaderEtag),
                      GetStringHeader(simple_url_loader.get(),
                                      kHeaderXCupServerProof),
+                     GetStringHeader(simple_url_loader.get(), kHeaderCookie),
                      GetInt64Header(simple_url_loader.get(),
                                     kHeaderXRetryAfter));
           },
diff --git a/components/update_client/network.h b/components/update_client/network.h
index 5295154..01b59d1 100644
--- a/components/update_client/network.h
+++ b/components/update_client/network.h
@@ -32,6 +32,7 @@
                               int net_error,
                               const std::string& header_etag,
                               const std::string& header_x_cup_server_proof,
+                              const std::string& header_cookie,
                               int64_t xheader_retry_after_sec)>;
   using DownloadToFileCompleteCallback =
       base::OnceCallback<void(int net_error, int64_t content_size)>;
@@ -57,6 +58,11 @@
   // trusted.
   static constexpr char kHeaderXRetryAfter[] = "X-Retry-After";
 
+  // The HTTP 'Cookie' header to pass through to the
+  // `PostRequestCompleteCallback`. This header isn't used by the Omaha update
+  // protocol but is necessary for other uses of `NetworkFetcher`.
+  static constexpr char kHeaderCookie[] = "Cookie";
+
   NetworkFetcher(const NetworkFetcher&) = delete;
   NetworkFetcher& operator=(const NetworkFetcher&) = delete;
 
diff --git a/components/update_client/request_sender.cc b/components/update_client/request_sender.cc
index a3d55bc..1915035 100644
--- a/components/update_client/request_sender.cc
+++ b/components/update_client/request_sender.cc
@@ -114,7 +114,8 @@
         FROM_HERE,
         base::BindOnce(&RequestSender::SendInternalComplete, this,
                        static_cast<int>(ProtocolError::URL_FETCHER_FAILED),
-                       std::string(), std::string(), std::string(), 0));
+                       std::string(), std::string(), std::string(),
+                       std::string(), 0));
     return;
   }
   network_fetcher_->PostRequest(
@@ -129,6 +130,7 @@
     const std::string& response_body,
     const std::string& response_etag,
     const std::string& response_cup_server_proof,
+    const std::string& response_cookie,
     int retry_after_sec) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   VLOG(2) << "Omaha response received: " << response_body;
@@ -179,6 +181,7 @@
     int net_error,
     const std::string& header_etag,
     const std::string& xheader_cup_server_proof,
+    const std::string& header_cookie,
     int64_t xheader_retry_after_sec) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -202,7 +205,8 @@
       FROM_HERE,
       base::BindOnce(&RequestSender::SendInternalComplete, this, error,
                      std::move(response_body).value_or(std::string()),
-                     header_etag, xheader_cup_server_proof, retry_after_sec));
+                     header_etag, xheader_cup_server_proof, header_cookie,
+                     retry_after_sec));
 }
 
 void RequestSender::HandleSendError(int error, int retry_after_sec) {
diff --git a/components/update_client/request_sender.h b/components/update_client/request_sender.h
index 4cded1d7..340bba3 100644
--- a/components/update_client/request_sender.h
+++ b/components/update_client/request_sender.h
@@ -72,6 +72,7 @@
                                 int net_error,
                                 const std::string& header_etag,
                                 const std::string& xheader_cup_server_proof,
+                                const std::string& header_cookie,
                                 int64_t xheader_retry_after_sec);
 
   // Implements the error handling and url fallback mechanism.
@@ -83,6 +84,7 @@
                             const std::string& response_body,
                             const std::string& response_etag,
                             const std::string& response_cup_server_proof,
+                            const std::string& response_cookie,
                             int retry_after_sec);
 
   // Helper function to handle a non-continuable error in Send.
diff --git a/components/url_formatter/spoof_checks/common_words/common_words_util.cc b/components/url_formatter/spoof_checks/common_words/common_words_util.cc
index fa1f0935..d3d4141d4 100644
--- a/components/url_formatter/spoof_checks/common_words/common_words_util.cc
+++ b/components/url_formatter/spoof_checks/common_words/common_words_util.cc
@@ -21,8 +21,8 @@
 }  // namespace
 
 bool IsCommonWord(std::string_view word) {
-  return net::LookupStringInFixedSet(g_dafsa_params, word.data(),
-                                     word.size()) != net::kDafsaNotFound;
+  return net::LookupStringInFixedSet(g_dafsa_params, word) !=
+         net::kDafsaNotFound;
 }
 
 void SetCommonWordDAFSAForTesting(base::span<const uint8_t> dafsa) {
diff --git a/components/url_formatter/url_fixer_unittest.cc b/components/url_formatter/url_fixer_unittest.cc
index 39307c26..6f14361 100644
--- a/components/url_formatter/url_fixer_unittest.cc
+++ b/components/url_formatter/url_fixer_unittest.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/url_formatter/url_fixer.h"
 
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index e522423..1529754 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -705,7 +705,10 @@
     sources += [
       "display/overlay_dc_unittest.cc",
       "display_embedder/output_device_backing_unittest.cc",
+      "display_embedder/software_output_device_win_swapchain_unittest.cc",
       "display_embedder/software_output_device_win_unittest.cc",
+      "display_embedder/test_support/dcomp_mocks.cc",
+      "display_embedder/test_support/dcomp_mocks.h",
     ]
 
     deps += [
diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc
index fb14a82..40dbe97 100644
--- a/components/viz/service/display/display_unittest.cc
+++ b/components/viz/service/display/display_unittest.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "components/viz/service/display/display.h"
 
diff --git a/components/viz/service/display/renderer_perftest.cc b/components/viz/service/display/renderer_perftest.cc
index b7f06af..ff578be 100644
--- a/components/viz/service/display/renderer_perftest.cc
+++ b/components/viz/service/display/renderer_perftest.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include <array>
 
diff --git a/components/viz/service/display_embedder/DEPS b/components/viz/service/display_embedder/DEPS
index 32b3ac4..028e4e5 100644
--- a/components/viz/service/display_embedder/DEPS
+++ b/components/viz/service/display_embedder/DEPS
@@ -31,6 +31,7 @@
   "+gpu/ipc",
   "+gpu/skia_bindings",
   "+gpu/vulkan",
+  "+media/base/win/test_utils.h",
   "+media/gpu/chromeos/vulkan_overlay_adaptor.h",
   "+media/gpu/buildflags.h",
   "+mojo/public/cpp/bindings",
@@ -68,4 +69,7 @@
     "+ui/gl",
     "+ui/platform_window",
   ],
+  "software_output_device_win_swapchain_unittest.cc": [
+    "+media/base/win/d3d11_mocks.h",
+  ]
 }
diff --git a/components/viz/service/display_embedder/output_device_backing.h b/components/viz/service/display_embedder/output_device_backing.h
index d8763a8..f218f93 100644
--- a/components/viz/service/display_embedder/output_device_backing.h
+++ b/components/viz/service/display_embedder/output_device_backing.h
@@ -43,7 +43,7 @@
   OutputDeviceBacking(const OutputDeviceBacking&) = delete;
   OutputDeviceBacking& operator=(const OutputDeviceBacking&) = delete;
 
-  ~OutputDeviceBacking();
+  virtual ~OutputDeviceBacking();
 
   void RegisterClient(Client* client);
   void UnregisterClient(Client* client);
@@ -64,13 +64,13 @@
   size_t GetMaxViewportBytes();
 
   // Retrieve the ID3D11Device and IDCompositionDevice to use for presentation.
-  HRESULT GetOrCreateDXObjects(
+  virtual HRESULT GetOrCreateDXObjects(
       Microsoft::WRL::ComPtr<ID3D11Device>* d3d11_device,
       Microsoft::WRL::ComPtr<IDXGIFactory2>* dxgi_factory,
       Microsoft::WRL::ComPtr<IDCompositionDevice>* dcomp_device);
 
   // Returns the D3D11 staging texture or creates one if it doesn't exist.
-  Microsoft::WRL::ComPtr<ID3D11Texture2D> GetOrCreateStagingTexture();
+  virtual Microsoft::WRL::ComPtr<ID3D11Texture2D> GetOrCreateStagingTexture();
 
   // Returns the size needed for the largest viewport from registered clients,
   // in pixels.
diff --git a/components/viz/service/display_embedder/software_output_device_win_swapchain.cc b/components/viz/service/display_embedder/software_output_device_win_swapchain.cc
index acd4424..4c13c41 100644
--- a/components/viz/service/display_embedder/software_output_device_win_swapchain.cc
+++ b/components/viz/service/display_embedder/software_output_device_win_swapchain.cc
@@ -68,13 +68,19 @@
   }
 }
 
+bool SoftwareOutputDeviceWinSwapChain::UpdateWindowSize(
+    const gfx::Size& viewport_pixel_size) {
+  // Update the size of the child window.
+  return SetWindowPos(child_window_.window(), nullptr, 0, 0,
+                      viewport_pixel_size.width(), viewport_pixel_size.height(),
+                      SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS |
+                          SWP_NOOWNERZORDER | SWP_NOZORDER);
+}
+
 bool SoftwareOutputDeviceWinSwapChain::ResizeDelegated(
     const gfx::Size& viewport_pixel_size) {
   // Update window size.
-  if (!SetWindowPos(child_window_.window(), nullptr, 0, 0,
-                    viewport_pixel_size.width(), viewport_pixel_size.height(),
-                    SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOCOPYBITS |
-                        SWP_NOOWNERZORDER | SWP_NOZORDER)) {
+  if (!UpdateWindowSize(viewport_pixel_size)) {
     return false;
   }
 
diff --git a/components/viz/service/display_embedder/software_output_device_win_swapchain.h b/components/viz/service/display_embedder/software_output_device_win_swapchain.h
index 1b234ca..63520b70 100644
--- a/components/viz/service/display_embedder/software_output_device_win_swapchain.h
+++ b/components/viz/service/display_embedder/software_output_device_win_swapchain.h
@@ -43,6 +43,13 @@
   const gfx::Size& GetViewportPixelSize() const override;
   void ReleaseCanvas() override;
 
+  bool HasSwapChainForTesting() const { return !!dxgi_swapchain_; }
+
+  bool HasDeviceContextForTesting() const { return !!d3d11_device_context_; }
+
+ protected:
+  virtual bool UpdateWindowSize(const gfx::Size& viewport_pixel_size);
+
  private:
   raw_ptr<OutputDeviceBacking> const output_backing_;
   gl::ChildWindowWin child_window_;
diff --git a/components/viz/service/display_embedder/software_output_device_win_swapchain_unittest.cc b/components/viz/service/display_embedder/software_output_device_win_swapchain_unittest.cc
new file mode 100644
index 0000000..643f57ca
--- /dev/null
+++ b/components/viz/service/display_embedder/software_output_device_win_swapchain_unittest.cc
@@ -0,0 +1,311 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/software_output_device_win_swapchain.h"
+
+#include <windows.h>
+
+#include <dcomp.h>
+#include <dxgi1_2.h>
+#include <wrl/client.h>
+
+#include "base/win/scoped_gdi_object.h"
+#include "base/win/windows_types.h"
+#include "components/viz/service/display_embedder/output_device_backing.h"
+#include "components/viz/service/display_embedder/test_support/dcomp_mocks.h"
+#include "media/base/win/d3d11_mocks.h"
+#include "media/base/win/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace viz {
+namespace {
+
+constexpr gfx::Size kTestSize(1024, 768);
+
+class MockOutputDeviceBacking : public OutputDeviceBacking {
+ public:
+  MockOutputDeviceBacking() = default;
+  ~MockOutputDeviceBacking() override = default;
+
+  MOCK_METHOD(Microsoft::WRL::ComPtr<ID3D11Texture2D>,
+              GetOrCreateStagingTexture,
+              (),
+              (override));
+  MOCK_METHOD(HRESULT,
+              GetOrCreateDXObjects,
+              (Microsoft::WRL::ComPtr<ID3D11Device> * d3d11_device,
+               Microsoft::WRL::ComPtr<IDXGIFactory2>* dxgi_factory,
+               Microsoft::WRL::ComPtr<IDCompositionDevice>* dcomp_device),
+              (override));
+};
+
+class TestSoftwareOutputDeviceWinSwapChain
+    : public SoftwareOutputDeviceWinSwapChain {
+ public:
+  TestSoftwareOutputDeviceWinSwapChain(HWND hwnd,
+                                       HWND& child_hwnd,
+                                       OutputDeviceBacking* output_backing)
+      : SoftwareOutputDeviceWinSwapChain(hwnd, child_hwnd, output_backing) {}
+
+  // Override to avoid actual window operations.
+  bool UpdateWindowSize(const gfx::Size& viewport_pixel_size) override {
+    return resize_result_;
+  }
+
+  void set_resize_result(bool result) { resize_result_ = result; }
+
+ private:
+  bool resize_result_ = true;
+};
+
+class TestWindowClass {
+ public:
+  TestWindowClass() : window_(nullptr), child_window_(nullptr) {
+    // Create a temporary window for testing
+    window_ = CreateWindowEx(0, L"STATIC", L"Test Window", WS_OVERLAPPEDWINDOW,
+                             CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, nullptr,
+                             nullptr, GetModuleHandle(nullptr), nullptr);
+  }
+
+  ~TestWindowClass() { DestroyWindow(window_); }
+
+  HWND window() const { return window_; }
+  HWND child_window() const { return child_window_; }
+  void set_child_window(HWND hwnd) { child_window_ = hwnd; }
+
+ private:
+  HWND window_;
+  HWND child_window_;
+};
+
+class SoftwareOutputDeviceWinSwapChainTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    mock_output_backing_ =
+        std::make_unique<testing::NiceMock<MockOutputDeviceBacking>>();
+    mock_d3d11_device_ =
+        media::MakeComPtr<testing::NiceMock<media::D3D11DeviceMock>>();
+    mock_dxgi_factory_ =
+        media::MakeComPtr<testing::NiceMock<media::DXGIFactory2Mock>>();
+    mock_dcomp_device_ =
+        media::MakeComPtr<testing::NiceMock<media::DCompositionDeviceMock>>();
+    mock_dxgi_swapchain_ =
+        media::MakeComPtr<testing::NiceMock<media::DXGISwapChain1Mock>>();
+    mock_d3d11_device_context_ =
+        media::MakeComPtr<testing::NiceMock<media::D3D11DeviceContextMock>>();
+    mock_dcomp_visual_ =
+        media::MakeComPtr<testing::NiceMock<media::DCompositionVisualMock>>();
+    mock_dcomp_target_ =
+        media::MakeComPtr<testing::NiceMock<media::DCompositionTargetMock>>();
+
+    // Allow leak for the mock DCompositionDevice since it is prevented from
+    // being reset in the event of unexpected gpu process exit to allow for a
+    // final commit. Also allow leak for the mock DXGISwapChain because the
+    // DCompositionDevice maintains a reference to it via the root visual.
+    testing::Mock::AllowLeak(mock_dcomp_device_.Get());
+    testing::Mock::AllowLeak(mock_dxgi_swapchain_.Get());
+
+    ON_CALL(*mock_output_backing_, GetOrCreateDXObjects)
+        .WillByDefault(
+            [this](Microsoft::WRL::ComPtr<ID3D11Device>* d3d11_device,
+                   Microsoft::WRL::ComPtr<IDXGIFactory2>* dxgi_factory,
+                   Microsoft::WRL::ComPtr<IDCompositionDevice>* dcomp_device) {
+              *d3d11_device = mock_d3d11_device_;
+              *dxgi_factory = mock_dxgi_factory_;
+              *dcomp_device = mock_dcomp_device_;
+              return S_OK;
+            });
+    HWND child_hwnd = nullptr;
+    device_ = std::make_unique<TestSoftwareOutputDeviceWinSwapChain>(
+        window_class_.window(), child_hwnd, mock_output_backing_.get());
+  }
+
+  void TearDown() override {
+    testing::Mock::VerifyAndClearExpectations(mock_dxgi_swapchain_.Get());
+  }
+
+  void SetSwapChainExpectations() {
+    ON_CALL(*mock_dcomp_device_.Get(), CreateTargetForHwnd)
+        .WillByDefault(
+            media::SetComPointeeAndReturnOk<2>(mock_dcomp_target_.Get()));
+    ON_CALL(*mock_dcomp_device_.Get(), CreateVisual)
+        .WillByDefault(
+            media::SetComPointeeAndReturnOk<0>(mock_dcomp_visual_.Get()));
+    ON_CALL(*mock_d3d11_device_.Get(), GetImmediateContext)
+        .WillByDefault(
+            media::SetComPointee<0>(mock_d3d11_device_context_.Get()));
+    ON_CALL(*mock_dxgi_factory_.Get(), CreateSwapChainForComposition)
+        .WillByDefault(
+            media::SetComPointeeAndReturnOk<3>(mock_dxgi_swapchain_.Get()));
+
+    // Device should start with no D3D resources.
+    EXPECT_FALSE(device_->HasSwapChainForTesting());
+    EXPECT_FALSE(device_->HasDeviceContextForTesting());
+    EXPECT_CALL(*mock_output_backing_, GetOrCreateDXObjects).Times(1);
+    EXPECT_EQ(device_->GetViewportPixelSize(), gfx::Size(0, 0));
+  }
+
+  void DoResize(gfx::Size size) {
+    device_->Resize(size, 1.f);
+    EXPECT_EQ(device_->GetViewportPixelSize(), size);
+    EXPECT_TRUE(device_->HasSwapChainForTesting());
+    EXPECT_TRUE(device_->HasDeviceContextForTesting());
+  }
+
+  void DoBeginPaint(gfx::Size size) {
+    Microsoft::WRL::ComPtr<media::D3D11Texture2DMock>
+        mock_d3d11_staging_texture =
+            media::MakeComPtr<testing::NiceMock<media::D3D11Texture2DMock>>();
+    EXPECT_CALL(*mock_output_backing_, GetOrCreateStagingTexture)
+        .WillOnce(testing::Return(mock_d3d11_staging_texture));
+    auto desc = D3D11_TEXTURE2D_DESC{
+        .Width = static_cast<UINT>(size.width()),
+        .Height = static_cast<UINT>(size.height()),
+        .Format = DXGI_FORMAT_B8G8R8A8_UNORM,
+        .SampleDesc = {.Count = 1, .Quality = 0},
+        .Usage = D3D11_USAGE_STAGING,
+        .BindFlags = 0,
+        .CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE};
+    EXPECT_CALL(*mock_d3d11_staging_texture.Get(), GetDesc)
+        .WillOnce(
+            [desc](D3D11_TEXTURE2D_DESC* descriptor) { *descriptor = desc; });
+    EXPECT_CALL(*mock_d3d11_device_context_.Get(), Map)
+        .WillOnce(testing::Return(S_OK));
+    SkCanvas* canvas = device_->BeginPaintDelegated();
+    auto image_info = canvas->imageInfo();
+    EXPECT_EQ(image_info.width(), size.width());
+    EXPECT_EQ(image_info.height(), size.height());
+  }
+
+  TestWindowClass window_class_;
+  std::unique_ptr<testing::NiceMock<MockOutputDeviceBacking>>
+      mock_output_backing_;
+  std::unique_ptr<TestSoftwareOutputDeviceWinSwapChain> device_;
+
+  Microsoft::WRL::ComPtr<media::DXGIFactory2Mock> mock_dxgi_factory_;
+  Microsoft::WRL::ComPtr<media::D3D11DeviceMock> mock_d3d11_device_;
+  Microsoft::WRL::ComPtr<media::DCompositionDeviceMock> mock_dcomp_device_;
+  Microsoft::WRL::ComPtr<IDCompositionTarget> mock_dcomp_target_;
+  Microsoft::WRL::ComPtr<media::D3D11DeviceContextMock>
+      mock_d3d11_device_context_;
+  Microsoft::WRL::ComPtr<IDCompositionVisual> mock_dcomp_visual_;
+  Microsoft::WRL::ComPtr<media::DXGISwapChain1Mock> mock_dxgi_swapchain_;
+};
+
+}  // namespace
+
+// Test that ResizeDelegated creates D3D resources when none exist.
+TEST_F(SoftwareOutputDeviceWinSwapChainTest,
+       ResizeDelegatedCreatesD3DResources) {
+  SetSwapChainExpectations();
+  DoResize(kTestSize);
+}
+
+// Test that ResizeDelegated handles SetWindowPos failure.
+TEST_F(SoftwareOutputDeviceWinSwapChainTest, SetWindowPosFailure) {
+  // Set the resize to fail
+  device_->set_resize_result(false);
+  EXPECT_EQ(device_->GetViewportPixelSize(), gfx::Size(0, 0));
+  device_->Resize(kTestSize, 1.f);
+  EXPECT_EQ(device_->GetViewportPixelSize(), gfx::Size(0, 0));
+}
+
+// Test that ResizeDelegated fails without process crash if CreateTargetForHwnd
+// fails due to E_INVALIDARG. Such a scenario can occur if the window is deleted
+// before the resize is complete.
+TEST_F(SoftwareOutputDeviceWinSwapChainTest, CreateTargetForHwndInvalidArg) {
+  EXPECT_CALL(*mock_dcomp_device_.Get(), CreateTargetForHwnd)
+      .WillOnce(testing::Return(E_INVALIDARG));
+  EXPECT_EQ(device_->GetViewportPixelSize(), gfx::Size(0, 0));
+  device_->Resize(kTestSize, 1.f);
+  EXPECT_EQ(device_->GetViewportPixelSize(), gfx::Size(0, 0));
+}
+
+// Test the entire paint pipeline.
+TEST_F(SoftwareOutputDeviceWinSwapChainTest, Paint) {
+  SetSwapChainExpectations();
+  // Ensure present gets called in EndPaint.
+  ON_CALL(*mock_dxgi_swapchain_.Get(), Present1)
+      .WillByDefault(testing::Return(S_OK));
+
+  DoResize(kTestSize);
+  DoBeginPaint(kTestSize);
+  device_->EndPaintDelegated(
+      gfx::Rect(0, 0, kTestSize.width(), kTestSize.height()));
+}
+
+// Test that Map fails without process crash if the device is removed.
+TEST_F(SoftwareOutputDeviceWinSwapChainTest, FailedMapDueToDeviceRemoved) {
+  SetSwapChainExpectations();
+  EXPECT_CALL(*mock_d3d11_device_context_.Get(), Map)
+      .WillOnce(testing::Return(DXGI_ERROR_DEVICE_REMOVED));
+  EXPECT_CALL(*mock_d3d11_device_.Get(), GetDeviceRemovedReason)
+      .WillOnce(testing::Return(DXGI_ERROR_DEVICE_REMOVED));
+
+  DoResize(kTestSize);
+
+  Microsoft::WRL::ComPtr<media::D3D11Texture2DMock> mock_d3d11_staging_texture =
+      media::MakeComPtr<testing::NiceMock<media::D3D11Texture2DMock>>();
+  EXPECT_CALL(*mock_output_backing_, GetOrCreateStagingTexture)
+      .WillOnce(testing::Return(mock_d3d11_staging_texture));
+  // There should be no GPU process termination.
+  SkCanvas* canvas = device_->BeginPaintDelegated();
+  EXPECT_FALSE(!!canvas);
+}
+
+TEST_F(SoftwareOutputDeviceWinSwapChainTest, FailedPresentDueToDeviceRemoved) {
+  SetSwapChainExpectations();
+  EXPECT_CALL(*mock_dxgi_swapchain_.Get(), Present1)
+      .WillOnce(testing::Return(DXGI_ERROR_DEVICE_REMOVED));
+  EXPECT_CALL(*mock_d3d11_device_.Get(), GetDeviceRemovedReason)
+      .WillOnce(testing::Return(DXGI_ERROR_DEVICE_REMOVED));
+
+  DoResize(kTestSize);
+  DoBeginPaint(kTestSize);
+
+  // There should be no GPU process termination.
+  device_->EndPaintDelegated(
+      gfx::Rect(0, 0, kTestSize.width(), kTestSize.height()));
+}
+
+using SoftwareOutputDeviceWinSwapChainDeathTest =
+    SoftwareOutputDeviceWinSwapChainTest;
+
+// Ensure the GPU process is terminated if ResizeBuffers fails.
+TEST_F(SoftwareOutputDeviceWinSwapChainDeathTest, ResizeBuffersFailure) {
+  SetSwapChainExpectations();
+  ON_CALL(*mock_dxgi_swapchain_.Get(),
+          ResizeBuffers(/*BufferCount=*/2, /*Width=*/1180, /*Height=*/620,
+                        /*NewFormat=*/testing::_, /*SwapChainFlags=*/0))
+      .WillByDefault(testing::Return(E_FAIL));
+
+  // Do first resize and ensure it is successful. We don't expect ResizeBuffers
+  // to be called here as it's the first Resize and the SwapChain will be
+  // initialized in the scall.
+  DoResize(kTestSize);
+
+  // SwapChain should be initialized, resulting in a ResizeBuffers call; which
+  // should fail triggering a crash.
+  EXPECT_DEATH(device_->Resize(gfx::Size(1180, 620), 1.f), "");
+}
+
+// Test that ResizeDelegated results in a process crash if
+// CreateSwapChainForComposition fails.
+TEST_F(SoftwareOutputDeviceWinSwapChainDeathTest,
+       CreateSwapSchainForCompositionFailure) {
+  ON_CALL(*mock_dcomp_device_.Get(), CreateTargetForHwnd)
+      .WillByDefault(
+          media::SetComPointeeAndReturnOk<2>(mock_dcomp_target_.Get()));
+  ON_CALL(*mock_dcomp_device_.Get(), CreateVisual)
+      .WillByDefault(
+          media::SetComPointeeAndReturnOk<0>(mock_dcomp_visual_.Get()));
+  ON_CALL(*mock_dxgi_factory_.Get(), CreateSwapChainForComposition)
+      .WillByDefault(testing::Return(E_OUTOFMEMORY));
+  EXPECT_DEATH(device_->Resize(kTestSize, 1.f), "");
+}
+
+}  // namespace viz
diff --git a/components/viz/service/display_embedder/test_support/dcomp_mocks.cc b/components/viz/service/display_embedder/test_support/dcomp_mocks.cc
new file mode 100644
index 0000000..0c7bbb5d
--- /dev/null
+++ b/components/viz/service/display_embedder/test_support/dcomp_mocks.cc
@@ -0,0 +1,18 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/service/display_embedder/test_support/dcomp_mocks.h"
+
+namespace media {
+
+DCompositionDeviceMock::DCompositionDeviceMock() = default;
+DCompositionDeviceMock::~DCompositionDeviceMock() = default;
+
+DCompositionTargetMock::DCompositionTargetMock() = default;
+DCompositionTargetMock::~DCompositionTargetMock() = default;
+
+DCompositionVisualMock::DCompositionVisualMock() = default;
+DCompositionVisualMock::~DCompositionVisualMock() = default;
+
+}  // namespace media
diff --git a/components/viz/service/display_embedder/test_support/dcomp_mocks.h b/components/viz/service/display_embedder/test_support/dcomp_mocks.h
new file mode 100644
index 0000000..21ad2a36d
--- /dev/null
+++ b/components/viz/service/display_embedder/test_support/dcomp_mocks.h
@@ -0,0 +1,153 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_TEST_SUPPORT_DCOMP_MOCKS_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_TEST_SUPPORT_DCOMP_MOCKS_H_
+
+#include <dcomp.h>
+#include <wrl.h>
+
+#include "media/base/win/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+
+// Example mock for IDCompositionDevice, modeled after D3D11DeviceMock.
+class DCompositionDeviceMock
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          IDCompositionDevice> {
+ public:
+  DCompositionDeviceMock();
+  ~DCompositionDeviceMock() override;
+
+  // IUnknown
+  MOCK_STDCALL_METHOD2(QueryInterface, HRESULT(REFIID, void**));
+  MOCK_STDCALL_METHOD0(AddRef, ULONG());
+  MOCK_STDCALL_METHOD0(Release, ULONG());
+
+  // IDCompositionDevice
+  MOCK_STDCALL_METHOD0(Commit, HRESULT());
+  MOCK_STDCALL_METHOD0(WaitForCommitCompletion, HRESULT());
+  MOCK_STDCALL_METHOD1(GetFrameStatistics,
+                       HRESULT(DCOMPOSITION_FRAME_STATISTICS*));
+  MOCK_STDCALL_METHOD3(CreateTargetForHwnd,
+                       HRESULT(HWND, BOOL, IDCompositionTarget**));
+  MOCK_STDCALL_METHOD1(CreateVisual, HRESULT(IDCompositionVisual**));
+  MOCK_STDCALL_METHOD5(CreateSurface,
+                       HRESULT(UINT,
+                               UINT,
+                               DXGI_FORMAT,
+                               DXGI_ALPHA_MODE,
+                               IDCompositionSurface**));
+  MOCK_STDCALL_METHOD5(CreateVirtualSurface,
+                       HRESULT(UINT,
+                               UINT,
+                               DXGI_FORMAT,
+                               DXGI_ALPHA_MODE,
+                               IDCompositionVirtualSurface**));
+  MOCK_STDCALL_METHOD2(CreateSurfaceFromHandle, HRESULT(HANDLE, IUnknown**));
+  MOCK_STDCALL_METHOD2(CreateSurfaceFromHwnd, HRESULT(HWND, IUnknown**));
+  MOCK_STDCALL_METHOD1(CreateTranslateTransform,
+                       HRESULT(IDCompositionTranslateTransform**));
+  MOCK_STDCALL_METHOD1(CreateScaleTransform,
+                       HRESULT(IDCompositionScaleTransform**));
+  MOCK_STDCALL_METHOD1(CreateRotateTransform,
+                       HRESULT(IDCompositionRotateTransform**));
+  MOCK_STDCALL_METHOD1(CreateSkewTransform,
+                       HRESULT(IDCompositionSkewTransform**));
+  MOCK_STDCALL_METHOD1(CreateMatrixTransform,
+                       HRESULT(IDCompositionMatrixTransform**));
+  MOCK_STDCALL_METHOD3(CreateTransformGroup,
+                       HRESULT(IDCompositionTransform**,
+                               UINT,
+                               IDCompositionTransform**));
+  MOCK_STDCALL_METHOD1(CreateTranslateTransform3D,
+                       HRESULT(IDCompositionTranslateTransform3D**));
+  MOCK_STDCALL_METHOD1(CreateScaleTransform3D,
+                       HRESULT(IDCompositionScaleTransform3D**));
+  MOCK_STDCALL_METHOD1(CreateRotateTransform3D,
+                       HRESULT(IDCompositionRotateTransform3D**));
+  MOCK_STDCALL_METHOD1(CreateMatrixTransform3D,
+                       HRESULT(IDCompositionMatrixTransform3D**));
+  MOCK_STDCALL_METHOD3(CreateTransform3DGroup,
+                       HRESULT(IDCompositionTransform3D**,
+                               UINT,
+                               IDCompositionTransform3D**));
+  MOCK_STDCALL_METHOD1(CreateEffectGroup, HRESULT(IDCompositionEffectGroup**));
+  MOCK_STDCALL_METHOD1(CreateRectangleClip,
+                       HRESULT(IDCompositionRectangleClip**));
+  MOCK_STDCALL_METHOD1(CreateAnimation, HRESULT(IDCompositionAnimation**));
+  MOCK_STDCALL_METHOD1(CheckDeviceState, HRESULT(BOOL*));
+};
+
+class DCompositionTargetMock
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          IDCompositionTarget> {
+ public:
+  DCompositionTargetMock();
+  ~DCompositionTargetMock() override;
+
+  // IUnknown
+  MOCK_STDCALL_METHOD2(QueryInterface, HRESULT(REFIID, void**));
+  MOCK_STDCALL_METHOD0(AddRef, ULONG());
+  MOCK_STDCALL_METHOD0(Release, ULONG());
+
+  // IDCompositionTarget
+  MOCK_STDCALL_METHOD1(SetRoot, HRESULT(IDCompositionVisual*));
+};
+
+class DCompositionVisualMock
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          IDCompositionVisual> {
+ public:
+  DCompositionVisualMock();
+  ~DCompositionVisualMock() override;
+
+  // IUnknown
+  MOCK_STDCALL_METHOD2(QueryInterface, HRESULT(REFIID riid, void** ppvObject));
+  MOCK_STDCALL_METHOD0(AddRef, ULONG());
+  MOCK_STDCALL_METHOD0(Release, ULONG());
+
+  // IDCompositionVisual (with all overloaded methods left as-is)
+  MOCK_STDCALL_METHOD1(SetOffsetX, HRESULT(float offsetX));
+  MOCK_STDCALL_METHOD1(SetOffsetX, HRESULT(IDCompositionAnimation* animation));
+
+  MOCK_STDCALL_METHOD1(SetOffsetY, HRESULT(float offsetY));
+  MOCK_STDCALL_METHOD1(SetOffsetY, HRESULT(IDCompositionAnimation* animation));
+
+  MOCK_STDCALL_METHOD1(SetTransform, HRESULT(const D2D_MATRIX_3X2_F& matrix));
+  MOCK_STDCALL_METHOD1(SetTransform,
+                       HRESULT(IDCompositionTransform* transform));
+
+  MOCK_STDCALL_METHOD1(SetTransformParent,
+                       HRESULT(IDCompositionVisual* visual));
+  MOCK_STDCALL_METHOD1(SetEffect, HRESULT(IDCompositionEffect* effect));
+
+  MOCK_STDCALL_METHOD1(SetBitmapInterpolationMode,
+                       HRESULT(DCOMPOSITION_BITMAP_INTERPOLATION_MODE mode));
+  MOCK_STDCALL_METHOD1(SetBorderMode, HRESULT(DCOMPOSITION_BORDER_MODE mode));
+
+  // The interface calls these "SetClip", so we keep them as such:
+  MOCK_STDCALL_METHOD1(SetClip, HRESULT(const D2D_RECT_F& rect));
+  MOCK_STDCALL_METHOD1(SetClip, HRESULT(IDCompositionClip* clip));
+
+  MOCK_STDCALL_METHOD1(SetContent, HRESULT(IUnknown* content));
+
+  MOCK_STDCALL_METHOD3(AddVisual,
+                       HRESULT(IDCompositionVisual* visual,
+                               BOOL insertAbove,
+                               IDCompositionVisual* referenceVisual));
+  MOCK_STDCALL_METHOD1(RemoveVisual, HRESULT(IDCompositionVisual* visual));
+  MOCK_STDCALL_METHOD0(RemoveAllVisuals, HRESULT());
+
+  MOCK_STDCALL_METHOD1(SetCompositeMode,
+                       HRESULT(DCOMPOSITION_COMPOSITE_MODE mode));
+};
+
+}  // namespace media
+
+#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_TEST_SUPPORT_DCOMP_MOCKS_H_
diff --git a/components/webapps/services/web_app_origin_association/web_app_origin_association_parser_impl_unittest.cc b/components/webapps/services/web_app_origin_association/web_app_origin_association_parser_impl_unittest.cc
index 28dec86d..a8e2421 100644
--- a/components/webapps/services/web_app_origin_association/web_app_origin_association_parser_impl_unittest.cc
+++ b/components/webapps/services/web_app_origin_association/web_app_origin_association_parser_impl_unittest.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "base/json/json_reader.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -117,7 +118,12 @@
   ASSERT_TRUE(!association);
   ASSERT_FALSE(errors.empty());
   ASSERT_EQ(1u, errors.size());
-  EXPECT_EQ(errors[0]->message, "EOF while parsing a list at line 1 column 5");
+  if (base::JSONReader::UsingRust()) {
+    EXPECT_EQ(errors[0]->message,
+              "EOF while parsing a list at line 1 column 5");
+  } else {
+    EXPECT_EQ(errors[0]->message, "Line: 1, column: 6, Syntax error.");
+  }
 
   histogram_tester_.ExpectBucketCount(
       kParseResultHistogram,
diff --git a/components/webapps/services/web_app_origin_association/web_app_origin_association_parser_unittest.cc b/components/webapps/services/web_app_origin_association/web_app_origin_association_parser_unittest.cc
index 5ad3b20..afa40c9 100644
--- a/components/webapps/services/web_app_origin_association/web_app_origin_association_parser_unittest.cc
+++ b/components/webapps/services/web_app_origin_association/web_app_origin_association_parser_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/webapps/services/web_app_origin_association/web_app_origin_association_parser.h"
 
+#include "base/json/json_reader.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -64,7 +65,11 @@
   EXPECT_TRUE(IsAssociationNull(association));
   EXPECT_EQ(1u, GetErrorCount());
 
-  EXPECT_EQ(errors()[0], "EOF while parsing a value at line 1 column 0");
+  if (base::JSONReader::UsingRust()) {
+    EXPECT_EQ(errors()[0], "EOF while parsing a value at line 1 column 0");
+  } else {
+    EXPECT_EQ(errors()[0], "Line: 1, column: 1, Unexpected token.");
+  }
 }
 
 TEST_F(WebAppOriginAssociationParserTest, NoContentParses) {
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index bdc48f4..cf87c47 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2072,9 +2072,6 @@
     "screenlock_monitor/screenlock_monitor_device_source.h",
     "screenlock_monitor/screenlock_monitor_source.cc",
     "screenlock_monitor/screenlock_monitor_source.h",
-    "security/coop/coop_related_group.cc",
-    "security/coop/coop_related_group.h",
-    "security/coop/coop_swap_result.h",
     "security/coop/cross_origin_isolation_mode.h",
     "security/coop/cross_origin_opener_policy_access_report_manager.cc",
     "security/coop/cross_origin_opener_policy_access_report_manager.h",
diff --git a/content/browser/browsing_instance.cc b/content/browser/browsing_instance.cc
index 88a4025..3d6bc8e 100644
--- a/content/browser/browsing_instance.cc
+++ b/content/browser/browsing_instance.cc
@@ -9,7 +9,6 @@
 #include "base/containers/contains.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/origin_agent_cluster_isolation_state.h"
-#include "content/browser/security/coop/coop_related_group.h"
 #include "content/browser/site_info.h"
 #include "content/browser/site_instance_group.h"
 #include "content/browser/site_instance_impl.h"
@@ -32,9 +31,7 @@
     const WebExposedIsolationInfo& web_exposed_isolation_info,
     bool is_guest,
     bool is_fenced,
-    bool is_fixed_storage_partition,
-    const scoped_refptr<CoopRelatedGroup>& coop_related_group,
-    std::optional<url::Origin> common_coop_origin)
+    bool is_fixed_storage_partition)
     : isolation_context_(
           BrowsingInstanceId::FromUnsafeValue(next_browsing_instance_id_++),
           BrowserOrResourceContext(browser_context),
@@ -45,23 +42,11 @@
       active_contents_count_(0u),
       default_site_instance_(nullptr),
       web_exposed_isolation_info_(web_exposed_isolation_info),
-      coop_related_group_(coop_related_group),
-      common_coop_origin_(common_coop_origin),
       is_fixed_storage_partition_(is_fixed_storage_partition) {
   DCHECK(browser_context);
   if (is_guest) {
     CHECK(is_fixed_storage_partition);
   }
-
-  // If we get passed an empty group, build a new one. This is the common case.
-  if (!coop_related_group_) {
-    coop_related_group_ =
-        base::WrapRefCounted<CoopRelatedGroup>(new CoopRelatedGroup(
-            browser_context, is_guest, is_fenced, is_fixed_storage_partition_));
-  }
-  DCHECK(coop_related_group_);
-
-  coop_related_group_->RegisterBrowsingInstance(this);
 }
 
 BrowserContext* BrowsingInstance::GetBrowserContext() const {
@@ -142,14 +127,6 @@
   return instance;
 }
 
-scoped_refptr<SiteInstanceImpl>
-BrowsingInstance::GetCoopRelatedSiteInstanceForURL(
-    const UrlInfo& url_info,
-    bool allow_default_instance) {
-  return coop_related_group_->GetCoopRelatedSiteInstanceForURL(
-      url_info, allow_default_instance);
-}
-
 scoped_refptr<SiteInstanceImpl> BrowsingInstance::GetSiteInstanceForURLHelper(
     const UrlInfo& url_info,
     bool allow_default_instance) {
@@ -260,8 +237,6 @@
       ChildProcessSecurityPolicyImpl::GetInstance();
   policy->RemoveOptInIsolatedOriginsForBrowsingInstance(
       isolation_context_.browsing_instance_id());
-
-  coop_related_group_->UnregisterBrowsingInstance(this);
 }
 
 SiteInfo BrowsingInstance::ComputeSiteInfoForURL(
@@ -339,21 +314,13 @@
   return result;
 }
 
-size_t BrowsingInstance::GetCoopRelatedGroupActiveContentsCount() {
-  return coop_related_group_->active_contents_count();
-}
-
 void BrowsingInstance::IncrementActiveContentsCount() {
   active_contents_count_++;
-
-  coop_related_group_->increment_active_contents_count();
 }
 
 void BrowsingInstance::DecrementActiveContentsCount() {
   DCHECK_LT(0u, active_contents_count_);
   active_contents_count_--;
-
-  coop_related_group_->decrement_active_contents_count();
 }
 
 }  // namespace content
diff --git a/content/browser/browsing_instance.h b/content/browser/browsing_instance.h
index ad176257..e8b90bf1 100644
--- a/content/browser/browsing_instance.h
+++ b/content/browser/browsing_instance.h
@@ -15,7 +15,6 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "content/browser/isolation_context.h"
-#include "content/browser/security/coop/coop_related_group.h"
 #include "content/browser/web_exposed_isolation_info.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_context.h"
@@ -59,10 +58,10 @@
 // be manually deleted.
 //
 // BrowsingInstance has no public members, as it is designed to be
-// visible only from the SiteInstance and CoopRelatedGroup classes. To get a new
-// SiteInstance that is part of the same BrowsingInstance, use
-// SiteInstance::GetRelatedSiteInstance. Because of this, BrowsingInstances and
-// SiteInstances are tested together in site_instance_unittest.cc.
+// visible only from the SiteInstance class. To get a new SiteInstance that is
+// part of the same BrowsingInstance, use SiteInstance::GetRelatedSiteInstance.
+// Because of this, BrowsingInstances and SiteInstances are tested together in
+// site_instance_unittest.cc.
 //
 // Note that a browsing instance in the browser is independently tracked in
 // the renderer inside blink::Page::RelatedPages() method (in theory the browser
@@ -79,7 +78,6 @@
   friend class base::RefCounted<BrowsingInstance>;
   friend class SiteInstanceGroup;
   friend class SiteInstanceImpl;
-  friend class CoopRelatedGroup;
   FRIEND_TEST_ALL_PREFIXES(SiteInstanceGroupTest, BrowsingInstanceLifetime);
   FRIEND_TEST_ALL_PREFIXES(SiteInstanceTest, OneSiteInstancePerSite);
   FRIEND_TEST_ALL_PREFIXES(SiteInstanceTest,
@@ -108,23 +106,12 @@
   // if `is_guest` is true. Note that `is_guest`, `is_fenced`, and
   // `is_fixed_storage_partition` cannot change over the lifetime of the
   // BrowsingInstance.
-  //
-  // `coop_related_group` represents the CoopRelatedGroup to which this
-  // BrowsingInstance belongs. Pages that live in BrowsingInstances in the same
-  // group can communicate with each other through a subset of the WindowProxy
-  // APIs. This is only used for COOP logic and for all other cases should
-  // simply be nullptr. The constructor will take care of building a new group.
-  //
-  // If `common_coop_origin` is set, it indicates that all documents hosted by
-  // the BrowsingInstance have the same COOP value defined by the given origin.
   explicit BrowsingInstance(
       BrowserContext* context,
       const WebExposedIsolationInfo& web_exposed_isolation_info,
       bool is_guest,
       bool is_fenced,
-      bool is_fixed_storage_partition,
-      const scoped_refptr<CoopRelatedGroup>& coop_related_group,
-      std::optional<url::Origin> common_coop_origin);
+      bool is_fixed_storage_partition);
 
   ~BrowsingInstance();
 
@@ -183,14 +170,6 @@
   scoped_refptr<SiteInstanceImpl> GetSiteInstanceForSiteInfo(
       const SiteInfo& site_info);
 
-  // Return a SiteInstance in the same CoopRelatedGroup as this
-  // BrowsingInstance. It might or might not be in a new BrowsingInstance, and
-  // if it reuses an existing BrowsingInstance of the group, it might reuse an
-  // appropriate SiteInstance as well.
-  scoped_refptr<SiteInstanceImpl> GetCoopRelatedSiteInstanceForURL(
-      const UrlInfo& url_info,
-      bool allow_default_instance);
-
   // Returns a SiteInfo with site and process-lock URLs for |url_info| that are
   // identical with what these values would be if we called
   // GetSiteInstanceForURL() with the same `url_info` and
@@ -230,21 +209,10 @@
   // BrowsingInstance.
   void UnregisterSiteInstance(SiteInstanceImpl* site_instance);
 
-  // Returns the token uniquely identifying the CoopRelatedGroup this
-  // BrowsingInstance belongs to. This might be used in the renderer, as opposed
-  // to IDs.
-  base::UnguessableToken coop_related_group_token() const {
-    return coop_related_group_->token();
-  }
-
   // Returns the token uniquely identifying this BrowsingInstance. See member
   // declaration for more context.
   base::UnguessableToken token() const { return token_; }
 
-  // Returns the total number of WebContents either living in this
-  // BrowsingInstance or that can communicate with it via the CoopRelatedGroup.
-  size_t GetCoopRelatedGroupActiveContentsCount();
-
   // Tracks the number of WebContents currently in this BrowsingInstance.
   // Note: We also separately track the number of WebContents in the entire
   // CoopRelatedGroup, and keep the per-BrowsingInstance counts for validity
@@ -281,9 +249,7 @@
 
   SiteInstanceImpl* default_site_instance() { return default_site_instance_; }
 
-  const std::optional<url::Origin>& common_coop_origin() const {
-    return common_coop_origin_;
-  }
+  size_t active_contents_count() { return active_contents_count_; }
 
   // The next available browser-global BrowsingInstance ID.
   static int next_browsing_instance_id_;
@@ -334,34 +300,6 @@
   // StoragePartitionConfig here.
   std::optional<StoragePartitionConfig> storage_partition_config_;
 
-  // The CoopRelatedGroup this BrowsingInstance belongs to. BrowsingInstances in
-  // the same CoopRelatedGroup have limited window proxy access to each other.
-  // In most cases, a CoopRelatedGroup will only contain a single
-  // BrowsingInstance, unless pages that use COOP: restrict-properties headers
-  // are involved.
-  scoped_refptr<CoopRelatedGroup> coop_related_group_;
-
-  // If set, indicates that all documents in this BrowsingInstance share the
-  // same COOP value defined by the given origin. In practice, this can only be
-  // the case for COOP: same-origin and COOP: restrict-properties.
-  //
-  // For COOP: same-origin, this will be enforced by COOP swap rules and the
-  // value is recorded for invariant checking.
-  //
-  // For COOP: restrict-properties, this is also used to make sure that the
-  // BrowsingInstance is suitable when we're trying to put a new document into
-  // an existing BrowsingInstance that is part of the CoopRelatedGroup. To
-  // prevent unwanted access, a document with COOP: restrict-properties set from
-  // origin a.com should only be put in a BrowsingInstance that holds such
-  // documents. This would otherwise break the access guarantees that we have
-  // given, of only being able to DOM script same-origin same-COOP documents,
-  // and to have limited cross-origin communication with all other pages.
-  //
-  // TODO(crbug.com/40879437): This assumes that popups opened from
-  // cross-origin iframes are opened with no-opener. Once COOP inheritance for
-  // those cases is figured out, change the mentions of origin to "COOP origin".
-  std::optional<url::Origin> common_coop_origin_;
-
   // Set to true if the StoragePartition should be preserved across future
   // navigations in the frames belonging to this BrowsingInstance. For <webview>
   // tags, this is always true.
diff --git a/content/browser/interest_group/ad_auction_service_impl_unittest.cc b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
index af556d8..04a31b97 100644
--- a/content/browser/interest_group/ad_auction_service_impl_unittest.cc
+++ b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
@@ -4224,6 +4224,78 @@
   EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
 }
 
+// UpdateJSONParserCrash fails on Android or with the Rust parser because in
+// those conditions the data decoder doesn't use a separate process to parse
+// JSON. On other platforms, the C++ parser runs out-of-proc for safety.
+#if !BUILDFLAG(IS_ANDROID)
+
+// The server response is valid, but we simulate the JSON parser (which may
+// run in a separate process) crashing, so the update doesn't happen.
+TEST_F(AdAuctionServiceImplTest, UpdateJSONParserCrash) {
+  // Disable the Rust JSON parser, as it is in-process and cannot crash.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatureState(base::features::kUseRustJsonParser, false);
+
+  network_responder_->RegisterUpdateResponse(kUpdateUrlPath, R"({
+"ads": [{"renderURL": "https://example.com/new_render"
+        }]
+})");
+
+  blink::InterestGroup interest_group = CreateInterestGroup();
+  // Set a long expiration delta so that we can advance to the next rate limit
+  // period without the interest group expiring.
+  interest_group.expiry = base::Time::Now() + base::Days(30);
+  interest_group.update_url = kUpdateUrlA;
+  interest_group.bidding_url = kBiddingLogicUrlA;
+  interest_group.trusted_bidding_signals_url = kTrustedBiddingSignalsUrlA;
+  interest_group.trusted_bidding_signals_keys.emplace();
+  interest_group.trusted_bidding_signals_keys->push_back("key1");
+  interest_group.ads.emplace();
+  blink::InterestGroup::Ad ad(
+      /*render_gurl=*/GURL("https://example.com/render"),
+      /*metadata=*/std::nullopt);
+  interest_group.ads->emplace_back(std::move(ad));
+  JoinInterestGroupAndFlush(interest_group);
+  EXPECT_EQ(1, GetJoinCount(kOriginA, kInterestGroupName));
+
+  // Simulate the JSON service crashing instead of returning a result.
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
+  in_process_data_decoder.SimulateJsonParserCrash(
+      /*drop=*/true);
+
+  UpdateInterestGroupNoFlush();
+  task_environment()->RunUntilIdle();
+
+  // Check that the ads didn't change.
+  scoped_refptr<StorageInterestGroups> groups =
+      GetInterestGroupsForOwner(kOriginA);
+  ASSERT_EQ(groups->size(), 1u);
+  auto group = groups->GetInterestGroups()[0]->interest_group;
+  ASSERT_TRUE(group.ads.has_value());
+  ASSERT_EQ(group.ads->size(), 1u);
+  EXPECT_EQ(group.ads.value()[0].render_url(), "https://example.com/render");
+
+  // Try another IG update, this time with no crash. It should succceed.
+  // (We need to advance time since this next attempt is rate-limited).
+  in_process_data_decoder.SimulateJsonParserCrash(
+      /*drop=*/false);
+  task_environment()->FastForwardBy(
+      InterestGroupStorage::kUpdateSucceededBackoffPeriod);
+  UpdateInterestGroupNoFlush();
+  task_environment()->RunUntilIdle();
+
+  // Check that the ads *did* change this time.
+  groups = GetInterestGroupsForOwner(kOriginA);
+  ASSERT_EQ(groups->size(), 1u);
+  group = groups->GetInterestGroups()[0]->interest_group;
+  ASSERT_TRUE(group.ads.has_value());
+  ASSERT_EQ(group.ads->size(), 1u);
+  EXPECT_EQ(group.ads.value()[0].render_url(),
+            "https://example.com/new_render");
+}
+
+#endif  // !BUILDFLAG(IS_ANDROID)
+
 // Trigger an update, but block it via ContentBrowserClient policy.
 // The update shouldn't happen.
 TEST_F(AdAuctionServiceImplTest, UpdateBlockedByContentBrowserClient) {
diff --git a/content/browser/preloading/prerender/prerender_browsertest.cc b/content/browser/preloading/prerender/prerender_browsertest.cc
index db95f8b..c194b59 100644
--- a/content/browser/preloading/prerender/prerender_browsertest.cc
+++ b/content/browser/preloading/prerender/prerender_browsertest.cc
@@ -15046,77 +15046,9 @@
   EXPECT_FALSE(prerender_observer.was_activated());
 }
 
-class PrerenderWarmUpCompositorBrowserTest
-    : public PrerenderBrowserTest,
-      public testing::WithParamInterface<std::tuple<bool, bool, std::string>> {
- public:
-  static std::string DescribeParams(
-      const testing::TestParamInfo<ParamType>& info) {
-    auto [warm_up_compositor, prerender2_warm_up_compositor,
-          prerender2_warm_up_compositor_trigger_point] = info.param;
-    std::stringstream params_description;
-    params_description << "kWarmUpCompositor";
-    params_description << (warm_up_compositor ? "Enabled" : "Disabled");
-    params_description << "_kPrerender2WarmUpCompositor";
-    params_description << (prerender2_warm_up_compositor ? "Enabled"
-                                                         : "Disabled");
-    params_description << "_" << prerender2_warm_up_compositor_trigger_point;
-    return params_description.str();
-  }
-
-  PrerenderWarmUpCompositorBrowserTest() {
-    std::vector<base::test::FeatureRefAndParams> enabled_features;
-    std::vector<base::test::FeatureRef> disabled_features;
-    auto [warm_up_compositor, prerender2_warm_up_compositor,
-          prerender2_warm_up_compositor_trigger_point] = GetParam();
-    if (warm_up_compositor) {
-      enabled_features.push_back({features::kWarmUpCompositor, {}});
-    } else {
-      disabled_features.push_back(features::kWarmUpCompositor);
-    }
-
-    if (prerender2_warm_up_compositor) {
-      enabled_features.push_back(
-          {blink::features::kPrerender2WarmUpCompositor,
-           {{"trigger_point", prerender2_warm_up_compositor_trigger_point}}});
-    } else {
-      disabled_features.push_back(blink::features::kPrerender2WarmUpCompositor);
-    }
-
-    feature_list_.InitWithFeaturesAndParameters(enabled_features,
-                                                disabled_features);
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    PrerenderWarmUpCompositorBrowserTest,
-    // Flips `kWarmUpCompositor` (cc) and `kPrerender2WarmUpCompositor`(blink)
-    // and puts the value of `kPrerender2WarmUpCompositorTriggerPoint` (if
-    // latter flag is enabled).
-    testing::Values(
-        std::make_tuple(true, true, "did_commit_load"),
-        std::make_tuple(true, true, "did_dispatch_dom_content_loaded_event"),
-        std::make_tuple(true, true, "did_finish_load"),
-        // `kWarmUpCompositor` controls the independent cc internal feature of
-        // warming up, and `kPrerender2WarmUpCompositor` manages the trigger
-        // point of that feature for prerender case. Therefore, warming up on
-        // prerender should not performed in the first place unless both flags
-        // are enabled.
-        std::make_tuple(true, false, ""),
-        std::make_tuple(false, true, "did_commit_load"),
-        std::make_tuple(false, true, "did_dispatch_dom_content_loaded_event"),
-        std::make_tuple(false, true, "did_finish_load")),
-    PrerenderWarmUpCompositorBrowserTest::DescribeParams);
-
-// Test that the prerendering page does not crash when enabling compositor
-// warming up features.
-// TODO(crbug.com/41496019): Check whether the warming up is actually happening.
-IN_PROC_BROWSER_TEST_P(PrerenderWarmUpCompositorBrowserTest,
-                       WarmingUpCCDoesntInvokeCrashes) {
+// Tests that the prerendering page does not crash when performing compositor
+// warming up.
+IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, WarmingUpCCDoesntInvokeCrashes) {
   const GURL initial_url = GetUrl("/empty.html");
   const GURL prerendering_url = GetUrl("/empty.html?prerender");
 
diff --git a/content/browser/renderer_host/browsing_context_group_swap.cc b/content/browser/renderer_host/browsing_context_group_swap.cc
index e242be90..9f1856f 100644
--- a/content/browser/renderer_host/browsing_context_group_swap.cc
+++ b/content/browser/renderer_host/browsing_context_group_swap.cc
@@ -23,11 +23,6 @@
           ShouldSwapBrowsingInstance::kYes_ForceSwap};
 }
 
-BrowsingContextGroupSwap BrowsingContextGroupSwap::CreateRelatedCoopSwap() {
-  return {BrowsingContextGroupSwapType::kRelatedCoopSwap,
-          ShouldSwapBrowsingInstance::kYes_ForceSwap};
-}
-
 BrowsingContextGroupSwap BrowsingContextGroupSwap::CreateSecuritySwap() {
   return {BrowsingContextGroupSwapType::kSecuritySwap,
           ShouldSwapBrowsingInstance::kYes_ForceSwap};
@@ -44,7 +39,6 @@
       return false;
 
     case BrowsingContextGroupSwapType::kCoopSwap:
-    case BrowsingContextGroupSwapType::kRelatedCoopSwap:
     case BrowsingContextGroupSwapType::kSecuritySwap:
     case BrowsingContextGroupSwapType::kProactiveSwap:
       return true;
@@ -57,7 +51,6 @@
     case BrowsingContextGroupSwapType::kNoSwap:
     case BrowsingContextGroupSwapType::kSecuritySwap:
     case BrowsingContextGroupSwapType::kProactiveSwap:
-    case BrowsingContextGroupSwapType::kRelatedCoopSwap:
       return false;
 
     case BrowsingContextGroupSwapType::kCoopSwap:
@@ -74,7 +67,6 @@
       return false;
 
     case BrowsingContextGroupSwapType::kCoopSwap:
-    case BrowsingContextGroupSwapType::kRelatedCoopSwap:
       return true;
   }
   NOTREACHED();
diff --git a/content/browser/renderer_host/browsing_context_group_swap.h b/content/browser/renderer_host/browsing_context_group_swap.h
index 68da509..954d7c6 100644
--- a/content/browser/renderer_host/browsing_context_group_swap.h
+++ b/content/browser/renderer_host/browsing_context_group_swap.h
@@ -19,13 +19,6 @@
   kNoSwap,
   // Used for swaps forced by a non matching COOP policy.
   kCoopSwap,
-  // Used for some swaps forced by a non matching COOP: restrict-properties
-  // policy. It puts the new document into a related browsing context group.
-  //
-  // Contrary to unrelated BrowsingContext groups, the communication in between
-  // two related browsing context groups is possible, but limited to using
-  // Window.postMessage() and Window.closed only.
-  kRelatedCoopSwap,
   // Used for swaps forced by a non-COOP security reason. This could be a
   // navigation from a WebUI page to a normal page for example.
   kSecuritySwap,
@@ -48,20 +41,11 @@
   static BrowsingContextGroupSwap CreateProactiveSwap(
       ShouldSwapBrowsingInstance reason);
 
-  // CreateRelatedCoopSwap() should only be used in cases where we can
-  // guarantee that we will not reuse the current browsing context group as part
-  // of the CoopRelatedGroup reuse mechanism. If the browsing context group ends
-  // up being reused, this will very likely cause a crash. Cases with
-  // CoopSwapResult::kRelatedSwap should provide such guarantees.
-  static BrowsingContextGroupSwap CreateRelatedCoopSwap();
-
   BrowsingContextGroupSwapType type() const { return type_; }
   ShouldSwapBrowsingInstance reason() const { return reason_.value(); }
 
   // Returns whether we should use a different browsing context group for the
-  // navigation. Note that this does not indicate whether it should stay in the
-  // same CoopRelatedGroup. To know if it should, verify type() ==
-  // kRelatedCoopSwap.
+  // navigation.
   bool ShouldSwap() const;
 
   // Indicates whether the proxies to other documents in this browsing context
diff --git a/content/browser/renderer_host/browsing_context_group_swap_unittest.cc b/content/browser/renderer_host/browsing_context_group_swap_unittest.cc
index 4e32c8d..e22c4af 100644
--- a/content/browser/renderer_host/browsing_context_group_swap_unittest.cc
+++ b/content/browser/renderer_host/browsing_context_group_swap_unittest.cc
@@ -42,19 +42,6 @@
   EXPECT_EQ(ShouldSwapBrowsingInstance::kYes_ForceSwap, coop_swap.reason());
 }
 
-TEST(BrowsingContextGroupSwap, CreateRelatedCoopSwap) {
-  BrowsingContextGroupSwap related_coop_swap =
-      BrowsingContextGroupSwap::CreateRelatedCoopSwap();
-
-  EXPECT_EQ(BrowsingContextGroupSwapType::kRelatedCoopSwap,
-            related_coop_swap.type());
-  EXPECT_TRUE(related_coop_swap.ShouldSwap());
-  EXPECT_FALSE(related_coop_swap.ShouldClearProxiesOnCommit());
-  EXPECT_TRUE(related_coop_swap.ShouldClearWindowName());
-  EXPECT_EQ(ShouldSwapBrowsingInstance::kYes_ForceSwap,
-            related_coop_swap.reason());
-}
-
 TEST(BrowsingContextGroupSwap, CreateSecuritySwap) {
   BrowsingContextGroupSwap security_swap =
       BrowsingContextGroupSwap::CreateSecuritySwap();
diff --git a/content/browser/renderer_host/browsing_context_state.cc b/content/browser/renderer_host/browsing_context_state.cc
index 208f4c2..90f3db4 100644
--- a/content/browser/renderer_host/browsing_context_state.cc
+++ b/content/browser/renderer_host/browsing_context_state.cc
@@ -38,12 +38,10 @@
 BrowsingContextState::BrowsingContextState(
     blink::mojom::FrameReplicationStatePtr replication_state,
     RenderFrameHostImpl* parent,
-    std::optional<BrowsingInstanceId> browsing_instance_id,
-    std::optional<base::UnguessableToken> coop_related_group_token)
+    std::optional<BrowsingInstanceId> browsing_instance_id)
     : replication_state_(std::move(replication_state)),
       parent_(parent),
-      browsing_instance_id_(browsing_instance_id),
-      coop_related_group_token_(coop_related_group_token) {
+      browsing_instance_id_(browsing_instance_id) {
   TRACE_EVENT_BEGIN("navigation", "BrowsingContextState",
                     perfetto::Track::FromPointer(this),
                     "browsing_context_state_when_created", this);
@@ -78,7 +76,7 @@
     // CHECK to verify that the proxy is being accessed from the correct
     // BrowsingContextState. As both BrowsingContextState (in non-legacy mode)
     // and RenderFrameProxyHost (via SiteInstance) are tied to a given
-    // CoopRelatedGroup, the CoopRelatedGroupId of the BrowsingContextState
+    // BrowsingInstance, the browsing_instance_id of the BrowsingContextState
     // (in the non-legacy mode) and of the SiteInstanceGroup should match. If
     // they do not, the code calling this method has likely chosen the wrong
     // BrowsingContextState (e.g. one from the current RenderFrameHost rather
@@ -89,8 +87,8 @@
     // Note: Outer delegates are an exception, and when we're expecting to
     // interact with one, we should pass in the proper `proxy_access_mode` to
     // not end up in this condition.
-    CHECK_EQ(coop_related_group_token_.value(),
-             site_instance_group->coop_related_group_token());
+    CHECK_EQ(browsing_instance_id_.value(),
+             site_instance_group->browsing_instance_id());
   }
   auto it = proxy_hosts_.find(site_instance_group->GetId());
   if (it != proxy_hosts_.end()) {
@@ -107,8 +105,8 @@
               kSwapForCrossBrowsingInstanceNavigations &&
       proxy_access_mode == ProxyAccessMode::kRegular) {
     // See comments in GetRenderFrameProxyHost for why this check is needed.
-    CHECK_EQ(coop_related_group_token_.value(),
-             site_instance_group->coop_related_group_token());
+    CHECK_EQ(browsing_instance_id_.value(),
+             site_instance_group->browsing_instance_id());
   }
   TRACE_EVENT("navigation", "BrowsingContextState::DeleteRenderFrameProxyHost",
               ChromeTrackEvent::kBrowsingContextState, this,
@@ -142,8 +140,8 @@
               kSwapForCrossBrowsingInstanceNavigations &&
       proxy_access_mode == ProxyAccessMode::kRegular) {
     // See comments in GetRenderFrameProxyHost for why this check is needed.
-    CHECK_EQ(coop_related_group_token_.value(),
-             site_instance_group->coop_related_group_token());
+    CHECK_EQ(browsing_instance_id_.value(),
+             site_instance_group->browsing_instance_id());
   }
 
   auto site_instance_group_id = site_instance_group->GetId();
@@ -520,11 +518,6 @@
     proto->set_browsing_instance_id(browsing_instance_id_.value().value());
   }
 
-  if (coop_related_group_token_.has_value()) {
-    proto->set_coop_related_group_token(
-        coop_related_group_token_.value().ToString());
-  }
-
   perfetto::TracedDictionary dict = std::move(proto).AddDebugAnnotations();
   dict.Add("this", static_cast<const void*>(this));
 }
diff --git a/content/browser/renderer_host/browsing_context_state.h b/content/browser/renderer_host/browsing_context_state.h
index aa0f870..2788dfd 100644
--- a/content/browser/renderer_host/browsing_context_state.h
+++ b/content/browser/renderer_host/browsing_context_state.h
@@ -11,7 +11,6 @@
 #include "base/memory/safe_ref.h"
 #include "base/unguessable_token.h"
 #include "content/browser/renderer_host/render_frame_proxy_host.h"
-#include "content/browser/security/coop/coop_related_group.h"
 #include "content/browser/site_instance_group.h"
 #include "content/public/browser/browsing_instance_id.h"
 #include "services/network/public/cpp/permissions_policy/permissions_policy_declaration.h"
@@ -79,17 +78,14 @@
       std::unordered_map<SiteInstanceGroupId,
                          std::unique_ptr<RenderFrameProxyHost>>;
 
-  // Currently `browsing_instance_id` and `coop_related_group_id` will be null
-  // iff the legacy mode is enabled, as the legacy mode BrowsingContextState is
-  // 1:1 with FrameTreeNode and therefore doesn't have a dedicated associated
-  // BrowsingInstance or CoopRelatedGroup.
-  // TODO(crbug.com/40205442): Make `browsing_instance_id` and
-  // `coop_related_group_id` non-optional when the legacy path is removed.
-  BrowsingContextState(
-      blink::mojom::FrameReplicationStatePtr replication_state,
-      RenderFrameHostImpl* parent,
-      std::optional<BrowsingInstanceId> browsing_instance_id,
-      std::optional<base::UnguessableToken> coop_related_group_token);
+  // Currently `browsing_instance_id` will be null iff the legacy mode is
+  // enabled, as the legacy mode BrowsingContextState is 1:1 with FrameTreeNode
+  // and therefore doesn't have a dedicated associated BrowsingInstance.
+  // TODO(crbug.com/40205442): Make `browsing_instance_id` non-optional when the
+  // legacy path is removed.
+  BrowsingContextState(blink::mojom::FrameReplicationStatePtr replication_state,
+                       RenderFrameHostImpl* parent,
+                       std::optional<BrowsingInstanceId> browsing_instance_id);
 
   // Returns a const reference to the map of proxy hosts. The keys are
   // SiteInstanceGroup IDs, the values are RenderFrameProxyHosts.
@@ -142,15 +138,13 @@
   }
 
   // All proxies except outer delegate proxies should belong to the same
-  // CoopRelatedGroup as their BrowsingContextState.
+  // BrowsingInstance as their BrowsingContextState.
   //
   // When kSwapForCrossBrowsingInstanceNavigations is enabled, we might change
   // BrowsingContextState during a navigation. To ensure that we haven't mixed
-  // up things, we CHECK that proxies are in the same CoopRelatedGroup. This
-  // includes proxies in the BrowsingInstance as well as proxies for COOP:
-  // restrict-properties related contexts. We do this CHECK in all functions for
-  // creating, deleting, and accessing proxies. See
-  // BrowsingContextState::GetRenderFrameProxyHostImpl() for an example.
+  // up things, we CHECK that proxies are in the same BrowsingInstance. We do
+  // this CHECK in all functions for creating, deleting, and accessing proxies.
+  // See BrowsingContextState::GetRenderFrameProxyHostImpl() for an example.
   //
   // When we expect to be in one the exception cases we specify it via the
   // ProxyAccessMode enum below, which will disable the CHECKs.
@@ -305,15 +299,13 @@
   // main frame BrowsingContextState.
   const raw_ptr<RenderFrameHostImpl> parent_;
 
-  // ID of the BrowsingInstance and token of the CoopRelatedGroup to which this
-  // BrowsingContextState belongs. Currently `browsing_instance_id` and
-  // `coop_related_group_token` will be null iff the legacy mode is enabled, as
-  // the legacy mode BrowsingContextState is 1:1 with FrameTreeNode and
-  // therefore doesn't have a dedicated associated BrowsingInstance or
-  // CoopRelatedGroup. TODO(crbug.com/40205442): Make `browsing_instance_id` and
-  // `coop_related_group_token` non-optional when the legacy path is removed.
+  // ID of the BrowsingInstance to which this BrowsingContextState belongs.
+  // Currently `browsing_instance_id` and will be null iff the legacy mode is
+  // enabled, as the legacy mode BrowsingContextState is 1:1 with FrameTreeNode
+  // and therefore doesn't have a dedicated associated BrowsingInstance.
+  // TODO(crbug.com/40205442): Make `browsing_instance_id` non-optional when the
+  // legacy path is removed.
   const std::optional<BrowsingInstanceId> browsing_instance_id_;
-  const std::optional<base::UnguessableToken> coop_related_group_token_;
 
   base::WeakPtrFactory<BrowsingContextState> weak_factory_{this};
 };
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 8a0e2b9..dccc2e0 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -4132,13 +4132,6 @@
       .WithCrossOriginIsolationKey(cross_origin_isolation_key)
       .WithIsPdf(is_pdf_);
 
-  // Records in the UrlInfo if COOP: same-origin or COOP: restrict-properties
-  // was set, and from which origin.
-  auto common_coop_origin = ComputeCommonCoopOrigin();
-  if (common_coop_origin.has_value()) {
-    url_info_init.WithCommonCoopOrigin(common_coop_origin.value());
-  }
-
   // Navigations with SiteInstances which have fixed storage partition (e.g.
   // <webview> tags) should always stay in the current StoragePartition.
   SiteInstanceImpl* current_instance =
@@ -6736,8 +6729,7 @@
         frame_tree_node_->current_frame_host()->GetSiteInstance();
     SiteInstanceImpl* target_site_instance =
         activated_entry->render_frame_host()->GetSiteInstance();
-    CHECK(!target_site_instance->IsCoopRelatedSiteInstance(
-        current_site_instance));
+    CHECK(!target_site_instance->IsRelatedSiteInstance(current_site_instance));
     browsing_context_group_swap_ =
         BrowsingContextGroupSwap::CreateSecuritySwap();
 
@@ -10668,42 +10660,6 @@
              : WebExposedIsolationInfo::CreateIsolated(origin);
 }
 
-std::optional<url::Origin> NavigationRequest::ComputeCommonCoopOrigin() {
-  // Embedded content that cannot set COOP directly should inherit their COOP
-  // common origin from their embedder. For iframes, this is to ensure that they
-  // do not reuse a SiteInstance in the wrong page. For other embedded
-  // content it is simply for consistency as they should never try to get
-  // another SiteInstance in the same CoopRelatedGroup anyway. For this reason
-  // we use IsOutermostMainFrame.
-  if (!frame_tree_node_->IsOutermostMainFrame()) {
-    return frame_tree_node_->current_frame_host()
-        ->GetMainFrame()
-        ->GetSiteInstance()
-        ->GetCommonCoopOrigin();
-  }
-
-  using CoopValue = network::mojom::CrossOriginOpenerPolicyValue;
-
-  switch (coop_status().current_coop().value) {
-    case CoopValue::kSameOrigin:
-    case CoopValue::kSameOriginPlusCoep:
-    case CoopValue::kNoopenerAllowPopups:
-      // If we're early in the navigation process and the PolicyContainer was
-      // not yet computed, use a best effort origin.
-      // TODO(crbug.com/40879437): This is probably not very helpful. If
-      // we have a { Value+Origin } COOP bundle, we should be able to return a
-      // nullopt value that is distinct from { unsafe-none, nullopt }, similar
-      // to what exists for WebExposedIsolationInfo.
-      return policy_container_builder_->HasComputedPolicies()
-                 ? coop_status().current_coop().origin
-                 : GetTentativeOriginAtRequestTime();
-
-    case CoopValue::kUnsafeNone:
-    case CoopValue::kSameOriginAllowPopups:
-      return std::nullopt;
-  };
-}
-
 void NavigationRequest::MaybeAssignInvalidPrerenderFrameTreeNodeId() {
   if (!prerender_frame_tree_node_id_.has_value()) {
     // This navigation won't activate a prerendered page. Otherwise,
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index 36ff6be..b215ec3 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -2277,18 +2277,6 @@
   // a network response yet, or when going to an "about:blank" page.
   std::optional<WebExposedIsolationInfo> ComputeWebExposedIsolationInfo();
 
-  // Computes whether the navigation is for a document that should live in a
-  // BrowsingInstance only containing other documents with the same COOP value
-  // set by the same origin. This is the case if this document or its top-level
-  // document sets COOP: same-origin or COOP: restrict-properties. If it is a
-  // top-level document, simply return its origin, otherwise inherit the
-  // top-level document value.
-  //
-  // If the return value is nullopt, it indicates that neither COOP: same-origin
-  // nor COOP: restrict-properties were used for this document or for its parent
-  // in the case of a subframe.
-  std::optional<url::Origin> ComputeCommonCoopOrigin();
-
   // Assign an invalid frame tree node id to `prerender_frame_tree_node_id_`.
   // Called as soon as when we are certain that this navigation won't activate a
   // prerendered page. This is needed because `IsPrerenderedPageActivation()`,
diff --git a/content/browser/renderer_host/navigator.cc b/content/browser/renderer_host/navigator.cc
index 9cc3ae7..54ce1ea5 100644
--- a/content/browser/renderer_host/navigator.cc
+++ b/content/browser/renderer_host/navigator.cc
@@ -725,9 +725,15 @@
       navigation_request->browsing_context_group_swap().ShouldSwap()) {
     SiteInstanceImpl* final_site_instance =
         render_frame_host->GetSiteInstance();
+    // TODO(crbug.com/412965095): Now that the CoopRelatedGroup no longer
+    // exists, the BrowsingContextGroupInfo class can be deleted and replaced by
+    // a simple token, the BrowsingInstance token. In the meantime, just pass
+    // the BrowsingInstanceToken as a CoopGroupToken so that we do not send an
+    // empty token.
     blink::BrowsingContextGroupInfo browsing_context_group_info(
         final_site_instance->browsing_instance_token(),
-        final_site_instance->coop_related_group_token());
+        /*coop_related_group_token=*/final_site_instance
+            ->browsing_instance_token());
     frame_tree.root()->render_manager()->ExecutePageBroadcastMethod(
         [&browsing_context_group_info](RenderViewHostImpl* rvh) {
           if (auto& broadcast = rvh->GetAssociatedPageBroadcast()) {
diff --git a/content/browser/renderer_host/render_frame_host_delegate.cc b/content/browser/renderer_host/render_frame_host_delegate.cc
index 94f051a..566e4ba 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.cc
+++ b/content/browser/renderer_host/render_frame_host_delegate.cc
@@ -189,12 +189,6 @@
   return std::vector<RenderFrameHostImpl*>();
 }
 
-std::vector<RenderFrameHostImpl*>
-RenderFrameHostDelegate::GetActiveTopLevelDocumentsInCoopRelatedGroup(
-    RenderFrameHostImpl* render_frame_host) {
-  return std::vector<RenderFrameHostImpl*>();
-}
-
 PrerenderHostRegistry* RenderFrameHostDelegate::GetPrerenderHostRegistry() {
   return nullptr;
 }
diff --git a/content/browser/renderer_host/render_frame_host_delegate.h b/content/browser/renderer_host/render_frame_host_delegate.h
index fafbd520..c7bac7c2 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.h
+++ b/content/browser/renderer_host/render_frame_host_delegate.h
@@ -704,12 +704,6 @@
   GetActiveTopLevelDocumentsInBrowsingContextGroup(
       RenderFrameHostImpl* render_frame_host);
 
-  // Returns the list of top-level RenderFrameHosts hosting active documents
-  // that belong to the same CoopRelatedGroup as `render_frame_host`.
-  virtual std::vector<RenderFrameHostImpl*>
-  GetActiveTopLevelDocumentsInCoopRelatedGroup(
-      RenderFrameHostImpl* render_frame_host);
-
   // Returns the PrerenderHostRegistry to start/cancel prerendering. This
   // doesn't return nullptr except for some tests.
   virtual PrerenderHostRegistry* GetPrerenderHostRegistry();
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index ad821a0..66c2547 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -2588,10 +2588,10 @@
   // 1) Their opener in RenderFrameHostImpl::CreateNewWindow().
   // 2) Their navigation in RenderFrameHostImpl::DidCommitNavigationInternal().
   virtual_browsing_context_group_ = CrossOriginOpenerPolicyAccessReportManager::
-      GetNewVirtualBrowsingContextGroup();
+      NextVirtualBrowsingContextGroup();
   soap_by_default_virtual_browsing_context_group_ =
       CrossOriginOpenerPolicyAccessReportManager::
-          GetNewVirtualBrowsingContextGroup();
+          NextVirtualBrowsingContextGroup();
 
   // IdleManager should be unique per RenderFrame to provide proper isolation
   // of overrides.
@@ -9836,12 +9836,12 @@
   int popup_virtual_browsing_context_group =
       params->opener_suppressed
           ? CrossOriginOpenerPolicyAccessReportManager::
-                GetNewVirtualBrowsingContextGroup()
+                NextVirtualBrowsingContextGroup()
           : top_level_opener->virtual_browsing_context_group();
   int popup_soap_by_default_virtual_browsing_context_group =
       params->opener_suppressed
           ? CrossOriginOpenerPolicyAccessReportManager::
-                GetNewVirtualBrowsingContextGroup()
+                NextVirtualBrowsingContextGroup()
           : top_level_opener->soap_by_default_virtual_browsing_context_group();
 
   // If the opener is suppressed or script access is disallowed, we should
@@ -9940,9 +9940,14 @@
       new_main_rfh->GetDevToolsFrameToken(), wait_for_debugger,
       new_main_rfh->GetDocumentToken(),
       new_main_rfh->policy_container_host()->CreatePolicyContainerForBlink(),
+      // TODO(crbug.com/412965095): Now that the CoopRelatedGroup no longer
+      // exists, replace BrowsingContextGroupInfo by a single token. In the
+      // meantime, just pass the BrowsingInstanceToken as a CoopGroupToken so
+      // that we do not send an empty token.
       blink::BrowsingContextGroupInfo(
           new_main_rfh->GetSiteInstance()->browsing_instance_token(),
-          new_main_rfh->GetSiteInstance()->coop_related_group_token()),
+          /*coop_related_group_token=*/new_main_rfh->GetSiteInstance()
+              ->browsing_instance_token()),
       delegate_->GetColorProviderColorMaps(),
       std::move(partitioned_popin_params), /*widget_screen_rect=*/std::nullopt,
       /*window_screen_rect=*/std::nullopt);
@@ -16121,10 +16126,15 @@
   CHECK(!commit_params->browsing_context_group_info.has_value());
   if (is_main_frame() &&
       navigation_request->browsing_context_group_swap().ShouldSwap()) {
+    // TODO(crbug.com/412965095): Now that the CoopRelatedGroup no longer
+    // exists, replace BrowsingContextGroupInfo by a single token. In the
+    // meantime, just pass the BrowsingInstanceToken as a CoopGroupToken so that
+    // we do not send an empty token.
     commit_params->browsing_context_group_info =
         blink::BrowsingContextGroupInfo(
             GetSiteInstance()->browsing_instance_token(),
-            GetSiteInstance()->coop_related_group_token());
+            /*coop_related_group_token=*/GetSiteInstance()
+                ->browsing_instance_token());
   }
 
   auto* cookie_deprecation_label_manager =
@@ -16204,10 +16214,15 @@
   CHECK(!commit_params->browsing_context_group_info.has_value());
   if (is_main_frame() &&
       navigation_request->browsing_context_group_swap().ShouldSwap()) {
+    // TODO(crbug.com/412965095): Now that the CoopRelatedGroup no longer
+    // exists, replace BrowsingContextGroupInfo by a single token. In the
+    // meantime, just pass the BrowsingInstanceToken as a CoopGroupToken so that
+    // we do not send an empty token.
     commit_params->browsing_context_group_info =
         blink::BrowsingContextGroupInfo(
             GetSiteInstance()->browsing_instance_token(),
-            GetSiteInstance()->coop_related_group_token());
+            /*coop_related_group_token=*/GetSiteInstance()
+                ->browsing_instance_token());
   }
 
   {
diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc
index d26c850e..b61dfcc 100644
--- a/content/browser/renderer_host/render_frame_host_manager.cc
+++ b/content/browser/renderer_host/render_frame_host_manager.cc
@@ -667,10 +667,7 @@
           frame_tree_node_->parent(),
           is_legacy_browsing_context_state_mode
               ? static_cast<std::optional<BrowsingInstanceId>>(std::nullopt)
-              : site_instance->GetBrowsingInstanceId(),
-          is_legacy_browsing_context_state_mode
-              ? static_cast<std::optional<base::UnguessableToken>>(std::nullopt)
-              : site_instance->coop_related_group_token());
+              : site_instance->GetBrowsingInstanceId());
   browsing_context_state->CommitFramePolicy(initial_main_frame_policy);
   browsing_context_state->SetFrameName(name, "");
   UpdateProcessReusePolicyForProcessPerSiteWithMainFrameThreshold(
@@ -719,10 +716,7 @@
           frame_tree_node_->parent(),
           is_legacy_browsing_context_state_mode
               ? static_cast<std::optional<BrowsingInstanceId>>(std::nullopt)
-              : site_instance->GetBrowsingInstanceId(),
-          is_legacy_browsing_context_state_mode
-              ? static_cast<std::optional<base::UnguessableToken>>(std::nullopt)
-              : site_instance->coop_related_group_token());
+              : site_instance->GetBrowsingInstanceId());
   browsing_context_state->CommitFramePolicy(frame_policy);
   SetRenderFrameHost(CreateRenderFrameHost(
       CreateFrameCase::kInitChild, site_instance, frame_routing_id,
@@ -2569,7 +2563,7 @@
     bool is_reload,
     bool is_same_document,
     IsSameSiteGetter& is_same_site,
-    CoopSwapResult coop_swap_result,
+    bool coop_swap,
     bool was_server_redirect,
     bool should_replace_current_entry,
     bool has_rel_opener) {
@@ -2600,7 +2594,7 @@
         ShouldSwapBrowsingInstance::kNo_RendererDebugURL);
   }
 
-  if (coop_swap_result == CoopSwapResult::kSwap) {
+  if (coop_swap) {
     return BrowsingContextGroupSwap::CreateCoopSwap();
   }
 
@@ -2728,14 +2722,6 @@
     }
   }
 
-  // We've checked that we didn't need to do a hard BrowsingInstance swap. If
-  // COOP: restrict-properties asks for it, do a BrowsingInstance swap that
-  // preserves a reference to the previous BrowsingInstance. Such
-  // BrowsingInstances are said to be "related".
-  if (coop_swap_result == CoopSwapResult::kSwapRelated) {
-    return BrowsingContextGroupSwap::CreateRelatedCoopSwap();
-  }
-
   // When doing a history navigation, we cannot assume that the page will behave
   // in the same way as it did previously. It could change headers, lead to an
   // error page, etc. We only check the destination_instance once we're done
@@ -2745,11 +2731,8 @@
   // BrowsingInstance. This is why this block is after security checks, but
   // before proactive BrowsingInstance swap.
   if (destination_instance) {
-    if (!destination_instance->IsCoopRelatedSiteInstance(current_instance)) {
-      return BrowsingContextGroupSwap::CreateSecuritySwap();
-    }
     if (!destination_instance->IsRelatedSiteInstance(current_instance)) {
-      return BrowsingContextGroupSwap::CreateRelatedCoopSwap();
+      return BrowsingContextGroupSwap::CreateSecuritySwap();
     }
     return BrowsingContextGroupSwap::CreateNoSwap(
         ShouldSwapBrowsingInstance::kNo_AlreadyHasMatchingBrowsingInstance);
@@ -2941,7 +2924,7 @@
     IsSameSiteGetter& is_same_site,
     bool dest_is_view_source_mode,
     bool was_server_redirect,
-    CoopSwapResult coop_swap_result,
+    bool coop_swap,
     bool should_replace_current_entry,
     bool force_new_browsing_instance,
     bool has_rel_opener,
@@ -2996,7 +2979,7 @@
                 current_effective_url, current_is_view_source_mode,
                 source_instance, current_instance, dest_instance, dest_url_info,
                 dest_is_view_source_mode, transition, error_page_process,
-                is_reload, is_same_document, is_same_site, coop_swap_result,
+                is_reload, is_same_document, is_same_site, coop_swap,
                 was_server_redirect, should_replace_current_entry,
                 has_rel_opener);
 
@@ -3163,9 +3146,7 @@
   // beyond COOP.
   ProcessReuseOnCOOPType coop_process_reuse_type =
       ProcessReuseOnCOOPType::kNone;
-  if (should_swap_result->type() == BrowsingContextGroupSwapType::kCoopSwap ||
-      should_swap_result->type() ==
-          BrowsingContextGroupSwapType::kRelatedCoopSwap) {
+  if (should_swap_result->type() == BrowsingContextGroupSwapType::kCoopSwap) {
     if (candidate_instance && candidate_instance != new_instance &&
         candidate_instance->GetSiteInfo() == new_instance->GetSiteInfo()) {
       coop_process_reuse_type = ProcessReuseOnCOOPType::kDifferentSiteInstance;
@@ -3187,9 +3168,7 @@
     DCHECK(frame_tree_node_->IsMainFrame());
     new_instance->ReuseExistingProcessIfPossible(process_to_reuse);
   }
-  if (should_swap_result->type() == BrowsingContextGroupSwapType::kCoopSwap ||
-      should_swap_result->type() ==
-          BrowsingContextGroupSwapType::kRelatedCoopSwap) {
+  if (should_swap_result->type() == BrowsingContextGroupSwapType::kCoopSwap) {
     if (new_instance->HasProcess()) {
       RecordProcessReuseOnCoopResult(coop_process_reuse_type, true);
     } else {
@@ -3358,15 +3337,6 @@
                                     SiteInstanceRelation::RELATED);
     }
 
-    if (browsing_context_group_swap.type() ==
-        BrowsingContextGroupSwapType::kRelatedCoopSwap) {
-      // If we're dealing with COOP: restrict-properties, we need to stay in the
-      // same CoopRelatedGroup, so that further navigations get a
-      // chance to preserve their scriptability.
-      return SiteInstanceDescriptor(
-          computed_url_info, SiteInstanceRelation::RELATED_IN_COOP_GROUP);
-    }
-
     return SiteInstanceDescriptor(computed_url_info,
                                   SiteInstanceRelation::UNRELATED);
   }
@@ -3386,29 +3356,6 @@
   bool can_use_source_instance =
       CanUseSourceSiteInstance(dest_url_info, source_instance,
                                was_server_redirect, error_page_process, reason);
-  if (browsing_context_group_swap.type() ==
-      BrowsingContextGroupSwapType::kRelatedCoopSwap) {
-    // We typically expect `source_instance` to be in the same BrowsingInstance
-    // as `current_instance`. However when extensions use the chrome.tabs.update
-    // API to navigate to about:blank, `source_instance` is set to the
-    // extension's SiteInstance, which should be in a different
-    // BrowsingInstance. In that case, `source_instance` should not be in a
-    // different BrowsingInstance in the same CoopRelatedGroup as
-    // `current_instance`, but use its own extension's CoopRelatedGroup. Note
-    // that it can be in another BrowsingInstance in another CoopRelatedGroup,
-    // which we have to consider for the kSwap case below.
-    // TODO(crbug.com/40186710): Add a test verifying that we cannot end
-    // up in that situation using chrome.tabs.update. This could be the case if
-    // an extension use that API to navigate from a COOP: restrict-properties
-    // page to about:blank.
-    CHECK(!can_use_source_instance ||
-          source_instance->IsRelatedSiteInstance(current_instance) ||
-          !source_instance->IsCoopRelatedSiteInstance(current_instance));
-    AppendReason(reason,
-                 "DetermineSiteInstanceForURL => related_in_COOP_group");
-    return SiteInstanceDescriptor(dest_url_info,
-                                  SiteInstanceRelation::RELATED_IN_COOP_GROUP);
-  }
 
   // If a swap is required, we need to force the SiteInstance AND
   // BrowsingInstance to be different ones, using CreateForURL.
@@ -3641,20 +3588,12 @@
     bool was_server_redirect) {
   // Start by verifying that the dest_instance is compatible with the browsing
   // context group swap decision.
-  if (browsing_context_group_swap.ShouldSwap()) {
-    // 1. If we've decided that the target SiteInstance cannot be in the same
-    // BrowsingInstance, and that the dest_instance is, we should not reuse it.
-    if (dest_instance->IsRelatedSiteInstance(current_instance)) {
-      return false;
-    }
 
-    // 2. If we aren't looking for a SiteInstance in the same CoopRelatedGroup,
-    // then don't use a dest_instance in that group.
-    if (browsing_context_group_swap.type() !=
-            BrowsingContextGroupSwapType::kRelatedCoopSwap &&
-        dest_instance->IsCoopRelatedSiteInstance(current_instance)) {
-      return false;
-    }
+  // If we've decided that the target SiteInstance cannot be in the same
+  // BrowsingInstance, and that the dest_instance is, we should not reuse it.
+  if (browsing_context_group_swap.ShouldSwap() &&
+      dest_instance->IsRelatedSiteInstance(current_instance)) {
+    return false;
   }
 
   // Note: The later call to IsSuitableForUrlInfo does not have context
@@ -3824,11 +3763,6 @@
         descriptor.dest_url_info);
   }
 
-  if (descriptor.relation == SiteInstanceRelation::RELATED_IN_COOP_GROUP) {
-    return current_instance->GetCoopRelatedSiteInstanceImpl(
-        descriptor.dest_url_info);
-  }
-
   // At this point we know an unrelated site instance must be returned.
 
   // If the current SiteInstance has fixed storage partition (e.g. <webview>
@@ -3845,7 +3779,7 @@
   // `candidate_instance` is the SiteInstance that was created at request start
   // time.
   if (candidate_instance &&
-      !current_instance->IsCoopRelatedSiteInstance(candidate_instance) &&
+      !current_instance->IsRelatedSiteInstance(candidate_instance) &&
       candidate_instance->DoesSiteInfoForURLMatch(dest_url_info)) {
     return candidate_instance;
   }
@@ -4002,8 +3936,8 @@
     SiteInstanceGroup* new_group,
     bool recovering_without_early_commit,
     const scoped_refptr<BrowsingContextState>& browsing_context_state) {
-  // Only create opener proxies if they are in the same CoopRelatedGroup.
-  if (new_group->IsCoopRelatedSiteInstanceGroup(old_group)) {
+  // Only create opener proxies if they are in the same BrowsingInstance.
+  if (new_group->IsRelatedSiteInstanceGroup(old_group)) {
     CreateOpenerProxies(new_group, frame_tree_node_, browsing_context_state);
   } else {
     // Ensure that the frame tree has RenderFrameProxyHosts for the
@@ -4287,8 +4221,7 @@
             render_frame_host_->browsing_context_state()
                 ->current_replication_state()
                 .Clone(),
-            frame_tree_node_->parent(), new_instance->GetBrowsingInstanceId(),
-            new_instance->coop_related_group_token());
+            frame_tree_node_->parent(), new_instance->GetBrowsingInstanceId());
 
         // Add a proxy to the outer delegate if one exists, as this is not
         // copied over to the new BrowsingContextState otherwise.
@@ -4515,37 +4448,6 @@
   }
 }
 
-void RenderFrameHostManager::CreateRenderFrameProxyAndAncestorChainIfNeeded(
-    SiteInstanceGroup* group) {
-  SiteInstanceGroup* current_site_instance_group =
-      current_frame_host()->GetSiteInstance()->group();
-  CHECK(!group->IsRelatedSiteInstanceGroup(current_site_instance_group));
-  CHECK(group->IsCoopRelatedSiteInstanceGroup(current_site_instance_group));
-
-  // If the frame we need to create a proxy for is a subframe, we need to make
-  // sure the entire ancestor chain exists as proxies as well, otherwise the
-  // subframe proxy would be floating around. Note: we only need to create
-  // ancestors in this frame tree, so we can use IsMainFrame().
-  std::vector<FrameTreeNode*> ancestor_chain;
-  FrameTreeNode* ancestor = frame_tree_node_;
-  while (ancestor) {
-    ancestor_chain.push_back(ancestor);
-    if (ancestor->IsMainFrame()) {
-      ancestor = nullptr;
-    } else {
-      ancestor = ancestor->parent()->frame_tree_node();
-    }
-  }
-
-  // Create proxies, from the top-level frame down to the initially specified
-  // subframe. TODO(crbug.com/40186710): Verify that the behavior is
-  // correct if the frame is pending deletion.
-  for (FrameTreeNode* node : base::Reversed(ancestor_chain)) {
-    node->render_manager()->CreateRenderFrameProxy(
-        group, node->current_frame_host()->browsing_context_state());
-  }
-}
-
 void RenderFrameHostManager::CreateProxiesForChildFrame(FrameTreeNode* child) {
   TRACE_EVENT_INSTANT(
       "navigation", "RenderFrameHostManager::CreateProxiesForChildFrame_Parent",
@@ -4768,7 +4670,7 @@
           request->ComputeErrorPageProcess(), is_reload,
           request->IsSameDocument(), is_same_site,
           request->commit_params().is_view_source, request->WasServerRedirect(),
-          request->coop_status().browsing_instance_swap_result(),
+          request->coop_status().browsing_instance_swap(),
           request->common_params().should_replace_current_entry,
           request->force_new_browsing_instance(),
           request->begin_params().has_rel_opener, browsing_context_group_swap,
@@ -5567,8 +5469,7 @@
 void RenderFrameHostManager::CollectOpenerFrameTrees(
     SiteInstanceGroup* site_instance_group,
     std::vector<FrameTree*>* opener_frame_trees,
-    std::unordered_set<FrameTreeNode*>* nodes_with_back_links,
-    std::unordered_set<FrameTreeNode*>* cross_browsing_context_group_openers) {
+    std::unordered_set<FrameTreeNode*>* nodes_with_back_links) {
   CHECK(opener_frame_trees);
   opener_frame_trees->push_back(&frame_tree_node_->frame_tree());
 
@@ -5586,27 +5487,6 @@
       if (!node->opener())
         continue;
 
-      // Do not iterate recursively on FrameTrees in different BrowsingInstances
-      // in the same CoopRelatedGroup. Instead, simply record the direct opener
-      // in `cross_browsing_context_group_openers`. We can end up here with
-      // BrowsingInstance not in the same CoopRelatedGroup for rare cases
-      // involving outer delegate proxies. For example when a chrome app webview
-      // gets a new opener, we will iterate this opener tree and create proxies
-      // for newly connected frames in the outer delegate SiteInstanceGroup. We
-      // do not want to interact with these, so explicitly verify the
-      // CoopRelatedGroups match.
-      // TODO(crbug.com/40266207): It is not clear that this iteration is
-      // actually useful for outer delegate proxies. See if this can be
-      // prevented to simplify logic here.
-      SiteInstanceGroup* opener_sig =
-          node->opener()->current_frame_host()->GetSiteInstance()->group();
-      if (site_instance_group &&
-          !site_instance_group->IsRelatedSiteInstanceGroup(opener_sig) &&
-          site_instance_group->IsCoopRelatedSiteInstanceGroup(opener_sig)) {
-        cross_browsing_context_group_openers->insert(node->opener());
-        continue;
-      }
-
       FrameTree& opener_tree = node->opener()->frame_tree();
       const auto& existing_tree_it =
           std::ranges::find(*opener_frame_trees, &opener_tree);
@@ -5641,19 +5521,8 @@
   // web_contents_impl.cc.
   std::vector<FrameTree*> opener_frame_trees;
   std::unordered_set<FrameTreeNode*> nodes_with_back_links;
-  std::unordered_set<FrameTreeNode*> cross_browsing_context_group_openers;
 
-  CollectOpenerFrameTrees(group, &opener_frame_trees, &nodes_with_back_links,
-                          &cross_browsing_context_group_openers);
-
-  // Create the proxies for openers outside of this BrowsingInstance. They are
-  // created separately on purpose, because we do not want to create proxies for
-  // their entire tree, only the single point of contact with this
-  // BrowsingInstance (and for any necessary ancestor frames).
-  for (auto* node : cross_browsing_context_group_openers) {
-    node->render_manager()->CreateRenderFrameProxyAndAncestorChainIfNeeded(
-        group);
-  }
+  CollectOpenerFrameTrees(group, &opener_frame_trees, &nodes_with_back_links);
 
   // Create opener proxies for frame trees, processing furthest openers from
   // this node first and this node last.  In the common case without cycles,
diff --git a/content/browser/renderer_host/render_frame_host_manager.h b/content/browser/renderer_host/render_frame_host_manager.h
index 1e3521b7..9db49a1 100644
--- a/content/browser/renderer_host/render_frame_host_manager.h
+++ b/content/browser/renderer_host/render_frame_host_manager.h
@@ -410,12 +410,6 @@
       const scoped_refptr<BrowsingContextState>& browsing_context_state,
       BatchedProxyIPCSender* batched_proxy_ipc_sender = nullptr);
 
-  // Similar to `CreateRenderFrameProxy` but also creates the minimal ancestor
-  // chain of proxies in `group` to support a subframe. This only exists to
-  // support CoopRelatedGroup proxy creation and should not be used for other
-  // cases. It is CHECKed that `group` must be cross-BrowsingInstance.
-  void CreateRenderFrameProxyAndAncestorChainIfNeeded(SiteInstanceGroup* group);
-
   // Creates proxies for a new child frame at FrameTreeNode |child| in all
   // SiteInstances for which the current frame has proxies.  This method is
   // called on the parent of a new child frame before the child leaves the
@@ -719,10 +713,6 @@
     // Note: Using this value requires passing in a valid `source_site_instance`
     // to ConvertToSiteInstance.
     RELATED_IN_GROUP,
-    // A SiteInstance in a different BrowsingInstance, but in the same
-    // CoopRelatedGroup. Only used for COOP: restrict-properties
-    // navigations.
-    RELATED_IN_COOP_GROUP,
     // A SiteInstance in the same browsing instance as the current.
     RELATED,
     // A pre-existing SiteInstance that might or might not be in the same
@@ -802,7 +792,7 @@
       bool is_reload,
       bool is_same_document,
       IsSameSiteGetter& is_same_site,
-      CoopSwapResult coop_swap_result,
+      bool coop_swap,
       bool was_server_redirect,
       bool should_replace_current_entry,
       bool has_rel_opener);
@@ -829,7 +819,7 @@
       IsSameSiteGetter& is_same_site,
       bool dest_is_view_source_mode,
       bool was_server_redirect,
-      CoopSwapResult coop_swap_result,
+      bool coop_swap,
       bool should_replace_current_entry,
       bool force_new_browsing_instance,
       bool has_rel_opener,
@@ -957,17 +947,10 @@
   // etc). If the traversal encounters a node with an opener pointing to a
   // FrameTree that has already been traversed (such as when there's a cycle),
   // the node is added to `nodes_with_back_links`.
-  //
-  // This function does not recursively iterate on trees living in a different
-  // BrowsingInstance from `site_instance_group`, which may have maintained an
-  // opener using COOP: restrict-properties. When such openers are encountered,
-  // they are added to `cross_browsing_context_group_openers`. Tests can set
-  // `site_instance_group` to null to iterate through all trees.
   void CollectOpenerFrameTrees(
       SiteInstanceGroup* site_instance_group,
       std::vector<FrameTree*>* opener_frame_trees,
-      std::unordered_set<FrameTreeNode*>* nodes_with_back_links,
-      std::unordered_set<FrameTreeNode*>* cross_browsing_context_group_openers);
+      std::unordered_set<FrameTreeNode*>* nodes_with_back_links);
 
   // Create RenderFrameProxies and inactive RenderViewHosts in the given
   // SiteInstanceGroup for the current node's FrameTree. Used as a helper
diff --git a/content/browser/renderer_host/render_frame_host_manager_unittest.cc b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
index 8ba44847..439afea 100644
--- a/content/browser/renderer_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
@@ -589,8 +589,7 @@
       std::unordered_set<FrameTreeNode*>*
           cross_browsing_context_group_openers) {
     node->render_manager()->CollectOpenerFrameTrees(
-        site_instance_group, opener_frame_trees, nodes_with_back_links,
-        cross_browsing_context_group_openers);
+        site_instance_group, opener_frame_trees, nodes_with_back_links);
   }
 
  private:
diff --git a/content/browser/renderer_host/render_frame_proxy_host.cc b/content/browser/renderer_host/render_frame_proxy_host.cc
index 52fecb01..dc3a4db2 100644
--- a/content/browser/renderer_host/render_frame_proxy_host.cc
+++ b/content/browser/renderer_host/render_frame_proxy_host.cc
@@ -375,19 +375,7 @@
 void RenderFrameProxyHost::UpdateOpener() {
   // Another frame in this proxy's SiteInstanceGroup may reach the new opener by
   // first reaching this proxy and then referencing its window.opener.  Ensure
-  // the new opener's proxy exists in this case. If this is already a proxy for
-  // a frame in another BrowsingInstance in the same CoopRelatedGroup, we should
-  // not add extra proxies, as these are not discoverable via window.opener
-  // because property access is restricted.
-  SiteInstanceGroup* current_group =
-      frame_tree_node_->current_frame_host()->GetSiteInstance()->group();
-  bool is_coop_rp_proxy =
-      current_group->IsCoopRelatedSiteInstanceGroup(site_instance_group()) &&
-      !current_group->IsRelatedSiteInstanceGroup(site_instance_group());
-  if (is_coop_rp_proxy) {
-    return;
-  }
-
+  // the new opener's proxy exists in this case.
   if (frame_tree_node_->opener()) {
     frame_tree_node_->opener()->render_manager()->CreateOpenerProxies(
         site_instance_group(), frame_tree_node_,
@@ -618,7 +606,7 @@
     // but that the source is related to the embedder, allowing frames related
     // to the embedder to also message the guest.
     if (target_embedder_rfh &&
-        site_instance_group()->IsCoopRelatedSiteInstanceGroup(
+        site_instance_group()->IsRelatedSiteInstanceGroup(
             target_embedder_rfh->GetSiteInstance()->group())) {
       is_embedder_to_guest_communication = true;
     }
@@ -636,7 +624,7 @@
       // Note that this is not checking that the source and target are related,
       // but that the target is related to the embedder.
       if (source_embedder_rfh &&
-          target_group->IsCoopRelatedSiteInstanceGroup(
+          target_group->IsRelatedSiteInstanceGroup(
               source_embedder_rfh->GetSiteInstance()->group())) {
         is_guest_to_embedder_communication = true;
       }
@@ -644,9 +632,9 @@
   }
 
   // Only deliver the message if the request came from a RenderFrameHost in the
-  // same CoopRelatedGroup or if this is a message between a guest and its
+  // same BrowsingInstance or if this is a message between a guest and its
   // embedder.
-  if (!target_group->IsCoopRelatedSiteInstanceGroup(site_instance_group()) &&
+  if (!target_group->IsRelatedSiteInstanceGroup(site_instance_group()) &&
       !is_embedder_to_guest_communication &&
       !is_guest_to_embedder_communication) {
     return;
@@ -684,16 +672,7 @@
             ->SynchronizeVisualPropertiesIgnoringPendingAck();
       }
 
-      if (!target_group->IsRelatedSiteInstanceGroup(site_instance_group()) &&
-          target_group->IsCoopRelatedSiteInstanceGroup(site_instance_group())) {
-        // If we're getting messaged by a frame in a different BrowsingInstance
-        // in the same CoopRelatedGroup, we should create only the proxies
-        // needed for event.source (and its ancestor chain).
-        source_rfh->frame_tree_node()
-            ->render_manager()
-            ->CreateRenderFrameProxyAndAncestorChainIfNeeded(
-                target_rfh->GetSiteInstance()->group());
-      } else if (is_embedder_to_guest_communication) {
+      if (is_embedder_to_guest_communication) {
         // We create a RenderFrameProxyHost for the embedder in the guest's
         // render process but we intentionally do not expose the embedder's
         // opener chain to it.
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 00b1dc2a..9d430eb 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -604,11 +604,16 @@
   // cross-browsing-context-group popup to B, the RenderViewHost for the opener
   // in B's process should have A's BrowsingContextGroupInfo, which is the
   // current page in the opener.
+  // TODO(crbug.com/412965095): Now that the CoopRelatedGroup no longer exist,
+  // BrowsingContextGroupInfo can be replaced by a simple
+  // BrowsingContextGroupToken. In the meantime, just pass the
+  // BrowsingInstanceToken as a CoopGroupToken so that we do not send an empty
+  // token.
   params->browsing_context_group_info = blink::BrowsingContextGroupInfo(
       frame_tree_->GetMainFrame()->GetSiteInstance()->browsing_instance_token(),
       frame_tree_->GetMainFrame()
           ->GetSiteInstance()
-          ->coop_related_group_token());
+          ->browsing_instance_token());
 
   // RenderViewHostImpl is reused after a crash, so reset any endpoint that
   // might be a leftover from a crash.
diff --git a/content/browser/security/coop/coop_related_group.cc b/content/browser/security/coop/coop_related_group.cc
deleted file mode 100644
index 635e5a7..0000000
--- a/content/browser/security/coop/coop_related_group.cc
+++ /dev/null
@@ -1,117 +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 "content/browser/security/coop/coop_related_group.h"
-
-#include "content/browser/browsing_instance.h"
-#include "content/browser/site_instance_impl.h"
-
-#include "base/logging.h"
-
-namespace content {
-
-CoopRelatedGroup::CoopRelatedGroup(BrowserContext* browser_context,
-                                   bool is_guest,
-                                   bool is_fenced,
-                                   bool is_fixed_storage_partition)
-    : browser_context_(browser_context),
-      is_guest_(is_guest),
-      is_fenced_(is_fenced),
-      is_fixed_storage_partition_(is_fixed_storage_partition) {
-  if (is_guest_) {
-    CHECK(is_fixed_storage_partition_);
-  }
-}
-
-CoopRelatedGroup::~CoopRelatedGroup() = default;
-
-scoped_refptr<BrowsingInstance>
-CoopRelatedGroup::FindSuitableBrowsingInstanceForCoopPolicy(
-    const std::optional<url::Origin>& common_coop_origin,
-    const WebExposedIsolationInfo& web_exposed_isolation_info) {
-  for (BrowsingInstance* current_browsing_instance :
-       coop_related_browsing_instances_) {
-    // Note: We don't need to know if the common_coop_origin value is the result
-    // of COOP: same-origin or COOP: restrict-properties. We will only ever
-    // reach this function when doing a swap within the CoopRelatedGroup, so it
-    // is necessarily for COOP: restrict-properties. WebExposedIsolationInfo is
-    // used to know if COEP was set together with it or not.
-    if ((current_browsing_instance->common_coop_origin() ==
-         common_coop_origin) &&
-        (current_browsing_instance->web_exposed_isolation_info() ==
-         web_exposed_isolation_info)) {
-      return base::WrapRefCounted<BrowsingInstance>(current_browsing_instance);
-    }
-  }
-
-  return nullptr;
-}
-
-scoped_refptr<BrowsingInstance>
-CoopRelatedGroup::GetOrCreateBrowsingInstanceForCoopPolicy(
-    const std::optional<url::Origin>& common_coop_origin,
-    const WebExposedIsolationInfo& web_exposed_isolation_info) {
-  scoped_refptr<BrowsingInstance> browsing_instance =
-      FindSuitableBrowsingInstanceForCoopPolicy(common_coop_origin,
-                                                web_exposed_isolation_info);
-
-  if (browsing_instance.get()) {
-    return browsing_instance;
-  }
-
-  return base::WrapRefCounted<BrowsingInstance>(new BrowsingInstance(
-      browser_context_, web_exposed_isolation_info, is_guest_, is_fenced_,
-      is_fixed_storage_partition_, base::WrapRefCounted<CoopRelatedGroup>(this),
-      common_coop_origin));
-}
-
-void CoopRelatedGroup::RegisterBrowsingInstance(
-    BrowsingInstance* browsing_instance) {
-  // We should never register the same BrowsingInstance twice. If that happens,
-  // we're not reusing the BrowsingInstance via GetBrowsingInstanceForCoop()
-  // somewhere when we should be doing so.
-  auto it = find(coop_related_browsing_instances_.begin(),
-                 coop_related_browsing_instances_.end(), browsing_instance);
-  CHECK(it == coop_related_browsing_instances_.end());
-
-  // We should also never record a second BrowsingInstance with the same Policy
-  // as an existing BrowsingInstance.
-  scoped_refptr<BrowsingInstance> duplicated_policy_browsing_instance =
-      FindSuitableBrowsingInstanceForCoopPolicy(
-          browsing_instance->common_coop_origin(),
-          browsing_instance->web_exposed_isolation_info());
-  CHECK(duplicated_policy_browsing_instance.get() == nullptr);
-
-  CHECK(browsing_instance->is_fixed_storage_partition() ==
-        is_fixed_storage_partition_);
-
-  coop_related_browsing_instances_.push_back(browsing_instance);
-}
-
-void CoopRelatedGroup::UnregisterBrowsingInstance(
-    BrowsingInstance* browsing_instance) {
-  auto it = find(coop_related_browsing_instances_.begin(),
-                 coop_related_browsing_instances_.end(), browsing_instance);
-  CHECK(it != coop_related_browsing_instances_.end());
-
-  coop_related_browsing_instances_.erase(it);
-}
-
-scoped_refptr<SiteInstanceImpl>
-CoopRelatedGroup::GetCoopRelatedSiteInstanceForURL(const UrlInfo& url_info,
-                                                   bool allow_default_si) {
-  // Fenced frames should never be able to request other SiteInstances in the
-  // same CoopRelatedGroup, as they cannot open popups without noopener and COOP
-  // is not enforced within the frame.
-  DCHECK(!is_fenced_);
-  scoped_refptr<BrowsingInstance> target_browsing_instance =
-      GetOrCreateBrowsingInstanceForCoopPolicy(
-          url_info.common_coop_origin,
-          url_info.web_exposed_isolation_info.value_or(
-              WebExposedIsolationInfo::CreateNonIsolated()));
-  return target_browsing_instance->GetSiteInstanceForURL(url_info,
-                                                         allow_default_si);
-}
-
-}  // namespace content
diff --git a/content/browser/security/coop/coop_related_group.h b/content/browser/security/coop/coop_related_group.h
deleted file mode 100644
index 896da68..0000000
--- a/content/browser/security/coop/coop_related_group.h
+++ /dev/null
@@ -1,174 +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.
-
-#ifndef CONTENT_BROWSER_SECURITY_COOP_COOP_RELATED_GROUP_H_
-#define CONTENT_BROWSER_SECURITY_COOP_COOP_RELATED_GROUP_H_
-
-#include <optional>
-#include <vector>
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_refptr.h"
-#include "content/browser/url_info.h"
-#include "content/browser/web_exposed_isolation_info.h"
-#include "content/common/content_export.h"
-#include "url/origin.h"
-
-namespace content {
-
-class BrowserContext;
-class BrowsingInstance;
-class SiteInstanceImpl;
-
-// A CoopRelatedGroup is a set of browsing context groups that can communicate
-// with each other via a limited subset of properties
-// (currently window.postMessage() and window.closed). Documents in
-// BrowsingContexts that are not part of the same CoopRelatedGroup cannot get
-// references to each other's Window by any means at all. CoopRelatedGroup,
-// browsing context groups (BrowsingInstances) and Agent Clusters (roughly, but
-// not strictly equivalent to SiteInstances) provide three tiers of
-// communication capabilities:
-// - Documents in the same Agent Cluster can synchronously DOM script each
-//   other.
-// - Documents in the same browsing context group can asynchronously interact
-//   with each other, via cross-origin Window properties.
-// - Documents in the same CoopRelatedGroup can only message each
-//   other and observe window.closed.
-//
-// These layers have a 1->n relationship pattern: a CoopRelatedGroup contains 1
-// or more browsing context groups, itself containing 1 or more agent clusters.
-// Each layer is refcounted and therefore kept alive by the layer below it, with
-// individual SiteInstances at the base, being kept alive manually.
-//
-// When no document inside a browsing context group sets COOP:
-// restrict-properties, the CoopRelatedGroup contains only a single browsing
-// context group. CoopRelatedGroups containing more than a single browsing
-// context group occur when COOP: restrict-properties forces a browsing context
-// group swap in the same CoopRelatedGroup. It allows retaining a relationship
-// to the opener across browsing context groups, hence creating the actual
-// communication channel.
-//
-// Like BrowsingInstance, CoopRelatedGroup has no public members, as it is
-// designed to be interacted with only from the BrowsingInstance class, itself
-// only reachable from SiteInstance. To get a new SiteInstance that is part of
-// the same CoopRelatedGroup but in a different BrowsingInstance, use
-// SiteInstanceImpl::GetCoopRelatedSiteInstance. Because of this,
-// CoopRelatedGroups are tested in site_instance_impl_unittest.cc.
-class CONTENT_EXPORT CoopRelatedGroup final
-    : public base::RefCounted<CoopRelatedGroup> {
- public:
-  CoopRelatedGroup(const CoopRelatedGroup&) = delete;
-  CoopRelatedGroup& operator=(const CoopRelatedGroup&) = delete;
-
- private:
-  friend class base::RefCounted<CoopRelatedGroup>;
-  friend class BrowsingInstance;
-  friend class CoopRelatedGroupTest;
-
-  explicit CoopRelatedGroup(BrowserContext* browser_context,
-                            bool is_guest,
-                            bool is_fenced,
-                            bool is_fixed_storage_partition);
-  ~CoopRelatedGroup();
-
-  // Returns the token uniquely identifying this CoopRelatedGroup.
-  base::UnguessableToken token() const { return token_; }
-
-  // Returns a SiteInstance in this CoopRelatedGroup, depending on the passed
-  // `url_info`. It might reuse an existing BrowsingInstance that is part of the
-  // group if one is suitable, given its COOP value, origin and cross-origin
-  // isolation state. If none is suitable, a new BrowsingInstance with the
-  // appropriate characteristics will be created.
-  //
-  // `allow_default_site_instance` is used to specify whether the returned
-  // SiteInstance can be the default SiteInstance.
-  scoped_refptr<SiteInstanceImpl> GetCoopRelatedSiteInstanceForURL(
-      const UrlInfo& url_info,
-      bool allow_default_site_instance);
-
-  // These functions keep the group informed of the BrowsingInstances that are
-  // alive and part of it. It is necessary for the BrowsingInstance reuse
-  // mechanism. They should be called in the constructor and destructor of
-  // BrowsingInstance.
-  void RegisterBrowsingInstance(BrowsingInstance* browsing_instance);
-  void UnregisterBrowsingInstance(BrowsingInstance* browsing_instance);
-
-  // Internal helpers that return a BrowsingInstance for a given COOP "Policy"
-  // which includes whether COOP: restrict-properties was set and from which
-  // origin, as well as whether it was augmented with COEP.
-  // `FindSuitableBrowsingInstanceForCoopPolicy` only returns an existing
-  // BrowsingInstance with the given Policy, while
-  // `GetOrCreateBrowsingInstanceForCoopPolicy` will create a new one if no
-  // suitable BrowsingInstance exists in this group.
-  scoped_refptr<BrowsingInstance> FindSuitableBrowsingInstanceForCoopPolicy(
-      const std::optional<url::Origin>& common_coop_origin,
-      const WebExposedIsolationInfo& web_exposed_isolation_info);
-  scoped_refptr<BrowsingInstance> GetOrCreateBrowsingInstanceForCoopPolicy(
-      const std::optional<url::Origin>& common_coop_origin,
-      const WebExposedIsolationInfo& web_exposed_isolation_info);
-
-  // Tracks the number of WebContents currently in this CoopRelatedGroup.
-  // Note: We also separately track the number of WebContents in specific
-  // BrowsingInstances, for validity checks.
-  size_t active_contents_count() const { return active_contents_count_; }
-  void increment_active_contents_count() { active_contents_count_++; }
-  void decrement_active_contents_count() {
-    DCHECK_LT(0u, active_contents_count_);
-    active_contents_count_--;
-  }
-
-  // Recorded with the first BrowsingInstance and used to create new
-  // BrowsingInstances. All BrowsingInstances in a CoopRelatedGroup should share
-  // the same BrowserContext, therefore recording it at creation time is fine.
-  raw_ptr<BrowserContext, DanglingUntriaged> browser_context_;
-
-  // Whether all the documents presented in this CoopRelatedGroup are for guest
-  // views.
-  bool is_guest_;
-
-  // Whether all the documents presented in this CoopRelatedGroup are for a
-  // fenced frame.
-  bool is_fenced_;
-
-  // Whether all the documents presented in this CoopRelatedGroup have fixed
-  // storage partition config.
-  //
-  // TODO(crbug.com/40943418): We actually always want this behavior. Remove
-  // this bit when we are ready.
-  bool is_fixed_storage_partition_;
-
-  // All the BrowsingInstances belonging to this CoopRelatedGroup. They are not
-  // owned by this group, but collectively own it instead. To keep track of the
-  // group members we therefore use raw_ptrs, and add or delete members of the
-  // group via the RegisterBrowsingInstance and UnregisterBrowsingInstance
-  // methods. These are called from the BrowsingInstance constructor and
-  // destructor respectively.
-  //
-  // There exists at most one BrowsingInstance hosting documents with the same
-  // "Policy", namely a combination of whether COOP: restrict-properties was set
-  // and from which origin, and whether it set COEP as well. This gives us three
-  // types of BrowsingInstances:
-  // - The ones with COOP: restrict-properties set from a given origin.
-  // - The ones with COOP: restrict-properties-plus-COEP set from a given
-  //   origin.
-  // - A single BrowsingInstance for all the rest.
-  //
-  // We make sure we do not create two BrowsingInstances with the same
-  // Policy when running RegisterBrowsingInstance.
-  std::vector<raw_ptr<BrowsingInstance>> coop_related_browsing_instances_;
-
-  // Number of all WebContents currently using any of the BrowsingInstances in
-  // this group. This is used to determine if there are multiple windows in the
-  // group, to know whether certain actions (e.g. putting a page into the
-  // BFCache) are allowed.
-  size_t active_contents_count_{0u};
-
-  // A token uniquely identifying this CoopRelatedGroup. This can be sent to the
-  // renderer process if needed, without security risks.
-  const base::UnguessableToken token_ = base::UnguessableToken::Create();
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_SECURITY_COOP_COOP_RELATED_GROUP_H_;
diff --git a/content/browser/security/coop/coop_swap_result.h b/content/browser/security/coop/coop_swap_result.h
deleted file mode 100644
index 49399f5f..0000000
--- a/content/browser/security/coop/coop_swap_result.h
+++ /dev/null
@@ -1,27 +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.
-
-#ifndef CONTENT_BROWSER_SECURITY_COOP_COOP_SWAP_RESULT_H_
-#define CONTENT_BROWSER_SECURITY_COOP_COOP_SWAP_RESULT_H_
-
-namespace content {
-
-// This enum indicates what the comparison of Cross-Origin-Opener-Policy headers
-// has yielded.
-enum class CoopSwapResult {
-  // Indicates that no BrowsingContext group swap is required, based on COOP
-  // values.
-  kNoSwap,
-  // Indicates that a BrowsingContext group swap is required, but that we should
-  // use a BrowsingContext group that is "related", preserving restricted
-  // openers.
-  kSwapRelated,
-  // Indicates that a BrowsingContext group swap is required, and that should
-  // sever all links between the two BrowsingContext groups, opener, names, etc.
-  kSwap
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_SECURITY_COOP_COOP_SWAP_RESULT_H_
diff --git a/content/browser/security/coop/cross_origin_opener_policy_access_report_manager.cc b/content/browser/security/coop/cross_origin_opener_policy_access_report_manager.cc
index 896bd421..88c25e4 100644
--- a/content/browser/security/coop/cross_origin_opener_policy_access_report_manager.cc
+++ b/content/browser/security/coop/cross_origin_opener_policy_access_report_manager.cc
@@ -21,15 +21,6 @@
 
 namespace {
 
-// A map of virtual browsing context groups to the coop related groups they
-// belong to. This mimics the real behavior of BrowsingInstance and
-// CoopRelatedGroup. It is useful to restrict access in a more granular manner
-// and to account for browsing context group reuse.
-std::map<int, int>& GetVirtualBrowsingContextGroupToCoopRelatedGroupMap() {
-  static auto& bcg_to_coop_group_map = *new std::map<int, int>();
-  return bcg_to_coop_group_map;
-}
-
 std::optional<blink::FrameToken> GetFrameToken(
     FrameTreeNode* frame,
     SiteInstanceGroup* site_instance_group) {
@@ -47,22 +38,18 @@
 }
 
 // Find all the related windows that might try to access the new document in
-// `frame`, but are in a different virtual browsing context group. The
-// associated boolean indicates whether they are in the same virtual
-// CoopRelatedGroup.
-std::vector<std::pair<FrameTreeNode*, bool>> CollectOtherWindowForCoopAccess(
+// `frame`, but are in a different virtual browsing context group.
+std::vector<FrameTreeNode*> CollectOtherWindowForCoopAccess(
     FrameTreeNode* frame) {
   DCHECK(frame->IsMainFrame());
-  std::map<int, int>& bcg_to_coop_group_map =
-      GetVirtualBrowsingContextGroupToCoopRelatedGroupMap();
   int virtual_browsing_context_group =
       frame->current_frame_host()->virtual_browsing_context_group();
 
-  std::vector<std::pair<FrameTreeNode*, bool>> out;
+  std::vector<FrameTreeNode*> out;
   for (RenderFrameHostImpl* rfh :
        frame->current_frame_host()
            ->delegate()
-           ->GetActiveTopLevelDocumentsInCoopRelatedGroup(
+           ->GetActiveTopLevelDocumentsInBrowsingContextGroup(
                frame->current_frame_host())) {
     // Filter out windows from the same virtual browsing context group.
     if (rfh->virtual_browsing_context_group() ==
@@ -70,18 +57,7 @@
       continue;
     }
 
-    // Every virtual browsing context group should have an associated virtual
-    // CoopRelatedGroup.
-    CHECK(bcg_to_coop_group_map.find(rfh->virtual_browsing_context_group()) !=
-          bcg_to_coop_group_map.end());
-    CHECK(bcg_to_coop_group_map.find(virtual_browsing_context_group) !=
-          bcg_to_coop_group_map.end());
-
-    bool is_in_same_virtual_coop_related_group =
-        bcg_to_coop_group_map[rfh->virtual_browsing_context_group()] ==
-        bcg_to_coop_group_map[virtual_browsing_context_group];
-    out.emplace_back(rfh->frame_tree_node(),
-                     is_in_same_virtual_coop_related_group);
+    out.emplace_back(rfh->frame_tree_node());
   }
   return out;
 }
@@ -102,42 +78,35 @@
 
   // Find all the related windows that might try to access the new document,
   // but are from a different virtual browsing context group.
-  std::vector<std::pair<FrameTreeNode*, bool>> other_main_frames =
+  std::vector<FrameTreeNode*> other_main_frames =
       CollectOtherWindowForCoopAccess(frame);
 
-  // Fenced frame roots are in their own browsing context group in a separate
-  // coop related group and shouldn't be joined with any other main frames.
+  // Fenced frame roots are in their own BrowsingInstance and shouldn't be
+  // joined with any other main frames.
   DCHECK(!frame->IsInFencedFrameTree() || other_main_frames.empty());
 
   CrossOriginOpenerPolicyAccessReportManager* access_manager_frame =
       frame->current_frame_host()->coop_access_report_manager();
 
-  for (const std::pair<FrameTreeNode*, bool>& other : other_main_frames) {
-    FrameTreeNode* other_ftn = other.first;
-    bool is_in_same_virtual_coop_related_group = other.second;
+  for (FrameTreeNode* other_ftn : other_main_frames) {
     CrossOriginOpenerPolicyAccessReportManager* access_manager_other =
         other_ftn->current_frame_host()->coop_access_report_manager();
 
     // If the current frame has a reporter, install the access monitors to
     // monitor the accesses between this frame and the other frame.
-    access_manager_frame->MonitorAccesses(
-        frame, other_ftn, is_in_same_virtual_coop_related_group);
-    access_manager_frame->MonitorAccesses(
-        other_ftn, frame, is_in_same_virtual_coop_related_group);
+    access_manager_frame->MonitorAccesses(frame, other_ftn);
+    access_manager_frame->MonitorAccesses(other_ftn, frame);
 
     // If the other frame has a reporter, install the access monitors to monitor
     // the accesses between this frame and the other frame.
-    access_manager_other->MonitorAccesses(
-        frame, other_ftn, is_in_same_virtual_coop_related_group);
-    access_manager_other->MonitorAccesses(
-        other_ftn, frame, is_in_same_virtual_coop_related_group);
+    access_manager_other->MonitorAccesses(frame, other_ftn);
+    access_manager_other->MonitorAccesses(other_ftn, frame);
   }
 }
 
 void CrossOriginOpenerPolicyAccessReportManager::MonitorAccesses(
     FrameTreeNode* accessing_node,
-    FrameTreeNode* accessed_node,
-    bool is_in_same_virtual_coop_related_group) {
+    FrameTreeNode* accessed_node) {
   DCHECK_NE(accessing_node, accessed_node);
   DCHECK(accessing_node->current_frame_host()->coop_access_report_manager() ==
              this ||
@@ -167,63 +136,16 @@
       this ==
       accessing_node->current_frame_host()->coop_access_report_manager();
 
+  // TODO(crbug.com/412965095): Remove the is_in_same_virtual_coop_related_group
+  // boolean from the Mojo interface and cleanup the COOP restrict-properties
+  // renderer reporting code.
   accessing_node->current_frame_host()
       ->GetAssociatedLocalMainFrame()
       ->InstallCoopAccessMonitor(
           *accessed_window_token,
           coop_reporter_->CreateReporterParams(access_from_coop_page,
                                                accessing_node, accessed_node),
-          is_in_same_virtual_coop_related_group);
-}
-
-// static
-int CrossOriginOpenerPolicyAccessReportManager::
-    GetNewVirtualBrowsingContextGroup() {
-  std::map<int, int>& bcg_to_coop_group_map =
-      GetVirtualBrowsingContextGroupToCoopRelatedGroupMap();
-
-  // Assign the newly created virtual browsing context group to a new virtual
-  // CoopRelatedGroup.
-  int virtual_browsing_context_group_id = NextVirtualBrowsingContextGroup();
-  bcg_to_coop_group_map[virtual_browsing_context_group_id] =
-      NextVirtualCoopRelatedGroup();
-  return virtual_browsing_context_group_id;
-}
-
-// static
-int CrossOriginOpenerPolicyAccessReportManager::GetVirtualBrowsingContextGroup(
-    CoopSwapResult enforce_result,
-    CoopSwapResult report_only_result,
-    int current_virtual_browsing_context_group) {
-  // This function should only ever be called if we require a different virtual
-  // browsing context group.
-  CHECK(enforce_result != CoopSwapResult::kNoSwap ||
-        report_only_result != CoopSwapResult::kNoSwap);
-
-  int next_browsing_context_group_id = NextVirtualBrowsingContextGroup();
-  std::map<int, int>& bcg_to_coop_group_map =
-      GetVirtualBrowsingContextGroupToCoopRelatedGroupMap();
-
-  // If a swap in a different CoopRelatedGroup would be required, simply create
-  // a new virtual browsing context group in a new virtual CoopRelatedGroup.
-  if (enforce_result == CoopSwapResult::kSwap ||
-      report_only_result == CoopSwapResult::kSwap) {
-    bcg_to_coop_group_map[next_browsing_context_group_id] =
-        NextVirtualCoopRelatedGroup();
-    return next_browsing_context_group_id;
-  }
-
-  // If a swap in the same CoopRelatedGroup would be required, create a new
-  // virtual browsing context group in the current virtual CoopRelatedGroup.
-  // TODO(crbug.com/40276687): This is not strictly correct, because
-  // browsing context groups can be reused when navigating in the same
-  // CoopRelatedGroup. Pass in the isolation information to make it as close
-  // to reality as possible.
-  int current_virtual_coop_related_group =
-      bcg_to_coop_group_map[current_virtual_browsing_context_group];
-  bcg_to_coop_group_map[next_browsing_context_group_id] =
-      current_virtual_coop_related_group;
-  return next_browsing_context_group_id;
+          false);
 }
 
 // static
@@ -233,10 +155,4 @@
   return ++id;
 }
 
-// static
-int CrossOriginOpenerPolicyAccessReportManager::NextVirtualCoopRelatedGroup() {
-  static int id = -1;
-  return ++id;
-}
-
 }  // namespace content
diff --git a/content/browser/security/coop/cross_origin_opener_policy_access_report_manager.h b/content/browser/security/coop/cross_origin_opener_policy_access_report_manager.h
index 900342fc..5382320 100644
--- a/content/browser/security/coop/cross_origin_opener_policy_access_report_manager.h
+++ b/content/browser/security/coop/cross_origin_opener_policy_access_report_manager.h
@@ -9,7 +9,6 @@
 
 #include "base/values.h"
 #include "content/browser/security/coop/cross_origin_opener_policy_reporter.h"
-#include "content/browser/security/coop/coop_swap_result.h"
 
 namespace content {
 
@@ -36,37 +35,14 @@
   // CoopAccessMonitor. The first window is identified by |node|.
   static void InstallAccessMonitorsIfNeeded(FrameTreeNode* node);
 
-  // Return a new virtual browsing context group ID belong to a new
-  // CoopRelatedGroup.
-  static int GetNewVirtualBrowsingContextGroup();
-
-  // Return a virtual browsing context group ID that is suitable given the
-  // information passed in. `enforce_result` and `report_only_result` indicate
-  // whether actual COOP values and report-only COOP values, respectively, need
-  // a browsing context group swap. It also accounts for swapping in the same
-  // CoopRelatedGroup or in a different one. Combined with
-  // `current_virtual_browsing_context_group`, we can decide what virtual
-  // browsing context group is suitable and return its ID.
-  static int GetVirtualBrowsingContextGroup(
-      CoopSwapResult enforce_result,
-      CoopSwapResult report_only_result,
-      int current_virtual_browsing_context_group);
-
- private:
   // Generate a new, previously unused, virtual browsing context group ID.
   static int NextVirtualBrowsingContextGroup();
 
-  // Generate a new, previously unused, virtual CoopRelatedGroup ID.
-  static int NextVirtualCoopRelatedGroup();
-
+ private:
   // Install the CoopAccessMonitors monitoring accesses from `accessing_node`
-  // toward `accessed_node`. `is_in_same_virtual_coop_related_group` indicates
-  // whether the two nodes would be in the same CoopRelatedGroup. If that's the
-  // case, do not report window.postMessage() and window.closed accesses, which
-  // would be permitted by COOP: restrict-properties.
+  // toward `accessed_node`.
   void MonitorAccesses(FrameTreeNode* accessing_node,
-                       FrameTreeNode* accessed_node,
-                       bool is_in_same_virtual_coop_related_group);
+                       FrameTreeNode* accessed_node);
 
   std::unique_ptr<CrossOriginOpenerPolicyReporter> coop_reporter_;
 };
diff --git a/content/browser/security/coop/cross_origin_opener_policy_browsertest.cc b/content/browser/security/coop/cross_origin_opener_policy_browsertest.cc
index 2281627..9f9360d 100644
--- a/content/browser/security/coop/cross_origin_opener_policy_browsertest.cc
+++ b/content/browser/security/coop/cross_origin_opener_policy_browsertest.cc
@@ -4070,21 +4070,27 @@
   int rph_id_3 = current_frame_host()->GetProcess()->GetDeprecatedID();
   EXPECT_EQ(rph_id_2, rph_id_3);
 
-  // The original speculative RFH should always be destroyed.
-  //
-  // Subtle note: this happens even when bfcache is enabled. With bfcache,
-  // we force a BrowsingInstance swap at the very beginning when the navigation
-  // to `url_2` starts.  So when we learn about COOP at response time, the
+  // This test is parameterized on whether the bfcache is enabled.  With
+  // bfcache, we force a BrowsingInstance swap at the very beginning when the
+  // navigation to `url_2` starts, so there's no need to create a new
+  // SiteInstance when we learn about COOP at response time, since the
   // candidate (speculative RFH's) SiteInstance is already in a fresh
-  // BrowsingInstance. However, it cannot be reused, because COOP requires a
-  // BrowsingInstance with b.test as its common_coop_origin(), and the
-  // candidate SiteInstance's BrowsingInstance has no common_coop_origin(), so
-  // it cannot be reused, and we end up creating a new speculative RFH and
-  // destroying the original one.
-  EXPECT_TRUE(speculative_rfh.IsDestroyed());
+  // BrowsingInstance.  Therefore, with bfcache, the original speculative RFH
+  // will be the RFH that eventually commits.  Otherwise, the original
+  // speculative RFH should be destroyed and replaced by another RFH.
+  //
+  // When BackForwardCache is not enabled, the speculative RFH is created in the
+  // current BrowsingInstance, and is not appropriate for a navigation to the
+  // COOP page. So we destroy it, but we can still reuse its process for the new
+  // SiteInstance we create for the navigation.
+  if (IsBackForwardCacheEnabled()) {
+    EXPECT_FALSE(speculative_rfh.IsDestroyed());
+  } else {
+    EXPECT_TRUE(speculative_rfh.IsDestroyed());
 
-  histogram_tester.ExpectUniqueSample(
-      "Navigation.ProcessReuseOnCOOP.DifferentSiteInstance", true, 1);
+    histogram_tester.ExpectUniqueSample(
+        "Navigation.ProcessReuseOnCOOP.DifferentSiteInstance", true, 1);
+  }
 }
 
 // Ensure that same-site navigations that result in a COOP mismatch avoid an
@@ -4113,13 +4119,14 @@
   // process because the navigation is same-site.  Otherwise, the navigation
   // should stay in the current RenderFrameHost.
   int rph_id_2;
+  RenderFrameHost* speculative_rfh = nullptr;
   if (IsBackForwardCacheEnabled() || ShouldCreateNewHostForAllFrames()) {
     navigation.WaitForSpeculativeRenderFrameHostCreation();
-    RenderFrameHost* speculative_rfh = web_contents()
-                                           ->GetPrimaryFrameTree()
-                                           .root()
-                                           ->render_manager()
-                                           ->speculative_frame_host();
+    speculative_rfh = web_contents()
+                          ->GetPrimaryFrameTree()
+                          .root()
+                          ->render_manager()
+                          ->speculative_frame_host();
     ASSERT_TRUE(speculative_rfh);
     rph_id_2 = speculative_rfh->GetProcess()->GetDeprecatedID();
     EXPECT_EQ(rph_id_1, rph_id_2);
@@ -4132,6 +4139,8 @@
     rph_id_2 = rph_id_1;
   }
 
+  RenderFrameHostWrapper speculative_rfh_checker(speculative_rfh);
+
   // Allow the navigation to receive the response commit.
   EXPECT_TRUE(navigation.WaitForNavigationFinished());
   EXPECT_TRUE(navigation.was_successful());
@@ -4145,6 +4154,12 @@
   // and the old process wasn't already locked to a.test.  In that case, a
   // process swap is required, since we are going from an unlocked process to a
   // locked process.
+  //
+  // When BFCache is enabled, we do a proactive BrowsingInstance swap at the
+  // beginning of navigation, which has created a brand new speculative
+  // RenderFrameHost. This speculative RenderFrameHost in a new BrowsingInstance
+  // can be used to commit the navigation to the COOP page, so it is not
+  // destroyed.
   int rph_id_3 = current_frame_host()->GetProcess()->GetDeprecatedID();
   if (SiteIsolationPolicy::IsSiteIsolationForCOOPEnabled()) {
     EXPECT_NE(rph_id_2, rph_id_3);
@@ -4158,7 +4173,9 @@
   } else {
     EXPECT_EQ(rph_id_2, rph_id_3);
 
-    if (IsBackForwardCacheEnabled() || ShouldCreateNewHostForAllFrames()) {
+    if (IsBackForwardCacheEnabled()) {
+      EXPECT_FALSE(speculative_rfh_checker.IsDestroyed());
+    } else if (ShouldCreateNewHostForAllFrames()) {
       histogram_tester.ExpectUniqueSample(
           "Navigation.ProcessReuseOnCOOP.DifferentSiteInstance", true, 1);
     } else {
diff --git a/content/browser/security/coop/cross_origin_opener_policy_status.cc b/content/browser/security/coop/cross_origin_opener_policy_status.cc
index b769ed39..2d03dd3 100644
--- a/content/browser/security/coop/cross_origin_opener_policy_status.cc
+++ b/content/browser/security/coop/cross_origin_opener_policy_status.cc
@@ -27,10 +27,9 @@
 // The SiteInstance selection model heavily relies on this function to be
 // correct, and documents these assumptions, many of which would crash the
 // browser if incorrect. In particular, it assumes that returning a value of
-// CoopSwapResult::kSwapRelated means that the current browsing context group
-// will NOT be found suitable for reuse, as that would effectively mean no swap
-// has happened.
-CoopSwapResult ShouldSwapBrowsingInstanceForCrossOriginOpenerPolicy(
+// true means that the current browsing context group will NOT be found suitable
+// for reuse, as that would effectively mean no swap has happened.
+bool ShouldSwapBrowsingInstanceForCrossOriginOpenerPolicy(
     network::mojom::CrossOriginOpenerPolicyValue initiator_coop,
     const url::Origin& initiator_origin,
     bool is_navigation_from_initial_empty_document,
@@ -42,12 +41,12 @@
     case CrossOriginOpenerPolicyValue::kUnsafeNone:
       switch (destination_coop) {
         case CrossOriginOpenerPolicyValue::kUnsafeNone:
-          return CoopSwapResult::kNoSwap;
+          return false;
         case CrossOriginOpenerPolicyValue::kSameOriginAllowPopups:
         case CrossOriginOpenerPolicyValue::kSameOrigin:
         case CrossOriginOpenerPolicyValue::kSameOriginPlusCoep:
         case CrossOriginOpenerPolicyValue::kNoopenerAllowPopups:
-          return CoopSwapResult::kSwap;
+          return true;
       }
 
     // "same-origin-allow-popups" is used to stay in the same BrowsingInstance
@@ -55,41 +54,33 @@
     case CrossOriginOpenerPolicyValue::kSameOriginAllowPopups:
       switch (destination_coop) {
         case CrossOriginOpenerPolicyValue::kUnsafeNone:
-          return is_navigation_from_initial_empty_document
-                     ? CoopSwapResult::kNoSwap
-                     : CoopSwapResult::kSwap;
+          return !is_navigation_from_initial_empty_document;
         case CrossOriginOpenerPolicyValue::kSameOriginAllowPopups:
-          return initiator_origin.IsSameOriginWith(destination_origin)
-                     ? CoopSwapResult::kNoSwap
-                     : CoopSwapResult::kSwap;
+          return !initiator_origin.IsSameOriginWith(destination_origin);
         case CrossOriginOpenerPolicyValue::kSameOrigin:
         case CrossOriginOpenerPolicyValue::kSameOriginPlusCoep:
         case CrossOriginOpenerPolicyValue::kNoopenerAllowPopups:
-          return CoopSwapResult::kSwap;
+          return true;
       }
 
     case CrossOriginOpenerPolicyValue::kNoopenerAllowPopups:
       switch (destination_coop) {
         case CrossOriginOpenerPolicyValue::kUnsafeNone:
-          return CoopSwapResult::kNoSwap;
+          return false;
         case CrossOriginOpenerPolicyValue::kNoopenerAllowPopups:
           return (is_navigation_from_initial_empty_document ||
-                  !initiator_origin.IsSameOriginWith(destination_origin))
-                     ? CoopSwapResult::kSwap
-                     : CoopSwapResult::kNoSwap;
+                  !initiator_origin.IsSameOriginWith(destination_origin));
         case CrossOriginOpenerPolicyValue::kSameOriginAllowPopups:
         case CrossOriginOpenerPolicyValue::kSameOrigin:
         case CrossOriginOpenerPolicyValue::kSameOriginPlusCoep:
-          return CoopSwapResult::kSwap;
+          return true;
       }
 
     // "same-origin" requires that both policies and origin match.
     case CrossOriginOpenerPolicyValue::kSameOrigin:
     case CrossOriginOpenerPolicyValue::kSameOriginPlusCoep:
-      return (initiator_coop == destination_coop &&
-              initiator_origin.IsSameOriginWith(destination_origin))
-                 ? CoopSwapResult::kNoSwap
-                 : CoopSwapResult::kSwap;
+      return initiator_coop != destination_coop ||
+             !initiator_origin.IsSameOriginWith(destination_origin);
   }
 }
 
@@ -178,10 +169,10 @@
     // We should force a COOP browsing instance swap to avoid certain
     // opener+error pages exploits, see https://crbug.com/1256823 and
     // https://github.com/whatwg/html/issues/7345.
-    browsing_instance_swap_result_ = CoopSwapResult::kSwap;
+    browsing_instance_swap_ = true;
     virtual_browsing_context_group_ =
         CrossOriginOpenerPolicyAccessReportManager::
-            GetNewVirtualBrowsingContextGroup();
+            NextVirtualBrowsingContextGroup();
 
     return network::mojom::BlockedByResponseReason::
         kCoopSandboxedIFrameCannotNavigateToCoopPage;
@@ -243,35 +234,34 @@
   const url::Origin current_coop_origin =
       current_coop_.origin.value_or(url::Origin());
 
-  CoopSwapResult cross_origin_policy_swap =
+  bool cross_origin_policy_swap =
       ShouldSwapBrowsingInstanceForCrossOriginOpenerPolicy(
           current_coop_.value, current_coop_origin,
           is_navigation_from_initial_empty_document_, response_coop.value,
           response_coop_origin);
 
-  // Over the whole redirect chain, keep track of the "strongest" way the new
-  // context must be separated from the previous one.
-  // kNoSwap < kSwapRelated < kSwap.
-  if (cross_origin_policy_swap > browsing_instance_swap_result_) {
-    browsing_instance_swap_result_ = cross_origin_policy_swap;
-  }
+  // Any mismatch in COOP during any step of the redirect chain will result in
+  // the whole navigation requiring a BrowsingInstance swap. Update the tracking
+  // of necessary browsing instance swap for the navigation based on the result
+  // of checking COOP for this step of the redirect chain.
+  browsing_instance_swap_ |= cross_origin_policy_swap;
 
   // Both report only cases (navigation from and to document) use the following
   // result, computing the need of a browsing context group swap based on both
   // documents' report-only values.
-  CoopSwapResult report_only_coop_swap =
+  bool report_only_coop_swap =
       ShouldSwapBrowsingInstanceForCrossOriginOpenerPolicy(
           current_coop_.report_only_value, current_coop_origin,
           is_navigation_from_initial_empty_document_,
           response_coop.report_only_value, response_coop_origin);
 
-  CoopSwapResult navigating_to_report_only_coop_swap =
+  bool navigating_to_report_only_coop_swap =
       ShouldSwapBrowsingInstanceForCrossOriginOpenerPolicy(
           current_coop_.value, current_coop_origin,
           is_navigation_from_initial_empty_document_,
           response_coop.report_only_value, response_coop_origin);
 
-  CoopSwapResult navigating_from_report_only_coop_swap =
+  bool navigating_from_report_only_coop_swap =
       ShouldSwapBrowsingInstanceForCrossOriginOpenerPolicy(
           current_coop_.report_only_value, current_coop_origin,
           is_navigation_from_initial_empty_document_, response_coop.value,
@@ -280,15 +270,14 @@
   bool has_other_window_in_browsing_context_group =
       frame_tree_node_->current_frame_host()
           ->delegate()
-          ->GetActiveTopLevelDocumentsInCoopRelatedGroup(
+          ->GetActiveTopLevelDocumentsInBrowsingContextGroup(
               frame_tree_node_->current_frame_host())
           .size() > 1;
 
-  // If this response's COOP causes a BrowsingInstance swap in another
-  // CoopRelatedGroup, report this to the previous COOP reporter and/or the COOP
-  // reporter of the response if they exist. Do not report swaps in the same
-  // CoopRelatedGroup, as these do not sever the opener.
-  if (cross_origin_policy_swap == CoopSwapResult::kSwap) {
+  // If this response's COOP causes a BrowsingInstance swap, report this to the
+  // previous COOP reporter and/or the COOP reporter of the response if they
+  // exist.
+  if (cross_origin_policy_swap) {
     // Using current_origin_ instead of current_coop_.origin here because
     // we only care about whether the actual origin has changed when determining
     // whether to show previous URL.
@@ -313,32 +302,14 @@
   // (from/to). If both report-only values match however, skip reports, as the
   // website likely wants to deploy COOP on both pages and is not interested in
   // these kind of reports.
-  bool has_matching_report_only =
-      report_only_coop_swap == CoopSwapResult::kNoSwap;
-  CoopSwapResult virtual_browsing_instance_swap_result =
-      CoopSwapResult::kNoSwap;
-  if (!has_matching_report_only) {
-    // Find the strongest type of swap that would happen, between the previous
-    // and next report-only values.
-    if (navigating_from_report_only_coop_swap == CoopSwapResult::kSwap ||
-        navigating_to_report_only_coop_swap == CoopSwapResult::kSwap) {
-      virtual_browsing_instance_swap_result = CoopSwapResult::kSwap;
-    } else if (navigating_from_report_only_coop_swap ==
-                   CoopSwapResult::kSwapRelated ||
-               navigating_to_report_only_coop_swap ==
-                   CoopSwapResult::kSwapRelated) {
-      virtual_browsing_instance_swap_result = CoopSwapResult::kSwapRelated;
-    }
-  }
+  bool virtual_browsing_instance_swap =
+      report_only_coop_swap && (navigating_from_report_only_coop_swap ||
+                                navigating_to_report_only_coop_swap);
 
-  // If this response's report-only COOP would cause a BrowsingInstance swap
-  // in another CoopRelatedGroup, report this to the previous COOP reporter
-  // and/or the COOP reporter of the response if they exist. Do not report swaps
-  // in the same CoopRelatedGroup, as these do not sever the opener.
-  if (virtual_browsing_instance_swap_result == CoopSwapResult::kSwap) {
-    // If this response's report-only COOP would cause a BrowsingInstance swap,
-    // report this to the previous COOP reporter and/or the COOP reporter of
-    // the response if they exist.
+  // If this response's report-only COOP would cause a BrowsingInstance swap,
+  // report this to the previous COOP reporter and/or the COOP reporter of the
+  // response if they exist.
+  if (virtual_browsing_instance_swap) {
     if (has_other_window_in_browsing_context_group) {
       if (response_origin.IsSameOriginWith(response_coop_origin)) {
         response_reporter->QueueNavigationToCOOPReport(
@@ -356,14 +327,10 @@
     }
   }
 
-  if (browsing_instance_swap_result_ != CoopSwapResult::kNoSwap ||
-      virtual_browsing_instance_swap_result != CoopSwapResult::kNoSwap) {
+  if (browsing_instance_swap_ || virtual_browsing_instance_swap) {
     virtual_browsing_context_group_ =
         CrossOriginOpenerPolicyAccessReportManager::
-            GetVirtualBrowsingContextGroup(
-                browsing_instance_swap_result_,
-                virtual_browsing_instance_swap_result,
-                virtual_browsing_context_group_);
+            NextVirtualBrowsingContextGroup();
   }
 
   // Check if a COOP of same-origin-allow-popups by default would result in a
@@ -371,11 +338,10 @@
   if (ShouldSwapBrowsingInstanceForCrossOriginOpenerPolicy(
           current_coop_.soap_by_default_value, current_coop_origin,
           is_navigation_from_initial_empty_document_,
-          response_coop.soap_by_default_value,
-          response_coop_origin) != CoopSwapResult::kNoSwap) {
+          response_coop.soap_by_default_value, response_coop_origin)) {
     soap_by_default_virtual_browsing_context_group_ =
         CrossOriginOpenerPolicyAccessReportManager::
-            GetNewVirtualBrowsingContextGroup();
+            NextVirtualBrowsingContextGroup();
   }
 
   // Finally, update the current COOP, origin and reporter to those of the
diff --git a/content/browser/security/coop/cross_origin_opener_policy_status.h b/content/browser/security/coop/cross_origin_opener_policy_status.h
index bee7c10..f0b4b59 100644
--- a/content/browser/security/coop/cross_origin_opener_policy_status.h
+++ b/content/browser/security/coop/cross_origin_opener_policy_status.h
@@ -10,7 +10,6 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/scoped_observation.h"
-#include "content/browser/security/coop/coop_swap_result.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_process_host_observer.h"
 #include "services/network/public/cpp/cross_origin_opener_policy.h"
@@ -33,9 +32,8 @@
 // Helper function that returns whether the BrowsingInstance should change
 // following COOP rules defined in:
 //
-// https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e#changes-to-navigation
-CONTENT_EXPORT CoopSwapResult
-ShouldSwapBrowsingInstanceForCrossOriginOpenerPolicy(
+// https://html.spec.whatwg.org/#browsing-context-group-switches-due-to-cross-origin-opener-policy
+CONTENT_EXPORT bool ShouldSwapBrowsingInstanceForCrossOriginOpenerPolicy(
     network::mojom::CrossOriginOpenerPolicyValue initiator_coop,
     const url::Origin& initiator_origin,
     bool is_navigation_from_initial_empty_document,
@@ -66,13 +64,9 @@
   // Calling this function is safe because it can only tighten security.
   // This is used by _unfencedTop in fenced frames to ensure that navigations
   // leaving the fenced context create a new browsing instance.
-  void ForceBrowsingInstanceSwap() {
-    browsing_instance_swap_result_ = CoopSwapResult::kSwap;
-  }
+  void ForceBrowsingInstanceSwap() { browsing_instance_swap_ = true; }
 
-  CoopSwapResult browsing_instance_swap_result() const {
-    return browsing_instance_swap_result_;
-  }
+  bool browsing_instance_swap() const { return browsing_instance_swap_; }
 
   // The virtual browsing context group of the document to commit. Initially,
   // the navigation inherits the virtual browsing context group of the current
@@ -144,7 +138,7 @@
   // Tracks whether the new document created by the navigation needs to be
   // created in a different BrowsingContext group. This is updated after every
   // redirect, and after receiving the final response.
-  CoopSwapResult browsing_instance_swap_result_ = CoopSwapResult::kNoSwap;
+  bool browsing_instance_swap_ = false;
 
   int virtual_browsing_context_group_;
 
diff --git a/content/browser/security/coop/cross_origin_opener_policy_status_unittest.cc b/content/browser/security/coop/cross_origin_opener_policy_status_unittest.cc
index cd7b701..b79f2a3 100644
--- a/content/browser/security/coop/cross_origin_opener_policy_status_unittest.cc
+++ b/content/browser/security/coop/cross_origin_opener_policy_status_unittest.cc
@@ -13,7 +13,6 @@
 namespace content {
 
 using COOP = network::mojom::CrossOriginOpenerPolicyValue;
-using Result = CoopSwapResult;
 using CrossOriginOpenerPolicyStatusTest = testing::Test;
 
 TEST(CrossOriginOpenerPolicyStatusTest,
@@ -21,49 +20,36 @@
   struct TestCase {
     COOP coop_from;
     COOP coop_to;
-    CoopSwapResult expect_swap_same_origin;
-    CoopSwapResult expect_swap_cross_origin;
-    CoopSwapResult expect_swap_new_popup;
+    bool expect_swap_same_origin;
+    bool expect_swap_cross_origin;
+    bool expect_swap_new_popup;
   } cases[] = {
       // 'unsafe-none' -> *
-      {COOP::kUnsafeNone, COOP::kUnsafeNone, Result::kNoSwap, Result::kNoSwap,
-       Result::kNoSwap},
-      {COOP::kUnsafeNone, COOP::kSameOrigin, Result::kSwap, Result::kSwap,
-       Result::kSwap},
-      {COOP::kUnsafeNone, COOP::kSameOriginPlusCoep, Result::kSwap,
-       Result::kSwap, Result::kSwap},
-      {COOP::kUnsafeNone, COOP::kSameOriginAllowPopups, Result::kSwap,
-       Result::kSwap, Result::kSwap},
+      {COOP::kUnsafeNone, COOP::kUnsafeNone, false, false, false},
+      {COOP::kUnsafeNone, COOP::kSameOrigin, true, true, true},
+      {COOP::kUnsafeNone, COOP::kSameOriginPlusCoep, true, true, true},
+      {COOP::kUnsafeNone, COOP::kSameOriginAllowPopups, true, true, true},
 
       // 'same-origin' -> *
-      {COOP::kSameOrigin, COOP::kUnsafeNone, Result::kSwap, Result::kSwap,
-       Result::kSwap},
-      {COOP::kSameOrigin, COOP::kSameOrigin, Result::kNoSwap, Result::kSwap,
-       Result::kSwap},
-      {COOP::kSameOrigin, COOP::kSameOriginPlusCoep, Result::kSwap,
-       Result::kSwap, Result::kSwap},
-      {COOP::kSameOrigin, COOP::kSameOriginAllowPopups, Result::kSwap,
-       Result::kSwap, Result::kSwap},
+      {COOP::kSameOrigin, COOP::kUnsafeNone, true, true, true},
+      {COOP::kSameOrigin, COOP::kSameOrigin, false, true, true},
+      {COOP::kSameOrigin, COOP::kSameOriginPlusCoep, true, true, true},
+      {COOP::kSameOrigin, COOP::kSameOriginAllowPopups, true, true, true},
 
       // 'same-origin' + COEP -> *
-      {COOP::kSameOriginPlusCoep, COOP::kUnsafeNone, Result::kSwap,
-       Result::kSwap, Result::kSwap},
-      {COOP::kSameOriginPlusCoep, COOP::kSameOrigin, Result::kSwap,
-       Result::kSwap, Result::kSwap},
-      {COOP::kSameOriginPlusCoep, COOP::kSameOriginPlusCoep, Result::kNoSwap,
-       Result::kSwap, Result::kSwap},
-      {COOP::kSameOriginPlusCoep, COOP::kSameOriginAllowPopups, Result::kSwap,
-       Result::kSwap, Result::kSwap},
+      {COOP::kSameOriginPlusCoep, COOP::kUnsafeNone, true, true, true},
+      {COOP::kSameOriginPlusCoep, COOP::kSameOrigin, true, true, true},
+      {COOP::kSameOriginPlusCoep, COOP::kSameOriginPlusCoep, false, true, true},
+      {COOP::kSameOriginPlusCoep, COOP::kSameOriginAllowPopups, true, true,
+       true},
 
       // 'same-origin-allow-popups' -> *
-      {COOP::kSameOriginAllowPopups, COOP::kUnsafeNone, Result::kSwap,
-       Result::kSwap, Result::kNoSwap},
-      {COOP::kSameOriginAllowPopups, COOP::kSameOrigin, Result::kSwap,
-       Result::kSwap, Result::kSwap},
-      {COOP::kSameOriginAllowPopups, COOP::kSameOriginPlusCoep, Result::kSwap,
-       Result::kSwap, Result::kSwap},
-      {COOP::kSameOriginAllowPopups, COOP::kSameOriginAllowPopups,
-       Result::kNoSwap, Result::kSwap, Result::kSwap},
+      {COOP::kSameOriginAllowPopups, COOP::kUnsafeNone, true, true, false},
+      {COOP::kSameOriginAllowPopups, COOP::kSameOrigin, true, true, true},
+      {COOP::kSameOriginAllowPopups, COOP::kSameOriginPlusCoep, true, true,
+       true},
+      {COOP::kSameOriginAllowPopups, COOP::kSameOriginAllowPopups, false, true,
+       true},
   };
   for (const auto& test : cases) {
     url::Origin A = url::Origin::Create(GURL("https://www.a.com"));
diff --git a/content/browser/service_worker/service_worker_main_resource_loader.cc b/content/browser/service_worker/service_worker_main_resource_loader.cc
index c3a2b338..68a0d4d6 100644
--- a/content/browser/service_worker/service_worker_main_resource_loader.cc
+++ b/content/browser/service_worker/service_worker_main_resource_loader.cc
@@ -253,6 +253,7 @@
 
   RaceNetworkRequestMode race_network_request_mode =
       RaceNetworkRequestMode::kDefault;
+  std::optional<network::mojom::ServiceWorkerRouterSourceType> source_type;
   // Check if registered static router rules match the request.
   if (active_worker->router_evaluator()) {
     CHECK(active_worker->router_evaluator()->IsValid());
@@ -284,12 +285,12 @@
         blink::mojom::WebFeature::kServiceWorkerStaticRouter_Evaluate);
     if (eval_result) {  // matched the rule.
       const auto& sources = eval_result->sources;
-      auto source_type = sources[0].type;
-      set_matched_router_source_type(source_type);
+      source_type = sources[0].type;
+      set_matched_router_source_type(*source_type);
       router_info->rule_id_matched = eval_result->id;
       router_info->matched_source_type = source_type;
 
-      switch (source_type) {
+      switch (*source_type) {
         case network::mojom::ServiceWorkerRouterSourceType::kNetwork: {
           response_head_->service_worker_router_info->actual_source_type =
               network::mojom::ServiceWorkerRouterSourceType::kNetwork;
@@ -335,64 +336,60 @@
           race_network_request_mode = RaceNetworkRequestMode::kSkipped;
           break;
         case network::mojom::ServiceWorkerRouterSourceType::kCache:
-          cache_matcher_ = std::make_unique<ServiceWorkerCacheStorageMatcher>(
-              sources[0].cache_source->cache_name,
-              blink::mojom::FetchAPIRequest::From(resource_request_),
-              active_worker,
-              base::BindOnce(
-                  &ServiceWorkerMainResourceLoader::DidDispatchFetchEvent,
-                  weak_factory_.GetWeakPtr()));
-          cache_matcher_->Run();
-          // If the kServiceWorkerStaticRouterStartServiceWorker feature is
-          // enabled, it starts the ServiceWorker manually since we don't
-          // instantiate ServiceWorkerFetchDispatcher, which involves the
-          // ServiceWorker startup.
-          base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-              FROM_HERE,
-              base::BindOnce(
-                  [](scoped_refptr<ServiceWorkerVersion> active_worker) {
-                    if (active_worker->running_status() !=
-                            blink::EmbeddedWorkerStatus::kRunning &&
-                        base::FeatureList::IsEnabled(
-                            features::
-                                kServiceWorkerStaticRouterStartServiceWorker)) {
-                      active_worker->StartWorker(
-                          ServiceWorkerMetrics::EventType::STATIC_ROUTER,
-                          base::DoNothing());
-                    }
-                  },
-                  active_worker));
+          CreateAndRunCacheMatcher(sources[0].cache_source->cache_name,
+                                   active_worker);
           return;
         case network::mojom::ServiceWorkerRouterSourceType::
-            kRaceNetworkAndCache:
-          // TODO(crbug.com/370844790): implement race network and cache
+            kRaceNetworkAndCache: {
+          race_network_request_mode = RaceNetworkRequestMode::kForced;
+          CreateAndRunCacheMatcher(
+              sources[0].race_network_and_cache_source->cache_source.cache_name,
+              active_worker);
           break;
+        }
       }
     }
   }
 
-  // Dispatch the fetch event.
-  fetch_dispatcher_ = std::make_unique<ServiceWorkerFetchDispatcher>(
-      blink::mojom::FetchAPIRequest::From(resource_request_),
-      resource_request_.destination, /*client_id=*/fetch_event_client_id_,
-      /*resulting_client_id=*/service_worker_client_->client_uuid(), active_worker,
-      base::BindOnce(&ServiceWorkerMainResourceLoader::DidPrepareFetchEvent,
-                     weak_factory_.GetWeakPtr(), active_worker,
-                     active_worker->running_status()),
-      base::BindOnce(&ServiceWorkerMainResourceLoader::DidDispatchFetchEvent,
-                     weak_factory_.GetWeakPtr()));
+  if (!source_type.has_value() ||
+      *source_type !=
+          network::mojom::ServiceWorkerRouterSourceType::kRaceNetworkAndCache) {
+    // Dispatch the fetch event.
+    fetch_dispatcher_ = std::make_unique<ServiceWorkerFetchDispatcher>(
+        blink::mojom::FetchAPIRequest::From(resource_request_),
+        resource_request_.destination, /*client_id=*/fetch_event_client_id_,
+        /*resulting_client_id=*/service_worker_client_->client_uuid(),
+        active_worker,
+        base::BindOnce(&ServiceWorkerMainResourceLoader::DidPrepareFetchEvent,
+                       weak_factory_.GetWeakPtr(), active_worker,
+                       active_worker->running_status()),
+        base::BindOnce(&ServiceWorkerMainResourceLoader::DidDispatchFetchEvent,
+                       weak_factory_.GetWeakPtr()));
+  }
 
   if (service_worker_client_->IsContainerForWindowClient()) {
     MaybeDispatchPreload(race_network_request_mode, context, active_worker);
   }
 
   if (race_network_request_mode == RaceNetworkRequestMode::kForced) {
-    if (base::FeatureList::IsEnabled(
-            features::
-                kServiceWorkerStaticRouterRaceNetworkRequestPerformanceImprovement)) {
-      active_worker->CountFeature(
-          blink::mojom::WebFeature::
-              kServiceWorkerStaticRouter_RaceNetworkAndFetchHandlerImprovement);
+    CHECK(source_type.has_value());
+    switch (*source_type) {
+      case network::mojom::ServiceWorkerRouterSourceType::kNetwork:
+      case network::mojom::ServiceWorkerRouterSourceType::kCache:
+      case network::mojom::ServiceWorkerRouterSourceType::kFetchEvent:
+        NOTREACHED();
+      case network::mojom::ServiceWorkerRouterSourceType::
+          kRaceNetworkAndFetchEvent:
+        if (base::FeatureList::IsEnabled(
+                features::
+                    kServiceWorkerStaticRouterRaceNetworkRequestPerformanceImprovement)) {
+          active_worker->CountFeature(
+              blink::mojom::WebFeature::
+                  kServiceWorkerStaticRouter_RaceNetworkAndFetchHandlerImprovement);
+        }
+        break;
+      case network::mojom::ServiceWorkerRouterSourceType::kRaceNetworkAndCache:
+        return;
     }
   }
   // Record worker start time here as |fetch_dispatcher_| will start a service
@@ -563,10 +560,12 @@
   mojo::PendingRemote<network::mojom::URLLoaderFactory> remote_factory;
   forwarded_race_network_request_url_loader_factory_->Clone(
       remote_factory.InitWithNewPipeAndPassReceiver());
-  fetch_dispatcher_->set_race_network_request_token(
-      base::UnguessableToken::Create());
-  fetch_dispatcher_->set_race_network_request_loader_factory(
-      std::move(remote_factory));
+  if (fetch_dispatcher_) {
+    fetch_dispatcher_->set_race_network_request_token(
+        base::UnguessableToken::Create());
+    fetch_dispatcher_->set_race_network_request_loader_factory(
+        std::move(remote_factory));
+  }
 
   mojo::PendingRemote<network::mojom::URLLoaderClient> client_to_pass;
   race_network_request_url_loader_client_->Bind(&client_to_pass);
@@ -598,7 +597,8 @@
 
 bool ServiceWorkerMainResourceLoader::MaybeStartNavigationPreload(
     scoped_refptr<ServiceWorkerContextWrapper> context_wrapper) {
-  if (fetch_dispatcher_->MaybeStartNavigationPreload(
+  if (fetch_dispatcher_ &&
+      fetch_dispatcher_->MaybeStartNavigationPreload(
           resource_request_, context_wrapper, service_worker_client_)) {
     SetDispatchedPreloadType(DispatchedPreloadType::kNavigationPreload);
     return true;
@@ -773,7 +773,7 @@
 
   // Transition the state if the fetch result is fallback. This is a special
   // treatment for the case when RaceNetworkRequest and AutoPreload successfully
-  // dispatced the network request.
+  // dispatched the network request.
   if (is_fallback && !is_race_network_request_aborted) {
     switch (commit_responsibility()) {
       case FetchResponseFrom::kNoResponseYet:
@@ -881,7 +881,9 @@
   }
 
   if (IsMatchedRouterSourceType(
-          network::mojom::ServiceWorkerRouterSourceType::kCache)) {
+          network::mojom::ServiceWorkerRouterSourceType::kCache) ||
+      IsMatchedRouterSourceType(network::mojom::ServiceWorkerRouterSourceType::
+                                    kRaceNetworkAndCache)) {
     CHECK(cache_matcher_);
     CHECK(response_head_->service_worker_router_info);
     response_head_->load_timing.service_worker_cache_lookup_start =
@@ -939,7 +941,9 @@
 
   // Determine the actual route type of static routing API when it is used.
   // If `race-network-and-fetch` was specified, we are setting `kFetchEvent`
-  // since executing this code means that the fetch event won. For other
+  // since executing this code means that the fetch event won.
+  // If `race-network-and-cache` was specified, set `kCache`as the
+  // `actual_source_type`, since the cache won the race. For other
   // cases (`kCache`, `kFetchEvent`), the `matched_source_type` will be the
   // `actual_source_type`.
   if (auto* route_info = response_head_->service_worker_router_info.get()) {
@@ -949,6 +953,12 @@
                 kRaceNetworkAndFetchEvent) {
       route_info->actual_source_type =
           network::mojom::ServiceWorkerRouterSourceType::kFetchEvent;
+    } else if (route_info->matched_source_type &&
+               *route_info->matched_source_type ==
+                   network::mojom::ServiceWorkerRouterSourceType::
+                       kRaceNetworkAndCache) {
+      route_info->actual_source_type =
+          network::mojom::ServiceWorkerRouterSourceType::kCache;
     } else {
       route_info->actual_source_type = route_info->matched_source_type;
     }
@@ -1214,17 +1224,20 @@
 void ServiceWorkerMainResourceLoader::SetCommitResponsibility(
     FetchResponseFrom fetch_response_from) {
   // Set the actual source type used in Static Routing API when
-  // `race-network-and-fetch` is used. Determine this by checking the
-  // commit responsibility. If it's not the service worker, the network
-  // has won.
-  // This check is conducted here since in the case of `knetwork`, it does
-  // not call `DidDispatchFetchEvent`, where we set the `actual_source_type`
-  // for the other sources, and the `response_head_` is already passed on.
+  // `race-network-and-fetch` or `race-network-and-cache` is used. Determine
+  // this by checking the commit responsibility. If it's not the service worker,
+  // the network has won. This check is conducted here since in the case of
+  // `knetwork`, it does not call `DidDispatchFetchEvent`, where we set the
+  // `actual_source_type` for the other sources, and the `response_head_` is
+  // already passed on.
   if (response_head_ && response_head_->service_worker_router_info &&
       response_head_->service_worker_router_info->matched_source_type &&
-      *response_head_->service_worker_router_info->matched_source_type ==
-          network::mojom::ServiceWorkerRouterSourceType::
-              kRaceNetworkAndFetchEvent &&
+      (*response_head_->service_worker_router_info->matched_source_type ==
+           network::mojom::ServiceWorkerRouterSourceType::
+               kRaceNetworkAndFetchEvent ||
+       *response_head_->service_worker_router_info->matched_source_type ==
+           network::mojom::ServiceWorkerRouterSourceType::
+               kRaceNetworkAndCache) &&
       fetch_response_from == FetchResponseFrom::kWithoutServiceWorker) {
     response_head_->service_worker_router_info->actual_source_type =
         network::mojom::ServiceWorkerRouterSourceType::kNetwork;
@@ -1741,6 +1754,35 @@
   return true;
 }
 
+void ServiceWorkerMainResourceLoader::CreateAndRunCacheMatcher(
+    const std::optional<std::string>& cache_name,
+    scoped_refptr<ServiceWorkerVersion> active_worker) {
+  cache_matcher_ = std::make_unique<ServiceWorkerCacheStorageMatcher>(
+      cache_name, blink::mojom::FetchAPIRequest::From(resource_request_),
+      active_worker,
+      base::BindOnce(&ServiceWorkerMainResourceLoader::DidDispatchFetchEvent,
+                     weak_factory_.GetWeakPtr()));
+  cache_matcher_->Run();
+  // If the kServiceWorkerStaticRouterStartServiceWorker feature is
+  // enabled, it starts the ServiceWorker manually since we don't
+  // instantiate ServiceWorkerFetchDispatcher, which involves the
+  // ServiceWorker startup.
+  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](scoped_refptr<ServiceWorkerVersion> active_worker) {
+            if (active_worker->running_status() !=
+                    blink::EmbeddedWorkerStatus::kRunning &&
+                base::FeatureList::IsEnabled(
+                    features::kServiceWorkerStaticRouterStartServiceWorker)) {
+              active_worker->StartWorker(
+                  ServiceWorkerMetrics::EventType::STATIC_ROUTER,
+                  base::DoNothing());
+            }
+          },
+          active_worker));
+}
+
 ServiceWorkerMainResourceLoaderWrapper::ServiceWorkerMainResourceLoaderWrapper(
     std::unique_ptr<ServiceWorkerMainResourceLoader> loader)
     : loader_(std::move(loader)) {}
diff --git a/content/browser/service_worker/service_worker_main_resource_loader.h b/content/browser/service_worker/service_worker_main_resource_loader.h
index 420ee14..bc53b17d 100644
--- a/content/browser/service_worker/service_worker_main_resource_loader.h
+++ b/content/browser/service_worker/service_worker_main_resource_loader.h
@@ -275,6 +275,10 @@
   void OnCompleteSyntheticNetworkRequest(
       const network::URLLoaderCompletionStatus& status);
 
+  void CreateAndRunCacheMatcher(
+      const std::optional<std::string>& cache_name,
+      scoped_refptr<ServiceWorkerVersion> active_worker);
+
   NavigationLoaderInterceptor::FallbackCallback fallback_callback_;
 
   int32_t request_id_ = 0;
diff --git a/content/browser/service_worker/service_worker_main_resource_loader_unittest.cc b/content/browser/service_worker/service_worker_main_resource_loader_unittest.cc
index e20204a8..64b0e18 100644
--- a/content/browser/service_worker/service_worker_main_resource_loader_unittest.cc
+++ b/content/browser/service_worker/service_worker_main_resource_loader_unittest.cc
@@ -628,9 +628,14 @@
         source.cache_source = cache_source;
         break;
       }
-      case network::mojom::ServiceWorkerRouterSourceType::kRaceNetworkAndCache:
-        // TODO(crbug.com/370844790): implement race network and cache
+      case network::mojom::ServiceWorkerRouterSourceType::
+          kRaceNetworkAndCache: {
+        source.race_network_and_cache_source.emplace();
+        blink::ServiceWorkerRouterCacheSource cache_source;
+        cache_source.cache_name = kTestCacheName;
+        source.race_network_and_cache_source->cache_source = cache_source;
         break;
+      }
     }
     rule.sources.emplace_back(source);
     rules.rules.emplace_back(rule);
@@ -758,9 +763,12 @@
         // timing info.
         expect_service_worker_timing = false;
       }
-      if (expected_info.service_worker_router_info->matched_source_type ==
-              network::mojom::ServiceWorkerRouterSourceType::
-                  kRaceNetworkAndFetchEvent &&
+      if ((expected_info.service_worker_router_info->matched_source_type ==
+               network::mojom::ServiceWorkerRouterSourceType::
+                   kRaceNetworkAndFetchEvent ||
+           expected_info.service_worker_router_info->matched_source_type ==
+               network::mojom::ServiceWorkerRouterSourceType::
+                   kRaceNetworkAndCache) &&
           expected_info.service_worker_router_info->actual_source_type ==
               network::mojom::ServiceWorkerRouterSourceType::kNetwork) {
         // If the matched router source is race and the actual source is
@@ -1880,5 +1888,161 @@
   }
 }
 
+// Similar to Basic test setup, but with matching race-network-and-cache static
+// routing rule and network wins.
+TEST_F(ServiceWorkerMainResourceLoaderTest,
+       StaticRoutingRaceNetworkAndCacheNetworkWin) {
+  base::HistogramTester histogram_tester;
+
+  SetupStaticRoutingRules(
+      network::mojom::ServiceWorkerRouterSourceType::kRaceNetworkAndCache);
+
+  SetupStoragePartition();
+
+  SetupNetworkResponse();
+
+  StartRequest(CreateRequest());
+  client_.RunUntilComplete();
+
+  EXPECT_EQ(net::OK, client_.completion_status().error_code);
+  auto& info = client_.response_head();
+  EXPECT_EQ(200, info->headers->response_code());
+  EXPECT_FALSE(info->load_timing.receive_headers_start.is_null());
+  EXPECT_FALSE(info->load_timing.receive_headers_end.is_null());
+  auto expected_info = CreateResponseInfoFromServiceWorker();
+  expected_info->was_fetched_via_service_worker = true;
+  auto expected_router_info = CreateExpectedMatchingServiceWorkerRouterInfo(
+      network::mojom::ServiceWorkerRouterSourceType::kRaceNetworkAndCache);
+  expected_router_info->actual_source_type =
+      network::mojom::ServiceWorkerRouterSourceType::kNetwork;
+  expected_info->service_worker_router_info = std::move(expected_router_info);
+  ExpectResponseInfo(*info, *expected_info);
+
+  histogram_tester.ExpectUniqueSample(kHistogramMainResourceFetchEvent,
+                                      blink::ServiceWorkerStatusCode::kOk, 0);
+  if (LoaderRecordsTimingMetrics()) {
+    histogram_tester.ExpectTotalCount(
+        "ServiceWorker.LoadTiming.MainFrame.MainResource."
+        "FetchHandlerEndToFallbackNetwork",
+        0);
+    histogram_tester.ExpectTotalCount(
+        "ServiceWorker.LoadTiming.MainFrame.MainResource."
+        "ResponseReceivedToCompleted2",
+        0);
+  }
+}
+
+// Similar to Basic test setup, but with matching race-network-and-cache static
+// routing rule and the network wins, but the response is not 2xx.
+TEST_F(ServiceWorkerMainResourceLoaderTest,
+       StaticRoutingRaceNetWorkAndCacheNetworkWinWithNon2xx) {
+  base::HistogramTester histogram_tester;
+
+  SetupStaticRoutingRules(
+      network::mojom::ServiceWorkerRouterSourceType::kRaceNetworkAndCache);
+
+  SetupNon2xxResponse();
+
+  base::Time response_time = base::Time::Now();
+  auto request = CreateRequestAndSetupCache(response_time);
+
+  StartRequest(CreateRequest());
+  client_.RunUntilComplete();
+
+  EXPECT_EQ(net::OK, client_.completion_status().error_code);
+  auto& info = client_.response_head();
+  EXPECT_EQ(200, info->headers->response_code());
+  EXPECT_FALSE(info->load_timing.receive_headers_start.is_null());
+  EXPECT_FALSE(info->load_timing.receive_headers_end.is_null());
+  EXPECT_FALSE(info->load_timing.service_worker_cache_lookup_start.is_null());
+  EXPECT_EQ(info->client_address_space,
+            network::mojom::IPAddressSpace::kPrivate);
+  EXPECT_TRUE(info->was_fetched_via_service_worker);
+  auto expected_info = CreateResponseInfoFromServiceWorker();
+  expected_info->service_worker_response_source =
+      network::mojom::FetchResponseSource::kCacheStorage;
+  expected_info->response_time = response_time;
+  expected_info->cache_storage_cache_name = kTestCacheName;
+  auto expected_router_info = CreateExpectedMatchingServiceWorkerRouterInfo(
+      network::mojom::ServiceWorkerRouterSourceType::kRaceNetworkAndCache);
+  expected_router_info->actual_source_type =
+      network::mojom::ServiceWorkerRouterSourceType::kCache;
+  expected_info->service_worker_router_info = std::move(expected_router_info);
+  ExpectResponseInfo(*info, *expected_info);
+
+  histogram_tester.ExpectUniqueSample(kHistogramMainResourceFetchEvent,
+                                      blink::ServiceWorkerStatusCode::kOk, 1);
+  if (LoaderRecordsTimingMetrics()) {
+    histogram_tester.ExpectTotalCount(
+        "ServiceWorker.LoadTiming.MainFrame.MainResource."
+        "ResponseReceivedToCompleted2",
+        0);
+    histogram_tester.ExpectTotalCount(
+        "ServiceWorker.LoadTiming.MainFrame.MainResource."
+        "FetchHandlerEndToFallbackNetwork",
+        0);
+  }
+}
+
+// Similar to Basic test setup, but with matching race-network-and-cache static
+// routing rule and cache wins.
+TEST_F(ServiceWorkerMainResourceLoaderTest,
+       StaticRoutingRaceNetWorkAndCacheCacheWin) {
+  base::HistogramTester histogram_tester;
+
+  SetupStaticRoutingRules(
+      network::mojom::ServiceWorkerRouterSourceType::kRaceNetworkAndCache);
+
+  SetupErrorNetworkResponse();
+
+  // Defer the race network request processing to receive the cache
+  // response first.
+  DeferRequestHandling();
+
+  base::Time response_time = base::Time::Now();
+  auto request = CreateRequestAndSetupCache(response_time);
+
+  StartRequest(std::move(request));
+  client_.RunUntilComplete();
+
+  // After receiving the cache response, resume the network request
+  // processing.
+  HandleDeferedRequest();
+
+  EXPECT_EQ(net::OK, client_.completion_status().error_code);
+  auto& info = client_.response_head();
+  EXPECT_EQ(200, info->headers->response_code());
+  EXPECT_FALSE(info->load_timing.receive_headers_start.is_null());
+  EXPECT_FALSE(info->load_timing.receive_headers_end.is_null());
+  EXPECT_FALSE(info->load_timing.service_worker_cache_lookup_start.is_null());
+  EXPECT_EQ(info->client_address_space,
+            network::mojom::IPAddressSpace::kPrivate);
+  EXPECT_TRUE(info->was_fetched_via_service_worker);
+  auto expected_info = CreateResponseInfoFromServiceWorker();
+  expected_info->service_worker_response_source =
+      network::mojom::FetchResponseSource::kCacheStorage;
+  expected_info->response_time = response_time;
+  expected_info->cache_storage_cache_name = kTestCacheName;
+  auto expected_router_info = CreateExpectedMatchingServiceWorkerRouterInfo(
+      network::mojom::ServiceWorkerRouterSourceType::kRaceNetworkAndCache);
+  expected_router_info->actual_source_type =
+      network::mojom::ServiceWorkerRouterSourceType::kCache;
+  expected_info->service_worker_router_info = std::move(expected_router_info);
+  ExpectResponseInfo(*info, *expected_info);
+
+  histogram_tester.ExpectUniqueSample(kHistogramMainResourceFetchEvent,
+                                      blink::ServiceWorkerStatusCode::kOk, 1);
+  if (LoaderRecordsTimingMetrics()) {
+    histogram_tester.ExpectTotalCount(
+        "ServiceWorker.LoadTiming.MainFrame.MainResource."
+        "ResponseReceivedToCompleted2",
+        0);
+    histogram_tester.ExpectTotalCount(
+        "ServiceWorker.LoadTiming.MainFrame.MainResource."
+        "FetchHandlerEndToFallbackNetwork",
+        0);
+  }
+}
+
 }  // namespace service_worker_main_resource_loader_unittest
 }  // namespace content
diff --git a/content/browser/site_instance_group.cc b/content/browser/site_instance_group.cc
index 7cc0eae..dc4c63f 100644
--- a/content/browser/site_instance_group.cc
+++ b/content/browser/site_instance_group.cc
@@ -106,11 +106,6 @@
   return browsing_instance_id() == group->browsing_instance_id();
 }
 
-bool SiteInstanceGroup::IsCoopRelatedSiteInstanceGroup(
-    SiteInstanceGroup* group) {
-  return coop_related_group_token() == group->coop_related_group_token();
-}
-
 void SiteInstanceGroup::RenderProcessHostDestroyed(RenderProcessHost* host) {
   DCHECK_EQ(process_->GetDeprecatedID(), host->GetDeprecatedID());
   process_->RemoveObserver(this);
@@ -149,9 +144,7 @@
                            WebExposedIsolationInfo::CreateNonIsolated(),
                            /*is_guest=*/false,
                            /*is_fenced=*/false,
-                           /*is_fixed_storage_partition=*/false,
-                           /*coop_related_group=*/nullptr,
-                           /*common_coop_origin=*/std::nullopt),
+                           /*is_fixed_storage_partition=*/false),
       process);
 }
 
diff --git a/content/browser/site_instance_group.h b/content/browser/site_instance_group.h
index 979c6e7..ddac1f98 100644
--- a/content/browser/site_instance_group.h
+++ b/content/browser/site_instance_group.h
@@ -14,7 +14,6 @@
 #include "base/unguessable_token.h"
 #include "content/browser/browsing_instance.h"
 #include "content/browser/renderer_host/agent_scheduling_group_host.h"
-#include "content/browser/security/coop/coop_related_group.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browsing_instance_id.h"
 #include "content/public/browser/render_process_host_observer.h"
@@ -134,12 +133,6 @@
   // used when a SiteInstanceGroup is available.
   bool IsRelatedSiteInstanceGroup(SiteInstanceGroup* group);
 
-  // Returns true if `group` is in the same CoopRelatedGroup as `this`. This can
-  // be true even though IsRelatedSiteInstanceGroup returns false, if the two
-  // SiteInstanceGroups are for different BrowsingInstances in the same
-  // CoopRelatedGroup.
-  bool IsCoopRelatedSiteInstanceGroup(SiteInstanceGroup* group);
-
   // Get the number of active frames which belong to this SiteInstanceGroup. If
   // there are no active frames left, all frames in this SiteInstanceGroup can
   // be safely discarded.
@@ -160,12 +153,6 @@
     return browsing_instance_->token();
   }
 
-  // Returns the token uniquely identifying the CoopRelatedGroup this
-  // SiteInstanceGroup belongs to.
-  base::UnguessableToken coop_related_group_token() const {
-    return browsing_instance_->coop_related_group_token();
-  }
-
   AgentSchedulingGroupHost& agent_scheduling_group() {
     DCHECK_EQ(agent_scheduling_group_->GetProcess(), &*process_);
     return *agent_scheduling_group_;
diff --git a/content/browser/site_instance_group_unittest.cc b/content/browser/site_instance_group_unittest.cc
index 15f2d731..7652ddca 100644
--- a/content/browser/site_instance_group_unittest.cc
+++ b/content/browser/site_instance_group_unittest.cc
@@ -30,8 +30,7 @@
     scoped_refptr<BrowsingInstance> browsing_instance = new BrowsingInstance(
         &browser_context, WebExposedIsolationInfo::CreateNonIsolated(),
         /*is_guest=*/false, /*is_fenced=*/false,
-        /*is_fixed_storage_partition=*/false, /*coop_related_group=*/nullptr,
-        /*common_coop_origin=*/std::nullopt);
+        /*is_fixed_storage_partition=*/false);
     group = new SiteInstanceGroup(browsing_instance.get(), process.get());
     browsing_instance_id = group->browsing_instance_id();
   }
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index adaa1cd42..4007e923 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -144,8 +144,7 @@
   return base::WrapRefCounted(new SiteInstanceImpl(new BrowsingInstance(
       browser_context, WebExposedIsolationInfo::CreateNonIsolated(),
       /*is_guest=*/false, /*is_fenced=*/false,
-      /*is_fixed_storage_partition=*/false,
-      /*coop_related_group=*/nullptr, /*common_coop_origin=*/std::nullopt)));
+      /*is_fixed_storage_partition=*/false)));
 }
 
 // static
@@ -163,12 +162,11 @@
   DCHECK(browser_context);
 
   // This will create a new SiteInstance and BrowsingInstance.
-  scoped_refptr<BrowsingInstance> instance(new BrowsingInstance(
-      browser_context,
-      url_info.web_exposed_isolation_info.value_or(
-          WebExposedIsolationInfo::CreateNonIsolated()),
-      is_guest, is_fenced, is_fixed_storage_partition,
-      /*coop_related_group=*/nullptr, url_info.common_coop_origin));
+  scoped_refptr<BrowsingInstance> instance(
+      new BrowsingInstance(browser_context,
+                           url_info.web_exposed_isolation_info.value_or(
+                               WebExposedIsolationInfo::CreateNonIsolated()),
+                           is_guest, is_fenced, is_fixed_storage_partition));
 
   // Note: The |allow_default_instance| value used here MUST match the value
   // used in DoesSiteForURLMatch().
@@ -198,8 +196,7 @@
       // It should be safe to just default this to true since the
       // BrowsingInstance is not shared with frames, and there are no
       // navigations happening in service workers.
-      /*is_fixed_storage_partition=*/true,
-      /*coop_related_group=*/nullptr, url_info.common_coop_origin));
+      /*is_fixed_storage_partition=*/true));
 
   // We do NOT want to allow the default site instance here because workers
   // need to be kept separate from other sites.
@@ -238,9 +235,7 @@
           browser_context, guest_site_info.web_exposed_isolation_info(),
           /*is_guest=*/true,
           /*is_fenced=*/false,
-          /*is_fixed_storage_partition=*/true,
-          /*coop_related_group=*/nullptr,
-          /*common_coop_origin=*/std::nullopt)));
+          /*is_fixed_storage_partition=*/true)));
 
   site_instance->SetSiteInfoInternal(guest_site_info);
   return site_instance;
@@ -274,9 +269,7 @@
           browser_context, embedder_site_instance->GetWebExposedIsolationInfo(),
           embedder_site_instance->IsGuest(),
           /*is_fenced=*/should_isolate_fenced_frames,
-          embedder_site_instance->IsFixedStoragePartition(),
-          /*coop_related_group=*/nullptr,
-          /*common_coop_origin=*/std::nullopt)));
+          embedder_site_instance->IsFixedStoragePartition())));
 
   // Give the new fenced frame SiteInstance the same site url as its embedder's
   // SiteInstance to allow it to reuse its embedder's process. We avoid doing
@@ -321,9 +314,7 @@
   scoped_refptr<BrowsingInstance> instance(new BrowsingInstance(
       browser_context, WebExposedIsolationInfo::CreateNonIsolated(),
       /*is_guest=*/false, /*is_fenced=*/false,
-      /*is_fixed_storage_partition=*/false,
-      /*coop_related_group=*/nullptr,
-      /*common_coop_origin=*/std::nullopt));
+      /*is_fixed_storage_partition=*/false));
   auto site_instance = instance->GetSiteInstanceForURL(
       UrlInfo(UrlInfoInit(url)), /* allow_default_instance */ false);
   site_instance->set_process_reuse_policy(
@@ -795,12 +786,6 @@
       url_info, site_instance_group_.get());
 }
 
-scoped_refptr<SiteInstanceImpl>
-SiteInstanceImpl::GetCoopRelatedSiteInstanceImpl(const UrlInfo& url_info) {
-  return browsing_instance_->GetCoopRelatedSiteInstanceForURL(
-      url_info, /* allow_default_instance */ true);
-}
-
 AgentSchedulingGroupHost& SiteInstanceImpl::GetOrCreateAgentSchedulingGroup() {
   // Currently GetOrCreateAgentSchedulingGroup is called in the following
   // cases:
@@ -850,7 +835,7 @@
 }
 
 size_t SiteInstanceImpl::GetRelatedActiveContentsCount() {
-  return browsing_instance_->GetCoopRelatedGroupActiveContentsCount();
+  return browsing_instance_->active_contents_count();
 }
 
 namespace {
@@ -1364,12 +1349,6 @@
     return false;
   }
 
-  // Similarly, the common_coop_origin in the UrlInfo and in this
-  // SiteInstance's BrowsingInstance must be compatible.
-  if (url_info.common_coop_origin != GetCommonCoopOrigin()) {
-    return false;
-  }
-
   // Similarly, the CrossOriginIsolationKeys should match.
   if (GetSiteInfo().agent_cluster_key() &&
       GetSiteInfo().agent_cluster_key()->GetCrossOriginIsolationKey() !=
@@ -1562,11 +1541,6 @@
               CrossOriginIsolationMode::kConcrete);
 }
 
-const std::optional<url::Origin>& SiteInstanceImpl::GetCommonCoopOrigin()
-    const {
-  return browsing_instance_->common_coop_origin();
-}
-
 // static
 void SiteInstance::StartIsolatingSite(
     BrowserContext* context,
@@ -1663,12 +1637,6 @@
   return nullptr;
 }
 
-bool SiteInstanceImpl::IsCoopRelatedSiteInstance(
-    const SiteInstanceImpl* instance) const {
-  return instance->browsing_instance_->coop_related_group_token() ==
-         browsing_instance_->coop_related_group_token();
-}
-
 void SiteInstanceImpl::SetProcessForTesting(RenderProcessHost* process) {
   SetProcessInternal(process);
 }
diff --git a/content/browser/site_instance_impl.h b/content/browser/site_instance_impl.h
index 1170e05..577cc9a 100644
--- a/content/browser/site_instance_impl.h
+++ b/content/browser/site_instance_impl.h
@@ -13,7 +13,6 @@
 #include "content/browser/browsing_instance.h"
 #include "content/browser/isolation_context.h"
 #include "content/browser/process_reuse_policy.h"
-#include "content/browser/security/coop/coop_related_group.h"
 #include "content/browser/site_info.h"
 #include "content/browser/web_exposed_isolation_info.h"
 #include "content/common/content_export.h"
@@ -147,14 +146,6 @@
   scoped_refptr<SiteInstanceImpl> GetMaybeGroupRelatedSiteInstanceImpl(
       const UrlInfo& url_info);
 
-  // This function is used during navigation to get a SiteInstance in the same
-  // CoopRelatedGroup. If the provided `url_info` matches one of the existing
-  // BrowsingInstance of that group, a new or already existing SiteInstance in
-  // that BrowsingInstance, will be picked. Therefore returning the same
-  // SiteInstance is possible, if called with perfectly matching `url_info`.
-  scoped_refptr<SiteInstanceImpl> GetCoopRelatedSiteInstanceImpl(
-      const UrlInfo& url_info);
-
   bool IsSameSiteWithURLInfo(const UrlInfo& url_info);
 
   // Returns an AgentSchedulingGroupHost, or creates one if
@@ -457,13 +448,6 @@
   // is_cross_origin_isolated property of the AgentClusterKey::IsolationKey.
   bool IsCrossOriginIsolated() const;
 
-  // Returns whether the two SiteInstances belong to the same CoopRelatedGroup.
-  // If so, a subset of JavaScript interactions that are permitted across
-  // origins (window.postMessage() and window.closed) should be supported. This
-  // is weaker than IsRelatedSiteInstance: if two SiteInstances belong to the
-  // same BrowsingInstance, they are related and COOP related.
-  bool IsCoopRelatedSiteInstance(const SiteInstanceImpl* instance) const;
-
   // Returns the token uniquely identifying the BrowsingInstance this
   // SiteInstance belongs to. Can safely be sent to the renderer unlike the
   // BrowsingInstanceID.
@@ -471,18 +455,6 @@
     return browsing_instance_->token();
   }
 
-  // Returns the token uniquely identifying the CoopRelatedGroup this
-  // SiteInstance belongs to. Can safely be sent to the renderer.
-  base::UnguessableToken coop_related_group_token() const {
-    return browsing_instance_->coop_related_group_token();
-  }
-
-  // Returns the unique origin of all top-level documents in this
-  // BrowsingInstance. This is only guaranteed by the use of a unique COOP value
-  // across the BrowsingInstance. It is empty if the BrowsingInstance does not
-  // contain COOP: same-origin or COOP: restrict-properties documents.
-  const std::optional<url::Origin>& GetCommonCoopOrigin() const;
-
   // Finds an existing SiteInstance in this SiteInstance's BrowsingInstance that
   // matches this `url_info` but with the `is_sandboxed_` flag true. It's
   // assumed that `url_info.url` is 'about:srcdoc' here, so the new SiteInstance
diff --git a/content/browser/site_instance_impl_unittest.cc b/content/browser/site_instance_impl_unittest.cc
index 0939917..7462763 100644
--- a/content/browser/site_instance_impl_unittest.cc
+++ b/content/browser/site_instance_impl_unittest.cc
@@ -1017,9 +1017,7 @@
   BrowsingInstance* browsing_instance = new BrowsingInstance(
       browser_context.get(), WebExposedIsolationInfo::CreateNonIsolated(),
       /*is_guest=*/false, /*is_fenced=*/false,
-      /*is_fixed_storage_partition=*/false,
-      /*coop_related_group=*/nullptr,
-      /*common_coop_origin=*/std::nullopt);
+      /*is_fixed_storage_partition=*/false);
 
   const GURL url_a1("http://www.google.com/1.html");
   scoped_refptr<SiteInstanceImpl> site_instance_a1(
@@ -1054,9 +1052,7 @@
   BrowsingInstance* browsing_instance2 = new BrowsingInstance(
       browser_context.get(), WebExposedIsolationInfo::CreateNonIsolated(),
       /*is_guest=*/false, /*is_fenced=*/false,
-      /*is_fixed_storage_partition=*/false,
-      /*coop_related_group=*/nullptr,
-      /*common_coop_origin=*/std::nullopt);
+      /*is_fixed_storage_partition=*/false);
   // Ensure the new SiteInstance is ref counted so that it gets deleted.
   scoped_refptr<SiteInstanceImpl> site_instance_a2_2(
       browsing_instance2->GetSiteInstanceForURL(
@@ -1100,9 +1096,7 @@
   scoped_refptr<BrowsingInstance> browsing_instance = new BrowsingInstance(
       browser_context.get(), WebExposedIsolationInfo::CreateNonIsolated(),
       /*is_guest=*/false, /*is_fenced=*/false,
-      /*is_fixed_storage_partition=*/false,
-      /*coop_related_group=*/nullptr,
-      /*common_coop_origin=*/std::nullopt);
+      /*is_fixed_storage_partition=*/false);
 
   const GURL url_a1("http://www.google.com/1.html");
   scoped_refptr<SiteInstanceImpl> site_instance_a1(
@@ -1137,9 +1131,7 @@
   BrowsingInstance* browsing_instance2 = new BrowsingInstance(
       browser_context.get(), WebExposedIsolationInfo::CreateNonIsolated(),
       /*is_guest=*/false, /*is_fenced=*/false,
-      /*is_fixed_storage_partition=*/false,
-      /*coop_related_group=*/nullptr,
-      /*common_coop_origin=*/std::nullopt);
+      /*is_fixed_storage_partition=*/false);
   scoped_refptr<SiteInstanceImpl> site_instance_a1_2(
       browsing_instance2->GetSiteInstanceForURL(
           UrlInfo::CreateForTesting(url_a1), false));
@@ -1154,9 +1146,7 @@
   BrowsingInstance* browsing_instance3 = new BrowsingInstance(
       browser_context2.get(), WebExposedIsolationInfo::CreateNonIsolated(),
       /*is_guest=*/false, /*is_fenced=*/false,
-      /*is_fixed_storage_partition=*/false,
-      /*coop_related_group=*/nullptr,
-      /*common_coop_origin=*/std::nullopt);
+      /*is_fixed_storage_partition=*/false);
   scoped_refptr<SiteInstanceImpl> site_instance_a2_3(
       browsing_instance3->GetSiteInstanceForURL(
           UrlInfo::CreateForTesting(url_a2), false));
@@ -2295,155 +2285,6 @@
       blank_with_opaque_unique_origin));
 }
 
-TEST_F(SiteInstanceTest, CoopRelatedSiteInstanceIdentity) {
-  const GURL test_url("https://example.com");
-
-  const auto base_instance = SiteInstanceImpl::CreateForUrlInfo(
-      context(), UrlInfo(UrlInfoInit(test_url)), /*is_guest=*/false,
-      /*is_fenced=*/false, /*is_fixed_storage_partition=*/false);
-
-  const auto derived_instance = base_instance->GetCoopRelatedSiteInstanceImpl(
-      UrlInfo(UrlInfoInit(test_url)));
-
-  EXPECT_EQ(derived_instance.get(), base_instance.get());
-  EXPECT_TRUE(derived_instance->IsRelatedSiteInstance(base_instance.get()));
-  EXPECT_TRUE(derived_instance->IsCoopRelatedSiteInstance(base_instance.get()));
-}
-
-TEST_F(SiteInstanceTest, CoopRelatedSiteInstanceCrossSite) {
-  const GURL test_url("https://example.com");
-
-  const auto base_instance = SiteInstanceImpl::CreateForUrlInfo(
-      context(), UrlInfo(UrlInfoInit(test_url)), /*is_guest=*/false,
-      /*is_fenced=*/false, /*is_fixed_storage_partition=*/false);
-
-  const auto derived_instance = base_instance->GetCoopRelatedSiteInstanceImpl(
-      UrlInfo(UrlInfoInit(GURL("https://other-example.com"))));
-
-  // Without full Site Isolation, we'll group different sites in the default
-  // SiteInstance.
-  if (!AreAllSitesIsolatedForTesting()) {
-    EXPECT_EQ(derived_instance.get(), base_instance.get());
-    return;
-  }
-
-  EXPECT_NE(derived_instance.get(), base_instance.get());
-  EXPECT_TRUE(derived_instance->IsRelatedSiteInstance(base_instance.get()));
-  EXPECT_TRUE(derived_instance->IsCoopRelatedSiteInstance(base_instance.get()));
-}
-
-TEST_F(SiteInstanceTest, CoopRelatedSiteInstanceIdenticalCoopOriginSameSite) {
-  const GURL test_url("https://example.com");
-
-  const auto base_instance = SiteInstanceImpl::CreateForUrlInfo(
-      context(),
-      UrlInfo(UrlInfoInit(test_url).WithCommonCoopOrigin(
-          url::Origin::Create(test_url))),
-      /*is_guest=*/false, /*is_fenced=*/false,
-      /*is_fixed_storage_partition=*/false);
-
-  const auto derived_instance = base_instance->GetCoopRelatedSiteInstanceImpl(
-      UrlInfo(UrlInfoInit(test_url).WithCommonCoopOrigin(
-          url::Origin::Create(test_url))));
-  EXPECT_EQ(derived_instance.get(), base_instance.get());
-  EXPECT_TRUE(derived_instance->IsRelatedSiteInstance(base_instance.get()));
-  EXPECT_TRUE(derived_instance->IsCoopRelatedSiteInstance(base_instance.get()));
-}
-
-TEST_F(SiteInstanceTest, CoopRelatedSiteInstanceIdenticalCoopOriginCrossSite) {
-  const GURL test_url("https://example.com");
-
-  const auto base_instance = SiteInstanceImpl::CreateForUrlInfo(
-      context(),
-      UrlInfo(UrlInfoInit(test_url).WithCommonCoopOrigin(
-          url::Origin::Create(test_url))),
-      /*is_guest=*/false, /*is_fenced=*/false,
-      /*is_fixed_storage_partition=*/false);
-
-  // COOP common origin might differ from the frame's actual origin (for
-  // example for cross-origin subframes), so we verify that this case is handled
-  // properly.
-  const auto derived_instance = base_instance->GetCoopRelatedSiteInstanceImpl(
-      UrlInfo(UrlInfoInit(GURL("https://other-example.com"))
-                  .WithCommonCoopOrigin(url::Origin::Create(test_url))));
-
-  // Without full Site Isolation, we'll group different sites in the default
-  // SiteInstance.
-  if (!AreAllSitesIsolatedForTesting()) {
-    EXPECT_EQ(derived_instance.get(), base_instance.get());
-    return;
-  }
-
-  EXPECT_NE(derived_instance.get(), base_instance.get());
-  EXPECT_TRUE(derived_instance->IsRelatedSiteInstance(base_instance.get()));
-  EXPECT_TRUE(derived_instance->IsCoopRelatedSiteInstance(base_instance.get()));
-}
-
-TEST_F(SiteInstanceTest, CoopRelatedSiteInstanceDifferentCoopOrigin) {
-  const GURL test_url("https://example.com");
-
-  // Start without a COOP origin.
-  const auto base_instance = SiteInstanceImpl::CreateForUrlInfo(
-      context(), UrlInfo(UrlInfoInit(test_url)), /*is_guest=*/false,
-      /*is_fenced=*/false, /*is_fixed_storage_partition=*/false);
-
-  const auto derived_instance = base_instance->GetCoopRelatedSiteInstanceImpl(
-      UrlInfo(UrlInfoInit(test_url).WithCommonCoopOrigin(
-          url::Origin::Create(test_url))));
-  EXPECT_NE(derived_instance.get(), base_instance.get());
-  EXPECT_FALSE(derived_instance->IsRelatedSiteInstance(base_instance.get()));
-  EXPECT_TRUE(derived_instance->IsCoopRelatedSiteInstance(base_instance.get()));
-}
-
-TEST_F(SiteInstanceTest, CoopRelatedSiteInstanceIdenticalCrossOriginIsolation) {
-  const GURL test_url("https://example.com");
-
-  const auto base_instance = SiteInstanceImpl::CreateForUrlInfo(
-      context(),
-      UrlInfo(UrlInfoInit(test_url).WithWebExposedIsolationInfo(
-          WebExposedIsolationInfo::CreateIsolated(
-              url::Origin::Create(test_url)))),
-      /*is_guest=*/false, /*is_fenced=*/false,
-      /*is_fixed_storage_partition=*/false);
-
-  const auto derived_instance = base_instance->GetCoopRelatedSiteInstanceImpl(
-      UrlInfo(UrlInfoInit(test_url).WithWebExposedIsolationInfo(
-          WebExposedIsolationInfo::CreateIsolated(
-              url::Origin::Create(test_url)))));
-  EXPECT_EQ(derived_instance.get(), base_instance.get());
-  EXPECT_TRUE(derived_instance->IsRelatedSiteInstance(base_instance.get()));
-  EXPECT_TRUE(derived_instance->IsCoopRelatedSiteInstance(base_instance.get()));
-}
-
-TEST_F(SiteInstanceTest, CoopRelatedSiteInstanceDifferentCrossOriginIsolation) {
-  const GURL test_url("https://example.com");
-
-  const auto base_instance = SiteInstanceImpl::CreateForUrlInfo(
-      context(), UrlInfo(UrlInfoInit(test_url)), /*is_guest=*/false,
-      /*is_fenced=*/false, /*is_fixed_storage_partition=*/false);
-
-  const auto derived_instance = base_instance->GetCoopRelatedSiteInstanceImpl(
-      UrlInfo(UrlInfoInit(test_url).WithWebExposedIsolationInfo(
-          WebExposedIsolationInfo::CreateIsolated(
-              url::Origin::Create(test_url)))));
-  EXPECT_NE(derived_instance.get(), base_instance.get());
-  EXPECT_FALSE(derived_instance->IsRelatedSiteInstance(base_instance.get()));
-  EXPECT_TRUE(derived_instance->IsCoopRelatedSiteInstance(base_instance.get()));
-}
-
-TEST_F(SiteInstanceTest, GroupTokensBuilding) {
-  const GURL test_url("https://example.com");
-  const auto base_instance = SiteInstanceImpl::CreateForUrlInfo(
-      context(), UrlInfo(UrlInfoInit(test_url)), /*is_guest=*/false,
-      /*is_fenced=*/false, /*is_fixed_storage_partition=*/false);
-
-  base::UnguessableToken browsing_instance_token =
-      base_instance->browsing_instance_token();
-  base::UnguessableToken coop_related_group_token =
-      base_instance->coop_related_group_token();
-  EXPECT_NE(browsing_instance_token, coop_related_group_token);
-}
-
 TEST_F(SiteInstanceTest, GroupTokensRelatedSiteInstances) {
   const GURL test_url("https://example.com");
   const auto base_instance = SiteInstanceImpl::CreateForUrlInfo(
@@ -2464,30 +2305,6 @@
   EXPECT_TRUE(derived_instance->IsRelatedSiteInstance(base_instance.get()));
   EXPECT_EQ(derived_instance->browsing_instance_token(),
             base_instance->browsing_instance_token());
-  EXPECT_EQ(derived_instance->coop_related_group_token(),
-            base_instance->coop_related_group_token());
-}
-
-TEST_F(SiteInstanceTest, GroupTokensCoopRelatedSiteInstances) {
-  const GURL test_url("https://example.com");
-  const auto base_instance = SiteInstanceImpl::CreateForUrlInfo(
-      context(), UrlInfo(UrlInfoInit(test_url)), /*is_guest=*/false,
-      /*is_fenced=*/false, /*is_fixed_storage_partition=*/false);
-
-  // Derive a SiteInstance that lives in the same CoopRelatedGroup but a
-  // different BrowsingInstance. Provide a different WebExposedIsolationInfo to
-  // make sure we do not reuse the BrowsingInstance.
-  const auto derived_instance = base_instance->GetCoopRelatedSiteInstanceImpl(
-      UrlInfo(UrlInfoInit(test_url).WithWebExposedIsolationInfo(
-          WebExposedIsolationInfo::CreateIsolated(
-              url::Origin::Create(test_url)))));
-  EXPECT_NE(derived_instance.get(), base_instance.get());
-  EXPECT_FALSE(derived_instance->IsRelatedSiteInstance(base_instance.get()));
-  EXPECT_TRUE(derived_instance->IsCoopRelatedSiteInstance(base_instance.get()));
-  EXPECT_NE(derived_instance->browsing_instance_token(),
-            base_instance->browsing_instance_token());
-  EXPECT_EQ(derived_instance->coop_related_group_token(),
-            base_instance->coop_related_group_token());
 }
 
 TEST_F(SiteInstanceTest, GroupTokensUnrelatedSiteInstances) {
@@ -2502,11 +2319,8 @@
 
   EXPECT_NE(other_instance.get(), base_instance.get());
   EXPECT_FALSE(other_instance->IsRelatedSiteInstance(base_instance.get()));
-  EXPECT_FALSE(other_instance->IsCoopRelatedSiteInstance(base_instance.get()));
   EXPECT_NE(other_instance->browsing_instance_token(),
             base_instance->browsing_instance_token());
-  EXPECT_NE(other_instance->coop_related_group_token(),
-            base_instance->coop_related_group_token());
 }
 
 namespace {
diff --git a/content/browser/url_info.cc b/content/browser/url_info.cc
index 333fa8a..c6a0218 100644
--- a/content/browser/url_info.cc
+++ b/content/browser/url_info.cc
@@ -29,7 +29,6 @@
       storage_partition_config(init.storage_partition_config_),
       web_exposed_isolation_info(init.web_exposed_isolation_info_),
       is_pdf(init.is_pdf_),
-      common_coop_origin(init.common_coop_origin_),
       cross_origin_isolation_key(init.cross_origin_isolation_key_) {
   // An origin-keyed process can only be used for origin-keyed agent clusters.
   // We can check this for the explicit header case here, and it is checked more
@@ -165,12 +164,6 @@
   return *this;
 }
 
-UrlInfoInit& UrlInfoInit::WithCommonCoopOrigin(
-    const url::Origin& common_coop_origin) {
-  common_coop_origin_ = common_coop_origin;
-  return *this;
-}
-
 UrlInfoInit& UrlInfoInit::WithCrossOriginIsolationKey(
     const std::optional<AgentClusterKey::CrossOriginIsolationKey>&
         cross_origin_isolation_key) {
diff --git a/content/browser/url_info.h b/content/browser/url_info.h
index 342687f0..ec4beb115 100644
--- a/content/browser/url_info.h
+++ b/content/browser/url_info.h
@@ -203,21 +203,6 @@
   // NativePage is created for a main frame navigation.
   bool is_pdf = false;
 
-  // If set, indicates that this UrlInfo is for a document that sets either
-  // COOP: same-origin or COOP: restrict-properties from the given origin. For
-  // subframes, it is inherited from the top-level frame. This is used to select
-  // an appropriate BrowsingInstance when navigating within a CoopRelatedGroup.
-  //
-  // Note: This cannot be part of the WebExposedIsolationInfo, because while it
-  // might force a different BrowsingInstance to be used, it may not force a
-  // strict process isolation, which non-matching web_exposed_isolation_info
-  // implies. Example: a top-level a.com document sets COOP:
-  // restrict-properties, and an a.com iframe in another tab has no COOP set.
-  // Under memory pressure they should be able to reuse the same process. This
-  // is not the case if the top-level document sets COOP: restrict-properties +
-  // COEP, because it then has an isolated WebExposedIsolationInfo.
-  std::optional<url::Origin> common_coop_origin;
-
   // The CrossOriginIsolationKey to use for the navigation. This represents the
   // isolation requested by the page itself through the use of COOP, COEP and
   // DIP. Right now, this is only set when DocumentIsolationPolicy is enabled,
@@ -251,7 +236,6 @@
   UrlInfoInit& WithWebExposedIsolationInfo(
       std::optional<WebExposedIsolationInfo> web_exposed_isolation_info);
   UrlInfoInit& WithIsPdf(bool is_pdf);
-  UrlInfoInit& WithCommonCoopOrigin(const url::Origin& origin);
   UrlInfoInit& WithCrossOriginIsolationKey(
       const std::optional<AgentClusterKey::CrossOriginIsolationKey>&
           cross_origin_isolation_key);
@@ -274,7 +258,6 @@
   std::optional<StoragePartitionConfig> storage_partition_config_;
   std::optional<WebExposedIsolationInfo> web_exposed_isolation_info_;
   bool is_pdf_ = false;
-  std::optional<url::Origin> common_coop_origin_;
   std::optional<AgentClusterKey::CrossOriginIsolationKey>
       cross_origin_isolation_key_;
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 2039ac7..4c48081c 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -9248,9 +9248,8 @@
 }
 
 std::vector<RenderFrameHostImpl*>
-WebContentsImpl::GetActiveTopLevelDocumentsInGroup(
-    RenderFrameHostImpl* render_frame_host,
-    GroupType group_type) {
+WebContentsImpl::GetActiveTopLevelDocumentsInBrowsingContextGroup(
+    RenderFrameHostImpl* render_frame_host) {
   std::vector<RenderFrameHostImpl*> out;
   for (WebContentsImpl* web_contents : GetAllWebContents()) {
     RenderFrameHostImpl* other_render_frame_host =
@@ -9262,18 +9261,8 @@
       continue;
     }
 
-    // If we're looking for frames in the same browsing context group, filter
-    // frames in different browsing context groups.
-    if (group_type == GroupType::kBrowsingContextGroup &&
-        !render_frame_host->GetSiteInstance()->IsRelatedSiteInstance(
-            other_render_frame_host->GetSiteInstance())) {
-      continue;
-    }
-
-    // If we're looking for frames in the same CoopRelatedGroup, filter frames
-    // in different CoopRelatedGroups.
-    if (group_type == GroupType::kCoopRelatedGroup &&
-        !render_frame_host->GetSiteInstance()->IsCoopRelatedSiteInstance(
+    // Filter frames in different browsing context groups.
+    if (!render_frame_host->GetSiteInstance()->IsRelatedSiteInstance(
             other_render_frame_host->GetSiteInstance())) {
       continue;
     }
@@ -9283,20 +9272,6 @@
   return out;
 }
 
-std::vector<RenderFrameHostImpl*>
-WebContentsImpl::GetActiveTopLevelDocumentsInBrowsingContextGroup(
-    RenderFrameHostImpl* render_frame_host) {
-  return GetActiveTopLevelDocumentsInGroup(render_frame_host,
-                                           GroupType::kBrowsingContextGroup);
-}
-
-std::vector<RenderFrameHostImpl*>
-WebContentsImpl::GetActiveTopLevelDocumentsInCoopRelatedGroup(
-    RenderFrameHostImpl* render_frame_host) {
-  return GetActiveTopLevelDocumentsInGroup(render_frame_host,
-                                           GroupType::kCoopRelatedGroup);
-}
-
 PrerenderHostRegistry* WebContentsImpl::GetPrerenderHostRegistry() {
   DCHECK(prerender_host_registry_);
   return prerender_host_registry_.get();
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index c0a0400..ebbefa0 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -916,9 +916,6 @@
   std::vector<RenderFrameHostImpl*>
   GetActiveTopLevelDocumentsInBrowsingContextGroup(
       RenderFrameHostImpl* render_frame_host) override;
-  std::vector<RenderFrameHostImpl*>
-  GetActiveTopLevelDocumentsInCoopRelatedGroup(
-      RenderFrameHostImpl* render_frame_host) override;
   PrerenderHostRegistry* GetPrerenderHostRegistry() override;
 #if BUILDFLAG(ENABLE_PPAPI)
   void OnPepperInstanceCreated(RenderFrameHostImpl* source,
@@ -2192,16 +2189,6 @@
       const mojom::CreateNewWindowParams& params,
       RenderFrameHostImpl* opener);
 
-  // Describes the different types of groups we can be interested in when
-  // looking for scriptable frames.
-  enum class GroupType { kBrowsingContextGroup, kCoopRelatedGroup };
-
-  // Returns a vector of all the top-level active frames in the same group type
-  // specified by `group_type`.
-  std::vector<RenderFrameHostImpl*> GetActiveTopLevelDocumentsInGroup(
-      RenderFrameHostImpl* render_frame_host,
-      GroupType group_type);
-
   // Creates a new ForwardingAudioStreamFactory.
   std::unique_ptr<ForwardingAudioStreamFactory> CreateAudioStreamFactory();
 
diff --git a/content/common/service_worker/service_worker_router_evaluator.cc b/content/common/service_worker/service_worker_router_evaluator.cc
index 27aa47d..f1dda13 100644
--- a/content/common/service_worker/service_worker_router_evaluator.cc
+++ b/content/common/service_worker/service_worker_router_evaluator.cc
@@ -820,7 +820,15 @@
           break;
         case network::mojom::ServiceWorkerRouterSourceType::
             kRaceNetworkAndCache:
-          // TODO(crbug.com/370844790): implement race network and cache
+          if (s.race_network_and_cache_source->cache_source.cache_name) {
+            base::Value::Dict out_s;
+            out_s.Set(
+                "race_network_and_cache_cache_name",
+                *s.race_network_and_cache_source->cache_source.cache_name);
+            source.Append(std::move(out_s));
+          } else {
+            source.Append("race-network-and-cache");
+          }
           break;
       }
     }
diff --git a/content/common/service_worker/service_worker_router_evaluator_unittest.cc b/content/common/service_worker/service_worker_router_evaluator_unittest.cc
index 30b372b..ad93016 100644
--- a/content/common/service_worker/service_worker_router_evaluator_unittest.cc
+++ b/content/common/service_worker/service_worker_router_evaluator_unittest.cc
@@ -1383,6 +1383,25 @@
       source.cache_source = cache_source;
       rule.sources.push_back(source);
     }
+    {
+      blink::ServiceWorkerRouterSource source;
+      source.type =
+          network::mojom::ServiceWorkerRouterSourceType::kRaceNetworkAndCache;
+      source.race_network_and_cache_source.emplace();
+      blink::ServiceWorkerRouterCacheSource cache_source;
+      source.race_network_and_cache_source->cache_source = cache_source;
+      rule.sources.push_back(source);
+    }
+    {
+      blink::ServiceWorkerRouterSource source;
+      source.type =
+          network::mojom::ServiceWorkerRouterSourceType::kRaceNetworkAndCache;
+      source.race_network_and_cache_source.emplace();
+      blink::ServiceWorkerRouterCacheSource cache_source;
+      cache_source.cache_name = "example_cache_name";
+      source.race_network_and_cache_source->cache_source = cache_source;
+      rule.sources.push_back(source);
+    }
     rules.rules.push_back(rule);
   }
   ASSERT_EQ(1U, rules.rules.size());
@@ -1431,6 +1450,12 @@
           source.Set("cache_name", "example_cache_name");
           sources.Append(std::move(source));
         }
+        sources.Append("race-network-and-cache");
+        {
+          base::Value::Dict source;
+          source.Set("race_network_and_cache_cache_name", "example_cache_name");
+          sources.Append(std::move(source));
+        }
         rule.Set("source", std::move(sources));
       }
     }
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityActionAndEventTracker.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityActionAndEventTracker.java
index bcdabfb..4401b0b5e 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityActionAndEventTracker.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityActionAndEventTracker.java
@@ -110,18 +110,7 @@
      * @return String representation of the given event
      */
     private static @Nullable String eventToString(AccessibilityEvent event) {
-        // Convert event type to a human readable String (except TYPE_WINDOW_CONTENT_CHANGED with no
-        // CONTENT_CHANGE_TYPE_STATE_DESCRIPTION flag or CONTENT_CHANGE_TYPE_PANE_TITLE flag)
-        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
-                && (event.getContentChangeTypes()
-                                & AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION)
-                        == 0
-                && (event.getContentChangeTypes()
-                                & AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_TITLE)
-                        == 0) {
-            return null;
-        }
-
+        // Convert event type to a human readable String
         StringBuilder builder = new StringBuilder();
         builder.append(AccessibilityEvent.eventTypeToString(event.getEventType()));
 
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java
index 3523e70..4fda149 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityEventsTest.java
@@ -173,21 +173,21 @@
     @EnableFeatures(ContentFeatureList.ACCESSIBILITY_DEPRECATE_TYPE_ANNOUNCE)
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_addAlertContent_exp() {
-        performTest("add-alert-content.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("add-alert-content.html", "add-alert-content-expected-android-exp.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_addChild() {
-        performTest("add-child.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("add-child.html", "add-child-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_addChildOfBody() {
-        performTest("add-child-of-body.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("add-child-of-body.html", "add-child-of-body-expected-android.txt");
     }
 
     @Test
@@ -218,7 +218,7 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_addHiddenAttribute() {
-        performTest("add-hidden-attribute.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("add-hidden-attribute.html", "add-hidden-attribute-expected-android.txt");
     }
 
     @Test
@@ -241,35 +241,37 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_anonymousBlockChildrenChanged() {
-        performTest("anonymous-block-children-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "anonymous-block-children-changed.html",
+                "anonymous-block-children-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaAtomicChanged() {
-        performTest("aria-atomic-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-atomic-changed.html", "aria-atomic-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaAtomicChanged2() {
-        performTest("aria-atomic-changed2.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-atomic-changed2.html", "aria-atomic-changed2-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaBusyChanged() {
-        performTest("aria-busy-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-busy-changed.html", "aria-busy-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaButtonExpand() {
-        performTest("aria-button-expand.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-button-expand.html", "aria-button-expand-expected-android.txt");
     }
 
     @Test
@@ -330,77 +332,84 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaComboboxUneditable() {
-        performTest("aria-combo-box-uneditable.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "aria-combo-box-uneditable.html", "aria-combo-box-uneditable-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaControlsChanged() {
-        performTest("aria-controls-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-controls-changed.html", "aria-controls-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaCurrentChanged() {
-        performTest("aria-current-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-current-changed.html", "aria-current-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaDisabledChanged() {
-        performTest("aria-disabled-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-disabled-changed.html", "aria-disabled-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaFlowTo() {
-        performTest("aria-flow-to.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-flow-to.html", "aria-flow-to-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaHasPopupChanged() {
-        performTest("aria-haspopup-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-haspopup-changed.html", "aria-haspopup-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaHiddenChanged() {
-        performTest("aria-hidden-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-hidden-changed.html", "aria-hidden-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaHiddenDescendantsAlreadyIgnored() {
-        performTest("aria-hidden-descendants-already-ignored.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "aria-hidden-descendants-already-ignored.html",
+                "aria-hidden-descendants-already-ignored-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaHiddenDescendants() {
-        performTest("aria-hidden-descendants.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-hidden-descendants.html", "aria-hidden-descendants-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaHiddenDescendantDisplayNone() {
-        performTest("aria-hidden-single-descendant-display-none.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "aria-hidden-single-descendant-display-none.html",
+                "aria-hidden-single-descendant-display-none-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaHiddenSingleDescendant() {
-        performTest("aria-hidden-single-descendant.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "aria-hidden-single-descendant.html",
+                "aria-hidden-single-descendant-expected-android.txt");
     }
 
     @Test
@@ -408,49 +417,52 @@
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaHiddenSingleDescendantVisibilityHidden() {
         performTest(
-                "aria-hidden-single-descendant-visibility-hidden.html", EMPTY_EXPECTATIONS_FILE);
+                "aria-hidden-single-descendant-visibility-hidden.html",
+                "aria-hidden-single-descendant-visibility-hidden-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaLevelChanged() {
-        performTest("aria-level-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-level-changed.html", "aria-level-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaLiveChanged() {
-        performTest("aria-live-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-live-changed.html", "aria-live-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/1190218")
     public void test_ariaMenuItemFocus() {
-        performTest("aria-menuitem-focus.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-menuitem-focus.html", "aria-menuitem-focus-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaMultilineChanged() {
-        performTest("aria-multiline-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-multiline-changed.html", "aria-multiline-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaMultiselectableChanged() {
-        performTest("aria-multiselectable-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "aria-multiselectable-changed.html",
+                "aria-multiselectable-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaPosinsetChanged() {
-        performTest("aria-posinset-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-posinset-changed.html", "aria-posinset-changed-expected-android.txt");
     }
 
     @Test
@@ -473,28 +485,28 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaReadonlyChanged() {
-        performTest("aria-readonly-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-readonly-changed.html", "aria-readonly-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaRelevantChanged() {
-        performTest("aria-relevant-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-relevant-changed.html", "aria-relevant-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaRelevantChanged2() {
-        performTest("aria-relevant-changed2.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-relevant-changed2.html", "aria-relevant-changed2-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaRequiredChanged() {
-        performTest("aria-required-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-required-changed.html", "aria-required-changed-expected-android.txt");
     }
 
     @Test
@@ -508,14 +520,16 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaSelectedChangedNewSubtree() {
-        performTest("aria-selected-changed-new-subtree.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "aria-selected-changed-new-subtree.html",
+                "aria-selected-changed-new-subtree-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaSetsizeChanged() {
-        performTest("aria-setsize-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-setsize-changed.html", "aria-setsize-changed-expected-android.txt");
     }
 
     @Test
@@ -548,70 +562,82 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaSortChanged() {
-        performTest("aria-sort-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-sort-changed.html", "aria-sort-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaSpinbuttonValueBothChanged() {
-        performTest("aria-spinbutton-value-both-change.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "aria-spinbutton-value-both-change.html",
+                "aria-spinbutton-value-both-change-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaSpinbuttonValueChanged() {
-        performTest("aria-spinbutton-value-change.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "aria-spinbutton-value-change.html",
+                "aria-spinbutton-value-change-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaSpinbuttonValuetextChanged() {
-        performTest("aria-spinbutton-valuetext-change.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "aria-spinbutton-valuetext-change.html",
+                "aria-spinbutton-valuetext-change-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaTextboxChildrenChange() {
-        performTest("aria-textbox-children-change.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "aria-textbox-children-change.html",
+                "aria-textbox-children-change-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaTextboxEditabilityChanges() {
-        performTest("aria-textbox-editability-changes.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "aria-textbox-editability-changes.html",
+                "aria-textbox-editability-changes-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaTextboxWithFocusableChildren() {
-        performTest("aria-textbox-with-focusable-children.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "aria-textbox-with-focusable-children.html",
+                "aria-textbox-with-focusable-children-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaTreeCollapse() {
-        performTest("aria-tree-collapse.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-tree-collapse.html", "aria-tree-collapse-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaTreeExpand() {
-        performTest("aria-tree-expand.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-tree-expand.html", "aria-tree-expand-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ariaTreeItemFocus() {
-        performTest("aria-treeitem-focus.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("aria-treeitem-focus.html", "aria-treeitem-focus-expected-android.txt");
     }
 
     @Test
@@ -625,7 +651,7 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_buttonRemoveChildren() {
-        performTest("button-remove-children.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("button-remove-children.html", "button-remove-children-expected-android.txt");
     }
 
     @Test
@@ -675,35 +701,37 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_checkboxValidity() {
-        performTest("checkbox-validity.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("checkbox-validity.html", "checkbox-validity-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_checkedMixedChanged() {
-        performTest("checked-mixed-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("checked-mixed-changed.html", "checked-mixed-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_checkedStateChanged() {
-        performTest("checked-state-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("checked-state-changed.html", "checked-state-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_childrenChangedOnlyOnAncestor() {
-        performTest("children-changed-only-on-ancestor.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "children-changed-only-on-ancestor.html",
+                "children-changed-only-on-ancestor-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_cssDisplayDescendants() {
-        performTest("css-display-descendants.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("css-display-descendants.html", "css-display-descendants-expected-android.txt");
     }
 
     @Test
@@ -717,28 +745,30 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_cssFlexTextUpdate() {
-        performTest("css-flex-text-update.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("css-flex-text-update.html", "css-flex-text-update-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_cssVisibilityCollapse() {
-        performTest("css-visibility-collapse.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("css-visibility-collapse.html", "css-visibility-collapse-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_cssVisibilityDescendants() {
-        performTest("css-visibility-descendants.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "css-visibility-descendants.html",
+                "css-visibility-descendants-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_cssVisibility() {
-        performTest("css-visibility.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("css-visibility.html", "css-visibility-expected-android.txt");
     }
 
     @Test
@@ -752,7 +782,7 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_descriptionChanged() {
-        performTest("description-change.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("description-change.html", "description-change-expected-android.txt");
     }
 
     @Test
@@ -777,35 +807,39 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_descriptionChangedIndirect() {
-        performTest("description-change-indirect.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "description-change-indirect.html",
+                "description-change-indirect-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_descriptionChangedNoRelation() {
-        performTest("description-change-no-relation.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "description-change-no-relation.html",
+                "description-change-no-relation-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_disabledStateChanged() {
-        performTest("disabled-state-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("disabled-state-changed.html", "disabled-state-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_documentTitleChanged() {
-        performTest("document-title-change.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("document-title-change.html", "document-title-change-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_expandedChanged() {
-        performTest("expanded-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("expanded-changed.html", "expanded-changed-expected-android.txt");
     }
 
     @Test
@@ -820,21 +854,22 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/1190218")
     public void test_focusListbox() {
-        performTest("focus-listbox.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("focus-listbox.html", "focus-listbox-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/1190218")
     public void test_focusListboxMultiselect() {
-        performTest("focus-listbox-multiselect.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "focus-listbox-multiselect.html", "focus-listbox-multiselect-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_formDisabledChanged() {
-        performTest("form-disabled-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("form-disabled-changed.html", "form-disabled-changed-expected-android.txt");
     }
 
     @Test
@@ -848,21 +883,21 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/1392791")
     public void test_immediateRefresh() {
-        performTest("immediate-refresh.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("immediate-refresh.html", "immediate-refresh-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_innerHtmlChanged() {
-        performTest("inner-html-change.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("inner-html-change.html", "inner-html-change-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_iframeSrcChanged() {
-        performTest("iframe-src-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("iframe-src-changed.html", "iframe-src-changed-expected-android.txt");
     }
 
     @Test
@@ -890,21 +925,23 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_inputTypeTextValueChanged() {
-        performTest("input-type-text-value-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "input-type-text-value-changed.html",
+                "input-type-text-value-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/1190218")
     public void test_listboxFocus() {
-        performTest("listbox-focus.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("listbox-focus.html", "listbox-focus-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_listboxNext() {
-        performTest("listbox-next.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("listbox-next.html", "listbox-next-expected-android.txt");
     }
 
     @Test
@@ -920,14 +957,16 @@
     @EnableFeatures(ContentFeatureList.ACCESSIBILITY_DEPRECATE_TYPE_ANNOUNCE)
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_liveRegionAdd_exp() {
-        performTest("live-region-add.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("live-region-add.html", "live-region-add-expected-android-exp.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_liveRegionAddLiveAttribute() {
-        performTest("live-region-add-live-attribute.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "live-region-add-live-attribute.html",
+                "live-region-add-live-attribute-expected-android.txt");
     }
 
     @Test
@@ -943,7 +982,7 @@
     @EnableFeatures(ContentFeatureList.ACCESSIBILITY_DEPRECATE_TYPE_ANNOUNCE)
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_liveRegionChanged_exp() {
-        performTest("live-region-change.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("live-region-change.html", "live-region-change-expected-android-exp.txt");
     }
 
     @Test
@@ -961,7 +1000,9 @@
     @EnableFeatures(ContentFeatureList.ACCESSIBILITY_DEPRECATE_TYPE_ANNOUNCE)
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_liveRegionChangedInnerHtml_exp() {
-        performTest("live-region-change-innerhtml.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "live-region-change-innerhtml.html",
+                "live-region-change-innerhtml-expected-android-exp.txt");
     }
 
     @Test
@@ -979,14 +1020,16 @@
     @EnableFeatures(ContentFeatureList.ACCESSIBILITY_DEPRECATE_TYPE_ANNOUNCE)
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_liveRegionChangedInnerText_exp() {
-        performTest("live-region-change-innertext.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "live-region-change-innertext.html",
+                "live-region-change-innertext-expected-android-exp.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_liveRegionCreate() {
-        performTest("live-region-create.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("live-region-create.html", "live-region-create-expected-android.txt");
     }
 
     @Test
@@ -1020,91 +1063,93 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_liveRegionOff() {
-        performTest("live-region-off.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("live-region-off.html", "live-region-off-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_liveRegionRemove() {
-        performTest("live-region-remove.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("live-region-remove.html", "live-region-remove-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_menuBarShowHideMenus() {
-        performTest("menubar-show-hide-menus.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("menubar-show-hide-menus.html", "menubar-show-hide-menus-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_menulistCollapse() {
-        performTest("menulist-collapse.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("menulist-collapse.html", "menulist-collapse-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_menulistCollapseNext() {
-        performTest("menulist-collapse-next.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("menulist-collapse-next.html", "menulist-collapse-next-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_menulistExpand() {
-        performTest("menulist-expand.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("menulist-expand.html", "menulist-expand-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/1190218")
     public void test_menulistFocus() {
-        performTest("menulist-focus.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("menulist-focus.html", "menulist-focus-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_menulistNext() {
-        performTest("menulist-next.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("menulist-next.html", "menulist-next-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_menuOpenedClosed() {
-        performTest("menu-opened-closed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("menu-opened-closed.html", "menu-opened-closed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_multipleAriaPropertiesChanged() {
-        performTest("multiple-aria-properties-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "multiple-aria-properties-changed.html",
+                "multiple-aria-properties-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_nameChanged() {
-        performTest("name-change.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("name-change.html", "name-change-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_nameChangedIndirect() {
-        performTest("name-change-indirect.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("name-change-indirect.html", "name-change-indirect-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "crbug.com/382549182")
     public void test_navigationApi() {
-        performTest("navigation-api.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("navigation-api.html", "navigation-api-expected-android.txt");
     }
 
     @Test
@@ -1118,28 +1163,35 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_rangeValueIsReadonlyChanged() {
-        performTest("range-value-is-readonly-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "range-value-is-readonly-changed.html",
+                "range-value-is-readonly-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_rangeValueMaximumChanged() {
-        performTest("range-value-maximum-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "range-value-maximum-changed.html",
+                "range-value-maximum-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_rangeValueMinimumChanged() {
-        performTest("range-value-minimum-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "range-value-minimum-changed.html",
+                "range-value-minimum-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_rangeValueStepChanged() {
-        performTest("range-value-step-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "range-value-step-changed.html", "range-value-step-changed-expected-android.txt");
     }
 
     @Test
@@ -1154,14 +1206,14 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_removeChild() {
-        performTest("remove-child.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("remove-child.html", "remove-child-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_removeHiddenAttribute() {
-        performTest("remove-hidden-attribute.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("remove-hidden-attribute.html", "remove-hidden-attribute-expected-android.txt");
     }
 
     @Test
@@ -1228,14 +1280,16 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_selectSelectedAddRemove() {
-        performTest("select-selected-add-remove.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "select-selected-add-remove.html",
+                "select-selected-add-remove-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_styleChanged() {
-        performTest("style-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("style-changed.html", "style-changed-expected-android.txt");
     }
 
     @Test
@@ -1269,63 +1323,73 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_tabIndexAddedOnAriaHidden() {
-        performTest("tabindex-added-on-aria-hidden.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "tabindex-added-on-aria-hidden.html",
+                "tabindex-added-on-aria-hidden-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_tabIndexAddedOnPlainDiv() {
-        performTest("tabindex-added-on-plain-div.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "tabindex-added-on-plain-div.html",
+                "tabindex-added-on-plain-div-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_tabIndexRemoveOnAriaHidden() {
-        performTest("tabindex-removed-on-aria-hidden.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "tabindex-removed-on-aria-hidden.html",
+                "tabindex-removed-on-aria-hidden-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_tabIndexRemovedOnPlainDiv() {
-        performTest("tabindex-removed-on-plain-div.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "tabindex-removed-on-plain-div.html",
+                "tabindex-removed-on-plain-div-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/1190218")
     public void test_tbodyFocus() {
-        performTest("tbody-focus.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("tbody-focus.html", "tbody-focus-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_textAlignChanged() {
-        performTest("text-align-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("text-align-changed.html", "text-align-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_textChangedContenteditable() {
-        performTest("text-changed-contenteditable.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "text-changed-contenteditable.html",
+                "text-changed-contenteditable-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_textChanged() {
-        performTest("text-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("text-changed.html", "text-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_textIndentChanged() {
-        performTest("text-indent-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("text-indent-changed.html", "text-indent-changed-expected-android.txt");
     }
 
     @Test
@@ -1357,34 +1421,36 @@
     @SmallTest
     @DisabledTest(message = "https://crbug.com/1190218")
     public void test_tfootFocus() {
-        performTest("tfoot-focus.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("tfoot-focus.html", "tfoot-focus-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/1190218")
     public void test_theadFocus() {
-        performTest("thead-focus.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("thead-focus.html", "thead-focus-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_valudIsReadonlyChanged() {
-        performTest("value-is-readonly-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "value-is-readonly-changed.html", "value-is-readonly-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_ValueValueChanged() {
-        performTest("value-value-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest("value-value-changed.html", "value-value-changed-expected-android.txt");
     }
 
     @Test
     @SmallTest
     @DisabledTest(message = "https://crbug.com/414363686")
     public void test_visibilityHiddenChanged() {
-        performTest("visibility-hidden-changed.html", EMPTY_EXPECTATIONS_FILE);
+        performTest(
+                "visibility-hidden-changed.html", "visibility-hidden-changed-expected-android.txt");
     }
 }
diff --git a/content/renderer/service_worker/service_worker_subresource_loader.cc b/content/renderer/service_worker/service_worker_subresource_loader.cc
index 476b1d9..b9761791 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader.cc
+++ b/content/renderer/service_worker/service_worker_subresource_loader.cc
@@ -438,7 +438,13 @@
           return;
         case network::mojom::ServiceWorkerRouterSourceType::
             kRaceNetworkAndCache:
-          // TODO(crbug.com/370844790): implement race network and cache
+          race_network_request_mode = kForced;
+          controller_connector_->CallCacheStorageMatch(
+              sources[0].race_network_and_cache_source->cache_source.cache_name,
+              blink::mojom::FetchAPIRequest::From(resource_request_),
+              base::BindOnce(
+                  &ServiceWorkerSubresourceLoader::DidCacheStorageMatch,
+                  weak_factory_.GetWeakPtr(), base::TimeTicks::Now()));
           break;
       }
     }
@@ -1359,17 +1365,20 @@
 void ServiceWorkerSubresourceLoader::SetCommitResponsibility(
     FetchResponseFrom fetch_response_from) {
   // Set the actual source type used in Static Routing API when
-  // `race-network-and-fetch` is used. Determine this by checking the
-  // commit responsibility. If it's not the service worker, the network
-  // has won.
+  // `race-network-and-fetch` or `race-network-and-race` is used.
+  // Determine this by checking the commit responsibility. If it's not the
+  // service worker, the network has won.
   // This check is conducted here since in the case of `knetwork`, it does
   // not call `DidDispatchFetchEvent`, where we set the `actual_source_type`
   // for the other sources, and the `response_head_` is already passed on.
   if (response_head_ && response_head_->service_worker_router_info &&
       response_head_->service_worker_router_info->matched_source_type &&
-      *response_head_->service_worker_router_info->matched_source_type ==
-          network::mojom::ServiceWorkerRouterSourceType::
-              kRaceNetworkAndFetchEvent &&
+      (*response_head_->service_worker_router_info->matched_source_type ==
+           network::mojom::ServiceWorkerRouterSourceType::
+               kRaceNetworkAndFetchEvent ||
+       *response_head_->service_worker_router_info->matched_source_type ==
+           network::mojom::ServiceWorkerRouterSourceType::
+               kRaceNetworkAndCache) &&
       fetch_response_from == FetchResponseFrom::kWithoutServiceWorker) {
     response_head_->service_worker_router_info->actual_source_type =
         network::mojom::ServiceWorkerRouterSourceType::kNetwork;
diff --git a/content/test/content_test_bundle_data.filelist b/content/test/content_test_bundle_data.filelist
index 53cc522..3cc6954 100644
--- a/content/test/content_test_bundle_data.filelist
+++ b/content/test/content_test_bundle_data.filelist
@@ -2147,10 +2147,10 @@
 data/accessibility/css/reading-flow-out-of-flow-position.html
 data/accessibility/css/reading-flow-pseudo-elements-expected-blink.txt
 data/accessibility/css/reading-flow-pseudo-elements.html
-data/accessibility/css/reading-flow-shadow-dom-slot-expected-blink.txt
-data/accessibility/css/reading-flow-shadow-dom-slot.html
 data/accessibility/css/reading-flow-scroll-marker-expected-blink.txt
 data/accessibility/css/reading-flow-scroll-marker.html
+data/accessibility/css/reading-flow-shadow-dom-slot-expected-blink.txt
+data/accessibility/css/reading-flow-shadow-dom-slot.html
 data/accessibility/css/reading-flow.html
 data/accessibility/css/scroll-buttons-disabled-status-expected-blink.txt
 data/accessibility/css/scroll-buttons-disabled-status.html
@@ -2218,6 +2218,7 @@
 data/accessibility/display-locking/viewport-activation-expected-mac.txt
 data/accessibility/display-locking/viewport-activation-expected-win.txt
 data/accessibility/display-locking/viewport-activation.html
+data/accessibility/event/add-alert-content-expected-android-exp.txt
 data/accessibility/event/add-alert-content-expected-android.txt
 data/accessibility/event/add-alert-content-expected-auralinux.txt
 data/accessibility/event/add-alert-content-expected-mac.txt
@@ -2235,8 +2236,10 @@
 data/accessibility/event/add-alert-with-role-change-expected-win.txt
 data/accessibility/event/add-alert-with-role-change.html
 data/accessibility/event/add-alert.html
+data/accessibility/event/add-child-expected-android.txt
 data/accessibility/event/add-child-expected-auralinux.txt
 data/accessibility/event/add-child-expected-win.txt
+data/accessibility/event/add-child-of-body-expected-android.txt
 data/accessibility/event/add-child-of-body-expected-win.txt
 data/accessibility/event/add-child-of-body.html
 data/accessibility/event/add-child.html
@@ -2258,6 +2261,7 @@
 data/accessibility/event/add-dialog-no-info-expected-win.txt
 data/accessibility/event/add-dialog-no-info.html
 data/accessibility/event/add-dialog.html
+data/accessibility/event/add-hidden-attribute-expected-android.txt
 data/accessibility/event/add-hidden-attribute-expected-auralinux.txt
 data/accessibility/event/add-hidden-attribute-expected-win.txt
 data/accessibility/event/add-hidden-attribute-subtree-expected-android.txt
@@ -2270,6 +2274,7 @@
 data/accessibility/event/add-subtree-expected-uia-win.txt
 data/accessibility/event/add-subtree-expected-win.txt
 data/accessibility/event/add-subtree.html
+data/accessibility/event/anonymous-block-children-changed-expected-android.txt
 data/accessibility/event/anonymous-block-children-changed-expected-auralinux.txt
 data/accessibility/event/anonymous-block-children-changed.html
 data/accessibility/event/aria-activedescendant-element-tree-changes-expected-auralinux.txt
@@ -2278,14 +2283,18 @@
 data/accessibility/event/aria-activedescendant-element-tree-changes.html
 data/accessibility/event/aria-activedescendant-id-and-tree-changes-expected-auralinux.txt
 data/accessibility/event/aria-activedescendant-id-and-tree-changes.html
+data/accessibility/event/aria-atomic-changed-expected-android.txt
 data/accessibility/event/aria-atomic-changed-expected-uia-win.txt
 data/accessibility/event/aria-atomic-changed.html
+data/accessibility/event/aria-atomic-changed2-expected-android.txt
 data/accessibility/event/aria-atomic-changed2-expected-uia-win.txt
 data/accessibility/event/aria-atomic-changed2.html
+data/accessibility/event/aria-busy-changed-expected-android.txt
 data/accessibility/event/aria-busy-changed-expected-auralinux.txt
 data/accessibility/event/aria-busy-changed-expected-mac.txt
 data/accessibility/event/aria-busy-changed-expected-uia-win.txt
 data/accessibility/event/aria-busy-changed.html
+data/accessibility/event/aria-button-expand-expected-android.txt
 data/accessibility/event/aria-button-expand-expected-auralinux.txt
 data/accessibility/event/aria-button-expand.html
 data/accessibility/event/aria-checked-changed-expected-android.txt
@@ -2327,16 +2336,20 @@
 data/accessibility/event/aria-combo-box-next-expected-uia-win.txt
 data/accessibility/event/aria-combo-box-next-expected-win.txt
 data/accessibility/event/aria-combo-box-next.html
+data/accessibility/event/aria-combo-box-uneditable-expected-android.txt
 data/accessibility/event/aria-combo-box-uneditable-expected-mac.txt
 data/accessibility/event/aria-combo-box-uneditable-expected-win.txt
 data/accessibility/event/aria-combo-box-uneditable.html
+data/accessibility/event/aria-controls-changed-expected-android.txt
 data/accessibility/event/aria-controls-changed-expected-uia-win.txt
 data/accessibility/event/aria-controls-changed.html
+data/accessibility/event/aria-current-changed-expected-android.txt
 data/accessibility/event/aria-current-changed-expected-auralinux.txt
 data/accessibility/event/aria-current-changed-expected-mac.txt
 data/accessibility/event/aria-current-changed-expected-uia-win.txt
 data/accessibility/event/aria-current-changed-expected-win.txt
 data/accessibility/event/aria-current-changed.html
+data/accessibility/event/aria-disabled-changed-expected-android.txt
 data/accessibility/event/aria-disabled-changed-expected-auralinux.txt
 data/accessibility/event/aria-disabled-changed-expected-mac.txt
 data/accessibility/event/aria-disabled-changed-expected-uia-win.txt
@@ -2346,46 +2359,60 @@
 data/accessibility/event/aria-expanded-and-collapsed-reparenting-expected-auralinux.txt
 data/accessibility/event/aria-expanded-and-collapsed-reparenting.html
 data/accessibility/event/aria-expanded-and-collapsed.html
+data/accessibility/event/aria-flow-to-expected-android.txt
 data/accessibility/event/aria-flow-to-expected-uia-win.txt
 data/accessibility/event/aria-flow-to.html
+data/accessibility/event/aria-haspopup-changed-expected-android.txt
 data/accessibility/event/aria-haspopup-changed-expected-uia-win.txt
 data/accessibility/event/aria-haspopup-changed.html
+data/accessibility/event/aria-hidden-changed-expected-android.txt
 data/accessibility/event/aria-hidden-changed-expected-auralinux.txt
 data/accessibility/event/aria-hidden-changed-expected-uia-win.txt
 data/accessibility/event/aria-hidden-changed.html
+data/accessibility/event/aria-hidden-descendants-already-ignored-expected-android.txt
 data/accessibility/event/aria-hidden-descendants-already-ignored-expected-auralinux.txt
 data/accessibility/event/aria-hidden-descendants-already-ignored-expected-uia-win.txt
 data/accessibility/event/aria-hidden-descendants-already-ignored-expected-win.txt
 data/accessibility/event/aria-hidden-descendants-already-ignored.html
+data/accessibility/event/aria-hidden-descendants-expected-android.txt
 data/accessibility/event/aria-hidden-descendants-expected-uia-win.txt
 data/accessibility/event/aria-hidden-descendants-expected-win.txt
 data/accessibility/event/aria-hidden-descendants.html
+data/accessibility/event/aria-hidden-single-descendant-display-none-expected-android.txt
 data/accessibility/event/aria-hidden-single-descendant-display-none-expected-auralinux.txt
 data/accessibility/event/aria-hidden-single-descendant-display-none-expected-uia-win.txt
 data/accessibility/event/aria-hidden-single-descendant-display-none-expected-win.txt
 data/accessibility/event/aria-hidden-single-descendant-display-none.html
+data/accessibility/event/aria-hidden-single-descendant-expected-android.txt
 data/accessibility/event/aria-hidden-single-descendant-expected-uia-win.txt
 data/accessibility/event/aria-hidden-single-descendant-expected-win.txt
+data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-android.txt
 data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-auralinux.txt
 data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-uia-win.txt
 data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-win.txt
 data/accessibility/event/aria-hidden-single-descendant-visibility-hidden.html
 data/accessibility/event/aria-hidden-single-descendant.html
+data/accessibility/event/aria-level-changed-expected-android.txt
 data/accessibility/event/aria-level-changed-expected-uia-win.txt
 data/accessibility/event/aria-level-changed.html
+data/accessibility/event/aria-live-changed-expected-android.txt
 data/accessibility/event/aria-live-changed-expected-auralinux.txt
 data/accessibility/event/aria-live-changed-expected-mac.txt
 data/accessibility/event/aria-live-changed-expected-uia-win.txt
 data/accessibility/event/aria-live-changed.html
+data/accessibility/event/aria-menuitem-focus-expected-android.txt
 data/accessibility/event/aria-menuitem-focus-expected-auralinux.txt
 data/accessibility/event/aria-menuitem-focus.html
+data/accessibility/event/aria-multiline-changed-expected-android.txt
 data/accessibility/event/aria-multiline-changed-expected-uia-win.txt
 data/accessibility/event/aria-multiline-changed.html
+data/accessibility/event/aria-multiselectable-changed-expected-android.txt
 data/accessibility/event/aria-multiselectable-changed-expected-auralinux.txt
 data/accessibility/event/aria-multiselectable-changed-expected-mac.txt
 data/accessibility/event/aria-multiselectable-changed-expected-uia-win.txt
 data/accessibility/event/aria-multiselectable-changed-expected-win.txt
 data/accessibility/event/aria-multiselectable-changed.html
+data/accessibility/event/aria-posinset-changed-expected-android.txt
 data/accessibility/event/aria-posinset-changed-expected-uia-win.txt
 data/accessibility/event/aria-posinset-changed.html
 data/accessibility/event/aria-pressed-changed-expected-android.txt
@@ -2398,12 +2425,16 @@
 data/accessibility/event/aria-pressed-changes-button-role-expected-uia-win.txt
 data/accessibility/event/aria-pressed-changes-button-role-expected-win.txt
 data/accessibility/event/aria-pressed-changes-button-role.html
+data/accessibility/event/aria-readonly-changed-expected-android.txt
 data/accessibility/event/aria-readonly-changed-expected-uia-win.txt
 data/accessibility/event/aria-readonly-changed.html
+data/accessibility/event/aria-relevant-changed-expected-android.txt
 data/accessibility/event/aria-relevant-changed-expected-uia-win.txt
 data/accessibility/event/aria-relevant-changed.html
+data/accessibility/event/aria-relevant-changed2-expected-android.txt
 data/accessibility/event/aria-relevant-changed2-expected-uia-win.txt
 data/accessibility/event/aria-relevant-changed2.html
+data/accessibility/event/aria-required-changed-expected-android.txt
 data/accessibility/event/aria-required-changed-expected-mac.txt
 data/accessibility/event/aria-required-changed-expected-uia-win.txt
 data/accessibility/event/aria-required-changed-expected-win.txt
@@ -2411,12 +2442,14 @@
 data/accessibility/event/aria-selected-changed-expected-android.txt
 data/accessibility/event/aria-selected-changed-expected-uia-win.txt
 data/accessibility/event/aria-selected-changed-expected-win.txt
+data/accessibility/event/aria-selected-changed-new-subtree-expected-android.txt
 data/accessibility/event/aria-selected-changed-new-subtree-expected-auralinux.txt
 data/accessibility/event/aria-selected-changed-new-subtree-expected-mac.txt
 data/accessibility/event/aria-selected-changed-new-subtree-expected-uia-win.txt
 data/accessibility/event/aria-selected-changed-new-subtree-expected-win.txt
 data/accessibility/event/aria-selected-changed-new-subtree.html
 data/accessibility/event/aria-selected-changed.html
+data/accessibility/event/aria-setsize-changed-expected-android.txt
 data/accessibility/event/aria-setsize-changed-expected-uia-win.txt
 data/accessibility/event/aria-setsize-changed.html
 data/accessibility/event/aria-slider-value-both-change-expected-android.txt
@@ -2437,41 +2470,51 @@
 data/accessibility/event/aria-slider-valuetext-change-expected-uia-win.txt
 data/accessibility/event/aria-slider-valuetext-change-expected-win.txt
 data/accessibility/event/aria-slider-valuetext-change.html
+data/accessibility/event/aria-sort-changed-expected-android.txt
 data/accessibility/event/aria-sort-changed-expected-auralinux.txt
 data/accessibility/event/aria-sort-changed-expected-uia-win.txt
 data/accessibility/event/aria-sort-changed-expected-win.txt
 data/accessibility/event/aria-sort-changed.html
+data/accessibility/event/aria-spinbutton-value-both-change-expected-android.txt
 data/accessibility/event/aria-spinbutton-value-both-change-expected-auralinux.txt
 data/accessibility/event/aria-spinbutton-value-both-change-expected-mac.txt
 data/accessibility/event/aria-spinbutton-value-both-change-expected-uia-win.txt
 data/accessibility/event/aria-spinbutton-value-both-change-expected-win.txt
 data/accessibility/event/aria-spinbutton-value-both-change.html
+data/accessibility/event/aria-spinbutton-value-change-expected-android.txt
 data/accessibility/event/aria-spinbutton-value-change-expected-auralinux.txt
 data/accessibility/event/aria-spinbutton-value-change-expected-mac.txt
 data/accessibility/event/aria-spinbutton-value-change-expected-uia-win.txt
 data/accessibility/event/aria-spinbutton-value-change-expected-win.txt
 data/accessibility/event/aria-spinbutton-value-change.html
+data/accessibility/event/aria-spinbutton-valuetext-change-expected-android.txt
 data/accessibility/event/aria-spinbutton-valuetext-change-expected-auralinux.txt
 data/accessibility/event/aria-spinbutton-valuetext-change-expected-mac.txt
 data/accessibility/event/aria-spinbutton-valuetext-change-expected-uia-win.txt
 data/accessibility/event/aria-spinbutton-valuetext-change-expected-win.txt
 data/accessibility/event/aria-spinbutton-valuetext-change.html
+data/accessibility/event/aria-textbox-children-change-expected-android.txt
 data/accessibility/event/aria-textbox-children-change-expected-auralinux.txt
 data/accessibility/event/aria-textbox-children-change-expected-mac.txt
 data/accessibility/event/aria-textbox-children-change-expected-win.txt
 data/accessibility/event/aria-textbox-children-change.html
+data/accessibility/event/aria-textbox-editability-changes-expected-android.txt
 data/accessibility/event/aria-textbox-editability-changes-expected-auralinux.txt
 data/accessibility/event/aria-textbox-editability-changes-expected-mac.txt
 data/accessibility/event/aria-textbox-editability-changes-expected-win.txt
 data/accessibility/event/aria-textbox-editability-changes.html
+data/accessibility/event/aria-textbox-with-focusable-children-expected-android.txt
 data/accessibility/event/aria-textbox-with-focusable-children-expected-auralinux.txt
 data/accessibility/event/aria-textbox-with-focusable-children-expected-mac.txt
 data/accessibility/event/aria-textbox-with-focusable-children-expected-win.txt
 data/accessibility/event/aria-textbox-with-focusable-children.html
+data/accessibility/event/aria-tree-collapse-expected-android.txt
 data/accessibility/event/aria-tree-collapse-expected-mac.txt
 data/accessibility/event/aria-tree-collapse.html
+data/accessibility/event/aria-tree-expand-expected-android.txt
 data/accessibility/event/aria-tree-expand-expected-mac.txt
 data/accessibility/event/aria-tree-expand.html
+data/accessibility/event/aria-treeitem-focus-expected-android.txt
 data/accessibility/event/aria-treeitem-focus-expected-auralinux.txt
 data/accessibility/event/aria-treeitem-focus-expected-mac.txt
 data/accessibility/event/aria-treeitem-focus-expected-uia-win.txt
@@ -2482,6 +2525,7 @@
 data/accessibility/event/button-click-expected-auralinux.txt
 data/accessibility/event/button-click-expected-uia-win.txt
 data/accessibility/event/button-click.html
+data/accessibility/event/button-remove-children-expected-android.txt
 data/accessibility/event/button-remove-children-expected-auralinux.txt
 data/accessibility/event/button-remove-children-expected-uia-win.txt
 data/accessibility/event/button-remove-children-expected-win.txt
@@ -2509,48 +2553,60 @@
 data/accessibility/event/carousel-with-tabs-expected-android.txt
 data/accessibility/event/carousel-with-tabs-expected-auralinux.txt
 data/accessibility/event/carousel-with-tabs.html
+data/accessibility/event/checkbox-validity-expected-android.txt
 data/accessibility/event/checkbox-validity-expected-auralinux.txt
 data/accessibility/event/checkbox-validity.html
+data/accessibility/event/checked-mixed-changed-expected-android.txt
 data/accessibility/event/checked-mixed-changed-expected-auralinux.txt
 data/accessibility/event/checked-mixed-changed-expected-mac.txt
 data/accessibility/event/checked-mixed-changed-expected-uia-win.txt
 data/accessibility/event/checked-mixed-changed-expected-win.txt
 data/accessibility/event/checked-mixed-changed.html
+data/accessibility/event/checked-state-changed-expected-android.txt
 data/accessibility/event/checked-state-changed-expected-auralinux.txt
 data/accessibility/event/checked-state-changed-expected-mac.txt
 data/accessibility/event/checked-state-changed-expected-uia-win.txt
 data/accessibility/event/checked-state-changed-expected-win.txt
 data/accessibility/event/checked-state-changed.html
+data/accessibility/event/children-changed-only-on-ancestor-expected-android.txt
 data/accessibility/event/children-changed-only-on-ancestor-expected-auralinux.txt
 data/accessibility/event/children-changed-only-on-ancestor-expected-uia-win.txt
 data/accessibility/event/children-changed-only-on-ancestor-expected-win.txt
 data/accessibility/event/children-changed-only-on-ancestor.html
+data/accessibility/event/css-display-descendants-expected-android.txt
 data/accessibility/event/css-display-descendants-expected-win.txt
 data/accessibility/event/css-display-descendants.html
 data/accessibility/event/css-display-expected-android.txt
 data/accessibility/event/css-display-expected-auralinux.txt
 data/accessibility/event/css-display-expected-win.txt
 data/accessibility/event/css-display.html
+data/accessibility/event/css-flex-text-update-expected-android.txt
 data/accessibility/event/css-flex-text-update-expected-auralinux.txt
 data/accessibility/event/css-flex-text-update-expected-mac.txt
 data/accessibility/event/css-flex-text-update-expected-uia-win.txt
 data/accessibility/event/css-flex-text-update-expected-win.txt
 data/accessibility/event/css-flex-text-update.html
+data/accessibility/event/css-visibility-collapse-expected-android.txt
 data/accessibility/event/css-visibility-collapse-expected-win.txt
 data/accessibility/event/css-visibility-collapse.html
+data/accessibility/event/css-visibility-descendants-expected-android.txt
 data/accessibility/event/css-visibility-descendants-expected-win.txt
 data/accessibility/event/css-visibility-descendants.html
+data/accessibility/event/css-visibility-expected-android.txt
 data/accessibility/event/css-visibility-expected-win.txt
 data/accessibility/event/css-visibility.html
 data/accessibility/event/delete-subtree-expected-android.txt
 data/accessibility/event/delete-subtree-expected-auralinux.txt
 data/accessibility/event/delete-subtree.html
+data/accessibility/event/description-change-expected-android.txt
 data/accessibility/event/description-change-expected-auralinux.txt
 data/accessibility/event/description-change-expected-uia-win.txt
 data/accessibility/event/description-change-expected-win.txt
+data/accessibility/event/description-change-indirect-expected-android.txt
 data/accessibility/event/description-change-indirect-expected-uia-win.txt
 data/accessibility/event/description-change-indirect-expected-win.txt
 data/accessibility/event/description-change-indirect.html
+data/accessibility/event/description-change-no-relation-expected-android.txt
 data/accessibility/event/description-change-no-relation-expected-auralinux.txt
 data/accessibility/event/description-change-no-relation-expected-uia-win.txt
 data/accessibility/event/description-change-no-relation-expected-win.txt
@@ -2560,25 +2616,31 @@
 data/accessibility/event/description-changed-pane-title.html
 data/accessibility/event/description-changed-subtree-expected-android.txt
 data/accessibility/event/description-changed-subtree.html
+data/accessibility/event/disabled-state-changed-expected-android.txt
 data/accessibility/event/disabled-state-changed-expected-auralinux.txt
 data/accessibility/event/disabled-state-changed-expected-win.txt
 data/accessibility/event/disabled-state-changed.html
+data/accessibility/event/document-title-change-expected-android.txt
 data/accessibility/event/document-title-change.html
 data/accessibility/event/document-title-changed-expected-auralinux.txt
+data/accessibility/event/expanded-changed-expected-android.txt
 data/accessibility/event/expanded-changed-expected-auralinux.txt
 data/accessibility/event/expanded-changed-expected-mac.txt
 data/accessibility/event/expanded-changed-expected-uia-win.txt
 data/accessibility/event/expanded-changed.html
+data/accessibility/event/focus-listbox-expected-android.txt
 data/accessibility/event/focus-listbox-expected-auralinux.txt
 data/accessibility/event/focus-listbox-expected-mac.txt
 data/accessibility/event/focus-listbox-expected-uia-win.txt
 data/accessibility/event/focus-listbox-expected-win.txt
+data/accessibility/event/focus-listbox-multiselect-expected-android.txt
 data/accessibility/event/focus-listbox-multiselect-expected-auralinux.txt
 data/accessibility/event/focus-listbox-multiselect-expected-mac.txt
 data/accessibility/event/focus-listbox-multiselect-expected-uia-win.txt
 data/accessibility/event/focus-listbox-multiselect-expected-win.txt
 data/accessibility/event/focus-listbox-multiselect.html
 data/accessibility/event/focus-listbox.html
+data/accessibility/event/form-disabled-changed-expected-android.txt
 data/accessibility/event/form-disabled-changed-expected-mac.txt
 data/accessibility/event/form-disabled-changed-expected-win.txt
 data/accessibility/event/form-disabled-changed.html
@@ -2587,14 +2649,17 @@
 data/accessibility/event/form-required-changed-expected-uia-win.txt
 data/accessibility/event/form-required-changed-expected-win.txt
 data/accessibility/event/form-required-changed.html
+data/accessibility/event/iframe-src-changed-expected-android.txt
 data/accessibility/event/iframe-src-changed-expected-auralinux.txt
 data/accessibility/event/iframe-src-changed.html
+data/accessibility/event/immediate-refresh-expected-android.txt
 data/accessibility/event/immediate-refresh-expected-auralinux.txt
 data/accessibility/event/immediate-refresh.html
 data/accessibility/event/individual-nodes-become-ignored-but-included-expected-auralinux.txt
 data/accessibility/event/individual-nodes-become-ignored-but-included.html
 data/accessibility/event/individual-nodes-become-ignored-expected-auralinux.txt
 data/accessibility/event/individual-nodes-become-ignored.html
+data/accessibility/event/inner-html-change-expected-android.txt
 data/accessibility/event/inner-html-change-expected-uia-win.txt
 data/accessibility/event/inner-html-change-expected-win.txt
 data/accessibility/event/inner-html-change.html
@@ -2604,40 +2669,48 @@
 data/accessibility/event/input-combobox-dialog.html
 data/accessibility/event/input-combobox-expected-android.txt
 data/accessibility/event/input-combobox.html
+data/accessibility/event/input-type-text-value-changed-expected-android.txt
 data/accessibility/event/input-type-text-value-changed-expected-mac.txt
 data/accessibility/event/input-type-text-value-changed-expected-uia-win.txt
 data/accessibility/event/input-type-text-value-changed-expected-win.txt
 data/accessibility/event/input-type-text-value-changed.html
+data/accessibility/event/listbox-focus-expected-android.txt
 data/accessibility/event/listbox-focus-expected-auralinux.txt
 data/accessibility/event/listbox-focus-expected-mac.txt
 data/accessibility/event/listbox-focus-expected-uia-win.txt
 data/accessibility/event/listbox-focus-expected-win.txt
 data/accessibility/event/listbox-focus.html
+data/accessibility/event/listbox-next-expected-android.txt
 data/accessibility/event/listbox-next-expected-auralinux.txt
 data/accessibility/event/listbox-next-expected-mac.txt
 data/accessibility/event/listbox-next-expected-uia-win.txt
 data/accessibility/event/listbox-next-expected-win.txt
 data/accessibility/event/listbox-next.html
+data/accessibility/event/live-region-add-expected-android-exp.txt
 data/accessibility/event/live-region-add-expected-android.txt
 data/accessibility/event/live-region-add-expected-auralinux.txt
 data/accessibility/event/live-region-add-expected-mac.txt
 data/accessibility/event/live-region-add-expected-uia-win.txt
 data/accessibility/event/live-region-add-expected-win.txt
+data/accessibility/event/live-region-add-live-attribute-expected-android.txt
 data/accessibility/event/live-region-add-live-attribute-expected-auralinux.txt
 data/accessibility/event/live-region-add-live-attribute-expected-mac.txt
 data/accessibility/event/live-region-add-live-attribute-expected-uia-win.txt
 data/accessibility/event/live-region-add-live-attribute.html
 data/accessibility/event/live-region-add.html
+data/accessibility/event/live-region-change-expected-android-exp.txt
 data/accessibility/event/live-region-change-expected-android.txt
 data/accessibility/event/live-region-change-expected-auralinux.txt
 data/accessibility/event/live-region-change-expected-mac.txt
 data/accessibility/event/live-region-change-expected-uia-win.txt
 data/accessibility/event/live-region-change-expected-win.txt
+data/accessibility/event/live-region-change-innerhtml-expected-android-exp.txt
 data/accessibility/event/live-region-change-innerhtml-expected-android.txt
 data/accessibility/event/live-region-change-innerhtml-expected-auralinux.txt
 data/accessibility/event/live-region-change-innerhtml-expected-mac.txt
 data/accessibility/event/live-region-change-innerhtml-expected-win.txt
 data/accessibility/event/live-region-change-innerhtml.html
+data/accessibility/event/live-region-change-innertext-expected-android-exp.txt
 data/accessibility/event/live-region-change-innertext-expected-android.txt
 data/accessibility/event/live-region-change-innertext-expected-auralinux.txt
 data/accessibility/event/live-region-change-innertext-expected-mac.txt
@@ -2646,6 +2719,7 @@
 data/accessibility/event/live-region-change-on-freshly-unignored-node-expected-uia-win.txt
 data/accessibility/event/live-region-change-on-freshly-unignored-node.html
 data/accessibility/event/live-region-change.html
+data/accessibility/event/live-region-create-expected-android.txt
 data/accessibility/event/live-region-create-expected-auralinux.txt
 data/accessibility/event/live-region-create-expected-mac.txt
 data/accessibility/event/live-region-create-expected-uia-win.txt
@@ -2668,24 +2742,29 @@
 data/accessibility/event/live-region-off-expected-uia-win.txt
 data/accessibility/event/live-region-off-expected-win.txt
 data/accessibility/event/live-region-off.html
+data/accessibility/event/live-region-remove-expected-android.txt
 data/accessibility/event/live-region-remove-expected-auralinux.txt
 data/accessibility/event/live-region-remove-expected-mac.txt
 data/accessibility/event/live-region-remove-expected-win.txt
 data/accessibility/event/live-region-remove.html
+data/accessibility/event/menu-opened-closed-expected-android.txt
 data/accessibility/event/menu-opened-closed-expected-auralinux.txt
 data/accessibility/event/menu-opened-closed-expected-mac.txt
 data/accessibility/event/menu-opened-closed-expected-uia-win.txt
 data/accessibility/event/menu-opened-closed-expected-win.txt
 data/accessibility/event/menu-opened-closed.html
+data/accessibility/event/menubar-show-hide-menus-expected-android.txt
 data/accessibility/event/menubar-show-hide-menus-expected-auralinux.txt
 data/accessibility/event/menubar-show-hide-menus-expected-mac.txt
 data/accessibility/event/menubar-show-hide-menus-expected-uia-win.txt
 data/accessibility/event/menubar-show-hide-menus-expected-win.txt
 data/accessibility/event/menubar-show-hide-menus.html
+data/accessibility/event/menulist-collapse-expected-android.txt
 data/accessibility/event/menulist-collapse-expected-auralinux.txt
 data/accessibility/event/menulist-collapse-expected-mac.txt
 data/accessibility/event/menulist-collapse-expected-uia-win.txt
 data/accessibility/event/menulist-collapse-expected-win.txt
+data/accessibility/event/menulist-collapse-next-expected-android.txt
 data/accessibility/event/menulist-collapse-next-expected-auralinux.txt
 data/accessibility/event/menulist-collapse-next-expected-mac.txt
 data/accessibility/event/menulist-collapse-next-expected-uia-win.txt
@@ -2704,32 +2783,39 @@
 data/accessibility/event/menulist-custom-next-expected-mac.txt
 data/accessibility/event/menulist-custom-next-expected-win.txt
 data/accessibility/event/menulist-custom-next.html
+data/accessibility/event/menulist-expand-expected-android.txt
 data/accessibility/event/menulist-expand-expected-auralinux.txt
 data/accessibility/event/menulist-expand-expected-mac.txt
 data/accessibility/event/menulist-expand-expected-win.txt
 data/accessibility/event/menulist-expand.html
+data/accessibility/event/menulist-focus-expected-android.txt
 data/accessibility/event/menulist-focus-expected-auralinux.txt
 data/accessibility/event/menulist-focus-expected-mac.txt
 data/accessibility/event/menulist-focus-expected-uia-win.txt
 data/accessibility/event/menulist-focus-expected-win.txt
 data/accessibility/event/menulist-focus.html
+data/accessibility/event/menulist-next-expected-android.txt
 data/accessibility/event/menulist-next-expected-auralinux.txt
 data/accessibility/event/menulist-next-expected-mac.txt
 data/accessibility/event/menulist-next-expected-win.txt
 data/accessibility/event/menulist-next.html
 data/accessibility/event/menulist-with-optgroup-next-expected-auralinux.txt
 data/accessibility/event/menulist-with-optgroup-next.html
+data/accessibility/event/multiple-aria-properties-changed-expected-android.txt
 data/accessibility/event/multiple-aria-properties-changed-expected-uia-win.txt
 data/accessibility/event/multiple-aria-properties-changed.html
+data/accessibility/event/name-change-expected-android.txt
 data/accessibility/event/name-change-expected-auralinux.txt
 data/accessibility/event/name-change-expected-mac.txt
 data/accessibility/event/name-change-expected-uia-win.txt
 data/accessibility/event/name-change-expected-win.txt
+data/accessibility/event/name-change-indirect-expected-android.txt
 data/accessibility/event/name-change-indirect-expected-mac.txt
 data/accessibility/event/name-change-indirect-expected-uia-win.txt
 data/accessibility/event/name-change-indirect-expected-win.txt
 data/accessibility/event/name-change-indirect.html
 data/accessibility/event/name-change.html
+data/accessibility/event/navigation-api-expected-android.txt
 data/accessibility/event/navigation-api-expected-auralinux.txt
 data/accessibility/event/navigation-api-expected-win.txt
 data/accessibility/event/navigation-api.html
@@ -2741,14 +2827,18 @@
 data/accessibility/event/pressed-state-changed-expected-mac.txt
 data/accessibility/event/pressed-state-changed-expected-win.txt
 data/accessibility/event/pressed-state-changed.html
+data/accessibility/event/range-value-is-readonly-changed-expected-android.txt
 data/accessibility/event/range-value-is-readonly-changed-expected-auralinux.txt
 data/accessibility/event/range-value-is-readonly-changed-expected-uia-win.txt
 data/accessibility/event/range-value-is-readonly-changed-expected-win.txt
 data/accessibility/event/range-value-is-readonly-changed.html
+data/accessibility/event/range-value-maximum-changed-expected-android.txt
 data/accessibility/event/range-value-maximum-changed-expected-uia-win.txt
 data/accessibility/event/range-value-maximum-changed.html
+data/accessibility/event/range-value-minimum-changed-expected-android.txt
 data/accessibility/event/range-value-minimum-changed-expected-uia-win.txt
 data/accessibility/event/range-value-minimum-changed.html
+data/accessibility/event/range-value-step-changed-expected-android.txt
 data/accessibility/event/range-value-step-changed-expected-uia-win.txt
 data/accessibility/event/range-value-step-changed-expected-win.txt
 data/accessibility/event/range-value-step-changed.html
@@ -2757,10 +2847,12 @@
 data/accessibility/event/range-value-value-changed-expected-uia-win.txt
 data/accessibility/event/range-value-value-changed-expected-win.txt
 data/accessibility/event/range-value-value-changed.html
+data/accessibility/event/remove-child-expected-android.txt
 data/accessibility/event/remove-child-expected-auralinux.txt
 data/accessibility/event/remove-child-expected-uia-win.txt
 data/accessibility/event/remove-child-expected-win.txt
 data/accessibility/event/remove-child.html
+data/accessibility/event/remove-hidden-attribute-expected-android.txt
 data/accessibility/event/remove-hidden-attribute-expected-auralinux.txt
 data/accessibility/event/remove-hidden-attribute-expected-win.txt
 data/accessibility/event/remove-hidden-attribute-subtree-expected-android.txt
@@ -2804,11 +2896,13 @@
 data/accessibility/event/scroll-vertical-scroll-percent-change-expected-uia-win.txt
 data/accessibility/event/scroll-vertical-scroll-percent-change-expected-win.txt
 data/accessibility/event/scroll-vertical-scroll-percent-change.html
+data/accessibility/event/select-selected-add-remove-expected-android.txt
 data/accessibility/event/select-selected-add-remove-expected-auralinux.txt
 data/accessibility/event/select-selected-add-remove-expected-mac.txt
 data/accessibility/event/select-selected-add-remove-expected-uia-win.txt
 data/accessibility/event/select-selected-add-remove-expected-win.txt
 data/accessibility/event/select-selected-add-remove.html
+data/accessibility/event/style-changed-expected-android.txt
 data/accessibility/event/style-changed-expected-auralinux.txt
 data/accessibility/event/style-changed-expected-uia-win.txt
 data/accessibility/event/style-changed-expected-win.txt
@@ -2829,33 +2923,42 @@
 data/accessibility/event/subtree-reparented-via-aria-owns-expected-mac.txt
 data/accessibility/event/subtree-reparented-via-aria-owns-expected-win.txt
 data/accessibility/event/subtree-reparented-via-aria-owns.html
+data/accessibility/event/tabindex-added-on-aria-hidden-expected-android.txt
 data/accessibility/event/tabindex-added-on-aria-hidden-expected-auralinux.txt
 data/accessibility/event/tabindex-added-on-aria-hidden-expected-win.txt
 data/accessibility/event/tabindex-added-on-aria-hidden.html
+data/accessibility/event/tabindex-added-on-plain-div-expected-android.txt
 data/accessibility/event/tabindex-added-on-plain-div-expected-auralinux.txt
 data/accessibility/event/tabindex-added-on-plain-div-expected-win.txt
 data/accessibility/event/tabindex-added-on-plain-div.html
+data/accessibility/event/tabindex-removed-on-aria-hidden-expected-android.txt
 data/accessibility/event/tabindex-removed-on-aria-hidden-expected-win.txt
 data/accessibility/event/tabindex-removed-on-aria-hidden.html
+data/accessibility/event/tabindex-removed-on-plain-div-expected-android.txt
 data/accessibility/event/tabindex-removed-on-plain-div-expected-auralinux.txt
 data/accessibility/event/tabindex-removed-on-plain-div-expected-win.txt
 data/accessibility/event/tabindex-removed-on-plain-div.html
+data/accessibility/event/tbody-focus-expected-android.txt
 data/accessibility/event/tbody-focus-expected-auralinux.txt
 data/accessibility/event/tbody-focus-expected-mac.txt
 data/accessibility/event/tbody-focus-expected-uia-win.txt
 data/accessibility/event/tbody-focus-expected-win.txt
 data/accessibility/event/tbody-focus.html
+data/accessibility/event/text-align-changed-expected-android.txt
 data/accessibility/event/text-align-changed-expected-auralinux.txt
 data/accessibility/event/text-align-changed-expected-win.txt
 data/accessibility/event/text-align-changed.html
+data/accessibility/event/text-changed-contenteditable-expected-android.txt
 data/accessibility/event/text-changed-contenteditable-expected-auralinux.txt
 data/accessibility/event/text-changed-contenteditable-expected-uia-win.txt
 data/accessibility/event/text-changed-contenteditable-expected-win.txt
 data/accessibility/event/text-changed-contenteditable.html
+data/accessibility/event/text-changed-expected-android.txt
 data/accessibility/event/text-changed-expected-auralinux.txt
 data/accessibility/event/text-changed-expected-uia-win.txt
 data/accessibility/event/text-changed-expected-win.txt
 data/accessibility/event/text-changed.html
+data/accessibility/event/text-indent-changed-expected-android.txt
 data/accessibility/event/text-indent-changed-expected-auralinux.txt
 data/accessibility/event/text-indent-changed-expected-win.txt
 data/accessibility/event/text-indent-changed.html
@@ -2870,23 +2973,28 @@
 data/accessibility/event/text-selection-inside-video-expected-android.txt
 data/accessibility/event/text-selection-inside-video-expected-auralinux.txt
 data/accessibility/event/text-selection-inside-video.html
+data/accessibility/event/tfoot-focus-expected-android.txt
 data/accessibility/event/tfoot-focus-expected-auralinux.txt
 data/accessibility/event/tfoot-focus-expected-mac.txt
 data/accessibility/event/tfoot-focus-expected-uia-win.txt
 data/accessibility/event/tfoot-focus-expected-win.txt
 data/accessibility/event/tfoot-focus.html
+data/accessibility/event/thead-focus-expected-android.txt
 data/accessibility/event/thead-focus-expected-auralinux.txt
 data/accessibility/event/thead-focus-expected-mac.txt
 data/accessibility/event/thead-focus-expected-uia-win.txt
 data/accessibility/event/thead-focus-expected-win.txt
 data/accessibility/event/thead-focus.html
+data/accessibility/event/value-is-readonly-changed-expected-android.txt
 data/accessibility/event/value-is-readonly-changed-expected-auralinux.txt
 data/accessibility/event/value-is-readonly-changed-expected-uia-win.txt
 data/accessibility/event/value-is-readonly-changed-expected-win.txt
 data/accessibility/event/value-is-readonly-changed.html
+data/accessibility/event/value-value-changed-expected-android.txt
 data/accessibility/event/value-value-changed-expected-uia-win.txt
 data/accessibility/event/value-value-changed-expected-win.txt
 data/accessibility/event/value-value-changed.html
+data/accessibility/event/visibility-hidden-changed-expected-android.txt
 data/accessibility/event/visibility-hidden-changed-expected-auralinux.txt
 data/accessibility/event/visibility-hidden-changed-expected-uia-win.txt
 data/accessibility/event/visibility-hidden-changed.html
diff --git a/content/test/data/accessibility/event/add-alert-content-expected-android-exp.txt b/content/test/data/accessibility/event/add-alert-content-expected-android-exp.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/add-alert-content-expected-android-exp.txt
diff --git a/content/test/data/accessibility/event/add-child-expected-android.txt b/content/test/data/accessibility/event/add-child-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/add-child-expected-android.txt
diff --git a/content/test/data/accessibility/event/add-child-of-body-expected-android.txt b/content/test/data/accessibility/event/add-child-of-body-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/add-child-of-body-expected-android.txt
diff --git a/content/test/data/accessibility/event/add-hidden-attribute-expected-android.txt b/content/test/data/accessibility/event/add-hidden-attribute-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/add-hidden-attribute-expected-android.txt
diff --git a/content/test/data/accessibility/event/anonymous-block-children-changed-expected-android.txt b/content/test/data/accessibility/event/anonymous-block-children-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/anonymous-block-children-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-atomic-changed-expected-android.txt b/content/test/data/accessibility/event/aria-atomic-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-atomic-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-atomic-changed2-expected-android.txt b/content/test/data/accessibility/event/aria-atomic-changed2-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-atomic-changed2-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-busy-changed-expected-android.txt b/content/test/data/accessibility/event/aria-busy-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-busy-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-button-expand-expected-android.txt b/content/test/data/accessibility/event/aria-button-expand-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-button-expand-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-combo-box-uneditable-expected-android.txt b/content/test/data/accessibility/event/aria-combo-box-uneditable-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-combo-box-uneditable-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-controls-changed-expected-android.txt b/content/test/data/accessibility/event/aria-controls-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-controls-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-current-changed-expected-android.txt b/content/test/data/accessibility/event/aria-current-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-current-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-disabled-changed-expected-android.txt b/content/test/data/accessibility/event/aria-disabled-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-disabled-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-flow-to-expected-android.txt b/content/test/data/accessibility/event/aria-flow-to-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-flow-to-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-haspopup-changed-expected-android.txt b/content/test/data/accessibility/event/aria-haspopup-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-haspopup-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-hidden-changed-expected-android.txt b/content/test/data/accessibility/event/aria-hidden-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-hidden-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-hidden-descendants-already-ignored-expected-android.txt b/content/test/data/accessibility/event/aria-hidden-descendants-already-ignored-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-hidden-descendants-already-ignored-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-hidden-descendants-expected-android.txt b/content/test/data/accessibility/event/aria-hidden-descendants-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-hidden-descendants-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-hidden-single-descendant-display-none-expected-android.txt b/content/test/data/accessibility/event/aria-hidden-single-descendant-display-none-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-hidden-single-descendant-display-none-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-hidden-single-descendant-expected-android.txt b/content/test/data/accessibility/event/aria-hidden-single-descendant-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-hidden-single-descendant-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-android.txt b/content/test/data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-hidden-single-descendant-visibility-hidden-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-level-changed-expected-android.txt b/content/test/data/accessibility/event/aria-level-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-level-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-live-changed-expected-android.txt b/content/test/data/accessibility/event/aria-live-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-live-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-menuitem-focus-expected-android.txt b/content/test/data/accessibility/event/aria-menuitem-focus-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-menuitem-focus-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-multiline-changed-expected-android.txt b/content/test/data/accessibility/event/aria-multiline-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-multiline-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-multiselectable-changed-expected-android.txt b/content/test/data/accessibility/event/aria-multiselectable-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-multiselectable-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-posinset-changed-expected-android.txt b/content/test/data/accessibility/event/aria-posinset-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-posinset-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-readonly-changed-expected-android.txt b/content/test/data/accessibility/event/aria-readonly-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-readonly-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-relevant-changed-expected-android.txt b/content/test/data/accessibility/event/aria-relevant-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-relevant-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-relevant-changed2-expected-android.txt b/content/test/data/accessibility/event/aria-relevant-changed2-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-relevant-changed2-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-required-changed-expected-android.txt b/content/test/data/accessibility/event/aria-required-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-required-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-selected-changed-new-subtree-expected-android.txt b/content/test/data/accessibility/event/aria-selected-changed-new-subtree-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-selected-changed-new-subtree-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-setsize-changed-expected-android.txt b/content/test/data/accessibility/event/aria-setsize-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-setsize-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-sort-changed-expected-android.txt b/content/test/data/accessibility/event/aria-sort-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-sort-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-spinbutton-value-both-change-expected-android.txt b/content/test/data/accessibility/event/aria-spinbutton-value-both-change-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-spinbutton-value-both-change-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-spinbutton-value-change-expected-android.txt b/content/test/data/accessibility/event/aria-spinbutton-value-change-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-spinbutton-value-change-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-spinbutton-valuetext-change-expected-android.txt b/content/test/data/accessibility/event/aria-spinbutton-valuetext-change-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-spinbutton-valuetext-change-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-textbox-children-change-expected-android.txt b/content/test/data/accessibility/event/aria-textbox-children-change-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-textbox-children-change-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-textbox-editability-changes-expected-android.txt b/content/test/data/accessibility/event/aria-textbox-editability-changes-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-textbox-editability-changes-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-textbox-with-focusable-children-expected-android.txt b/content/test/data/accessibility/event/aria-textbox-with-focusable-children-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-textbox-with-focusable-children-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-tree-collapse-expected-android.txt b/content/test/data/accessibility/event/aria-tree-collapse-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-tree-collapse-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-tree-expand-expected-android.txt b/content/test/data/accessibility/event/aria-tree-expand-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-tree-expand-expected-android.txt
diff --git a/content/test/data/accessibility/event/aria-treeitem-focus-expected-android.txt b/content/test/data/accessibility/event/aria-treeitem-focus-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-treeitem-focus-expected-android.txt
diff --git a/content/test/data/accessibility/event/button-remove-children-expected-android.txt b/content/test/data/accessibility/event/button-remove-children-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/button-remove-children-expected-android.txt
diff --git a/content/test/data/accessibility/event/checkbox-validity-expected-android.txt b/content/test/data/accessibility/event/checkbox-validity-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/checkbox-validity-expected-android.txt
diff --git a/content/test/data/accessibility/event/checked-mixed-changed-expected-android.txt b/content/test/data/accessibility/event/checked-mixed-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/checked-mixed-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/checked-state-changed-expected-android.txt b/content/test/data/accessibility/event/checked-state-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/checked-state-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-android.txt b/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/children-changed-only-on-ancestor-expected-android.txt
diff --git a/content/test/data/accessibility/event/css-display-descendants-expected-android.txt b/content/test/data/accessibility/event/css-display-descendants-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/css-display-descendants-expected-android.txt
diff --git a/content/test/data/accessibility/event/css-flex-text-update-expected-android.txt b/content/test/data/accessibility/event/css-flex-text-update-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/css-flex-text-update-expected-android.txt
diff --git a/content/test/data/accessibility/event/css-visibility-collapse-expected-android.txt b/content/test/data/accessibility/event/css-visibility-collapse-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/css-visibility-collapse-expected-android.txt
diff --git a/content/test/data/accessibility/event/css-visibility-descendants-expected-android.txt b/content/test/data/accessibility/event/css-visibility-descendants-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/css-visibility-descendants-expected-android.txt
diff --git a/content/test/data/accessibility/event/css-visibility-expected-android.txt b/content/test/data/accessibility/event/css-visibility-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/css-visibility-expected-android.txt
diff --git a/content/test/data/accessibility/event/description-change-expected-android.txt b/content/test/data/accessibility/event/description-change-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/description-change-expected-android.txt
diff --git a/content/test/data/accessibility/event/description-change-indirect-expected-android.txt b/content/test/data/accessibility/event/description-change-indirect-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/description-change-indirect-expected-android.txt
diff --git a/content/test/data/accessibility/event/description-change-no-relation-expected-android.txt b/content/test/data/accessibility/event/description-change-no-relation-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/description-change-no-relation-expected-android.txt
diff --git a/content/test/data/accessibility/event/disabled-state-changed-expected-android.txt b/content/test/data/accessibility/event/disabled-state-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/disabled-state-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/document-title-change-expected-android.txt b/content/test/data/accessibility/event/document-title-change-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/document-title-change-expected-android.txt
diff --git a/content/test/data/accessibility/event/expanded-changed-expected-android.txt b/content/test/data/accessibility/event/expanded-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/expanded-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/focus-listbox-expected-android.txt b/content/test/data/accessibility/event/focus-listbox-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/focus-listbox-expected-android.txt
diff --git a/content/test/data/accessibility/event/focus-listbox-multiselect-expected-android.txt b/content/test/data/accessibility/event/focus-listbox-multiselect-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/focus-listbox-multiselect-expected-android.txt
diff --git a/content/test/data/accessibility/event/form-disabled-changed-expected-android.txt b/content/test/data/accessibility/event/form-disabled-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/form-disabled-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/iframe-src-changed-expected-android.txt b/content/test/data/accessibility/event/iframe-src-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/iframe-src-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/immediate-refresh-expected-android.txt b/content/test/data/accessibility/event/immediate-refresh-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/immediate-refresh-expected-android.txt
diff --git a/content/test/data/accessibility/event/inner-html-change-expected-android.txt b/content/test/data/accessibility/event/inner-html-change-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/inner-html-change-expected-android.txt
diff --git a/content/test/data/accessibility/event/input-type-text-value-changed-expected-android.txt b/content/test/data/accessibility/event/input-type-text-value-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/input-type-text-value-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/listbox-focus-expected-android.txt b/content/test/data/accessibility/event/listbox-focus-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/listbox-focus-expected-android.txt
diff --git a/content/test/data/accessibility/event/listbox-next-expected-android.txt b/content/test/data/accessibility/event/listbox-next-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/listbox-next-expected-android.txt
diff --git a/content/test/data/accessibility/event/live-region-add-expected-android-exp.txt b/content/test/data/accessibility/event/live-region-add-expected-android-exp.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/live-region-add-expected-android-exp.txt
diff --git a/content/test/data/accessibility/event/live-region-add-live-attribute-expected-android.txt b/content/test/data/accessibility/event/live-region-add-live-attribute-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/live-region-add-live-attribute-expected-android.txt
diff --git a/content/test/data/accessibility/event/live-region-change-expected-android-exp.txt b/content/test/data/accessibility/event/live-region-change-expected-android-exp.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/live-region-change-expected-android-exp.txt
diff --git a/content/test/data/accessibility/event/live-region-change-innerhtml-expected-android-exp.txt b/content/test/data/accessibility/event/live-region-change-innerhtml-expected-android-exp.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/live-region-change-innerhtml-expected-android-exp.txt
diff --git a/content/test/data/accessibility/event/live-region-change-innertext-expected-android-exp.txt b/content/test/data/accessibility/event/live-region-change-innertext-expected-android-exp.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/live-region-change-innertext-expected-android-exp.txt
diff --git a/content/test/data/accessibility/event/live-region-create-expected-android.txt b/content/test/data/accessibility/event/live-region-create-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/live-region-create-expected-android.txt
diff --git a/content/test/data/accessibility/event/live-region-remove-expected-android.txt b/content/test/data/accessibility/event/live-region-remove-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/live-region-remove-expected-android.txt
diff --git a/content/test/data/accessibility/event/menu-opened-closed-expected-android.txt b/content/test/data/accessibility/event/menu-opened-closed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/menu-opened-closed-expected-android.txt
diff --git a/content/test/data/accessibility/event/menubar-show-hide-menus-expected-android.txt b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/menubar-show-hide-menus-expected-android.txt
diff --git a/content/test/data/accessibility/event/menulist-collapse-expected-android.txt b/content/test/data/accessibility/event/menulist-collapse-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/menulist-collapse-expected-android.txt
diff --git a/content/test/data/accessibility/event/menulist-collapse-next-expected-android.txt b/content/test/data/accessibility/event/menulist-collapse-next-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/menulist-collapse-next-expected-android.txt
diff --git a/content/test/data/accessibility/event/menulist-expand-expected-android.txt b/content/test/data/accessibility/event/menulist-expand-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/menulist-expand-expected-android.txt
diff --git a/content/test/data/accessibility/event/menulist-focus-expected-android.txt b/content/test/data/accessibility/event/menulist-focus-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/menulist-focus-expected-android.txt
diff --git a/content/test/data/accessibility/event/menulist-next-expected-android.txt b/content/test/data/accessibility/event/menulist-next-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/menulist-next-expected-android.txt
diff --git a/content/test/data/accessibility/event/multiple-aria-properties-changed-expected-android.txt b/content/test/data/accessibility/event/multiple-aria-properties-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/multiple-aria-properties-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/name-change-expected-android.txt b/content/test/data/accessibility/event/name-change-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/name-change-expected-android.txt
diff --git a/content/test/data/accessibility/event/name-change-indirect-expected-android.txt b/content/test/data/accessibility/event/name-change-indirect-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/name-change-indirect-expected-android.txt
diff --git a/content/test/data/accessibility/event/navigation-api-expected-android.txt b/content/test/data/accessibility/event/navigation-api-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/navigation-api-expected-android.txt
diff --git a/content/test/data/accessibility/event/range-value-is-readonly-changed-expected-android.txt b/content/test/data/accessibility/event/range-value-is-readonly-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/range-value-is-readonly-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/range-value-maximum-changed-expected-android.txt b/content/test/data/accessibility/event/range-value-maximum-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/range-value-maximum-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/range-value-minimum-changed-expected-android.txt b/content/test/data/accessibility/event/range-value-minimum-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/range-value-minimum-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/range-value-step-changed-expected-android.txt b/content/test/data/accessibility/event/range-value-step-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/range-value-step-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/remove-child-expected-android.txt b/content/test/data/accessibility/event/remove-child-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/remove-child-expected-android.txt
diff --git a/content/test/data/accessibility/event/remove-hidden-attribute-expected-android.txt b/content/test/data/accessibility/event/remove-hidden-attribute-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/remove-hidden-attribute-expected-android.txt
diff --git a/content/test/data/accessibility/event/select-selected-add-remove-expected-android.txt b/content/test/data/accessibility/event/select-selected-add-remove-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/select-selected-add-remove-expected-android.txt
diff --git a/content/test/data/accessibility/event/style-changed-expected-android.txt b/content/test/data/accessibility/event/style-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/style-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/tabindex-added-on-aria-hidden-expected-android.txt b/content/test/data/accessibility/event/tabindex-added-on-aria-hidden-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/tabindex-added-on-aria-hidden-expected-android.txt
diff --git a/content/test/data/accessibility/event/tabindex-added-on-plain-div-expected-android.txt b/content/test/data/accessibility/event/tabindex-added-on-plain-div-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/tabindex-added-on-plain-div-expected-android.txt
diff --git a/content/test/data/accessibility/event/tabindex-removed-on-aria-hidden-expected-android.txt b/content/test/data/accessibility/event/tabindex-removed-on-aria-hidden-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/tabindex-removed-on-aria-hidden-expected-android.txt
diff --git a/content/test/data/accessibility/event/tabindex-removed-on-plain-div-expected-android.txt b/content/test/data/accessibility/event/tabindex-removed-on-plain-div-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/tabindex-removed-on-plain-div-expected-android.txt
diff --git a/content/test/data/accessibility/event/tbody-focus-expected-android.txt b/content/test/data/accessibility/event/tbody-focus-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/tbody-focus-expected-android.txt
diff --git a/content/test/data/accessibility/event/text-align-changed-expected-android.txt b/content/test/data/accessibility/event/text-align-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/text-align-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/text-changed-contenteditable-expected-android.txt b/content/test/data/accessibility/event/text-changed-contenteditable-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/text-changed-contenteditable-expected-android.txt
diff --git a/content/test/data/accessibility/event/text-changed-expected-android.txt b/content/test/data/accessibility/event/text-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/text-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/text-indent-changed-expected-android.txt b/content/test/data/accessibility/event/text-indent-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/text-indent-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/tfoot-focus-expected-android.txt b/content/test/data/accessibility/event/tfoot-focus-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/tfoot-focus-expected-android.txt
diff --git a/content/test/data/accessibility/event/thead-focus-expected-android.txt b/content/test/data/accessibility/event/thead-focus-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/thead-focus-expected-android.txt
diff --git a/content/test/data/accessibility/event/value-is-readonly-changed-expected-android.txt b/content/test/data/accessibility/event/value-is-readonly-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/value-is-readonly-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/value-value-changed-expected-android.txt b/content/test/data/accessibility/event/value-value-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/value-value-changed-expected-android.txt
diff --git a/content/test/data/accessibility/event/visibility-hidden-changed-expected-android.txt b/content/test/data/accessibility/event/visibility-hidden-changed-expected-android.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/content/test/data/accessibility/event/visibility-hidden-changed-expected-android.txt
diff --git a/docs/experiments/prompt-api-for-extension.md b/docs/experiments/prompt-api-for-extension.md
index af1eafb..c75eb93 100644
--- a/docs/experiments/prompt-api-for-extension.md
+++ b/docs/experiments/prompt-api-for-extension.md
@@ -1,27 +1,18 @@
 # Prompt API for extension
 
-- Contact: builtin-ai@chromium.org
-
 This document describes the status of the current implementation of the
-[**Prompt API**](https://github.com/explainers-by-googlers/prompt-api)
-for Chrome extension, and how to verify.
-
-Note that this API is not available by default. Chrome plans to do an
-extension origin trial starting from M131 to evaluate its effectiveness
-and to allow extension authors to give feedback.
-
+[**Prompt API**](https://github.com/webmachinelearning/prompt-api) for Chrome
+extensions, and how to verify.
 
 ## What’s supported
 
-The API is implemented according to the
-[explainer](https://github.com/explainers-by-googlers/prompt-api), but the
-API namespace will be under `chrome.aiOriginTrial.languageModel` for the
-extension origin trial.
+The implementation generally intends to follow the
+[explainer](https://github.com/explainers-by-googlers/prompt-api).
 
 ## Activation
 
 The API can be enabled by participating in the
-[extension origin trial](https://developer.chrome.com/docs/web-platform/origin-trials#extensions)
+[extension origin trial](https://developer.chrome.com/blog/prompt-api-origin-trial)
 named `AIPromptAPIForExtension`. After obtaining the trial token, the
 extension authors need to configure it in the `manifest.json` together with
 the `aiLanguageModelOriginTrial` permission.
@@ -31,20 +22,21 @@
   "permissions": ["aiLanguageModelOriginTrial"],
   "trial_tokens": [<GENERATED_TOKEN>],
 }
-
 ```
 
 ## Verifying the API is working
 
-The extension authors can verify if the API is available by checking the
-`chrome.aiOriginTrial.languageModel` from the service worker script.
-If the `AILanguageModel` object is defined, the authors can follow the
-[explainer](https://github.com/explainers-by-googlers/prompt-api) to test
-the APIs usage.
+The extension authors can verify if the API is available by checking for the
+presence of `LanguageModel` or `chrome.aiOriginTrial.languageModel` entrypoints
+from extension window and worker scripts. If the object is defined, the authors
+can follow the [explainer](https://github.com/explainers-by-googlers/prompt-api)
+and [developer docs](https://developer.chrome.com/docs/extensions/ai/prompt-api)
+to check availability and test the APIs usage.
 
 ## Related Links
 
-- [Prompt API Explainer on GitHub](https://github.com/explainers-by-googlers/prompt-api)
-- [Reporting bugs](https://g-issues.chromium.org/issues/new?component=1583624&priority=P2&type=feature_request&template=0&noWizard=true)
-- [API Feedback](https://github.com/explainers-by-googlers/prompt-api/issues)
-<!-- TODO: link the DevRel doc with more details once it's published -->
+- [Explainer on GitHub](https://github.com/webmachinelearning/prompt-api)
+- [API feedback](https://github.com/webmachinelearning/prompt-api/issues)
+- [Reporting bugs](https://issues.chromium.org/issues/new?component=1583624)
+- [Extension origin trial](https://developer.chrome.com/blog/prompt-api-origin-trial)
+- [Developer docs](https://developer.chrome.com/docs/extensions/ai/prompt-api)
diff --git a/extensions/browser/sandboxed_unpacker_unittest.cc b/extensions/browser/sandboxed_unpacker_unittest.cc
index b13617b..394cc3d 100644
--- a/extensions/browser/sandboxed_unpacker_unittest.cc
+++ b/extensions/browser/sandboxed_unpacker_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
+#include "base/json/json_reader.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/path_service.h"
@@ -509,10 +510,17 @@
   // Check that there is no _locales folder.
   base::FilePath install_path = GetInstallPath().Append(kLocaleFolder);
   EXPECT_FALSE(base::PathExists(install_path));
-  EXPECT_TRUE(base::MatchPattern(GetInstallErrorMessage(),
-                                 u"*_locales?en_US?messages.json': EOF while "
-                                 u"parsing a string at line 4*"))
-      << GetInstallErrorMessage();
+  if (base::JSONReader::UsingRust()) {
+    EXPECT_TRUE(base::MatchPattern(GetInstallErrorMessage(),
+                                   u"*_locales?en_US?messages.json': EOF while "
+                                   u"parsing a string at line 4*"))
+        << GetInstallErrorMessage();
+  } else {
+    EXPECT_TRUE(base::MatchPattern(
+        GetInstallErrorMessage(),
+        u"*_locales?en_US?messages.json': Line: 4, column: 1,*"))
+        << GetInstallErrorMessage();
+  }
   ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
             GetInstallErrorType());
   EXPECT_EQ(static_cast<int>(
@@ -558,6 +566,21 @@
             GetInstallErrorDetail());
 }
 
+TEST_F(SandboxedUnpackerTest, JsonParserFails) {
+  // Disable the Rust JSON parser, as it is in-process and cannot crash.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatureState(base::features::kUseRustJsonParser, false);
+
+  in_process_data_decoder().SimulateJsonParserCrash(true);
+  InitSandboxedUnpacker();
+
+  SetupUnpacker("good_package.crx", "");
+  EXPECT_FALSE(InstallSucceeded());
+  EXPECT_FALSE(GetInstallErrorMessage().empty());
+  ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
+            GetInstallErrorType());
+}
+
 TEST_F(SandboxedUnpackerTest, ImageDecoderFails) {
   in_process_data_decoder().SimulateImageDecoderCrash(true);
   InitSandboxedUnpacker();
diff --git a/extensions/browser/service_worker/service_worker_task_queue.cc b/extensions/browser/service_worker/service_worker_task_queue.cc
index 3cc60628..49ea2de 100644
--- a/extensions/browser/service_worker/service_worker_task_queue.cc
+++ b/extensions/browser/service_worker/service_worker_task_queue.cc
@@ -95,14 +95,14 @@
     DCHECK(!process_manager->HasServiceWorker(*worker_id_) ||
            g_allow_multiple_workers_per_extension);
     // Clear stale renderer state if there's any.
-    renderer_state_ = RendererState::kInitial;
+    renderer_state_ = RendererState::kNotActive;
   }
   worker_id_ = worker_id;
 }
 
 bool ServiceWorkerTaskQueue::WorkerState::ready() const {
   return browser_state_ == BrowserState::kStarted &&
-         renderer_state_ == RendererState::kStarted && worker_id_.has_value();
+         renderer_state_ == RendererState::kActive && worker_id_.has_value();
 }
 
 ServiceWorkerTaskQueue::TestObserver::TestObserver() = default;
@@ -298,10 +298,10 @@
   //
   // TODO(lazyboy): Update the renderer state in RenderProcessExited() and
   // uncomment the following DCHECK:
-  // DCHECK_NE(RendererState::kStarted, worker_state->renderer_state_)
+  // DCHECK_NE(RendererState::kActive, worker_state->renderer_state_)
   //    << "Worker already started";
   worker_state->SetWorkerId(worker_id, ProcessManager::Get(browser_context_));
-  worker_state->SetRendererState(RendererState::kStarted);
+  worker_state->SetRendererState(RendererState::kActive);
 
   RunPendingTasksIfWorkerReady(context_id);
 }
@@ -334,8 +334,8 @@
     return;
   }
 
-  DCHECK_NE(RendererState::kStopped, worker_state->renderer_state());
-  worker_state->SetRendererState(RendererState::kStopped);
+  DCHECK_NE(RendererState::kNotActive, worker_state->renderer_state());
+  worker_state->SetRendererState(RendererState::kNotActive);
   worker_state->ResetWorkerId();
 
   if (g_test_observer) {
@@ -405,7 +405,7 @@
   if (worker_state->browser_state() != BrowserState::kReady) {
     return false;
   }
-  if (worker_state->renderer_state() != RendererState::kStarted) {
+  if (worker_state->renderer_state() != RendererState::kActive) {
     return false;
   }
 
@@ -554,7 +554,7 @@
   // receive tasks/events again and the renderer stop notifications are not 100%
   // reliable.
   worker_state->SetBrowserState(BrowserState::kInitial);
-  worker_state->SetRendererState(RendererState::kInitial);
+  worker_state->SetRendererState(RendererState::kNotActive);
   worker_state->ResetWorkerId();
 }
 
diff --git a/extensions/browser/service_worker/service_worker_task_queue.h b/extensions/browser/service_worker/service_worker_task_queue.h
index 08f6ff7..f1d667be 100644
--- a/extensions/browser/service_worker/service_worker_task_queue.h
+++ b/extensions/browser/service_worker/service_worker_task_queue.h
@@ -168,12 +168,10 @@
 
   // Render process worker state of an activated extension.
   enum class RendererState {
-    // Initial state, neither started nor stopped.
-    kInitial,
-    // Worker thread has started.
-    kStarted,
-    // Worker thread has not started or has been stopped.
-    kStopped,
+    // Worker thread has not started or has been stopped/terminated.
+    kNotActive,
+    // Worker thread has started and it's running.
+    kActive,
   };
 
   // The current worker related state of an activated extension.
@@ -204,7 +202,7 @@
 
    private:
     BrowserState browser_state_ = BrowserState::kInitial;
-    RendererState renderer_state_ = RendererState::kInitial;
+    RendererState renderer_state_ = RendererState::kNotActive;
 
     // Contains the worker's WorkerId associated with this WorkerState, once we
     // have discovered info about the worker.
diff --git a/extensions/common/extension_l10n_util_unittest.cc b/extensions/common/extension_l10n_util_unittest.cc
index 1210f71..7792f41 100644
--- a/extensions/common/extension_l10n_util_unittest.cc
+++ b/extensions/common/extension_l10n_util_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/json/json_reader.h"
 #include "base/path_service.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
@@ -105,10 +106,19 @@
                          errors::kLocalesInvalidLocale,
                          base::UTF16ToUTF8(de_messages_file.LossyDisplayName()),
                          "Variable $VAR$ used but not defined.")));
-  EXPECT_THAT(error, testing::HasSubstr(ErrorUtils::FormatErrorMessage(
-                         errors::kLocalesInvalidLocale,
-                         base::UTF16ToUTF8(es_messages_file.LossyDisplayName()),
-                         "expected value at line 1 column 24")));
+  if (base::JSONReader::UsingRust()) {
+    EXPECT_THAT(error,
+                testing::HasSubstr(ErrorUtils::FormatErrorMessage(
+                    errors::kLocalesInvalidLocale,
+                    base::UTF16ToUTF8(es_messages_file.LossyDisplayName()),
+                    "expected value at line 1 column 24")));
+  } else {
+    EXPECT_THAT(error,
+                testing::HasSubstr(ErrorUtils::FormatErrorMessage(
+                    errors::kLocalesInvalidLocale,
+                    base::UTF16ToUTF8(es_messages_file.LossyDisplayName()),
+                    "Line: 1, column: 24, Unexpected token.")));
+  }
   EXPECT_THAT(error, testing::HasSubstr(ErrorUtils::FormatErrorMessage(
                          errors::kLocalesInvalidLocale,
                          base::UTF16ToUTF8(fr_messages_file.LossyDisplayName()),
@@ -267,11 +277,19 @@
   std::string error;
   EXPECT_FALSE(extension_l10n_util::LoadMessageCatalogs(
       src_path, "en_US", GzippedMessagesPermission::kDisallow, &error));
-  EXPECT_NE(std::string::npos,
-            error.find(ErrorUtils::FormatErrorMessage(
-                errors::kLocalesInvalidLocale,
-                base::UTF16ToUTF8(messages_file.LossyDisplayName()),
-                "EOF while parsing a value at line 1 column 9")));
+  if (base::JSONReader::UsingRust()) {
+    EXPECT_NE(std::string::npos,
+              error.find(ErrorUtils::FormatErrorMessage(
+                  errors::kLocalesInvalidLocale,
+                  base::UTF16ToUTF8(messages_file.LossyDisplayName()),
+                  "EOF while parsing a value at line 1 column 9")));
+  } else {
+    EXPECT_NE(std::string::npos,
+              error.find(ErrorUtils::FormatErrorMessage(
+                  errors::kLocalesInvalidLocale,
+                  base::UTF16ToUTF8(messages_file.LossyDisplayName()),
+                  "Line: 1, column: 10,")));
+  }
 }
 
 TEST(ExtensionL10nUtil, LoadMessageCatalogsDuplicateKeys) {
diff --git a/extensions/common/file_util_unittest.cc b/extensions/common/file_util_unittest.cc
index c9b390b..a590e3c 100644
--- a/extensions/common/file_util_unittest.cc
+++ b/extensions/common/file_util_unittest.cc
@@ -442,10 +442,16 @@
       install_dir, ManifestLocation::kUnpacked, Extension::NO_FLAGS, &error));
   ASSERT_TRUE(extension.get() == nullptr);
   ASSERT_FALSE(error.empty());
-  ASSERT_NE(
-      std::string::npos,
-      error.find(manifest_errors::kManifestParseError +
-                 std::string("  expected `,` or `}` at line 2 column 16")));
+  if (base::JSONReader::UsingRust()) {
+    ASSERT_NE(
+        std::string::npos,
+        error.find(manifest_errors::kManifestParseError +
+                   std::string("  expected `,` or `}` at line 2 column 16")));
+  } else {
+    ASSERT_NE(std::string::npos,
+              error.find(manifest_errors::kManifestParseError +
+                         std::string("  Line: 2, column: 16,")));
+  }
 }
 
 TEST_F(FileUtilTest, ValidateThemeUTF8) {
diff --git a/extensions/common/user_scripts_allowed_state.h b/extensions/common/user_scripts_allowed_state.h
index cd45923..c257379 100644
--- a/extensions/common/user_scripts_allowed_state.h
+++ b/extensions/common/user_scripts_allowed_state.h
@@ -5,6 +5,8 @@
 #ifndef EXTENSIONS_COMMON_USER_SCRIPTS_ALLOWED_STATE_H_
 #define EXTENSIONS_COMMON_USER_SCRIPTS_ALLOWED_STATE_H_
 
+#include <optional>
+
 #include "extensions/common/extension_id.h"
 
 namespace extensions {
diff --git a/extensions/renderer/native_extension_bindings_system.cc b/extensions/renderer/native_extension_bindings_system.cc
index f5ef0b9a..cc4f6517 100644
--- a/extensions/renderer/native_extension_bindings_system.cc
+++ b/extensions/renderer/native_extension_bindings_system.cc
@@ -87,6 +87,7 @@
 
 constexpr char kStringNameAIOriginTrial[] = "aiOriginTrial";
 constexpr char kStringNameLanguageModel[] = "languageModel";
+constexpr char kStringNameGlobalLanguageModel[] = "LanguageModel";
 
 // Returns true if the given |api| is a "prefixed" api of the |root_api|; that
 // is, if the api begins with the root.
@@ -1165,9 +1166,8 @@
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> v8_context = context->v8_context();
 
-  // If the extension has requested for `kAILanguageModelOriginTrial`
-  // permission, we will set the `chrome.aiOriginTrial.languageModel` as a
-  // mirror of `self.ai.languageModel`.
+  // Extensions with `kAILanguageModelOriginTrial` permission may access global
+  // `chrome.aiOriginTrial.languageModel` and `LanguageModel` entrypoints.
   if (!context->extension() ||
       !context->extension()
            ->permissions_data()
@@ -1195,6 +1195,21 @@
   success = chrome_ai_object->CreateDataProperty(
       v8_context, language_model_name, language_model_value);
   CHECK(success.IsJust() && success.FromJust());
+
+  // Also ensure extensions with an OT token and permission support the
+  // experimental Web API's `LanguageModel` entrypoint, even when Web-specific
+  // flags aren't enabled. This approach should be simplified through API IDL
+  // extended attributes, as Web and Extension configuration states are aligned,
+  // and bindings for aiOriginTrial are aligned with other Built-In AI APIs.
+  v8::Local<v8::String> global_language_model_name =
+      gin::StringToSymbol(isolate, kStringNameGlobalLanguageModel);
+  if (!v8_context->Global()
+           ->HasRealNamedProperty(v8_context, global_language_model_name)
+           .FromJust()) {
+    success = v8_context->Global()->CreateDataProperty(
+        v8_context, global_language_model_name, language_model_value);
+    CHECK(success.IsJust() && success.FromJust());
+  }
 }
 
 }  // namespace extensions
diff --git a/internal b/internal
index 4c4da7a..f968a67 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 4c4da7a117aa9100f7a27e9f37a7e92b4bfcde60
+Subproject commit f968a67f8ed26827ecdbf84d5cb239dc600692f8
diff --git a/media/base/win/d3d11_mocks.cc b/media/base/win/d3d11_mocks.cc
index a57f9707..5247e122a 100644
--- a/media/base/win/d3d11_mocks.cc
+++ b/media/base/win/d3d11_mocks.cc
@@ -21,6 +21,9 @@
 DXGIFactoryMock::DXGIFactoryMock() = default;
 DXGIFactoryMock::~DXGIFactoryMock() = default;
 
+DXGIFactory2Mock::DXGIFactory2Mock() = default;
+DXGIFactory2Mock::~DXGIFactory2Mock() = default;
+
 DXGIDeviceMock::DXGIDeviceMock() = default;
 DXGIDeviceMock::~DXGIDeviceMock() = default;
 
@@ -56,4 +59,7 @@
 D3D11FenceMock::D3D11FenceMock() = default;
 D3D11FenceMock::~D3D11FenceMock() = default;
 
+DXGISwapChain1Mock::DXGISwapChain1Mock() = default;
+DXGISwapChain1Mock::~DXGISwapChain1Mock() = default;
+
 }  // namespace media
\ No newline at end of file
diff --git a/media/base/win/d3d11_mocks.h b/media/base/win/d3d11_mocks.h
index 43ac36f..3afb76f1 100644
--- a/media/base/win/d3d11_mocks.h
+++ b/media/base/win/d3d11_mocks.h
@@ -349,6 +349,64 @@
   MOCK_STDCALL_METHOD3(GetPrivateData, HRESULT(REFGUID, UINT*, void*));
 };
 
+class DXGIFactory2Mock
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          IDXGIFactory2> {
+ public:
+  DXGIFactory2Mock();
+  ~DXGIFactory2Mock() override;
+
+  // IDXGIFactory (inherited)
+  MOCK_STDCALL_METHOD2(CreateSoftwareAdapter, HRESULT(HMODULE, IDXGIAdapter**));
+  MOCK_STDCALL_METHOD3(CreateSwapChain,
+                       HRESULT(IUnknown*,
+                               DXGI_SWAP_CHAIN_DESC*,
+                               IDXGISwapChain**));
+  MOCK_STDCALL_METHOD2(EnumAdapters, HRESULT(UINT, IDXGIAdapter**));
+  MOCK_STDCALL_METHOD1(GetWindowAssociation, HRESULT(HWND*));
+  MOCK_STDCALL_METHOD2(MakeWindowAssociation, HRESULT(HWND, UINT));
+  MOCK_STDCALL_METHOD3(SetPrivateData, HRESULT(REFGUID, UINT, const void*));
+  MOCK_STDCALL_METHOD2(SetPrivateDataInterface,
+                       HRESULT(REFGUID, const IUnknown*));
+  MOCK_STDCALL_METHOD2(GetParent, HRESULT(REFIID, void**));
+  MOCK_STDCALL_METHOD3(GetPrivateData, HRESULT(REFGUID, UINT*, void*));
+
+  // IDXGIFactory1
+  MOCK_STDCALL_METHOD2(EnumAdapters1,
+                       HRESULT(UINT Adapter, IDXGIAdapter1** ppAdapter));
+  MOCK_STDCALL_METHOD0(IsCurrent, BOOL());
+
+  // IDXGIFactory2
+  MOCK_STDCALL_METHOD0(IsWindowedStereoEnabled, BOOL());
+  MOCK_STDCALL_METHOD6(CreateSwapChainForHwnd,
+                       HRESULT(IUnknown*,
+                               HWND,
+                               const DXGI_SWAP_CHAIN_DESC1*,
+                               const DXGI_SWAP_CHAIN_FULLSCREEN_DESC*,
+                               IDXGIOutput*,
+                               IDXGISwapChain1**));
+  MOCK_STDCALL_METHOD5(CreateSwapChainForCoreWindow,
+                       HRESULT(IUnknown*,
+                               IUnknown*,
+                               const DXGI_SWAP_CHAIN_DESC1*,
+                               IDXGIOutput*,
+                               IDXGISwapChain1**));
+  MOCK_STDCALL_METHOD2(GetSharedResourceAdapterLuid, HRESULT(HANDLE, LUID*));
+  MOCK_STDCALL_METHOD3(RegisterStereoStatusWindow, HRESULT(HWND, UINT, DWORD*));
+  MOCK_STDCALL_METHOD2(RegisterStereoStatusEvent, HRESULT(HANDLE, DWORD*));
+  MOCK_STDCALL_METHOD1(UnregisterStereoStatus, void(DWORD));
+  MOCK_STDCALL_METHOD3(RegisterOcclusionStatusWindow,
+                       HRESULT(HWND, UINT, DWORD*));
+  MOCK_STDCALL_METHOD2(RegisterOcclusionStatusEvent, HRESULT(HANDLE, DWORD*));
+  MOCK_STDCALL_METHOD1(UnregisterOcclusionStatus, void(DWORD));
+  MOCK_STDCALL_METHOD4(CreateSwapChainForComposition,
+                       HRESULT(IUnknown*,
+                               const DXGI_SWAP_CHAIN_DESC1*,
+                               IDXGIOutput*,
+                               IDXGISwapChain1**));
+};
+
 class DXGIDeviceMock
     : public Microsoft::WRL::RuntimeClass<
           Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
@@ -1576,6 +1634,58 @@
   MOCK_STDCALL_METHOD2(SetEventOnCompletion, HRESULT(UINT64, HANDLE));
 };
 
+class DXGISwapChain1Mock
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          IDXGISwapChain1> {
+ public:
+  DXGISwapChain1Mock();
+  ~DXGISwapChain1Mock() override;
+
+  // IUnknown
+  MOCK_STDCALL_METHOD2(QueryInterface, HRESULT(REFIID, void**));
+  MOCK_STDCALL_METHOD0(AddRef, ULONG());
+  MOCK_STDCALL_METHOD0(Release, ULONG());
+
+  // IDXGIObject
+  MOCK_STDCALL_METHOD3(SetPrivateData, HRESULT(REFGUID, UINT, const void*));
+  MOCK_STDCALL_METHOD2(SetPrivateDataInterface,
+                       HRESULT(REFGUID, const IUnknown*));
+  MOCK_STDCALL_METHOD3(GetPrivateData, HRESULT(REFGUID, UINT*, void*));
+  MOCK_STDCALL_METHOD2(GetParent, HRESULT(REFIID, void**));
+
+  // IDXGIDeviceSubObject
+  MOCK_STDCALL_METHOD2(GetDevice, HRESULT(REFIID, void**));
+
+  // IDXGISwapChain
+  MOCK_STDCALL_METHOD2(Present, HRESULT(UINT, UINT));
+  MOCK_STDCALL_METHOD3(GetBuffer, HRESULT(UINT, REFIID, void**));
+  MOCK_STDCALL_METHOD2(SetFullscreenState, HRESULT(BOOL, IDXGIOutput*));
+  MOCK_STDCALL_METHOD2(GetFullscreenState, HRESULT(BOOL*, IDXGIOutput**));
+  MOCK_STDCALL_METHOD1(GetDesc, HRESULT(DXGI_SWAP_CHAIN_DESC*));
+  MOCK_STDCALL_METHOD5(ResizeBuffers,
+                       HRESULT(UINT, UINT, UINT, DXGI_FORMAT, UINT));
+  MOCK_STDCALL_METHOD1(ResizeTarget, HRESULT(const DXGI_MODE_DESC*));
+  MOCK_STDCALL_METHOD1(GetContainingOutput, HRESULT(IDXGIOutput**));
+  MOCK_STDCALL_METHOD1(GetFrameStatistics, HRESULT(DXGI_FRAME_STATISTICS*));
+  MOCK_STDCALL_METHOD1(GetLastPresentCount, HRESULT(UINT*));
+
+  // IDXGISwapChain1
+  MOCK_STDCALL_METHOD3(Present1,
+                       HRESULT(UINT, UINT, const DXGI_PRESENT_PARAMETERS*));
+  MOCK_STDCALL_METHOD0(IsTemporaryMonoSupported, BOOL());
+  MOCK_STDCALL_METHOD1(GetRestrictToOutput, HRESULT(IDXGIOutput**));
+  MOCK_STDCALL_METHOD1(SetBackgroundColor, HRESULT(const DXGI_RGBA*));
+  MOCK_STDCALL_METHOD1(GetBackgroundColor, HRESULT(DXGI_RGBA*));
+  MOCK_STDCALL_METHOD1(SetRotation, HRESULT(DXGI_MODE_ROTATION));
+  MOCK_STDCALL_METHOD1(GetRotation, HRESULT(DXGI_MODE_ROTATION*));
+  MOCK_STDCALL_METHOD1(GetDesc1, HRESULT(DXGI_SWAP_CHAIN_DESC1* pDesc));
+  MOCK_STDCALL_METHOD1(GetFullscreenDesc,
+                       HRESULT(DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc));
+  MOCK_STDCALL_METHOD1(GetHwnd, HRESULT(HWND* pHwnd));
+  MOCK_STDCALL_METHOD2(GetCoreWindow, HRESULT(REFIID refiid, void** ppUnk));
+};
+
 }  // namespace media
 
 #endif  // MEDIA_BASE_WIN_D3D11_MOCKS_H_
diff --git a/media/filters/ffmpeg_aac_bitstream_converter.cc b/media/filters/ffmpeg_aac_bitstream_converter.cc
index 45e87a3a9..7fd37e9 100644
--- a/media/filters/ffmpeg_aac_bitstream_converter.cc
+++ b/media/filters/ffmpeg_aac_bitstream_converter.cc
@@ -183,8 +183,8 @@
   int header_plus_packet_size =
       packet->size + kAdtsHeaderSize;
   if (!stream_codec_parameters_->extradata) {
-    DLOG(ERROR) << "extradata is null";
-    return false;
+    DVLOG(3) << "extradata is null";
+    return true;
   }
   if (stream_codec_parameters_->extradata_size < 2) {
     DLOG(ERROR) << "extradata too small to contain MP4A header";
diff --git a/media/filters/ffmpeg_aac_bitstream_converter_unittest.cc b/media/filters/ffmpeg_aac_bitstream_converter_unittest.cc
index 61b2890..0e56a6e 100644
--- a/media/filters/ffmpeg_aac_bitstream_converter_unittest.cc
+++ b/media/filters/ffmpeg_aac_bitstream_converter_unittest.cc
@@ -23,7 +23,7 @@
 namespace {
 const int kAacMainProfile = 0;
 const int kAacLowComplexityProfile = 1;
-} // namespace
+}  // namespace
 
 // Class for testing the FFmpegAACBitstreamConverter.
 class FFmpegAACBitstreamConverterTest : public testing::Test {
@@ -69,7 +69,7 @@
   for (size_t i = 0;
        i < (dummy_packet.size() * sizeof(decltype(dummy_packet)::value_type));
        i++) {
-    dummy_packet[i] = i & 0xFF; // Repeated sequences of 0-255
+    dummy_packet[i] = i & 0xFF;  // Repeated sequences of 0-255
   }
 
   auto test_packet = ScopedAVPacket::Allocate();
@@ -111,8 +111,9 @@
   auto test_packet = ScopedAVPacket::Allocate();
   CreatePacket(test_packet.get(), dummy_packet, sizeof(dummy_packet));
 
-  // Try out the actual conversion. This should fail due to missing extradata.
-  EXPECT_FALSE(converter.ConvertPacket(test_packet.get()));
+  // Try out the actual conversion. This should not fail - conversion is
+  // necessary only when we have `extradata`.
+  EXPECT_TRUE(converter.ConvertPacket(test_packet.get()));
 }
 
 TEST_F(FFmpegAACBitstreamConverterTest, Conversion_AudioProfileType) {
@@ -121,8 +122,7 @@
   uint8_t dummy_packet[1000] = {};
 
   auto test_packet = ScopedAVPacket::Allocate();
-  CreatePacket(test_packet.get(), dummy_packet,
-               sizeof(dummy_packet));
+  CreatePacket(test_packet.get(), dummy_packet, sizeof(dummy_packet));
 
   EXPECT_TRUE(converter.ConvertPacket(test_packet.get()));
 
@@ -135,8 +135,7 @@
   FFmpegAACBitstreamConverter converter_he(&test_parameters_);
 
   test_packet = ScopedAVPacket::Allocate();
-  CreatePacket(test_packet.get(), dummy_packet,
-               sizeof(dummy_packet));
+  CreatePacket(test_packet.get(), dummy_packet, sizeof(dummy_packet));
 
   EXPECT_TRUE(converter_he.ConvertPacket(test_packet.get()));
 
@@ -148,8 +147,7 @@
   FFmpegAACBitstreamConverter converter_eld(&test_parameters_);
 
   test_packet = ScopedAVPacket::Allocate();
-  CreatePacket(test_packet.get(), dummy_packet,
-               sizeof(dummy_packet));
+  CreatePacket(test_packet.get(), dummy_packet, sizeof(dummy_packet));
 
   EXPECT_FALSE(converter_eld.ConvertPacket(test_packet.get()));
 }
@@ -160,8 +158,7 @@
   uint8_t dummy_packet[1000];
 
   auto test_packet = ScopedAVPacket::Allocate();
-  CreatePacket(test_packet.get(), dummy_packet,
-               sizeof(dummy_packet));
+  CreatePacket(test_packet.get(), dummy_packet, sizeof(dummy_packet));
 
   // Try out the actual conversion (should be successful and allocate new
   // packet and destroy the old one).
diff --git a/net/base/lookup_string_in_fixed_set.cc b/net/base/lookup_string_in_fixed_set.cc
index e062044..925f0f48 100644
--- a/net/base/lookup_string_in_fixed_set.cc
+++ b/net/base/lookup_string_in_fixed_set.cc
@@ -4,10 +4,11 @@
 
 #include "net/base/lookup_string_in_fixed_set.h"
 
-#include <cstdint>
+#include <stdint.h>
+
+#include <string_view>
 
 #include "base/check.h"
-#include "base/compiler_specific.h"
 #include "base/containers/span.h"
 
 namespace net {
@@ -186,16 +187,14 @@
 }
 
 int LookupStringInFixedSet(base::span<const uint8_t> graph,
-                           const char* key,
-                           size_t key_length) {
+                           std::string_view key) {
   // Do an incremental lookup until either the end of the graph is reached, or
-  // until every character in |key| is consumed.
+  // until every character in `key` is consumed.
   FixedSetIncrementalLookup lookup(graph);
-  const char* key_end = UNSAFE_TODO(key + key_length);
-  while (key != key_end) {
-    if (!lookup.Advance(*key))
+  for (char input : key) {
+    if (!lookup.Advance(input)) {
       return kDafsaNotFound;
-    UNSAFE_TODO(key++);
+    }
   }
   // The entire input was consumed without reaching the end of the graph. Return
   // the result code (if present) for the current position, or kDafsaNotFound.
diff --git a/net/base/lookup_string_in_fixed_set.h b/net/base/lookup_string_in_fixed_set.h
index d8a4357..e4b492a 100644
--- a/net/base/lookup_string_in_fixed_set.h
+++ b/net/base/lookup_string_in_fixed_set.h
@@ -6,8 +6,8 @@
 #define NET_BASE_LOOKUP_STRING_IN_FIXED_SET_H_
 
 #include <stddef.h>
+#include <stdint.h>
 
-#include <cstdint>
 #include <string_view>
 
 #include "base/containers/span.h"
@@ -27,20 +27,19 @@
   kDafsaPrivateRule = 4,    // key matched a private rule
 };
 
-// Looks up the string |key| with length |key_length| in a fixed set of
-// strings. The set of strings must be known at compile time. It is converted to
-// a graph structure named a DAFSA (Deterministic Acyclic Finite State
-// Automaton) by the script make_dafsa.py during compilation. This permits
-// efficient (in time and space) lookup. The graph generated by make_dafsa.py
-// takes the form of a constant byte array which should be supplied via the
-// |graph| parameter.  The return value is kDafsaNotFound, kDafsaFound, or a
-// bitmap consisting of one or more of kDafsaExceptionRule, kDafsaWildcardRule
-// and kDafsaPrivateRule ORed together.
+// Looks up the string `key` with in a fixed set of strings. The set of strings
+// must be known at compile time. It is converted to a graph structure named a
+// DAFSA (Deterministic Acyclic Finite State Automaton) by the script
+// make_dafsa.py during compilation. This permits efficient (in time and space)
+// lookup. The graph generated by make_dafsa.py takes the form of a constant
+// byte array which should be supplied via the |graph| parameter.  The return
+// value is kDafsaNotFound, kDafsaFound, or a bitmap consisting of one or more
+// of kDafsaExceptionRule, kDafsaWildcardRule and kDafsaPrivateRule ORed
+// together.
 //
 // TODO(nick): Replace this with FixedSetIncrementalLookup everywhere.
 NET_EXPORT int LookupStringInFixedSet(base::span<const uint8_t> graph,
-                                      const char* key,
-                                      size_t key_length);
+                                      std::string_view key);
 
 // Looks up the longest matching suffix for |host| with length |length| in a
 // reversed DAFSA. Partial matches must begin at a new component, i.e. host
diff --git a/net/base/lookup_string_in_fixed_set_fuzzer.cc b/net/base/lookup_string_in_fixed_set_fuzzer.cc
index bb547a2..2a8ba7a5 100644
--- a/net/base/lookup_string_in_fixed_set_fuzzer.cc
+++ b/net/base/lookup_string_in_fixed_set_fuzzer.cc
@@ -2,10 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "net/base/lookup_string_in_fixed_set.h"
+
 #include <stddef.h>
 #include <stdint.h>
 
-#include "net/base/lookup_string_in_fixed_set.h"
+#include <string_view>
+
+#include "base/compiler_specific.h"
 
 namespace {
 #include "net/base/registry_controlled_domains/effective_tld_names-inc.cc"
@@ -13,7 +17,8 @@
 
 // Entry point for LibFuzzer.
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  net::LookupStringInFixedSet(kDafsa, reinterpret_cast<const char*>(data),
-                              size);
+  net::LookupStringInFixedSet(
+      kDafsa, UNSAFE_BUFFERS(
+                  std::string_view(reinterpret_cast<const char*>(data), size)));
   return 0;
 }
diff --git a/net/base/lookup_string_in_fixed_set_unittest.cc b/net/base/lookup_string_in_fixed_set_unittest.cc
index e516b23..e0fbbfa 100644
--- a/net/base/lookup_string_in_fixed_set_unittest.cc
+++ b/net/base/lookup_string_in_fixed_set_unittest.cc
@@ -9,12 +9,12 @@
 
 #include "net/base/lookup_string_in_fixed_set.h"
 
-#include <string.h>
-
 #include <algorithm>
 #include <cstdint>
 #include <limits>
 #include <ostream>
+#include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
@@ -46,7 +46,7 @@
 }
 
 struct Expectation {
-  const char* const key;
+  std::string_view key;
   int value;
 };
 
@@ -56,8 +56,8 @@
 
 class LookupStringInFixedSetTest : public testing::TestWithParam<Expectation> {
  protected:
-  int LookupInGraph(base::span<const uint8_t> graph, const char* key) {
-    return LookupStringInFixedSet(graph, key, strlen(key));
+  int LookupInGraph(base::span<const uint8_t> graph, std::string_view key) {
+    return LookupStringInFixedSet(graph, key);
   }
 };
 
diff --git a/printing/android/java/src/org/chromium/printing/PrintingControllerImpl.java b/printing/android/java/src/org/chromium/printing/PrintingControllerImpl.java
index 0e567b5..2f938210 100644
--- a/printing/android/java/src/org/chromium/printing/PrintingControllerImpl.java
+++ b/printing/android/java/src/org/chromium/printing/PrintingControllerImpl.java
@@ -16,6 +16,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Log;
+import org.chromium.base.ResettersForTesting;
 import org.chromium.base.ThreadUtils;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
@@ -58,6 +59,8 @@
     /** The singleton instance for this class. */
     @VisibleForTesting protected static @Nullable PrintingController sInstance;
 
+    private static @Nullable PrintingController sInstanceForTesting;
+
     private @Nullable String mErrorMessage;
 
     private int mRenderProcessId;
@@ -103,6 +106,11 @@
         mPrintDocumentAdapterWrapper = new PrintDocumentAdapterWrapper(this);
     }
 
+    public static void setInstanceForTesting(PrintingController instanceForTesting) {
+        sInstanceForTesting = instanceForTesting;
+        ResettersForTesting.register(() -> sInstanceForTesting = null);
+    }
+
     /**
      * Returns the singleton instance, lazily creating one if needed.
      *
@@ -111,6 +119,8 @@
     public static PrintingController getInstance() {
         ThreadUtils.assertOnUiThread();
 
+        if (sInstanceForTesting != null) return sInstanceForTesting;
+
         if (sInstance == null) {
             sInstance = new PrintingControllerImpl();
         }
@@ -152,6 +162,11 @@
         return mIsBusy;
     }
 
+    @VisibleForTesting
+    public @Nullable Printable getPrintable() {
+        return mPrintable;
+    }
+
     @Override
     public void setPendingPrint(
             final Printable printable,
diff --git a/remoting/protocol/webrtc_audio_source_adapter.cc b/remoting/protocol/webrtc_audio_source_adapter.cc
index e7849d7..f09ccca 100644
--- a/remoting/protocol/webrtc_audio_source_adapter.cc
+++ b/remoting/protocol/webrtc_audio_source_adapter.cc
@@ -2,16 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "remoting/protocol/webrtc_audio_source_adapter.h"
 
+#include <algorithm>
 #include <utility>
+#include <vector>
 
 #include "base/check_op.h"
+#include "base/containers/span.h"
 #include "base/functional/bind.h"
 #include "base/observer_list.h"
 #include "base/synchronization/lock.h"
@@ -23,12 +21,28 @@
 
 namespace remoting::protocol {
 
+namespace {
+
 static const int kChannels = 2;
 static const int kBytesPerSample = 2;
 
 // Frame size expected by webrtc::AudioTrackSinkInterface.
 static constexpr base::TimeDelta kAudioFrameDuration = base::Milliseconds(10);
 
+// Notify all audio sinks about a new audio frame.
+void NotifyAudioSinks(
+    base::ObserverList<webrtc::AudioTrackSinkInterface>::Unchecked& audio_sinks,
+    base::span<const uint8_t> frame,
+    int sampling_rate,
+    size_t samples_per_frame) {
+  for (auto& observer : audio_sinks) {
+    observer.OnData(frame.data(), kBytesPerSample * 8, sampling_rate, kChannels,
+                    samples_per_frame);
+  }
+}
+
+}  // namespace
+
 class WebrtcAudioSourceAdapter::Core {
  public:
   Core();
@@ -112,44 +126,43 @@
     partial_frame_.clear();
   }
 
-  size_t samples_per_frame = (kAudioFrameDuration * sampling_rate_).InSeconds();
-  size_t bytes_per_frame = kBytesPerSample * kChannels * samples_per_frame;
+  const size_t samples_per_frame =
+      (kAudioFrameDuration * sampling_rate_).InSeconds();
+  const size_t bytes_per_frame =
+      kBytesPerSample * kChannels * samples_per_frame;
 
-  const std::string& data = packet->data(0);
-
-  size_t position = 0;
+  base::span<const uint8_t> input_data = base::as_byte_span(packet->data(0));
 
   base::AutoLock lock(audio_sinks_lock_);
 
+  // Stage 1: Fill and send |partial_frame_|.
   if (!partial_frame_.empty()) {
-    size_t bytes_to_append =
-        std::min(bytes_per_frame - partial_frame_.size(), data.size());
-    position += bytes_to_append;
-    partial_frame_.insert(partial_frame_.end(), data.data(),
-                          data.data() + bytes_to_append);
-    if (partial_frame_.size() < bytes_per_frame) {
-      // Still don't have full frame.
-      return;
-    }
+    const size_t needed_bytes = bytes_per_frame - partial_frame_.size();
+    const size_t copy_bytes = std::min(needed_bytes, input_data.size());
 
-    // Here |partial_frame_| always contains a full frame.
-    DCHECK_EQ(partial_frame_.size(), bytes_per_frame);
+    partial_frame_.insert(partial_frame_.end(), input_data.begin(),
+                          input_data.begin() + copy_bytes);
+    input_data = input_data.subspan(copy_bytes);
 
-    for (auto& observer : audio_sinks_) {
-      observer.OnData(&partial_frame_.front(), kBytesPerSample * 8,
-                      sampling_rate_, kChannels, samples_per_frame);
+    if (partial_frame_.size() == bytes_per_frame) {
+      NotifyAudioSinks(audio_sinks_, base::span<const uint8_t>(partial_frame_),
+                       sampling_rate_, samples_per_frame);
+      partial_frame_.clear();
     }
   }
 
-  while (position + bytes_per_frame <= data.size()) {
-    for (auto& observer : audio_sinks_) {
-      observer.OnData(data.data() + position, kBytesPerSample * 8,
-                      sampling_rate_, kChannels, samples_per_frame);
-    }
-    position += bytes_per_frame;
+  // Stage 2: Processing of |full_frames|.
+  const size_t full_frames = input_data.size() / bytes_per_frame;
+  for (size_t i = 0; i < full_frames; ++i) {
+    const auto frame = input_data.subspan(i * bytes_per_frame, bytes_per_frame);
+    NotifyAudioSinks(audio_sinks_, frame, sampling_rate_, samples_per_frame);
   }
 
-  partial_frame_.assign(data.data() + position, data.data() + data.size());
+  // Stage 3: Save remaining data.
+  const size_t processed_bytes = full_frames * bytes_per_frame;
+  const auto remaining = input_data.subspan(processed_bytes);
+  partial_frame_.insert(partial_frame_.end(), remaining.begin(),
+                        remaining.end());
 }
 
 WebrtcAudioSourceAdapter::WebrtcAudioSourceAdapter(
diff --git a/remoting/protocol/webrtc_audio_source_adapter_unittest.cc b/remoting/protocol/webrtc_audio_source_adapter_unittest.cc
index 4af3ba2..8973f12 100644
--- a/remoting/protocol/webrtc_audio_source_adapter_unittest.cc
+++ b/remoting/protocol/webrtc_audio_source_adapter_unittest.cc
@@ -2,16 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "remoting/protocol/webrtc_audio_source_adapter.h"
 
+#include <algorithm>
+#include <iterator>
 #include <numeric>
 #include <vector>
 
+#include "base/containers/span.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
@@ -48,9 +46,10 @@
     EXPECT_EQ(kChannels, static_cast<int>(number_of_channels));
     EXPECT_EQ((kSampleRate * kFrameDuration).InSeconds(),
               static_cast<int>(number_of_samples));
-    const int16_t* samples = reinterpret_cast<const int16_t*>(audio_data);
-    samples_.insert(samples_.end(), samples,
-                    samples + number_of_samples * kChannels);
+    const int16_t* samples = static_cast<const int16_t*>(audio_data);
+    size_t sample_count = number_of_samples * kChannels;
+    samples_.reserve(samples_.size() + sample_count);
+    std::copy_n(samples, sample_count, std::back_inserter(samples_));
   }
 
   const std::vector<int16_t>& samples() { return samples_; }
@@ -98,8 +97,8 @@
     }
 
     std::unique_ptr<AudioPacket> packet(new AudioPacket());
-    packet->add_data(reinterpret_cast<char*>(&(data[0])),
-                     num_samples * kChannels * sizeof(int16_t));
+    auto bytes = base::as_bytes(base::span(data));
+    packet->add_data(reinterpret_cast<const char*>(bytes.data()), bytes.size());
     packet->set_encoding(AudioPacket::ENCODING_RAW);
     packet->set_sampling_rate(AudioPacket::SAMPLING_RATE_48000);
     packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
diff --git a/services/data_decoder/BUILD.gn b/services/data_decoder/BUILD.gn
index 878f77a..e9edd2c 100644
--- a/services/data_decoder/BUILD.gn
+++ b/services/data_decoder/BUILD.gn
@@ -118,6 +118,22 @@
   }
 }
 
+if (use_blink) {
+  test("json_sanitizer_fuzztests") {
+    sources = [ "public/cpp/json_sanitizer_fuzztests.cc" ]
+
+    fuzztests =
+        [ "JsonSanitizerFuzzTest.SanitizerAndBaseJsonReaderDecoderMatch" ]
+
+    deps = [
+      "//base",
+      "//base/test:test_support",
+      "//services/data_decoder/public/cpp",
+      "//third_party/fuzztest:fuzztest_gtest_main",
+    ]
+  }
+}
+
 # This is a separate target because //third_party/libxml:libxml_utils
 # wants to limit its visibility to a select number of target, but the
 # "fuzzer_test" template expands on iOS to many targets that need to
diff --git a/services/data_decoder/DEPS b/services/data_decoder/DEPS
index d6423a26..c624c628 100644
--- a/services/data_decoder/DEPS
+++ b/services/data_decoder/DEPS
@@ -7,6 +7,7 @@
   "+net",
   "+services/data_decoder/public/cpp/android/safe_json_jni_headers",
   "+skia",
+  "+third_party/fuzztest",
   "+third_party/libxml/chromium",
   "+third_party/blink/public",
   "+third_party/skia",
diff --git a/services/data_decoder/public/cpp/data_decoder.cc b/services/data_decoder/public/cpp/data_decoder.cc
index 512b48a..44884fc4 100644
--- a/services/data_decoder/public/cpp/data_decoder.cc
+++ b/services/data_decoder/public/cpp/data_decoder.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "base/features.h"
 #include "base/functional/callback.h"
 #include "base/json/json_reader.h"
 #include "base/memory/ref_counted.h"
@@ -27,6 +28,11 @@
 #include "services/data_decoder/public/mojom/structured_headers_parser.mojom.h"
 #include "services/data_decoder/public/mojom/xml_parser.mojom.h"
 
+#if BUILDFLAG(IS_ANDROID)
+#include "base/types/expected_macros.h"
+#include "services/data_decoder/public/cpp/json_sanitizer.h"
+#endif
+
 #if !BUILDFLAG(USE_BLINK)
 #include "services/data_decoder/data_decoder_service.h"  // nogncheck
 #endif
@@ -198,11 +204,62 @@
       },
       base::ElapsedTimer(), std::move(callback));
 
-  base::JSONReader::Result result =
-      base::JSONReader::ReadAndReturnValueWithError(json, base::JSON_PARSE_RFC);
-  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-      FROM_HERE, base::BindOnce(&ParsingComplete, cancel_requests_,
-                                std::move(callback), std::move(result)));
+  if (base::JSONReader::UsingRust()) {
+    if (base::features::kUseRustJsonParserInCurrentSequence.Get()) {
+      base::JSONReader::Result result =
+          base::JSONReader::ReadAndReturnValueWithError(json,
+                                                        base::JSON_PARSE_RFC);
+      base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+          FROM_HERE, base::BindOnce(&ParsingComplete, cancel_requests_,
+                                    std::move(callback), std::move(result)));
+    } else {
+      base::ThreadPool::PostTaskAndReplyWithResult(
+          FROM_HERE, {base::TaskPriority::USER_VISIBLE},
+          base::BindOnce(
+              [](const std::string& json) {
+                return base::JSONReader::ReadAndReturnValueWithError(
+                    json, base::JSON_PARSE_RFC);
+              },
+              json),
+          base::BindOnce(&ParsingComplete, cancel_requests_,
+                         std::move(callback)));
+    }
+    return;
+  }
+
+#if BUILDFLAG(IS_ANDROID)
+  // For Android, if the full Rust parser is not available, we use the
+  // in-process sanitizer and then parse in-process.
+  JsonSanitizer::Sanitize(
+      json, base::BindOnce(
+                [](ValueParseCallback callback,
+                   scoped_refptr<CancellationFlag> is_cancelled,
+                   JsonSanitizer::Result result) {
+                  if (is_cancelled->data) {
+                    return;
+                  }
+
+                  RETURN_IF_ERROR(result, [&](std::string error) {
+                    std::move(callback).Run(base::unexpected(std::move(error)));
+                  });
+
+                  ParsingComplete(is_cancelled, std::move(callback),
+                                  base::JSONReader::ReadAndReturnValueWithError(
+                                      result.value(), base::JSON_PARSE_RFC));
+                },
+                std::move(callback), cancel_requests_));
+#else   // BUILDFLAG(IS_ANDROID)
+  // Parse JSON out-of-process.
+  auto request =
+      base::MakeRefCounted<ValueParseRequest<mojom::JsonParser, base::Value>>(
+          std::move(callback), cancel_requests_);
+  GetService()->BindJsonParser(request->BindRemote());
+  request->remote()->Parse(
+      json, base::JSON_PARSE_RFC,
+      base::BindOnce(&ValueParseRequest<mojom::JsonParser,
+                                        base::Value>::OnServiceValueOrError,
+                     request));
+#endif  // BUILDFLAG(IS_ANDROID)
 }
 
 // static
diff --git a/services/data_decoder/public/cpp/data_decoder_unittest.cc b/services/data_decoder/public/cpp/data_decoder_unittest.cc
index 0e8cf67..2eb9bf9 100644
--- a/services/data_decoder/public/cpp/data_decoder_unittest.cc
+++ b/services/data_decoder/public/cpp/data_decoder_unittest.cc
@@ -6,11 +6,13 @@
 
 #include <memory>
 
+#include "base/features.h"
 #include "base/functional/bind.h"
 #include "base/run_loop.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/types/expected.h"
 #include "base/values.h"
@@ -206,33 +208,63 @@
 
 class DataDecoderMultiThreadTest : public testing::Test {
  protected:
+  void TestJSONDecode() {
+    base::RunLoop run_loop;
+    DataDecoder decoder;
+    DataDecoder::ValueOrError result;
+    decoder.ParseJson(
+        // The magic 122.416294033786585 number comes from
+        // https://github.com/serde-rs/json/issues/707
+        "[ 122.416294033786585 ]",
+        base::BindLambdaForTesting(
+            [&run_loop, &result](DataDecoder::ValueOrError value_or_error) {
+              result = std::move(value_or_error);
+              run_loop.Quit();
+            }));
+    run_loop.Run();
+    histogram_tester_.ExpectTotalCount("Security.DataDecoder.Json.DecodingTime",
+                                       1);
+    ASSERT_TRUE(result.has_value());
+    ASSERT_TRUE(result->is_list());
+    base::Value::List& list = result->GetList();
+    ASSERT_EQ(1u, list.size());
+    EXPECT_TRUE(list[0].is_double());
+    EXPECT_EQ(122.416294033786585, list[0].GetDouble());
+  }
+
   base::test::TaskEnvironment task_environment_;
   base::HistogramTester histogram_tester_;
 };
 
+// Test basic JSON decoding without using Rust. We test only on Android,
+// because otherwise this would result in spawning a process.
+#if BUILDFLAG(IS_ANDROID)
+#define MAYBE_JSONDecodeNonRust JSONDecodeNonRust
+#else  // BUILDFLAG(IS_ANDROID)
+#define MAYBE_JSONDecodeNonRust DISABLED_JSONDecodeNonRust
+#endif  // BUILDFLAG(IS_ANDROID)
+TEST_F(DataDecoderMultiThreadTest, MAYBE_JSONDecodeNonRust) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(base::features::kUseRustJsonParser);
+  TestJSONDecode();
+}
+
 // Test basic JSON decoding using Rust, in a threadpool.
-TEST_F(DataDecoderMultiThreadTest, JSONDecode) {
-  base::RunLoop run_loop;
-  DataDecoder decoder;
-  DataDecoder::ValueOrError result;
-  decoder.ParseJson(
-      // The magic 122.416294033786585 number comes from
-      // https://github.com/serde-rs/json/issues/707
-      "[ 122.416294033786585 ]",
-      base::BindLambdaForTesting(
-          [&run_loop, &result](DataDecoder::ValueOrError value_or_error) {
-            result = std::move(value_or_error);
-            run_loop.Quit();
-          }));
-  run_loop.Run();
-  histogram_tester_.ExpectTotalCount("Security.DataDecoder.Json.DecodingTime",
-                                     1);
-  ASSERT_TRUE(result.has_value());
-  ASSERT_TRUE(result->is_list());
-  base::Value::List& list = result->GetList();
-  ASSERT_EQ(1u, list.size());
-  EXPECT_TRUE(list[0].is_double());
-  EXPECT_EQ(122.416294033786585, list[0].GetDouble());
+TEST_F(DataDecoderMultiThreadTest, JSONDecodeRustThreadpool) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      base::features::kUseRustJsonParser,
+      {{"UseRustJsonParserInCurrentSequence", "false"}});
+  TestJSONDecode();
+}
+
+// Test basic JSON decoding using Rust, in the main thread.
+TEST_F(DataDecoderMultiThreadTest, JSONDecodeRustCurrentSequence) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      base::features::kUseRustJsonParser,
+      {{"UseRustJsonParserInCurrentSequence", "true"}});
+  TestJSONDecode();
 }
 
 }  // namespace data_decoder
diff --git a/services/data_decoder/public/cpp/json_sanitizer_fuzztests.cc b/services/data_decoder/public/cpp/json_sanitizer_fuzztests.cc
new file mode 100644
index 0000000..38163ce
--- /dev/null
+++ b/services/data_decoder/public/cpp/json_sanitizer_fuzztests.cc
@@ -0,0 +1,62 @@
+// 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 <mutex>
+#include <optional>
+#include <string>
+#include <string_view>
+
+#include "base/check_op.h"
+#include "base/command_line.h"
+#include "base/json/json_reader.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_timeouts.h"
+#include "base/types/expected.h"
+#include "base/values.h"
+#include "build/buildflag.h"
+#include "services/data_decoder/public/cpp/json_sanitizer.h"
+#include "third_party/fuzztest/src/fuzztest/fuzztest.h"
+
+namespace data_decoder {
+namespace {
+
+void SanitizerAndBaseJsonReaderDecoderMatch(std::string_view input) {
+  [[maybe_unused]] static bool initialized = [] {
+#if BUILDFLAG(IS_WIN)
+    base::CommandLine::InitUsingArgvForTesting(0, nullptr);
+#else
+    CHECK(base::CommandLine::Init(0, nullptr));
+#endif
+    TestTimeouts::Initialize();
+    // Note that the documentation says not to call this in tests and to use
+    // base::test::ScopedCommandLine instead; however, that doesn't actually
+    // work until the current process's command line is first initialized at
+    // least once, which it never is in fuzz tests.
+    base::CommandLine::Reset();
+    return true;
+  }();
+  std::optional<base::Value> value =
+      base::JSONReader::Read(input, base::JSON_PARSE_RFC);
+  base::test::TaskEnvironment task_environment;
+  base::RunLoop loop;
+  JsonSanitizer::Result result;
+  JsonSanitizer::Sanitize(std::string(input), base::BindLambdaForTesting(
+                                                  [&](JsonSanitizer::Result r) {
+                                                    result = r;
+                                                    loop.Quit();
+                                                  }));
+  loop.Run();
+  // The JSON parser and the JSON sanitizer should agree on whether or not a
+  // given blob of JSON is valid. Additionally, the JSON sanitizer considers
+  // something to be invalid JSON if it does not decode to a dict or a list.
+  CHECK_EQ(value.has_value() && (value->is_dict() || value->is_list()),
+           result.has_value());
+}
+
+FUZZ_TEST(JsonSanitizerFuzzTest, SanitizerAndBaseJsonReaderDecoderMatch);
+
+}  // namespace
+}  // namespace data_decoder
diff --git a/services/data_decoder/public/cpp/test_support/in_process_data_decoder.cc b/services/data_decoder/public/cpp/test_support/in_process_data_decoder.cc
index 9ddde27..4ce31f3 100644
--- a/services/data_decoder/public/cpp/test_support/in_process_data_decoder.cc
+++ b/services/data_decoder/public/cpp/test_support/in_process_data_decoder.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/features.h"
 #include "base/task/sequenced_task_runner.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
@@ -26,6 +27,12 @@
   return nullptr;
 }
 
+void InProcessDataDecoder::SimulateJsonParserCrash(bool drop) {
+  CHECK(!base::FeatureList::IsEnabled(base::features::kUseRustJsonParser))
+      << "Rust JSON parser is in-process and cannot crash.";
+  drop_json_parsers_ = drop;
+}
+
 void InProcessDataDecoder::BindDataDecoderService(
     mojo::PendingReceiver<mojom::DataDecoderService> receiver) {
   if (!task_runner_->RunsTasksInCurrentSequence()) {
@@ -61,7 +68,9 @@
 
 void InProcessDataDecoder::BindJsonParser(
     mojo::PendingReceiver<mojom::JsonParser> receiver) {
-  GetForwardingInterface()->BindJsonParser(std::move(receiver));
+  if (!drop_json_parsers_) {
+    GetForwardingInterface()->BindJsonParser(std::move(receiver));
+  }
 }
 
 void InProcessDataDecoder::BindWebBundleParserFactory(
diff --git a/services/data_decoder/public/cpp/test_support/in_process_data_decoder.h b/services/data_decoder/public/cpp/test_support/in_process_data_decoder.h
index 7169bad..87ebf4a6 100644
--- a/services/data_decoder/public/cpp/test_support/in_process_data_decoder.h
+++ b/services/data_decoder/public/cpp/test_support/in_process_data_decoder.h
@@ -44,6 +44,9 @@
   // them. Useful for tests simulating service failures.
   void SimulateImageDecoderCrash(bool drop) { drop_image_decoders_ = drop; }
 
+  // Same as above but for JsonParser receivers.
+  void SimulateJsonParserCrash(bool drop);
+
   // Configures the service to use |binder| to bind
   // WebBundleParserFactory in subsequent
   // BindWebBundleParserFactory() calls.
@@ -79,6 +82,7 @@
   ::data_decoder::DataDecoderService service_;
   mojo::ReceiverSet<mojom::DataDecoderService> receivers_;
   bool drop_image_decoders_ = false;
+  bool drop_json_parsers_ = false;
   base::RepeatingCallback<void(
       mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>)>
       web_bundle_parser_factory_binder_;
diff --git a/services/screen_ai/public/mojom/screen_ai_factory.mojom b/services/screen_ai/public/mojom/screen_ai_factory.mojom
index d214aed..7ff8de1 100644
--- a/services/screen_ai/public/mojom/screen_ai_factory.mojom
+++ b/services/screen_ai/public/mojom/screen_ai_factory.mojom
@@ -60,7 +60,4 @@
   // Sets the shutdown handler.
   BindShutdownHandler(
     pending_remote<ScreenAIServiceShutdownHandler> shutdown_handler);
-
-  // Shuts down the service if there are no connected remotes.
-  ShutDownIfNoClients();
 };
diff --git a/services/screen_ai/screen_ai_service_impl.cc b/services/screen_ai/screen_ai_service_impl.cc
index 813993a5..68a53fbd 100644
--- a/services/screen_ai/screen_ai_service_impl.cc
+++ b/services/screen_ai/screen_ai_service_impl.cc
@@ -53,7 +53,9 @@
 constexpr int kMaxOcrDimension = 2048;
 
 // How often it would be checked that the service is idle and can be shutdown.
+// LINT.IfChange(kIdleCheckingDelay)
 constexpr base::TimeDelta kIdleCheckingDelay = base::Seconds(3);
+// LINT.ThenChange(//chrome/browser/screen_ai/optical_character_recognizer_browsertest.cc:kServiceIdleCheckingDelay)
 
 // How long to wait for a request to the library be responded, before assuming
 // that the library is not responsive.
@@ -266,7 +268,7 @@
   model_data_holder_ = std::make_unique<ModelDataHolder>();
   idle_checking_timer_ = std::make_unique<base::RepeatingTimer>();
   idle_checking_timer_->Start(FROM_HERE, kIdleCheckingDelay, this,
-                              &ScreenAIService::ShutDownIfNoClients);
+                              &ScreenAIService::ShutDownOnIdle);
 
   background_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
       {base::TaskPriority::BEST_EFFORT,
@@ -666,18 +668,12 @@
   return ComputeMainNode(tree, content_node_ids);
 }
 
-void ScreenAIService::ShutDownIfNoClients() {
+void ScreenAIService::ShutDownOnIdle() {
   const base::TimeTicks kIdlenessThreshold =
       base::TimeTicks::Now() - kIdleCheckingDelay;
-  bool ocr_not_needed =
-      !screen_ai_annotators_.size() || ocr_last_used_ < kIdlenessThreshold;
-  bool main_content_extractioncan_not_needed =
-      !screen2x_main_content_extractors_.size() ||
-      mce_last_used_ < kIdlenessThreshold;
-
-  if (ocr_not_needed && main_content_extractioncan_not_needed) {
+  if (ocr_last_used_ < kIdlenessThreshold &&
+      mce_last_used_ < kIdlenessThreshold) {
     screen_ai_shutdown_handler_->ShuttingDownOnIdle();
-    VLOG(2) << "Shutting down since no client or idle.";
     base::Process::TerminateCurrentProcessImmediately(0);
   }
 }
diff --git a/services/screen_ai/screen_ai_service_impl.h b/services/screen_ai/screen_ai_service_impl.h
index c4a5a19..5be7a8c 100644
--- a/services/screen_ai/screen_ai_service_impl.h
+++ b/services/screen_ai/screen_ai_service_impl.h
@@ -98,9 +98,6 @@
       InitializeOCRCallback callback) override;
 
   // mojom::ScreenAIServiceFactory:
-  void ShutDownIfNoClients() override;
-
-  // mojom::ScreenAIServiceFactory:
   void BindShutdownHandler(
       mojo::PendingRemote<mojom::ScreenAIServiceShutdownHandler>
           shutdown_handler) override;
@@ -130,6 +127,8 @@
   void OcrReceiverDisconnected();
   void MceReceiverDisconnected();
 
+  void ShutDownOnIdle();
+
   // Last time the feature is used. A null value means never, it is set when the
   // feature is initialized, and each time it is used.
   base::TimeTicks ocr_last_used_;
diff --git a/services/webnn/webnn_graph_builder_impl_unittest.cc b/services/webnn/webnn_graph_builder_impl_unittest.cc
index 6e622f2..7b7293e 100644
--- a/services/webnn/webnn_graph_builder_impl_unittest.cc
+++ b/services/webnn/webnn_graph_builder_impl_unittest.cc
@@ -17,6 +17,7 @@
 #include "mojo/public/cpp/test_support/test_utils.h"
 #include "services/webnn/error.h"
 #include "services/webnn/public/cpp/operand_descriptor.h"
+#include "services/webnn/public/cpp/webnn_types.h"
 #include "services/webnn/public/mojom/features.mojom-features.h"
 #include "services/webnn/public/mojom/webnn_tensor.mojom.h"
 #include "services/webnn/webnn_constant_operand.h"
@@ -35,9 +36,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder>& graph_builder_remote) {
   // Build a simple graph.
   GraphInfoBuilder builder(graph_builder_remote);
-  uint64_t input_operand_id = builder.BuildInput("input", /*dimensions=*/{2, 3},
-                                                 OperandDataType::kFloat32);
-  uint64_t output_operand_id = builder.BuildOutput(
+  OperandId input_operand_id = builder.BuildInput(
+      "input", /*dimensions=*/{2, 3}, OperandDataType::kFloat32);
+  OperandId output_operand_id = builder.BuildOutput(
       "output", /*dimensions=*/{2, 3}, OperandDataType::kFloat32);
   builder.BuildClamp(input_operand_id, output_operand_id, /*min_value=*/0.0,
                      /*max_value=*/1.0);
@@ -89,7 +90,7 @@
       mojo::PendingAssociatedReceiver<mojom::WebNNGraph> receiver,
       mojom::GraphInfoPtr graph_info,
       WebNNGraphImpl::ComputeResourceInfo compute_resource_info,
-      base::flat_map<uint64_t, std::unique_ptr<WebNNConstantOperand>>
+      base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
       /*constant_operands*/,
       CreateGraphImplCallback callback) override {
     // Asynchronously resolve `callback` so there's an opportunity for
@@ -233,10 +234,10 @@
   const std::array<float, 6> kConstantData{3.0, 4.0, 5.0, 6.0, 7.0, 8.0};
 
   GraphInfoBuilder builder(graph_builder_remote());
-  uint64_t constant_operand_id = builder.BuildConstant(
+  OperandId constant_operand_id = builder.BuildConstant(
       /*dimensions=*/{2, 3}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, kConstantData));
-  uint64_t output_operand_id = builder.BuildOutput(
+  OperandId output_operand_id = builder.BuildOutput(
       "output", /*dimensions=*/{2, 3}, OperandDataType::kFloat32);
   builder.BuildClamp(constant_operand_id, output_operand_id, /*min_value=*/5.0,
                      /*max_value=*/7.0);
@@ -315,10 +316,10 @@
       base::checked_cast<uint32_t>(std::numeric_limits<int32_t>::max() / 4), 2};
 
   GraphInfoBuilder builder(graph_builder_remote());
-  uint64_t input_operand_id = builder.BuildInput("input", large_tensor_shape,
-                                                 OperandDataType::kFloat32);
-  uint64_t output_operand_id = builder.BuildOutput("output", large_tensor_shape,
-                                                   OperandDataType::kFloat32);
+  OperandId input_operand_id = builder.BuildInput("input", large_tensor_shape,
+                                                  OperandDataType::kFloat32);
+  OperandId output_operand_id = builder.BuildOutput(
+      "output", large_tensor_shape, OperandDataType::kFloat32);
   builder.BuildClamp(input_operand_id, output_operand_id, /*min_value=*/0.0,
                      /*max_value=*/1.0);
   EXPECT_FALSE(
diff --git a/services/webnn/webnn_graph_impl.cc b/services/webnn/webnn_graph_impl.cc
index f28ae2e..3ba556a 100644
--- a/services/webnn/webnn_graph_impl.cc
+++ b/services/webnn/webnn_graph_impl.cc
@@ -15,6 +15,7 @@
 #include "base/types/pass_key.h"
 #include "services/webnn/error.h"
 #include "services/webnn/public/cpp/operand_descriptor.h"
+#include "services/webnn/public/cpp/webnn_types.h"
 #include "services/webnn/webnn_context_impl.h"
 #include "services/webnn/webnn_tensor_impl.h"
 
@@ -68,9 +69,9 @@
 WebNNGraphImpl::ComputeResourceInfo::ComputeResourceInfo(
     base::flat_map<std::string, OperandDescriptor> input_names_to_descriptors,
     base::flat_map<std::string, OperandDescriptor> output_names_to_descriptors,
-    base::flat_map<uint64_t, base::flat_set<size_t>>
+    base::flat_map<OperandId, base::flat_set<OperationId>>
         operand_to_dependent_operations,
-    base::flat_map<uint64_t, size_t> operand_to_producing_operation,
+    base::flat_map<OperandId, OperationId> operand_to_producing_operation,
     base::PassKey<WebNNGraphBuilderImpl> pass_key)
     : input_names_to_descriptors(std::move(input_names_to_descriptors)),
       output_names_to_descriptors(std::move(output_names_to_descriptors)),
diff --git a/services/webnn/webnn_graph_impl.h b/services/webnn/webnn_graph_impl.h
index 9e7d693..aff0840 100644
--- a/services/webnn/webnn_graph_impl.h
+++ b/services/webnn/webnn_graph_impl.h
@@ -13,6 +13,7 @@
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "services/webnn/public/cpp/operand_descriptor.h"
+#include "services/webnn/public/cpp/webnn_types.h"
 #include "services/webnn/public/mojom/webnn_graph.mojom.h"
 #include "services/webnn/webnn_object_impl.h"
 
@@ -33,9 +34,9 @@
             input_names_to_descriptors,
         base::flat_map<std::string, OperandDescriptor>
             output_names_to_descriptors,
-        base::flat_map<uint64_t, base::flat_set<size_t>>
+        base::flat_map<OperandId, base::flat_set<OperationId>>
             operand_to_dependent_operations,
-        base::flat_map<uint64_t, size_t> operand_to_producing_operation,
+        base::flat_map<OperandId, OperationId> operand_to_producing_operation,
         base::PassKey<WebNNGraphBuilderImpl> pass_key);
     ~ComputeResourceInfo();
 
@@ -47,9 +48,9 @@
 
     base::flat_map<std::string, OperandDescriptor> input_names_to_descriptors;
     base::flat_map<std::string, OperandDescriptor> output_names_to_descriptors;
-    base::flat_map<uint64_t, base::flat_set<size_t>>
+    base::flat_map<OperandId, base::flat_set<OperationId>>
         operand_to_dependent_operations;
-    base::flat_map<uint64_t, size_t> operand_to_producing_operation;
+    base::flat_map<OperandId, OperationId> operand_to_producing_operation;
   };
 
   // Constructs a graph where the receiever and implementation is owned by the
diff --git a/services/webnn/webnn_graph_impl_backend_test.cc b/services/webnn/webnn_graph_impl_backend_test.cc
index e3a886e..8ddeb79 100644
--- a/services/webnn/webnn_graph_impl_backend_test.cc
+++ b/services/webnn/webnn_graph_impl_backend_test.cc
@@ -27,6 +27,7 @@
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/webnn/buildflags.h"
+#include "services/webnn/public/cpp/webnn_types.h"
 #include "services/webnn/public/mojom/features.mojom-features.h"
 #include "services/webnn/public/mojom/webnn_context.mojom.h"
 #include "services/webnn/public/mojom/webnn_context_provider.mojom.h"
@@ -129,7 +130,7 @@
       named_input_remotes_and_handles;
   named_input_remotes_and_handles.reserve(graph_info->input_operands.size());
 
-  for (uint64_t operand_id : graph_info->input_operands) {
+  for (OperandId operand_id : graph_info->input_operands) {
     const mojom::Operand& operand =
         *graph_info->id_to_operand_map.at(operand_id);
     EXPECT_TRUE(operand.name.has_value());
@@ -157,7 +158,7 @@
       named_output_remotes_and_handles;
   named_output_remotes_and_handles.reserve(graph_info->output_operands.size());
 
-  for (uint64_t operand_id : graph_info->output_operands) {
+  for (OperandId operand_id : graph_info->output_operands) {
     const mojom::Operand& operand =
         *graph_info->id_to_operand_map.at(operand_id);
     EXPECT_TRUE(operand.name.has_value());
@@ -509,8 +510,8 @@
 
 void BuildFusibleOperation(GraphInfoBuilder& builder,
                            const FusibleOperationDescriptor& operation,
-                           uint64_t input_operand_id,
-                           uint64_t output_operand_id) {
+                           OperandId input_operand_id,
+                           OperandId output_operand_id) {
   switch (operation.kind) {
     case mojom::Operation::Tag::kElu: {
       CHECK(operation.alpha.has_value());
@@ -566,8 +567,8 @@
   std::optional<OperandInfo<T>> scale;
   std::optional<OperandInfo<T>> bias;
   struct BatchNormalizationAttributes {
-    std::optional<uint64_t> scale_operand_id;
-    std::optional<uint64_t> bias_operand_id;
+    std::optional<OperandId> scale_operand_id;
+    std::optional<OperandId> bias_operand_id;
     uint32_t axis = 1;
     float epsilon = 1e-5;
   };
@@ -581,13 +582,13 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t mean_operand_id =
+    OperandId mean_operand_id =
         builder.BuildInput("mean", mean.dimensions, mean.type);
-    uint64_t variance_operand_id =
+    OperandId variance_operand_id =
         builder.BuildInput("variance", variance.dimensions, variance.type);
-    uint64_t intermediate_operand_id =
+    OperandId intermediate_operand_id =
         builder.BuildIntermediateOperand(output.dimensions, output.type);
     if (scale.has_value()) {
       attributes.scale_operand_id =
@@ -602,7 +603,7 @@
         input_operand_id, mean_operand_id, variance_operand_id,
         intermediate_operand_id, std::move(attributes));
 
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     BuildFusibleOperation(builder, fusible_operation, intermediate_operand_id,
                           output_operand_id);
@@ -785,15 +786,15 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t filter_operand_id = builder.BuildConstant(
+    OperandId filter_operand_id = builder.BuildConstant(
         filter.dimensions, filter.type,
         base::as_byte_span(base::allow_nonunique_obj, filter.values));
-    uint64_t conv2d_output_operand_id =
+    OperandId conv2d_output_operand_id =
         builder.BuildIntermediateOperand(output.dimensions, output.type);
 
-    std::optional<uint64_t> bias_operand_id;
+    std::optional<OperandId> bias_operand_id;
     if (attributes.bias.has_value()) {
       bias_operand_id = builder.BuildConstant(
           attributes.bias->dimensions, attributes.bias->type,
@@ -805,7 +806,7 @@
                         conv2d_output_operand_id, std::move(attributes),
                         bias_operand_id);
 
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     BuildFusibleOperation(builder, fusible_operation, conv2d_output_operand_id,
                           output_operand_id);
@@ -1049,9 +1050,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         helper.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t lhs_operand_id =
+    OperandId lhs_operand_id =
         builder.BuildInput("lhs", lhs.dimensions, lhs.type);
-    uint64_t rhs_operand_id =
+    OperandId rhs_operand_id =
         builder.BuildInput("rhs", rhs.dimensions, rhs.type);
     auto graph_output_type = output.type;
 #if BUILDFLAG(IS_MAC)
@@ -1062,9 +1063,9 @@
       graph_output_type = OperandDataType::kInt32;
     }
 #endif  // BUILDFLAG(IS_MAC)
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, graph_output_type);
-    uint64_t element_wise_binary_output_operand_id = output_operand_id;
+    OperandId element_wise_binary_output_operand_id = output_operand_id;
 #if BUILDFLAG(IS_MAC)
     if (output.type == OperandDataType::kUint8) {
       element_wise_binary_output_operand_id = builder.BuildIntermediateOperand(
@@ -1107,17 +1108,17 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t lhs_operand_id =
+    OperandId lhs_operand_id =
         builder.BuildInput("lhs", lhs.dimensions, lhs.type);
-    uint64_t rhs_operand_id =
+    OperandId rhs_operand_id =
         builder.BuildInput("rhs", rhs.dimensions, rhs.type);
-    uint64_t intermediate_operand_id =
+    OperandId intermediate_operand_id =
         builder.BuildIntermediateOperand(output.dimensions, output.type);
     builder.BuildElementWiseBinary(mojom::ElementWiseBinary::Kind::kAdd,
                                    lhs_operand_id, rhs_operand_id,
                                    intermediate_operand_id);
 
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     BuildFusibleOperation(builder, fusible_operation, intermediate_operand_id,
                           output_operand_id);
@@ -1185,16 +1186,16 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_operand_id =
+  OperandId input_operand_id =
       builder.BuildInput("input", {2, 5}, OperandDataType::kFloat32);
-  uint64_t output1_operand_id =
+  OperandId output1_operand_id =
       builder.BuildOutput("output1", {2, 2}, OperandDataType::kFloat32);
-  uint64_t split_operand_id =
+  OperandId split_operand_id =
       builder.BuildIntermediateOperand({2, 3}, OperandDataType::kFloat32);
   builder.BuildSplit(input_operand_id, {output1_operand_id, split_operand_id},
                      1);
 
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output2", {3, 2}, OperandDataType::kFloat32);
   builder.BuildReshape(split_operand_id, output_operand_id);
 
@@ -1236,9 +1237,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     switch (tag) {
       case mojom::Operation::Tag::kClamp:
@@ -1376,12 +1377,12 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_operand_id =
+  OperandId input_operand_id =
       builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
-  uint64_t relu1_output_id =
+  OperandId relu1_output_id =
       builder.BuildIntermediateOperand({1, 2, 3, 4}, OperandDataType::kFloat32);
   builder.BuildRelu(input_operand_id, relu1_output_id);
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {1, 2, 3, 4}, OperandDataType::kFloat32);
   builder.BuildRelu(relu1_output_id, output_operand_id);
 
@@ -1411,12 +1412,12 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_operand_id =
+  OperandId input_operand_id =
       builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
-  uint64_t relu_output_id =
+  OperandId relu_output_id =
       builder.BuildIntermediateOperand({1, 2, 3, 4}, OperandDataType::kFloat32);
   builder.BuildRelu(input_operand_id, relu_output_id);
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {1, 1, 6, 4}, OperandDataType::kFloat32);
   builder.BuildReshape(relu_output_id, output_operand_id);
 
@@ -1445,12 +1446,12 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_operand_id =
+  OperandId input_operand_id =
       builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
-  uint64_t reshape_output_id =
+  OperandId reshape_output_id =
       builder.BuildIntermediateOperand({1, 1, 6, 4}, OperandDataType::kFloat32);
   builder.BuildReshape(input_operand_id, reshape_output_id);
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {1, 1, 6, 4}, OperandDataType::kFloat32);
   builder.BuildRelu(reshape_output_id, output_operand_id);
 
@@ -1477,12 +1478,12 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_operand_id =
+  OperandId input_operand_id =
       builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
-  uint64_t reshape_output_id =
+  OperandId reshape_output_id =
       builder.BuildIntermediateOperand({1, 1, 6, 4}, OperandDataType::kFloat32);
   builder.BuildReshape(input_operand_id, reshape_output_id);
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {1, 2, 3, 4}, OperandDataType::kFloat32);
   builder.BuildReshape(reshape_output_id, output_operand_id);
 
@@ -1509,12 +1510,12 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_operand_id =
+  OperandId input_operand_id =
       builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
-  uint64_t output1_operand_id =
+  OperandId output1_operand_id =
       builder.BuildOutput("output1", {1, 1, 6, 4}, OperandDataType::kFloat32);
   builder.BuildReshape(input_operand_id, output1_operand_id);
-  uint64_t output2_operand_id =
+  OperandId output2_operand_id =
       builder.BuildOutput("output2", {1, 2, 3, 4}, OperandDataType::kFloat32);
   builder.BuildRelu(input_operand_id, output2_operand_id);
 
@@ -1536,7 +1537,7 @@
 }
 
 struct GemmAttributes {
-  std::optional<uint64_t> c_operand_id;
+  std::optional<OperandId> c_operand_id;
   // TODO(crbug.com/40206287): Add test cases for below attributes.
   float alpha = 1.0;
   float beta = 1.0;
@@ -1559,11 +1560,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_a_operand_id =
+    OperandId input_a_operand_id =
         builder.BuildInput("input_a", input_a.dimensions, input_a.type);
-    uint64_t input_b_operand_id =
+    OperandId input_b_operand_id =
         builder.BuildInput("input_b", input_b.dimensions, input_b.type);
-    uint64_t intermediate_operand_id =
+    OperandId intermediate_operand_id =
         builder.BuildIntermediateOperand(output.dimensions, output.type);
     if (input_c.has_value()) {
       attributes.c_operand_id =
@@ -1573,7 +1574,7 @@
     builder.BuildGemm(input_a_operand_id, input_b_operand_id,
                       intermediate_operand_id, std::move(attributes));
 
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     BuildFusibleOperation(builder, fusible_operation, intermediate_operand_id,
                           output_operand_id);
@@ -1635,9 +1636,9 @@
 template <typename T>
 struct GruTester {
   struct GruAttributes {
-    std::optional<uint64_t> bias_operand_id;
-    std::optional<uint64_t> recurrent_bias_operand_id;
-    std::optional<uint64_t> initial_hidden_state_operand_id;
+    std::optional<OperandId> bias_operand_id;
+    std::optional<OperandId> recurrent_bias_operand_id;
+    std::optional<OperandId> initial_hidden_state_operand_id;
     bool reset_after = true;
     bool return_sequence = false;
     mojom::RecurrentNetworkDirection direction =
@@ -1666,11 +1667,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         helper.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t weight_operand_id =
+    OperandId weight_operand_id =
         builder.BuildInput("weight", weight.dimensions, weight.type);
-    uint64_t recurrent_weight_operand_id = builder.BuildInput(
+    OperandId recurrent_weight_operand_id = builder.BuildInput(
         "recurrentWeight", recurrent_weight.dimensions, recurrent_weight.type);
 
     if (bias.has_value()) {
@@ -1688,7 +1689,7 @@
                              initial_hidden_state->values));
     }
 
-    std::vector<uint64_t> output_operand_ids;
+    std::vector<OperandId> output_operand_ids;
     output_operand_ids.reserve(outputs.size());
     for (size_t i = 0; i < outputs.size(); ++i) {
       const auto& output = outputs[i];
@@ -1981,8 +1982,8 @@
 template <typename T>
 struct GruCellTester {
   struct GruCellAttributes {
-    std::optional<uint64_t> bias_operand_id;
-    std::optional<uint64_t> recurrent_bias_operand_id;
+    std::optional<OperandId> bias_operand_id;
+    std::optional<OperandId> recurrent_bias_operand_id;
     bool reset_after = true;
     mojom::GruWeightLayout layout = mojom::GruWeightLayout::kZrn;
     std::vector<mojom::RecurrentNetworkActivation> activations{
@@ -2007,13 +2008,13 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         helper.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t weight_operand_id =
+    OperandId weight_operand_id =
         builder.BuildInput("weight", weight.dimensions, weight.type);
-    uint64_t recurrent_weight_operand_id = builder.BuildInput(
+    OperandId recurrent_weight_operand_id = builder.BuildInput(
         "recurrentWeight", recurrent_weight.dimensions, recurrent_weight.type);
-    uint64_t hidden_state_operand_id = builder.BuildInput(
+    OperandId hidden_state_operand_id = builder.BuildInput(
         "hiddenState", hidden_state.dimensions, hidden_state.type);
 
     if (bias.has_value()) {
@@ -2025,7 +2026,7 @@
           "recurrentBias", recurrent_bias->dimensions, recurrent_bias->type);
     }
 
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
 
     builder.BuildGruCell(input_operand_id, weight_operand_id,
@@ -2139,19 +2140,19 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_a_operand_id =
+  OperandId input_a_operand_id =
       builder.BuildInput("input_a", {2, 2}, OperandDataType::kFloat32);
-  uint64_t input_b_operand_id =
+  OperandId input_b_operand_id =
       builder.BuildInput("input_b", {2, 2}, OperandDataType::kFloat32);
-  uint64_t intermediate_1_operand_id =
+  OperandId intermediate_1_operand_id =
       builder.BuildIntermediateOperand({2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(input_a_operand_id, input_b_operand_id,
                     intermediate_1_operand_id, GemmAttributes());
-  uint64_t intermediate_2_operand_id =
+  OperandId intermediate_2_operand_id =
       builder.BuildIntermediateOperand({2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(input_a_operand_id, input_b_operand_id,
                     intermediate_2_operand_id, GemmAttributes());
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(intermediate_1_operand_id, intermediate_2_operand_id,
                     output_operand_id, GemmAttributes());
@@ -2175,12 +2176,12 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_a_operand_id =
+  OperandId input_a_operand_id =
       builder.BuildInput("input_a", {2, 2}, OperandDataType::kFloat32);
-  uint64_t input_b_operand_id = builder.BuildConstant(
+  OperandId input_b_operand_id = builder.BuildConstant(
       {2, 2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_data));
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(input_a_operand_id, input_b_operand_id, output_operand_id,
                     GemmAttributes());
@@ -2201,8 +2202,8 @@
   std::optional<OperandInfo<T>> scale;
   std::optional<OperandInfo<T>> bias;
   struct InstanceNormalizationAttributes {
-    std::optional<uint64_t> scale_operand_id;
-    std::optional<uint64_t> bias_operand_id;
+    std::optional<OperandId> scale_operand_id;
+    std::optional<OperandId> bias_operand_id;
     float epsilon = 1e-5;
   };
   InstanceNormalizationAttributes attributes;
@@ -2215,9 +2216,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t intermediate_operand_id =
+    OperandId intermediate_operand_id =
         builder.BuildIntermediateOperand(output.dimensions, output.type);
     if (scale.has_value()) {
       attributes.scale_operand_id =
@@ -2231,7 +2232,7 @@
     builder.BuildInstanceNormalization(
         input_operand_id, intermediate_operand_id, std::move(attributes));
 
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     BuildFusibleOperation(builder, fusible_operation, intermediate_operand_id,
                           output_operand_id);
@@ -2278,8 +2279,8 @@
   std::optional<OperandInfo<T>> scale;
   std::optional<OperandInfo<T>> bias;
   struct LayerNormalizationAttributes {
-    std::optional<uint64_t> scale_operand_id;
-    std::optional<uint64_t> bias_operand_id;
+    std::optional<OperandId> scale_operand_id;
+    std::optional<OperandId> bias_operand_id;
     std::vector<uint32_t> axes;
     float epsilon = 1e-5;
   };
@@ -2293,9 +2294,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     if (scale.has_value()) {
       attributes.scale_operand_id =
@@ -2333,9 +2334,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t intermediate_operand_id =
+    OperandId intermediate_operand_id =
         builder.BuildIntermediateOperand(output.dimensions, output.type);
     if (scale.has_value()) {
       attributes.scale_operand_id =
@@ -2349,7 +2350,7 @@
     builder.BuildLayerNormalization(input_operand_id, intermediate_operand_id,
                                     std::move(attributes));
 
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     BuildFusibleOperation(builder, fusible_operation, intermediate_operand_id,
                           output_operand_id);
@@ -2442,11 +2443,11 @@
   std::optional<OperandInfo<T>> initial_hidden_state;
   std::optional<OperandInfo<T>> initial_cell_state;
   struct LstmAttributes {
-    std::optional<uint64_t> bias_operand_id;
-    std::optional<uint64_t> recurrent_bias_operand_id;
-    std::optional<uint64_t> peephole_weight_operand_id;
-    std::optional<uint64_t> initial_hidden_state_operand_id;
-    std::optional<uint64_t> initial_cell_state_operand_id;
+    std::optional<OperandId> bias_operand_id;
+    std::optional<OperandId> recurrent_bias_operand_id;
+    std::optional<OperandId> peephole_weight_operand_id;
+    std::optional<OperandId> initial_hidden_state_operand_id;
+    std::optional<OperandId> initial_cell_state_operand_id;
     bool return_sequence = false;
     mojom::RecurrentNetworkDirection direction =
         mojom::RecurrentNetworkDirection::kForward;
@@ -2466,11 +2467,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         helper.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t weight_operand_id =
+    OperandId weight_operand_id =
         builder.BuildInput("weight", weight.dimensions, weight.type);
-    uint64_t recurrent_weight_operand_id = builder.BuildInput(
+    OperandId recurrent_weight_operand_id = builder.BuildInput(
         "recurrentWeight", recurrent_weight.dimensions, recurrent_weight.type);
 
     if (bias.has_value()) {
@@ -2496,7 +2497,7 @@
                              initial_cell_state->type);
     }
 
-    std::vector<uint64_t> output_operand_ids;
+    std::vector<OperandId> output_operand_ids;
     output_operand_ids.reserve(outputs.size());
     for (size_t i = 0; i < outputs.size(); ++i) {
       const auto& output = outputs[i];
@@ -2646,14 +2647,14 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id = builder.BuildConstant(
+    OperandId input_operand_id = builder.BuildConstant(
         {steps, batch_size, input_size}, OperandDataType::kFloat32,
         base::as_byte_span(base::allow_nonunique_obj, input_data));
-    uint64_t weight_operand_id = builder.BuildConstant(
+    OperandId weight_operand_id = builder.BuildConstant(
         {direction_count, 4 * hidden_size, input_size},
         OperandDataType::kFloat32,
         base::as_byte_span(base::allow_nonunique_obj, weight_data));
-    uint64_t recurrent_weight_operand_id = builder.BuildConstant(
+    OperandId recurrent_weight_operand_id = builder.BuildConstant(
         {direction_count, 4 * hidden_size, hidden_size},
         OperandDataType::kFloat32,
         base::as_byte_span(base::allow_nonunique_obj, recurrent_weight_data));
@@ -2673,14 +2674,14 @@
                               mojom::RecurrentNetworkActivation::kRelu,
                               mojom::RecurrentNetworkActivation::kRelu};
 
-    uint64_t output_a_operand_id = builder.BuildOutput(
+    OperandId output_a_operand_id = builder.BuildOutput(
         "output0", {direction_count, batch_size, hidden_size},
         OperandDataType::kFloat32);
-    uint64_t output_b_operand_id = builder.BuildOutput(
+    OperandId output_b_operand_id = builder.BuildOutput(
         "output1", {direction_count, batch_size, hidden_size},
         OperandDataType::kFloat32);
-    std::vector<uint64_t> output_operand_ids{output_a_operand_id,
-                                             output_b_operand_id};
+    std::vector<OperandId> output_operand_ids{output_a_operand_id,
+                                              output_b_operand_id};
     builder.BuildLstm(input_operand_id, weight_operand_id,
                       recurrent_weight_operand_id,
                       std::move(output_operand_ids), steps, hidden_size,
@@ -2698,9 +2699,9 @@
 }
 
 struct LstmCellAttributes {
-  std::optional<uint64_t> bias_operand_id;
-  std::optional<uint64_t> recurrent_bias_operand_id;
-  std::optional<uint64_t> peephole_weight_operand_id;
+  std::optional<OperandId> bias_operand_id;
+  std::optional<OperandId> recurrent_bias_operand_id;
+  std::optional<OperandId> peephole_weight_operand_id;
   mojom::LstmWeightLayout layout = mojom::LstmWeightLayout::kIofg;
   std::vector<mojom::RecurrentNetworkActivation> activations = {
       mojom::RecurrentNetworkActivation::kSigmoid,
@@ -2726,16 +2727,16 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_operand_id = builder.BuildInput(
+  OperandId input_operand_id = builder.BuildInput(
       "input", {batch_size, input_size}, OperandDataType::kFloat32);
-  uint64_t weight_operand_id = builder.BuildInput(
+  OperandId weight_operand_id = builder.BuildInput(
       "weight", {4 * hidden_size, input_size}, OperandDataType::kFloat32);
-  uint64_t recurrent_weight_operand_id =
+  OperandId recurrent_weight_operand_id =
       builder.BuildInput("recurrentWeight", {4 * hidden_size, hidden_size},
                          OperandDataType::kFloat32);
-  uint64_t hidden_state_operand_id = builder.BuildInput(
+  OperandId hidden_state_operand_id = builder.BuildInput(
       "hiddenState", {batch_size, hidden_size}, OperandDataType::kFloat32);
-  uint64_t cell_state_operand_id = builder.BuildInput(
+  OperandId cell_state_operand_id = builder.BuildInput(
       "cellState", {batch_size, hidden_size}, OperandDataType::kFloat32);
 
   LstmCellAttributes attributes;
@@ -2743,12 +2744,12 @@
                             mojom::RecurrentNetworkActivation::kRelu,
                             mojom::RecurrentNetworkActivation::kRelu};
 
-  uint64_t output_a_operand_id = builder.BuildOutput(
+  OperandId output_a_operand_id = builder.BuildOutput(
       "output0", {batch_size, hidden_size}, OperandDataType::kFloat32);
-  uint64_t output_b_operand_id = builder.BuildOutput(
+  OperandId output_b_operand_id = builder.BuildOutput(
       "output1", {batch_size, hidden_size}, OperandDataType::kFloat32);
-  std::vector<uint64_t> output_operand_ids{output_a_operand_id,
-                                           output_b_operand_id};
+  std::vector<OperandId> output_operand_ids{output_a_operand_id,
+                                            output_b_operand_id};
   builder.BuildLstmCell(input_operand_id, weight_operand_id,
                         recurrent_weight_operand_id, hidden_state_operand_id,
                         cell_state_operand_id, std::move(output_operand_ids),
@@ -2784,30 +2785,30 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_a_operand_id =
+    OperandId input_a_operand_id =
         builder.BuildInput("input_a", input_a.dimensions, input_a.type);
     if (permutation_a) {
       std::vector<uint32_t> transposed_input_a_shape =
           PermuteArray(input_a.dimensions, permutation_a.value());
-      uint64_t transposed_input_a_id = builder.BuildIntermediateOperand(
+      OperandId transposed_input_a_id = builder.BuildIntermediateOperand(
           transposed_input_a_shape, input_a.type);
       builder.BuildTranspose(input_a_operand_id, transposed_input_a_id,
                              permutation_a.value());
       input_a_operand_id = transposed_input_a_id;
     }
-    uint64_t input_b_operand_id =
+    OperandId input_b_operand_id =
         builder.BuildInput("input_b", input_b.dimensions, input_b.type);
     if (permutation_b) {
       std::vector<uint32_t> transposed_input_b_shape =
           PermuteArray(input_b.dimensions, permutation_b.value());
-      uint64_t transposed_input_b_id = builder.BuildIntermediateOperand(
+      OperandId transposed_input_b_id = builder.BuildIntermediateOperand(
           transposed_input_b_shape, input_b.type);
       builder.BuildTranspose(input_b_operand_id, transposed_input_b_id,
                              permutation_b.value());
       input_b_operand_id = transposed_input_b_id;
     }
 
-    uint64_t output_operand_id;
+    OperandId output_operand_id;
     if (fusible_operation) {
       output_operand_id =
           builder.BuildIntermediateOperand(output.dimensions, output.type);
@@ -2820,7 +2821,7 @@
                         output_operand_id);
 
     if (fusible_operation) {
-      uint64_t intermediate_operand_id = output_operand_id;
+      OperandId intermediate_operand_id = output_operand_id;
       output_operand_id =
           builder.BuildOutput("output", output.dimensions, output.type);
       BuildFusibleOperation(builder, fusible_operation.value(),
@@ -2964,28 +2965,28 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_a_operand_id =
+  OperandId input_a_operand_id =
       builder.BuildInput("input_a", {2, 2}, OperandDataType::kFloat32);
-  uint64_t input_b_operand_id =
+  OperandId input_b_operand_id =
       builder.BuildInput("input_b", {2, 2}, OperandDataType::kFloat32);
   std::vector<float> constant_data = {1, 1, 1, 1};
-  uint64_t constant_a_operand_id = builder.BuildConstant(
+  OperandId constant_a_operand_id = builder.BuildConstant(
       {2, 2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_data));
-  uint64_t constant_b_operand_id = builder.BuildConstant(
+  OperandId constant_b_operand_id = builder.BuildConstant(
       {2, 2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_data));
 
   // The order of inputs are [input_a, constant_a, input_b, constant_b].
-  uint64_t intermediate_1_operand_id =
+  OperandId intermediate_1_operand_id =
       builder.BuildIntermediateOperand({2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(input_a_operand_id, constant_a_operand_id,
                     intermediate_1_operand_id, GemmAttributes());
-  uint64_t intermediate_2_operand_id =
+  OperandId intermediate_2_operand_id =
       builder.BuildIntermediateOperand({2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(input_b_operand_id, constant_b_operand_id,
                     intermediate_2_operand_id, GemmAttributes());
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(intermediate_1_operand_id, intermediate_2_operand_id,
                     output_operand_id, GemmAttributes());
@@ -3013,28 +3014,28 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_a_operand_id =
+  OperandId input_a_operand_id =
       builder.BuildInput("input_a", {2, 2}, OperandDataType::kFloat32);
-  uint64_t input_b_operand_id =
+  OperandId input_b_operand_id =
       builder.BuildInput("input_b", {2, 2}, OperandDataType::kFloat32);
   std::vector<float> constant_data = {1, 2, 3, 4};
-  uint64_t constant_a_operand_id = builder.BuildConstant(
+  OperandId constant_a_operand_id = builder.BuildConstant(
       {2, 2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_data));
-  uint64_t constant_b_operand_id = builder.BuildConstant(
+  OperandId constant_b_operand_id = builder.BuildConstant(
       {2, 2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_data));
 
   // The order of inputs are [constant_a, input_a, constant_b, input_b].
-  uint64_t intermediate_1_operand_id =
+  OperandId intermediate_1_operand_id =
       builder.BuildIntermediateOperand({2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(constant_a_operand_id, input_a_operand_id,
                     intermediate_1_operand_id, GemmAttributes());
-  uint64_t intermediate_2_operand_id =
+  OperandId intermediate_2_operand_id =
       builder.BuildIntermediateOperand({2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(constant_b_operand_id, input_b_operand_id,
                     intermediate_2_operand_id, GemmAttributes());
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(intermediate_1_operand_id, intermediate_2_operand_id,
                     output_operand_id, GemmAttributes());
@@ -3065,21 +3066,21 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_a_operand_id =
+  OperandId input_a_operand_id =
       builder.BuildInput("input_a", {2, 2}, OperandDataType::kFloat32);
-  uint64_t input_b_operand_id =
+  OperandId input_b_operand_id =
       builder.BuildInput("input_b", {2, 2}, OperandDataType::kFloat32);
   std::vector<float> constant_data = {1, 1};
-  uint64_t constant_c_operand_id = builder.BuildConstant(
+  OperandId constant_c_operand_id = builder.BuildConstant(
       {2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_data));
   // Reshape constant_c from [2] to [1, 2] and use it as operand c for gemm.
-  uint64_t reshape_operand_id =
+  OperandId reshape_operand_id =
       builder.BuildIntermediateOperand({1, 2}, OperandDataType::kFloat32);
   builder.BuildReshape(constant_c_operand_id, reshape_operand_id);
   GemmAttributes gemm_attributes;
   gemm_attributes.c_operand_id = reshape_operand_id;
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(input_a_operand_id, input_b_operand_id, output_operand_id,
                     gemm_attributes);
@@ -3107,17 +3108,17 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_a_operand_id =
+  OperandId input_a_operand_id =
       builder.BuildInput("input_a", {1, 1, 2, 2}, OperandDataType::kFloat32);
   std::vector<float> constant_data = {1, 1};
-  uint64_t constant_b_operand_id = builder.BuildConstant(
+  OperandId constant_b_operand_id = builder.BuildConstant(
       {2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_data));
   // Reshape constant_b from [2] to [1, 2] and use it as operand b for add.
-  uint64_t reshape_operand_id =
+  OperandId reshape_operand_id =
       builder.BuildIntermediateOperand({1, 2}, OperandDataType::kFloat32);
   builder.BuildReshape(constant_b_operand_id, reshape_operand_id);
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {1, 1, 2, 2}, OperandDataType::kFloat32);
   builder.BuildElementWiseBinary(mojom::ElementWiseBinary::Kind::kAdd,
                                  input_a_operand_id, reshape_operand_id,
@@ -3143,10 +3144,10 @@
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
   std::vector<float> constant_data = {-1, 0, 1};
-  uint64_t constant_operand_id = builder.BuildConstant(
+  OperandId constant_operand_id = builder.BuildConstant(
       {3}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_data));
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {3}, OperandDataType::kFloat32);
   builder.BuildRelu(constant_operand_id, output_operand_id);
 
@@ -3168,14 +3169,14 @@
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
   std::vector<float> constant_a_data = {1, 1, 1, 1};
-  uint64_t constant_a_operand_id = builder.BuildConstant(
+  OperandId constant_a_operand_id = builder.BuildConstant(
       {2, 2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_a_data));
   std::vector<float> constant_b_data = {2, 2, 2, 2};
-  uint64_t constant_b_operand_id = builder.BuildConstant(
+  OperandId constant_b_operand_id = builder.BuildConstant(
       {2, 2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_b_data));
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {2, 2}, OperandDataType::kFloat32);
   builder.BuildElementWiseBinary(mojom::ElementWiseBinary::Kind::kAdd,
                                  constant_a_operand_id, constant_b_operand_id,
@@ -3202,23 +3203,23 @@
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
   std::vector<float> constant_a_data = {1, 1, 1, 1};
-  uint64_t constant_a_operand_id = builder.BuildConstant(
+  OperandId constant_a_operand_id = builder.BuildConstant(
       {2, 2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_a_data));
   std::vector<float> constant_b_data = {2, 2, 2, 2};
-  uint64_t constant_b_operand_id = builder.BuildConstant(
+  OperandId constant_b_operand_id = builder.BuildConstant(
       {2, 2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_b_data));
-  uint64_t intermediate_operand_id =
+  OperandId intermediate_operand_id =
       builder.BuildIntermediateOperand({2, 2}, OperandDataType::kFloat32);
   builder.BuildElementWiseBinary(mojom::ElementWiseBinary::Kind::kAdd,
                                  constant_a_operand_id, constant_b_operand_id,
                                  intermediate_operand_id);
   std::vector<float> constant_c_data = {3, 3, 3, 3};
-  uint64_t constant_c_operand_id = builder.BuildConstant(
+  OperandId constant_c_operand_id = builder.BuildConstant(
       {2, 2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_c_data));
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {2, 2}, OperandDataType::kFloat32);
   builder.BuildElementWiseBinary(mojom::ElementWiseBinary::Kind::kMul,
                                  intermediate_operand_id, constant_c_operand_id,
@@ -3251,23 +3252,23 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_a_operand_id =
+  OperandId input_a_operand_id =
       builder.BuildInput("input_a", {1, 1, 2, 2}, OperandDataType::kFloat32);
-  uint64_t input_b_operand_id =
+  OperandId input_b_operand_id =
       builder.BuildInput("input_b", {1, 1, 2, 2}, OperandDataType::kFloat32);
-  uint64_t intermediate_1_operand_id =
+  OperandId intermediate_1_operand_id =
       builder.BuildIntermediateOperand({1, 1, 2, 2}, OperandDataType::kFloat32);
   builder.BuildElementWiseBinary(mojom::ElementWiseBinary::Kind::kAdd,
                                  input_a_operand_id, input_b_operand_id,
                                  intermediate_1_operand_id);
 
   // Relu.
-  uint64_t intermediate_2_operand_id =
+  OperandId intermediate_2_operand_id =
       builder.BuildIntermediateOperand({1, 1, 2, 2}, OperandDataType::kFloat32);
   builder.BuildRelu(intermediate_1_operand_id, intermediate_2_operand_id);
 
   // Max pooling.
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {1, 1, 2, 2}, OperandDataType::kFloat32);
   builder.BuildPool2d(mojom::Pool2d::Kind::kMaxPool2d,
                       intermediate_2_operand_id, output_operand_id,
@@ -3299,18 +3300,18 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_a_operand_id =
+  OperandId input_a_operand_id =
       builder.BuildInput("input_a", {1, 1, 2, 2}, OperandDataType::kFloat32);
-  uint64_t input_b_operand_id =
+  OperandId input_b_operand_id =
       builder.BuildInput("input_b", {1, 1, 2, 2}, OperandDataType::kFloat32);
-  uint64_t intermediate_1_operand_id =
+  OperandId intermediate_1_operand_id =
       builder.BuildIntermediateOperand({1, 1, 2, 2}, OperandDataType::kFloat32);
   builder.BuildElementWiseBinary(mojom::ElementWiseBinary::Kind::kAdd,
                                  input_a_operand_id, input_b_operand_id,
                                  intermediate_1_operand_id);
 
   // Max pooling.
-  uint64_t intermediate_2_operand_id =
+  OperandId intermediate_2_operand_id =
       builder.BuildIntermediateOperand({1, 1, 2, 2}, OperandDataType::kFloat32);
   builder.BuildPool2d(mojom::Pool2d::Kind::kMaxPool2d,
                       intermediate_1_operand_id, intermediate_2_operand_id,
@@ -3320,7 +3321,7 @@
                                        .dilations = {1, 1}});
 
   // Relu.
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {1, 1, 2, 2}, OperandDataType::kFloat32);
   builder.BuildRelu(intermediate_2_operand_id, output_operand_id);
 
@@ -3348,9 +3349,9 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_a_operand_id =
+  OperandId input_a_operand_id =
       builder.BuildInput("input_a", {1, 1, 2, 2}, OperandDataType::kFloat32);
-  uint64_t intermediate_1_operand_id =
+  OperandId intermediate_1_operand_id =
       builder.BuildIntermediateOperand({1, 1, 2, 2}, OperandDataType::kFloat32);
   builder.BuildPool2d(mojom::Pool2d::Kind::kMaxPool2d, input_a_operand_id,
                       intermediate_1_operand_id,
@@ -3360,16 +3361,16 @@
                                        .dilations = {1, 1}});
 
   // Add operation.
-  uint64_t input_b_operand_id =
+  OperandId input_b_operand_id =
       builder.BuildInput("input_b", {1, 1, 2, 2}, OperandDataType::kFloat32);
-  uint64_t intermediate_2_operand_id =
+  OperandId intermediate_2_operand_id =
       builder.BuildIntermediateOperand({1, 1, 2, 2}, OperandDataType::kFloat32);
   builder.BuildElementWiseBinary(mojom::ElementWiseBinary::Kind::kAdd,
                                  intermediate_1_operand_id, input_b_operand_id,
                                  intermediate_2_operand_id);
 
   // Relu.
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {1, 1, 2, 2}, OperandDataType::kFloat32);
   builder.BuildRelu(intermediate_2_operand_id, output_operand_id);
 
@@ -3397,21 +3398,21 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_operand_id1 =
+  OperandId input_operand_id1 =
       builder.BuildInput("input_a", {4, 3}, OperandDataType::kFloat16);
-  uint64_t input_operand_id2 =
+  OperandId input_operand_id2 =
       builder.BuildInput("input_b", {1, 1, 2, 3}, OperandDataType::kFloat16);
 
-  uint64_t reshape_operand_id =
+  OperandId reshape_operand_id =
       builder.BuildIntermediateOperand({1, 2, 2, 3}, OperandDataType::kFloat16);
   builder.BuildReshape(input_operand_id1, reshape_operand_id);
 
-  uint64_t concat_operand_id =
+  OperandId concat_operand_id =
       builder.BuildIntermediateOperand({1, 3, 2, 3}, OperandDataType::kFloat16);
   builder.BuildConcat({reshape_operand_id, input_operand_id2},
                       concat_operand_id, 1);
 
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {1, 3, 2, 3}, OperandDataType::kFloat16);
   builder.BuildClamp(concat_operand_id, output_operand_id, 1.25, 8.75);
 
@@ -3458,28 +3459,28 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_operand_id =
+  OperandId input_operand_id =
       builder.BuildInput("input", {1, 1, 1, 3}, OperandDataType::kFloat32);
 
   // [[[[1 2 3]]]] with shape (1, 1, 1, 3)
   std::vector<float> constant_data_a = {1, 2, 3};
-  uint64_t constant_a_operand_id = builder.BuildConstant(
+  OperandId constant_a_operand_id = builder.BuildConstant(
       {1, 1, 1, 3}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_data_a));
 
   // [[[[-1 -2 -3]
   //    [-4 -5 -6]]]] with shape (1, 1, 2, 3)
   std::vector<float> constant_data_b = {-1, -2, -3, -4, -5, -6};
-  uint64_t constant_b_operand_id = builder.BuildConstant(
+  OperandId constant_b_operand_id = builder.BuildConstant(
       {1, 1, 2, 3}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_data_b));
 
-  uint64_t concat_operand_id =
+  OperandId concat_operand_id =
       builder.BuildIntermediateOperand({1, 1, 2, 3}, OperandDataType::kFloat32);
   builder.BuildConcat({input_operand_id, constant_a_operand_id},
                       concat_operand_id, 2);
 
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {1, 2, 2, 3}, OperandDataType::kFloat32);
   builder.BuildConcat({concat_operand_id, constant_b_operand_id},
                       output_operand_id, 1);
@@ -3517,9 +3518,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildResample2d(input_operand_id, output_operand_id, attributes);
 
@@ -3577,14 +3578,14 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_operand_id =
+  OperandId input_operand_id =
       builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
 
-  uint64_t transpose_operand_id =
+  OperandId transpose_operand_id =
       builder.BuildIntermediateOperand({2, 1, 3, 4}, OperandDataType::kFloat32);
   builder.BuildTranspose(input_operand_id, transpose_operand_id, {1, 0, 2, 3});
 
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {4, 3, 1, 2}, OperandDataType::kFloat32);
   builder.BuildTranspose(transpose_operand_id, output_operand_id, {3, 2, 1, 0});
 
@@ -3631,14 +3632,14 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_operand_id =
+  OperandId input_operand_id =
       builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
 
-  uint64_t transpose_operand_id =
+  OperandId transpose_operand_id =
       builder.BuildIntermediateOperand({4, 3, 1, 2}, OperandDataType::kFloat32);
   builder.BuildTranspose(input_operand_id, transpose_operand_id, {3, 2, 0, 1});
 
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {4, 3, 1, 2}, OperandDataType::kFloat32);
   builder.BuildRelu(transpose_operand_id, output_operand_id);
 
@@ -3689,22 +3690,22 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_operand_id =
+  OperandId input_operand_id =
       builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
 
-  uint64_t transpose_operand_id =
+  OperandId transpose_operand_id =
       builder.BuildIntermediateOperand({4, 3, 1, 2}, OperandDataType::kFloat32);
   builder.BuildTranspose(input_operand_id, transpose_operand_id, {3, 2, 0, 1});
 
-  uint64_t reshape_operand_id1 =
+  OperandId reshape_operand_id1 =
       builder.BuildIntermediateOperand({2, 2, 6}, OperandDataType::kFloat32);
   builder.BuildReshape(transpose_operand_id, reshape_operand_id1);
 
-  uint64_t reshape_operand_id2 =
+  OperandId reshape_operand_id2 =
       builder.BuildIntermediateOperand({12, 2}, OperandDataType::kFloat32);
   builder.BuildReshape(reshape_operand_id1, reshape_operand_id2);
 
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {2, 12}, OperandDataType::kFloat32);
   builder.BuildTranspose(reshape_operand_id2, output_operand_id, {1, 0});
 
@@ -3744,15 +3745,15 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_operand_id =
+  OperandId input_operand_id =
       builder.BuildInput("input", {1, 2, 3, 2}, OperandDataType::kFloat32);
-  uint64_t relu_operand_id =
+  OperandId relu_operand_id =
       builder.BuildIntermediateOperand({1, 2, 3, 2}, OperandDataType::kFloat32);
   builder.BuildRelu(input_operand_id, relu_operand_id);
 
-  uint64_t output1_operand_id =
+  OperandId output1_operand_id =
       builder.BuildOutput("output1", {3, 4}, OperandDataType::kFloat32);
-  uint64_t output2_operand_id =
+  OperandId output2_operand_id =
       builder.BuildOutput("output2", {1, 2, 2, 3}, OperandDataType::kFloat32);
   builder.BuildReshape(relu_operand_id, output1_operand_id);
   builder.BuildTranspose(relu_operand_id, output2_operand_id, {0, 3, 1, 2});
@@ -3799,14 +3800,14 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 1, 5, 5}, OperandDataType::kFloat32);
-    uint64_t filter_operand_id = builder.BuildConstant(
+    OperandId filter_operand_id = builder.BuildConstant(
         {1, 1, 3, 3}, OperandDataType::kFloat32,
         base::as_byte_span(
             base::allow_nonunique_obj,
             {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}));
-    uint64_t conv2d_output_operand_id = builder.BuildIntermediateOperand(
+    OperandId conv2d_output_operand_id = builder.BuildIntermediateOperand(
         {1, 1, 5, 5}, OperandDataType::kFloat32);
 
     Conv2dTester<float>::Conv2dAttributes attributes{
@@ -3816,7 +3817,7 @@
                                    .values = {-100}},
     };
 
-    std::optional<uint64_t> bias_operand_id;
+    std::optional<OperandId> bias_operand_id;
     if (attributes.bias.has_value()) {
       bias_operand_id = builder.BuildConstant(
           attributes.bias->dimensions, attributes.bias->type,
@@ -3828,11 +3829,11 @@
                         filter_operand_id, conv2d_output_operand_id,
                         std::move(attributes), bias_operand_id);
 
-    uint64_t relu1_output_operand_id =
+    OperandId relu1_output_operand_id =
         builder.BuildOutput("output1", {1, 1, 5, 5}, OperandDataType::kFloat32);
     builder.BuildRelu(conv2d_output_operand_id, relu1_output_operand_id);
 
-    uint64_t relu2_output_operand_id =
+    OperandId relu2_output_operand_id =
         builder.BuildOutput("output2", {1, 1, 5, 5}, OperandDataType::kFloat32);
     builder.BuildRelu(conv2d_output_operand_id, relu2_output_operand_id);
 
@@ -3864,14 +3865,14 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 1, 5, 5}, OperandDataType::kFloat32);
-    uint64_t filter_operand_id = builder.BuildConstant(
+    OperandId filter_operand_id = builder.BuildConstant(
         {1, 1, 3, 3}, OperandDataType::kFloat32,
         base::as_byte_span(
             base::allow_nonunique_obj,
             {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}));
-    uint64_t conv2d_output_operand_id = builder.BuildIntermediateOperand(
+    OperandId conv2d_output_operand_id = builder.BuildIntermediateOperand(
         {1, 1, 5, 5}, OperandDataType::kFloat32);
 
     Conv2dTester<float>::Conv2dAttributes attributes{
@@ -3881,7 +3882,7 @@
                                    .values = {-100}},
     };
 
-    std::optional<uint64_t> bias_operand_id;
+    std::optional<OperandId> bias_operand_id;
     if (attributes.bias.has_value()) {
       bias_operand_id = builder.BuildConstant(
           attributes.bias->dimensions, attributes.bias->type,
@@ -3893,11 +3894,11 @@
                         filter_operand_id, conv2d_output_operand_id,
                         std::move(attributes), bias_operand_id);
 
-    uint64_t reshape_output_operand_id =
+    OperandId reshape_output_operand_id =
         builder.BuildOutput("output1", {1, 5, 1, 5}, OperandDataType::kFloat32);
     builder.BuildReshape(conv2d_output_operand_id, reshape_output_operand_id);
 
-    uint64_t relu_output_operand_id =
+    OperandId relu_output_operand_id =
         builder.BuildOutput("output2", {1, 1, 5, 5}, OperandDataType::kFloat32);
     builder.BuildRelu(conv2d_output_operand_id, relu_output_operand_id);
 
@@ -3931,14 +3932,14 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 1, 5, 5}, OperandDataType::kFloat32);
-    uint64_t filter_operand_id = builder.BuildConstant(
+    OperandId filter_operand_id = builder.BuildConstant(
         {1, 1, 3, 3}, OperandDataType::kFloat32,
         base::as_byte_span(
             base::allow_nonunique_obj,
             {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}));
-    uint64_t conv2d_output_operand_id = builder.BuildIntermediateOperand(
+    OperandId conv2d_output_operand_id = builder.BuildIntermediateOperand(
         {1, 1, 5, 5}, OperandDataType::kFloat32);
 
     Conv2dTester<float>::Conv2dAttributes attributes{
@@ -3948,7 +3949,7 @@
                                    .values = {-100}},
     };
 
-    std::optional<uint64_t> bias_operand_id;
+    std::optional<OperandId> bias_operand_id;
     if (attributes.bias.has_value()) {
       bias_operand_id = builder.BuildConstant(
           attributes.bias->dimensions, attributes.bias->type,
@@ -3961,7 +3962,7 @@
                         std::move(attributes), bias_operand_id);
     builder.AddOutput("output2", conv2d_output_operand_id);
 
-    uint64_t relu_output_operand_id =
+    OperandId relu_output_operand_id =
         builder.BuildOutput("output1", {1, 1, 5, 5}, OperandDataType::kFloat32);
     builder.BuildRelu(conv2d_output_operand_id, relu_output_operand_id);
 
diff --git a/services/webnn/webnn_graph_impl_unittest.cc b/services/webnn/webnn_graph_impl_unittest.cc
index 14d11b1d..f140df17 100644
--- a/services/webnn/webnn_graph_impl_unittest.cc
+++ b/services/webnn/webnn_graph_impl_unittest.cc
@@ -27,6 +27,7 @@
 #include "services/webnn/public/cpp/operand_descriptor.h"
 #include "services/webnn/public/cpp/supported_data_types.h"
 #include "services/webnn/public/cpp/webnn_errors.h"
+#include "services/webnn/public/cpp/webnn_types.h"
 #include "services/webnn/public/mojom/features.mojom-features.h"
 #include "services/webnn/public/mojom/webnn_context_provider.mojom.h"
 #include "services/webnn/public/mojom/webnn_graph.mojom.h"
@@ -118,7 +119,7 @@
       mojom::GraphInfoPtr graph_info,
       WebNNGraphImpl::ComputeResourceInfo compute_resource_info,
       base::flat_map<
-          uint64_t,
+          OperandId,
           std::unique_ptr<WebNNConstantOperand>> /*constant_operands*/,
       CreateGraphImplCallback callback) override {
     FakeWebNNGraphImpl::CreateAndBuild(std::move(receiver), this, *graph_info,
@@ -324,9 +325,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildArgMinMax(kind, input_operand_id, output_operand_id, axis,
                            keep_dimensions);
@@ -406,7 +407,7 @@
       mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
           BindNewGraphBuilderRemote();
       GraphInfoBuilder builder(remote);
-      uint64_t input_operand_id =
+      OperandId input_operand_id =
           builder.BuildInput("input", {2, 3, 4, 5}, OperandDataType::kInt32);
       builder.BuildArgMinMax(kind, input_operand_id, input_operand_id,
                              /*axis=*/0,
@@ -433,9 +434,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildClamp(input_operand_id, output_operand_id,
                        attributes.min_value, attributes.max_value);
@@ -540,9 +541,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildHardSigmoid(input_operand_id, output_operand_id, alpha, beta);
     EXPECT_EQ(builder.IsValidGraphForTesting(context_properties), expected);
@@ -603,8 +604,8 @@
   std::optional<OperandInfo> scale;
   std::optional<OperandInfo> bias;
   struct BatchNormalizationAttributes {
-    std::optional<uint64_t> scale_operand_id;
-    std::optional<uint64_t> bias_operand_id;
+    std::optional<OperandId> scale_operand_id;
+    std::optional<OperandId> bias_operand_id;
     uint32_t axis = 1;
     float epsilon = 1e-5;
   };
@@ -619,13 +620,13 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t mean_operand_id =
+    OperandId mean_operand_id =
         builder.BuildInput("mean", mean.dimensions, mean.type);
-    uint64_t variance_operand_id =
+    OperandId variance_operand_id =
         builder.BuildInput("variance", variance.dimensions, variance.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
 
     if (scale) {
@@ -854,11 +855,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
-    uint64_t mean_operand_id =
+    OperandId mean_operand_id =
         builder.BuildInput("mean", {2}, OperandDataType::kFloat32);
-    uint64_t variance_operand_id =
+    OperandId variance_operand_id =
         builder.BuildInput("variance", {2}, OperandDataType::kFloat32);
     builder.BuildBatchNormalization(
         input_operand_id, mean_operand_id, variance_operand_id,
@@ -872,11 +873,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
-    uint64_t mean_operand_id =
+    OperandId mean_operand_id =
         builder.BuildInput("mean", {2}, OperandDataType::kFloat32);
-    uint64_t variance_operand_id =
+    OperandId variance_operand_id =
         builder.BuildInput("variance", {2}, OperandDataType::kFloat32);
     builder.BuildBatchNormalization(
         input_operand_id, mean_operand_id, variance_operand_id, mean_operand_id,
@@ -889,11 +890,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
-    uint64_t mean_operand_id =
+    OperandId mean_operand_id =
         builder.BuildInput("mean", {2}, OperandDataType::kFloat32);
-    uint64_t variance_operand_id =
+    OperandId variance_operand_id =
         builder.BuildInput("variance", {2}, OperandDataType::kFloat32);
     builder.BuildBatchNormalization(
         input_operand_id, mean_operand_id, variance_operand_id,
@@ -916,14 +917,14 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    std::vector<uint64_t> input_operand_ids;
+    std::vector<OperandId> input_operand_ids;
     input_operand_ids.reserve(inputs.size());
     for (size_t i = 0; i < inputs.size(); ++i) {
       input_operand_ids.push_back(
           builder.BuildInput(base::StringPrintf("input%zu", i),
                              inputs[i].dimensions, inputs[i].type));
     }
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildConcat(std::move(input_operand_ids), output_operand_id, axis);
     EXPECT_EQ(builder.IsValidGraphForTesting(context_properties), expected);
@@ -1063,18 +1064,18 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t filter_operand_id =
+    OperandId filter_operand_id =
         builder.BuildInput("filter", filter.dimensions, filter.type);
 
-    std::optional<uint64_t> bias_operand_id;
+    std::optional<OperandId> bias_operand_id;
     if (attributes.bias) {
       bias_operand_id = builder.BuildInput("bias", attributes.bias->dimensions,
                                            attributes.bias->type);
     }
 
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildConv2d(type, input_operand_id, filter_operand_id,
                         output_operand_id, std::move(attributes),
@@ -1270,9 +1271,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 1, 5, 5}, OperandDataType::kFloat32);
-    uint64_t filter_operand_id =
+    OperandId filter_operand_id =
         builder.BuildInput("filter", {1, 1, 3, 3}, OperandDataType::kFloat32);
 
     builder.BuildConv2d(mojom::Conv2d::Kind::kDirect, input_operand_id,
@@ -1287,9 +1288,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 1, 5, 5}, OperandDataType::kFloat32);
-    uint64_t filter_operand_id =
+    OperandId filter_operand_id =
         builder.BuildInput("filter", {1, 1, 3, 3}, OperandDataType::kFloat32);
 
     builder.BuildConv2d(mojom::Conv2d::Kind::kDirect, input_operand_id,
@@ -1491,9 +1492,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 1, 3, 3}, OperandDataType::kFloat32);
-    uint64_t filter_operand_id =
+    OperandId filter_operand_id =
         builder.BuildInput("filter", {1, 1, 3, 3}, OperandDataType::kFloat32);
 
     builder.BuildConv2d(mojom::Conv2d::Kind::kTransposed, input_operand_id,
@@ -1508,9 +1509,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 1, 3, 3}, OperandDataType::kFloat32);
-    uint64_t filter_operand_id =
+    OperandId filter_operand_id =
         builder.BuildInput("filter", {1, 1, 3, 3}, OperandDataType::kFloat32);
 
     builder.BuildConv2d(mojom::Conv2d::Kind::kTransposed, input_operand_id,
@@ -1536,9 +1537,9 @@
 
     // Build the graph with mojo type.
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildCumulativeSum(input_operand_id, output_operand_id, axis,
                                exclusive, reversed);
@@ -1614,7 +1615,7 @@
     uint32_t axis = 0;
     bool exclusive = false;
     bool reversed = false;
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 1, 3, 3}, OperandDataType::kFloat32);
     builder.BuildCumulativeSum(input_operand_id, input_operand_id, axis,
                                exclusive, reversed);
@@ -1636,13 +1637,13 @@
 
     // Build the graph with mojo type.
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t scale_operand_id =
+    OperandId scale_operand_id =
         builder.BuildInput("scale", scale.dimensions, scale.type);
-    uint64_t zero_point_operand_id = builder.BuildInput(
+    OperandId zero_point_operand_id = builder.BuildInput(
         "zero_point", zero_point.dimensions, zero_point.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildDequantizeLinear(input_operand_id, scale_operand_id,
                                   zero_point_operand_id, output_operand_id);
@@ -1741,11 +1742,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2, 3}, OperandDataType::kInt8);
-    uint64_t scale_operand_id =
+    OperandId scale_operand_id =
         builder.BuildInput("scale", {2, 3}, OperandDataType::kFloat32);
-    uint64_t zero_point_operand_id =
+    OperandId zero_point_operand_id =
         builder.BuildInput("zero_point", {2, 3}, OperandDataType::kInt8);
     builder.BuildDequantizeLinear(input_operand_id, scale_operand_id,
                                   zero_point_operand_id, input_operand_id);
@@ -1757,11 +1758,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2, 3}, OperandDataType::kInt8);
-    uint64_t scale_operand_id =
+    OperandId scale_operand_id =
         builder.BuildInput("scale", {2, 3}, OperandDataType::kFloat32);
-    uint64_t zero_point_operand_id =
+    OperandId zero_point_operand_id =
         builder.BuildInput("zero_point", {2, 3}, OperandDataType::kInt8);
     builder.BuildDequantizeLinear(input_operand_id, scale_operand_id,
                                   zero_point_operand_id, scale_operand_id);
@@ -1773,11 +1774,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2, 3}, OperandDataType::kInt8);
-    uint64_t scale_operand_id =
+    OperandId scale_operand_id =
         builder.BuildInput("scale", {2, 3}, OperandDataType::kFloat32);
-    uint64_t zero_point_operand_id =
+    OperandId zero_point_operand_id =
         builder.BuildInput("zero_point", {2, 3}, OperandDataType::kInt8);
     builder.BuildDequantizeLinear(input_operand_id, scale_operand_id,
                                   zero_point_operand_id, zero_point_operand_id);
@@ -1865,11 +1866,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t lhs_operand_id =
+    OperandId lhs_operand_id =
         builder.BuildInput("lhs", lhs.dimensions, lhs.type);
-    uint64_t rhs_operand_id =
+    OperandId rhs_operand_id =
         builder.BuildInput("rhs", rhs.dimensions, rhs.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildElementWiseBinary(kind, lhs_operand_id, rhs_operand_id,
                                    output_operand_id);
@@ -1989,9 +1990,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildElementWiseUnary(kind, input_operand_id, output_operand_id);
     EXPECT_EQ(builder.IsValidGraphForTesting(context_properties), expected);
@@ -2313,9 +2314,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildElu(input_operand_id, output_operand_id, alpha);
 
@@ -2369,7 +2370,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2}, OperandDataType::kFloat32);
     builder.BuildElu(input_operand_id, input_operand_id, /*alpha*/ 1.0);
     EXPECT_FALSE(builder.IsValidGraphForTesting(context_properties));
@@ -2388,9 +2389,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildExpand(input_operand_id, output_operand_id);
 
@@ -2456,7 +2457,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2}, OperandDataType::kFloat32);
     builder.BuildExpand(input_operand_id, input_operand_id);
     EXPECT_FALSE(builder.IsValidGraphForTesting(context_properties));
@@ -2481,11 +2482,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t indices_operand_id = builder.BuildInput(
+    OperandId indices_operand_id = builder.BuildInput(
         "indices", attributes.indices.dimensions, attributes.indices.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildGather(input_operand_id, indices_operand_id, output_operand_id,
                         attributes.axis);
@@ -2573,9 +2574,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2, 3}, OperandDataType::kFloat32);
-    uint64_t indices_operand_id =
+    OperandId indices_operand_id =
         builder.BuildInput("indices", {2}, OperandDataType::kUint32);
     builder.BuildGather(input_operand_id, indices_operand_id, input_operand_id,
                         /*axis*/ 0);
@@ -2587,9 +2588,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {3}, OperandDataType::kUint32);
-    uint64_t indices_operand_id =
+    OperandId indices_operand_id =
         builder.BuildInput("indices", {3}, OperandDataType::kUint32);
     builder.BuildGather(input_operand_id, indices_operand_id,
                         indices_operand_id, /*axis*/ 0);
@@ -2610,11 +2611,11 @@
 
     // Build the graph with mojo type.
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t indices_operand_id = builder.BuildInput(
+    OperandId indices_operand_id = builder.BuildInput(
         "indices", attributes.indices.dimensions, attributes.indices.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildGatherElements(input_operand_id, indices_operand_id,
                                 output_operand_id, attributes.axis);
@@ -2708,9 +2709,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2, 3}, OperandDataType::kFloat32);
-    uint64_t indices_operand_id =
+    OperandId indices_operand_id =
         builder.BuildInput("indices", {2, 3}, OperandDataType::kUint32);
     builder.BuildGatherElements(input_operand_id, indices_operand_id,
                                 input_operand_id,
@@ -2723,9 +2724,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {3}, OperandDataType::kUint32);
-    uint64_t indices_operand_id =
+    OperandId indices_operand_id =
         builder.BuildInput("indices", {3}, OperandDataType::kUint32);
     builder.BuildGatherElements(input_operand_id, indices_operand_id,
                                 indices_operand_id, /*axis=*/0);
@@ -2746,11 +2747,11 @@
 
     // Build the graph with mojo type.
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t indices_operand_id =
+    OperandId indices_operand_id =
         builder.BuildInput("indices", indices.dimensions, indices.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildGatherND(input_operand_id, indices_operand_id,
                           output_operand_id);
@@ -2828,9 +2829,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2, 3}, OperandDataType::kUint32);
-    uint64_t indices_operand_id =
+    OperandId indices_operand_id =
         builder.BuildInput("indices", {2, 1}, OperandDataType::kUint32);
     builder.BuildGatherND(input_operand_id, indices_operand_id,
                           input_operand_id);
@@ -2842,9 +2843,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2, 1}, OperandDataType::kUint32);
-    uint64_t indices_operand_id =
+    OperandId indices_operand_id =
         builder.BuildInput("indices", {2, 1}, OperandDataType::kUint32);
     builder.BuildGatherND(input_operand_id, indices_operand_id,
                           indices_operand_id);
@@ -2864,9 +2865,9 @@
 
     // Build the graph with mojo type.
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildGelu(input_operand_id, output_operand_id);
     EXPECT_EQ(builder.IsValidGraphForTesting(context_properties), expected);
@@ -2910,7 +2911,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1}, OperandDataType::kFloat16);
     builder.BuildGelu(input_operand_id, input_operand_id);
     EXPECT_FALSE(builder.IsValidGraphForTesting(context_properties));
@@ -2922,7 +2923,7 @@
   OperandInfo b;
   std::optional<OperandInfo> c;
   struct GemmAttributes {
-    std::optional<uint64_t> c_operand_id;
+    std::optional<OperandId> c_operand_id;
     float alpha = 1.0;
     float beta = 1.0;
     bool a_transpose = false;
@@ -2939,9 +2940,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t a_operand_id = builder.BuildInput("a", a.dimensions, a.type);
-    uint64_t b_operand_id = builder.BuildInput("b", b.dimensions, b.type);
-    uint64_t output_operand_id =
+    OperandId a_operand_id = builder.BuildInput("a", a.dimensions, a.type);
+    OperandId b_operand_id = builder.BuildInput("b", b.dimensions, b.type);
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
 
     if (c) {
@@ -3066,9 +3067,9 @@
 
 struct GruTester {
   struct GruAttributes {
-    std::optional<uint64_t> bias_operand_id;
-    std::optional<uint64_t> recurrent_bias_operand_id;
-    std::optional<uint64_t> initial_hidden_state_operand_id;
+    std::optional<OperandId> bias_operand_id;
+    std::optional<OperandId> recurrent_bias_operand_id;
+    std::optional<OperandId> initial_hidden_state_operand_id;
     bool reset_after = true;
     bool return_sequence = false;
     mojom::RecurrentNetworkDirection direction =
@@ -3098,14 +3099,14 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t weight_operand_id =
+    OperandId weight_operand_id =
         builder.BuildInput("weight", weight.dimensions, weight.type);
-    uint64_t recurrent_weight_operand_id = builder.BuildInput(
+    OperandId recurrent_weight_operand_id = builder.BuildInput(
         "recurrentWeight", recurrent_weight.dimensions, recurrent_weight.type);
 
-    std::vector<uint64_t> output_operand_ids;
+    std::vector<OperandId> output_operand_ids;
     output_operand_ids.reserve(outputs.size());
     for (size_t i = 0; i < outputs.size(); ++i) {
       output_operand_ids.push_back(
@@ -3255,16 +3256,16 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id = builder.BuildInput(
+    OperandId input_operand_id = builder.BuildInput(
         "input", {steps, batch_size, input_size}, OperandDataType::kFloat32);
-    uint64_t weight_operand_id = builder.BuildInput(
+    OperandId weight_operand_id = builder.BuildInput(
         "weight", {num_directions, 3 * hidden_size, input_size},
         OperandDataType::kFloat32);
-    uint64_t recurrent_weight_operand_id = builder.BuildInput(
+    OperandId recurrent_weight_operand_id = builder.BuildInput(
         "recurrentWeight", {num_directions, 3 * hidden_size, hidden_size},
         OperandDataType::kFloat32);
 
-    uint64_t initial_hidden_state_operand_id = builder.BuildInput(
+    OperandId initial_hidden_state_operand_id = builder.BuildInput(
         "initialHiddenState", {num_directions, batch_size, hidden_size},
         OperandDataType::kFloat32);
 
@@ -3279,8 +3280,8 @@
 
 struct GruCellTester {
   struct GruCellAttributes {
-    std::optional<uint64_t> bias_operand_id;
-    std::optional<uint64_t> recurrent_bias_operand_id;
+    std::optional<OperandId> bias_operand_id;
+    std::optional<OperandId> recurrent_bias_operand_id;
     bool reset_after = true;
     mojom::GruWeightLayout layout = mojom::GruWeightLayout::kZrn;
     std::vector<mojom::RecurrentNetworkActivation> activations = {
@@ -3306,13 +3307,13 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t weight_operand_id =
+    OperandId weight_operand_id =
         builder.BuildInput("weight", weight.dimensions, weight.type);
-    uint64_t recurrent_weight_operand_id = builder.BuildInput(
+    OperandId recurrent_weight_operand_id = builder.BuildInput(
         "recurrentWeight", recurrent_weight.dimensions, recurrent_weight.type);
-    uint64_t hidden_state_operand_id = builder.BuildInput(
+    OperandId hidden_state_operand_id = builder.BuildInput(
         "hiddenState", hidden_state.dimensions, hidden_state.type);
 
     if (bias.has_value()) {
@@ -3324,7 +3325,7 @@
           "recurrentBias", recurrent_bias->dimensions, recurrent_bias->type);
     }
 
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
 
     builder.BuildGruCell(input_operand_id, weight_operand_id,
@@ -3670,15 +3671,15 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id = builder.BuildInput(
+    OperandId input_operand_id = builder.BuildInput(
         "input", {batch_size, input_size}, OperandDataType::kFloat32);
-    uint64_t weight_operand_id = builder.BuildInput(
+    OperandId weight_operand_id = builder.BuildInput(
         "weight", {3 * hidden_size, input_size}, OperandDataType::kFloat32);
-    uint64_t recurrent_weight_operand_id =
+    OperandId recurrent_weight_operand_id =
         builder.BuildInput("recurrentWeight", {3 * hidden_size, hidden_size},
                            OperandDataType::kFloat32);
 
-    uint64_t hidden_state_operand_id = builder.BuildInput(
+    OperandId hidden_state_operand_id = builder.BuildInput(
         "hiddenState", {batch_size, hidden_size}, OperandDataType::kFloat32);
 
     builder.BuildGruCell(input_operand_id, weight_operand_id,
@@ -3694,8 +3695,8 @@
   std::optional<OperandInfo> scale;
   std::optional<OperandInfo> bias;
   struct InstanceNormalizationAttributes {
-    std::optional<uint64_t> scale_operand_id;
-    std::optional<uint64_t> bias_operand_id;
+    std::optional<OperandId> scale_operand_id;
+    std::optional<OperandId> bias_operand_id;
     float epsilon = 1e-5;
   };
   InstanceNormalizationAttributes attributes;
@@ -3711,9 +3712,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
 
     if (scale) {
@@ -3853,7 +3854,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
     builder.BuildInstanceNormalization(
         input_operand_id, input_operand_id,
@@ -3866,9 +3867,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
-    uint64_t scale_operand_id =
+    OperandId scale_operand_id =
         builder.BuildInput("scale", {2}, OperandDataType::kFloat32);
 
     InstanceNormalizationTester::InstanceNormalizationAttributes attributes;
@@ -3884,9 +3885,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
-    uint64_t bias_operand_id =
+    OperandId bias_operand_id =
         builder.BuildInput("bias", {2}, OperandDataType::kFloat32);
 
     InstanceNormalizationTester::InstanceNormalizationAttributes attributes;
@@ -3903,8 +3904,8 @@
   std::optional<OperandInfo> scale;
   std::optional<OperandInfo> bias;
   struct LayerNormalizationAttributes {
-    std::optional<uint64_t> scale_operand_id;
-    std::optional<uint64_t> bias_operand_id;
+    std::optional<OperandId> scale_operand_id;
+    std::optional<OperandId> bias_operand_id;
     std::vector<uint32_t> axes;
     float epsilon = 1e-5;
   };
@@ -3919,9 +3920,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
 
     if (scale.has_value()) {
@@ -4053,7 +4054,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
     builder.BuildLayerNormalization(
         input_operand_id, input_operand_id,
@@ -4066,9 +4067,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
-    uint64_t scale_operand_id =
+    OperandId scale_operand_id =
         builder.BuildInput("scale", {1, 2, 3, 4}, OperandDataType::kFloat32);
 
     LayerNormalizationTester::LayerNormalizationAttributes attributes;
@@ -4085,9 +4086,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 2, 3, 4}, OperandDataType::kFloat32);
-    uint64_t bias_operand_id =
+    OperandId bias_operand_id =
         builder.BuildInput("bias", {1, 2, 3, 4}, OperandDataType::kFloat32);
 
     LayerNormalizationTester::LayerNormalizationAttributes attributes;
@@ -4102,11 +4103,11 @@
 
 struct LstmTester {
   struct LstmAttributes {
-    std::optional<uint64_t> bias_operand_id;
-    std::optional<uint64_t> recurrent_bias_operand_id;
-    std::optional<uint64_t> peephole_weight_operand_id;
-    std::optional<uint64_t> initial_hidden_state_operand_id;
-    std::optional<uint64_t> initial_cell_state_operand_id;
+    std::optional<OperandId> bias_operand_id;
+    std::optional<OperandId> recurrent_bias_operand_id;
+    std::optional<OperandId> peephole_weight_operand_id;
+    std::optional<OperandId> initial_hidden_state_operand_id;
+    std::optional<OperandId> initial_cell_state_operand_id;
     bool return_sequence = false;
     mojom::RecurrentNetworkDirection direction =
         mojom::RecurrentNetworkDirection::kForward;
@@ -4138,14 +4139,14 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t weight_operand_id =
+    OperandId weight_operand_id =
         builder.BuildInput("weight", weight.dimensions, weight.type);
-    uint64_t recurrent_weight_operand_id = builder.BuildInput(
+    OperandId recurrent_weight_operand_id = builder.BuildInput(
         "recurrentWeight", recurrent_weight.dimensions, recurrent_weight.type);
 
-    std::vector<uint64_t> output_operand_ids;
+    std::vector<OperandId> output_operand_ids;
     output_operand_ids.reserve(outputs.size());
     for (size_t i = 0; i < outputs.size(); ++i) {
       output_operand_ids.push_back(
@@ -4293,16 +4294,16 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id = builder.BuildInput(
+    OperandId input_operand_id = builder.BuildInput(
         "input", {steps, batch_size, input_size}, OperandDataType::kFloat32);
-    uint64_t weight_operand_id = builder.BuildInput(
+    OperandId weight_operand_id = builder.BuildInput(
         "weight", {direction_count, 4 * hidden_size, input_size},
         OperandDataType::kFloat32);
-    uint64_t recurrent_weight_operand_id = builder.BuildInput(
+    OperandId recurrent_weight_operand_id = builder.BuildInput(
         "recurrentWeight", {direction_count, 4 * hidden_size, hidden_size},
         OperandDataType::kFloat32);
 
-    uint64_t output_operand_id = builder.BuildOutput(
+    OperandId output_operand_id = builder.BuildOutput(
         "output", {direction_count, batch_size, hidden_size},
         OperandDataType::kFloat32);
     builder.BuildLstm(input_operand_id, weight_operand_id,
@@ -4324,19 +4325,19 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id = builder.BuildInput(
+    OperandId input_operand_id = builder.BuildInput(
         "input", {steps, batch_size, input_size}, OperandDataType::kFloat32);
-    uint64_t weight_operand_id = builder.BuildInput(
+    OperandId weight_operand_id = builder.BuildInput(
         "weight", {direction_count, 4 * hidden_size, input_size},
         OperandDataType::kFloat32);
-    uint64_t recurrent_weight_operand_id = builder.BuildInput(
+    OperandId recurrent_weight_operand_id = builder.BuildInput(
         "recurrentWeight", {direction_count, 4 * hidden_size, hidden_size},
         OperandDataType::kFloat32);
 
-    uint64_t initial_cell_state_operand_id = builder.BuildInput(
+    OperandId initial_cell_state_operand_id = builder.BuildInput(
         "initialCellState", {direction_count, batch_size, hidden_size},
         OperandDataType::kFloat32);
-    uint64_t output_operand_id = builder.BuildOutput(
+    OperandId output_operand_id = builder.BuildOutput(
         "output", {direction_count, batch_size, hidden_size},
         OperandDataType::kFloat32);
 
@@ -4351,9 +4352,9 @@
 
 struct LstmCellTester {
   struct LstmCellAttributes {
-    std::optional<uint64_t> bias_operand_id;
-    std::optional<uint64_t> recurrent_bias_operand_id;
-    std::optional<uint64_t> peephole_weight_operand_id;
+    std::optional<OperandId> bias_operand_id;
+    std::optional<OperandId> recurrent_bias_operand_id;
+    std::optional<OperandId> peephole_weight_operand_id;
     mojom::LstmWeightLayout layout = mojom::LstmWeightLayout::kIofg;
     std::vector<mojom::RecurrentNetworkActivation> activations = {
         mojom::RecurrentNetworkActivation::kSigmoid,
@@ -4381,18 +4382,18 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t weight_operand_id =
+    OperandId weight_operand_id =
         builder.BuildInput("weight", weight.dimensions, weight.type);
-    uint64_t recurrent_weight_operand_id = builder.BuildInput(
+    OperandId recurrent_weight_operand_id = builder.BuildInput(
         "recurrentWeight", recurrent_weight.dimensions, recurrent_weight.type);
-    uint64_t hidden_state_operand_id = builder.BuildInput(
+    OperandId hidden_state_operand_id = builder.BuildInput(
         "hiddenState", hidden_state.dimensions, hidden_state.type);
-    uint64_t cell_state_operand_id =
+    OperandId cell_state_operand_id =
         builder.BuildInput("cellState", cell_state.dimensions, cell_state.type);
 
-    std::vector<uint64_t> output_operand_ids;
+    std::vector<OperandId> output_operand_ids;
     output_operand_ids.reserve(outputs.size());
     for (size_t i = 0; i < outputs.size(); ++i) {
       output_operand_ids.push_back(
@@ -4598,18 +4599,18 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id = builder.BuildInput(
+    OperandId input_operand_id = builder.BuildInput(
         "input", {batch_size, input_size}, OperandDataType::kFloat32);
-    uint64_t weight_operand_id = builder.BuildInput(
+    OperandId weight_operand_id = builder.BuildInput(
         "weight", {4 * hidden_size, input_size}, OperandDataType::kFloat32);
-    uint64_t recurrent_weight_operand_id =
+    OperandId recurrent_weight_operand_id =
         builder.BuildInput("recurrentWeight", {4 * hidden_size, hidden_size},
                            OperandDataType::kFloat32);
-    uint64_t hidden_state_operand_id = builder.BuildInput(
+    OperandId hidden_state_operand_id = builder.BuildInput(
         "hiddenState", {batch_size, hidden_size}, OperandDataType::kFloat32);
-    uint64_t cell_state_operand_id = builder.BuildInput(
+    OperandId cell_state_operand_id = builder.BuildInput(
         "cellState", {batch_size, hidden_size}, OperandDataType::kFloat32);
-    uint64_t output_operand_id = builder.BuildOutput(
+    OperandId output_operand_id = builder.BuildOutput(
         "output", {batch_size, hidden_size}, OperandDataType::kFloat32);
 
     builder.BuildLstmCell(input_operand_id, weight_operand_id,
@@ -4634,9 +4635,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t a_operand_id = builder.BuildInput("a", a.dimensions, a.type);
-    uint64_t b_operand_id = builder.BuildInput("b", b.dimensions, b.type);
-    uint64_t output_operand_id =
+    OperandId a_operand_id = builder.BuildInput("a", a.dimensions, a.type);
+    OperandId b_operand_id = builder.BuildInput("b", b.dimensions, b.type);
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
 
     builder.BuildMatmul(a_operand_id, b_operand_id, output_operand_id);
@@ -4744,9 +4745,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t a_operand_id =
+    OperandId a_operand_id =
         builder.BuildInput("a", {2, 3}, OperandDataType::kFloat32);
-    uint64_t b_operand_id =
+    OperandId b_operand_id =
         builder.BuildInput("b", {3, 4}, OperandDataType::kFloat32);
     builder.BuildMatmul(a_operand_id, b_operand_id, a_operand_id);
     EXPECT_FALSE(builder.IsValidGraphForTesting(context_properties));
@@ -4769,9 +4770,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildPad(input_operand_id, output_operand_id, beginning_padding,
                      ending_padding, mode, value);
@@ -4843,7 +4844,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2, 3}, OperandDataType::kFloat32);
     builder.BuildPad(input_operand_id, input_operand_id, {1, 1}, {1, 1},
                      mojom::PaddingMode::Tag::kConstant, 0);
@@ -4878,9 +4879,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildPool2d(kind, input_operand_id, output_operand_id,
                         std::move(attributes));
@@ -5042,11 +5043,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t slope_operand_id =
+    OperandId slope_operand_id =
         builder.BuildInput("slope", slope.dimensions, slope.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildPrelu(input_operand_id, slope_operand_id, output_operand_id);
     EXPECT_EQ(builder.IsValidGraphForTesting(context_properties), expected);
@@ -5153,9 +5154,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2, 3}, OperandDataType::kFloat32);
-    uint64_t slope_operand_id =
+    OperandId slope_operand_id =
         builder.BuildInput("slope", {2, 3}, OperandDataType::kFloat32);
     builder.BuildPrelu(input_operand_id, slope_operand_id, input_operand_id);
     EXPECT_FALSE(builder.IsValidGraphForTesting(context_properties));
@@ -5166,9 +5167,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2, 3}, OperandDataType::kFloat32);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", {2, 3}, OperandDataType::kFloat32);
     builder.BuildPrelu(input_operand_id, output_operand_id, output_operand_id);
     EXPECT_FALSE(builder.IsValidGraphForTesting(context_properties));
@@ -5189,13 +5190,13 @@
 
     // Build the graph with mojo type.
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t scale_operand_id =
+    OperandId scale_operand_id =
         builder.BuildInput("scale", scale.dimensions, scale.type);
-    uint64_t zero_point_operand_id = builder.BuildInput(
+    OperandId zero_point_operand_id = builder.BuildInput(
         "zero_point", zero_point.dimensions, zero_point.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildQuantizeLinear(input_operand_id, scale_operand_id,
                                 zero_point_operand_id, output_operand_id);
@@ -5293,11 +5294,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2, 3}, OperandDataType::kFloat32);
-    uint64_t scale_operand_id =
+    OperandId scale_operand_id =
         builder.BuildInput("scale", {2, 3}, OperandDataType::kFloat32);
-    uint64_t zero_point_operand_id =
+    OperandId zero_point_operand_id =
         builder.BuildInput("zero_point", {2, 3}, OperandDataType::kInt8);
     builder.BuildQuantizeLinear(input_operand_id, scale_operand_id,
                                 zero_point_operand_id, input_operand_id);
@@ -5309,11 +5310,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2, 3}, OperandDataType::kFloat32);
-    uint64_t scale_operand_id =
+    OperandId scale_operand_id =
         builder.BuildInput("scale", {2, 3}, OperandDataType::kFloat32);
-    uint64_t zero_point_operand_id =
+    OperandId zero_point_operand_id =
         builder.BuildInput("zero_point", {2, 3}, OperandDataType::kInt8);
     builder.BuildQuantizeLinear(input_operand_id, scale_operand_id,
                                 zero_point_operand_id, scale_operand_id);
@@ -5325,11 +5326,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2, 3}, OperandDataType::kFloat32);
-    uint64_t scale_operand_id =
+    OperandId scale_operand_id =
         builder.BuildInput("scale", {2, 3}, OperandDataType::kFloat32);
-    uint64_t zero_point_operand_id =
+    OperandId zero_point_operand_id =
         builder.BuildInput("zero_point", {2, 3}, OperandDataType::kInt8);
     builder.BuildQuantizeLinear(input_operand_id, scale_operand_id,
                                 zero_point_operand_id, zero_point_operand_id);
@@ -5352,9 +5353,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildReduce(kind, input_operand_id, output_operand_id, axes,
                         keep_dimensions);
@@ -5611,7 +5612,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2, 3}, OperandDataType::kFloat32);
     builder.BuildReduce(mojom::Reduce::Kind::kSumSquare, input_operand_id,
                         input_operand_id, {0}, false);
@@ -5631,9 +5632,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildRelu(input_operand_id, output_operand_id);
     EXPECT_EQ(builder.IsValidGraphForTesting(context_properties), expected);
@@ -5702,9 +5703,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildResample2d(input_operand_id, output_operand_id, attributes);
     EXPECT_EQ(builder.IsValidGraphForTesting(context_properties), expected);
@@ -5911,7 +5912,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {1, 1, 2, 4}, OperandDataType::kFloat32);
     builder.BuildResample2d(input_operand_id, input_operand_id,
                             Resample2dTester::Resample2dAttributes{});
@@ -5932,9 +5933,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildReshape(input_operand_id, output_operand_id);
     EXPECT_EQ(builder.IsValidGraphForTesting(context_properties), expected);
@@ -5990,9 +5991,9 @@
 
     // Build the graph with mojo type.
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildReverse(input_operand_id, output_operand_id, std::move(axes));
     EXPECT_EQ(builder.IsValidGraphForTesting(context_properties), expected);
@@ -6043,7 +6044,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {3, 3}, OperandDataType::kFloat32);
     builder.BuildReverse(input_operand_id, input_operand_id, /*axes=*/{1});
     EXPECT_FALSE(builder.IsValidGraphForTesting(context_properties));
@@ -6065,13 +6066,13 @@
 
     // Build the graph with mojo type.
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t indices_operand_id =
+    OperandId indices_operand_id =
         builder.BuildInput("indices", indices.dimensions, indices.type);
-    uint64_t updates_operand_id =
+    OperandId updates_operand_id =
         builder.BuildInput("updates", updates.dimensions, updates.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildScatterElements(input_operand_id, indices_operand_id,
                                  updates_operand_id, output_operand_id, axis);
@@ -6210,11 +6211,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {3, 3}, OperandDataType::kFloat32);
-    uint64_t indices_operand_id =
+    OperandId indices_operand_id =
         builder.BuildInput("indices", {2, 3}, OperandDataType::kUint32);
-    uint64_t updates_operand_id =
+    OperandId updates_operand_id =
         builder.BuildInput("updates", {2, 3}, OperandDataType::kFloat32);
     builder.BuildScatterElements(input_operand_id, indices_operand_id,
                                  updates_operand_id, input_operand_id,
@@ -6237,13 +6238,13 @@
 
     // Build the graph with mojo type.
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t indices_operand_id =
+    OperandId indices_operand_id =
         builder.BuildInput("indices", indices.dimensions, indices.type);
-    uint64_t updates_operand_id =
+    OperandId updates_operand_id =
         builder.BuildInput("updates", updates.dimensions, updates.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildScatterND(input_operand_id, indices_operand_id,
                            updates_operand_id, output_operand_id);
@@ -6342,11 +6343,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {4, 4, 4}, OperandDataType::kFloat32);
-    uint64_t indices_operand_id =
+    OperandId indices_operand_id =
         builder.BuildInput("indices", {2, 1}, OperandDataType::kUint32);
-    uint64_t updates_operand_id =
+    OperandId updates_operand_id =
         builder.BuildInput("updates", {2, 4, 4}, OperandDataType::kFloat32);
     builder.BuildScatterND(input_operand_id, indices_operand_id,
                            updates_operand_id, input_operand_id);
@@ -6374,9 +6375,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildSlice(input_operand_id, output_operand_id, attributes.starts,
                        attributes.sizes, attributes.strides);
@@ -6480,9 +6481,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     switch (kind) {
       case FloatingPointUnaryKind::kHardSwish:
@@ -6556,7 +6557,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2}, OperandDataType::kFloat32);
     builder.BuildLeakyRelu(input_operand_id, input_operand_id,
                            /*alpha*/ 1.0);
@@ -6569,9 +6570,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2}, OperandDataType::kFloat32);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", {2}, OperandDataType::kFloat32);
     builder.BuildLeakyRelu(input_operand_id, output_operand_id,
                            /*alpha*/ NAN);
@@ -6584,7 +6585,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2}, OperandDataType::kFloat32);
     builder.BuildLinear(input_operand_id, input_operand_id,
                         /*alpha*/ 1.0, /*beta*/ 0.0);
@@ -6597,9 +6598,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2}, OperandDataType::kFloat32);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", {2}, OperandDataType::kFloat32);
     builder.BuildLinear(input_operand_id, output_operand_id,
                         /*alpha*/ NAN, /*beta*/ 0.0);
@@ -6612,9 +6613,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2}, OperandDataType::kFloat32);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", {2}, OperandDataType::kFloat32);
     builder.BuildLinear(input_operand_id, output_operand_id,
                         /*alpha*/ 1.0, /*beta*/ NAN);
@@ -6628,7 +6629,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2}, OperandDataType::kFloat32);
     builder.BuildSigmoid(input_operand_id, input_operand_id);
 
@@ -6640,7 +6641,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {2}, OperandDataType::kFloat32);
     builder.BuildTanh(input_operand_id, input_operand_id);
 
@@ -6661,9 +6662,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildSoftmax(input_operand_id, output_operand_id, axis);
     EXPECT_EQ(builder.IsValidGraphForTesting(context_properties), expected);
@@ -6749,9 +6750,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildSoftplus(input_operand_id, output_operand_id);
     EXPECT_EQ(builder.IsValidGraphForTesting(context_properties), expected);
@@ -6797,7 +6798,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {4, 6}, OperandDataType::kFloat32);
     builder.BuildSoftplus(input_operand_id, input_operand_id);
     EXPECT_FALSE(builder.IsValidGraphForTesting(context_properties));
@@ -6816,9 +6817,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildSoftsign(input_operand_id, output_operand_id);
     EXPECT_EQ(builder.IsValidGraphForTesting(context_properties), expected);
@@ -6865,7 +6866,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {4, 6}, OperandDataType::kFloat32);
     builder.BuildSoftsign(input_operand_id, input_operand_id);
     EXPECT_FALSE(builder.IsValidGraphForTesting(context_properties));
@@ -6885,10 +6886,10 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
 
-    std::vector<uint64_t> output_operand_ids;
+    std::vector<OperandId> output_operand_ids;
     for (size_t i = 0; i < outputs.size(); ++i) {
       output_operand_ids.push_back(
           builder.BuildOutput("output" + base::NumberToString(i),
@@ -6975,7 +6976,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id = builder.BuildInput("input", {4, 6}, kFloat32);
+    OperandId input_operand_id = builder.BuildInput("input", {4, 6}, kFloat32);
 
     builder.BuildSplit(input_operand_id, {input_operand_id}, 0);
     builder.BuildSplit(input_operand_id,
@@ -6997,9 +6998,9 @@
 
     // Build the graph with mojo type.
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildTile(input_operand_id, output_operand_id,
                       std::move(repetitions));
@@ -7085,7 +7086,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {4, 6}, OperandDataType::kFloat32);
     builder.BuildTile(input_operand_id, input_operand_id,
                       std::vector<uint32_t>{1, 2});
@@ -7106,9 +7107,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildTranspose(input_operand_id, output_operand_id,
                            std::move(permutation));
@@ -7196,9 +7197,9 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", input.dimensions, input.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildTriangular(input_operand_id, output_operand_id, upper,
                             diagonal);
@@ -7239,7 +7240,7 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t input_operand_id =
+    OperandId input_operand_id =
         builder.BuildInput("input", {4, 6}, OperandDataType::kFloat32);
 
     builder.BuildTriangular(input_operand_id, input_operand_id,
@@ -7262,13 +7263,13 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         test.BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t condition_operand_id =
+    OperandId condition_operand_id =
         builder.BuildInput("condition", condition.dimensions, condition.type);
-    uint64_t true_value_operand_id = builder.BuildInput(
+    OperandId true_value_operand_id = builder.BuildInput(
         "true_value", true_value.dimensions, true_value.type);
-    uint64_t false_value_operand_id = builder.BuildInput(
+    OperandId false_value_operand_id = builder.BuildInput(
         "false_value", false_value.dimensions, false_value.type);
-    uint64_t output_operand_id =
+    OperandId output_operand_id =
         builder.BuildOutput("output", output.dimensions, output.type);
     builder.BuildWhere(condition_operand_id, true_value_operand_id,
                        false_value_operand_id, output_operand_id);
@@ -7403,11 +7404,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t condition_operand_id =
+    OperandId condition_operand_id =
         builder.BuildInput("condition", {2, 4}, OperandDataType::kUint8);
-    uint64_t true_value_operand_id =
+    OperandId true_value_operand_id =
         builder.BuildInput("true_value", {2, 4}, OperandDataType::kFloat32);
-    uint64_t false_value_operand_id =
+    OperandId false_value_operand_id =
         builder.BuildInput("false_value", {2, 4}, OperandDataType::kFloat32);
     builder.BuildWhere(condition_operand_id, true_value_operand_id,
                        false_value_operand_id, condition_operand_id);
@@ -7419,11 +7420,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t condition_operand_id =
+    OperandId condition_operand_id =
         builder.BuildInput("condition", {2, 4}, OperandDataType::kUint8);
-    uint64_t true_value_operand_id =
+    OperandId true_value_operand_id =
         builder.BuildInput("true_value", {2, 4}, OperandDataType::kFloat32);
-    uint64_t false_value_operand_id =
+    OperandId false_value_operand_id =
         builder.BuildInput("false_value", {2, 4}, OperandDataType::kFloat32);
     builder.BuildWhere(condition_operand_id, true_value_operand_id,
                        false_value_operand_id, true_value_operand_id);
@@ -7435,11 +7436,11 @@
     mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
         BindNewGraphBuilderRemote();
     GraphInfoBuilder builder(remote);
-    uint64_t condition_operand_id =
+    OperandId condition_operand_id =
         builder.BuildInput("condition", {2, 4}, OperandDataType::kUint8);
-    uint64_t true_value_operand_id =
+    OperandId true_value_operand_id =
         builder.BuildInput("true_value", {2, 4}, OperandDataType::kFloat32);
-    uint64_t false_value_operand_id =
+    OperandId false_value_operand_id =
         builder.BuildInput("false_value", {2, 4}, OperandDataType::kFloat32);
     builder.BuildWhere(condition_operand_id, true_value_operand_id,
                        false_value_operand_id, false_value_operand_id);
@@ -7457,16 +7458,16 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  const uint64_t lhs_operand_id =
+  const OperandId lhs_operand_id =
       builder.BuildInput("lhs", kShape, kMojoDataType);
-  const uint64_t rhs_operand_id =
+  const OperandId rhs_operand_id =
       builder.BuildInput("rhs", kShape, kMojoDataType);
-  const uint64_t output_1_operand_id =
+  const OperandId output_1_operand_id =
       builder.BuildOutput("output1", kShape, kMojoDataType);
   builder.BuildElementWiseBinary(mojom::ElementWiseBinary::Kind::kAdd,
                                  lhs_operand_id, rhs_operand_id,
                                  output_1_operand_id);
-  const uint64_t output_2_operand_id =
+  const OperandId output_2_operand_id =
       builder.BuildOutput("output2", kShape, kMojoDataType);
   builder.BuildElementWiseBinary(mojom::ElementWiseBinary::Kind::kAdd,
                                  lhs_operand_id, rhs_operand_id,
@@ -7695,26 +7696,26 @@
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
   // The graph outputs are built first, and then inputs / constants.
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {2, 2}, OperandDataType::kFloat32);
-  uint64_t input_a_operand_id =
+  OperandId input_a_operand_id =
       builder.BuildInput("input_a", {2, 2}, OperandDataType::kFloat32);
   std::vector<float> constant_data = {5.0, 6.0, 7.0, 8.0};
-  uint64_t constant_a_operand_id = builder.BuildConstant(
+  OperandId constant_a_operand_id = builder.BuildConstant(
       {2, 2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_data));
 
-  uint64_t intermediate_1_operand_id =
+  OperandId intermediate_1_operand_id =
       builder.BuildIntermediateOperand({2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(input_a_operand_id, constant_a_operand_id,
                     intermediate_1_operand_id, GemmTester::GemmAttributes());
 
-  uint64_t input_b_operand_id =
+  OperandId input_b_operand_id =
       builder.BuildInput("input_b", {2, 2}, OperandDataType::kFloat32);
-  uint64_t constant_b_operand_id = builder.BuildConstant(
+  OperandId constant_b_operand_id = builder.BuildConstant(
       {2, 2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_data));
-  uint64_t intermediate_2_operand_id =
+  OperandId intermediate_2_operand_id =
       builder.BuildIntermediateOperand({2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(input_b_operand_id, constant_b_operand_id,
                     intermediate_2_operand_id, GemmTester::GemmAttributes());
@@ -7737,25 +7738,25 @@
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
   // The graph outputs are built first, and then inputs / constants.
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {2, 2}, OperandDataType::kFloat32);
   std::vector<float> constant_data = {5.0, 6.0, 7.0, 8.0};
-  uint64_t constant_a_operand_id = builder.BuildConstant(
+  OperandId constant_a_operand_id = builder.BuildConstant(
       {2, 2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_data));
-  uint64_t input_a_operand_id =
+  OperandId input_a_operand_id =
       builder.BuildInput("input_a", {2, 2}, OperandDataType::kFloat32);
-  uint64_t intermediate_1_operand_id =
+  OperandId intermediate_1_operand_id =
       builder.BuildIntermediateOperand({2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(constant_a_operand_id, input_a_operand_id,
                     intermediate_1_operand_id, GemmTester::GemmAttributes());
 
-  uint64_t input_b_operand_id =
+  OperandId input_b_operand_id =
       builder.BuildInput("input_b", {2, 2}, OperandDataType::kFloat32);
-  uint64_t constant_b_operand_id = builder.BuildConstant(
+  OperandId constant_b_operand_id = builder.BuildConstant(
       {2, 2}, OperandDataType::kFloat32,
       base::as_byte_span(base::allow_nonunique_obj, constant_data));
-  uint64_t intermediate_2_operand_id =
+  OperandId intermediate_2_operand_id =
       builder.BuildIntermediateOperand({2, 2}, OperandDataType::kFloat32);
   builder.BuildGemm(constant_b_operand_id, input_b_operand_id,
                     intermediate_2_operand_id, GemmTester::GemmAttributes());
@@ -7770,12 +7771,12 @@
   mojo::AssociatedRemote<mojom::WebNNGraphBuilder> remote =
       BindNewGraphBuilderRemote();
   GraphInfoBuilder builder(remote);
-  uint64_t input_operand_id =
+  OperandId input_operand_id =
       builder.BuildInput("input_a", {2, 2}, OperandDataType::kFloat32);
 
-  uint64_t intermediate_operand_id =
+  OperandId intermediate_operand_id =
       builder.BuildIntermediateOperand({2, 2}, OperandDataType::kFloat32);
-  uint64_t output_operand_id =
+  OperandId output_operand_id =
       builder.BuildOutput("output", {2, 2}, OperandDataType::kUint8);
   builder.BuildRelu(intermediate_operand_id, output_operand_id);
   builder.BuildRelu(input_operand_id, intermediate_operand_id);
diff --git a/services/webnn/webnn_test_utils.cc b/services/webnn/webnn_test_utils.cc
index 302f598..ce9b75ec 100644
--- a/services/webnn/webnn_test_utils.cc
+++ b/services/webnn/webnn_test_utils.cc
@@ -11,6 +11,7 @@
 #include "base/unguessable_token.h"
 #include "services/webnn/public/cpp/context_properties.h"
 #include "services/webnn/public/cpp/supported_tensors.h"
+#include "services/webnn/public/cpp/webnn_types.h"
 #include "services/webnn/webnn_context_impl.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 
@@ -23,43 +24,44 @@
 
 GraphInfoBuilder::~GraphInfoBuilder() = default;
 
-uint64_t GraphInfoBuilder::BuildOperand(const std::vector<uint32_t>& dimensions,
-                                        OperandDataType type,
-                                        mojom::Operand::Kind kind) {
+OperandId GraphInfoBuilder::BuildOperand(
+    const std::vector<uint32_t>& dimensions,
+    OperandDataType type,
+    mojom::Operand::Kind kind) {
   mojom::OperandPtr operand = mojom::Operand::New();
 
   operand->descriptor =
       OperandDescriptor::UnsafeCreateForTesting(type, dimensions);
   operand->kind = kind;
 
-  CHECK(graph_info_->id_to_operand_map.find(operand_id_) ==
-        graph_info_->id_to_operand_map.end());
-  graph_info_->id_to_operand_map[operand_id_] = std::move(operand);
-  return operand_id_++;
+  CHECK(graph_info_->id_to_operand_map
+            .insert({next_operand_id_, std::move(operand)})
+            .second);
+  return next_operand_id_++;
 }
 
-uint64_t GraphInfoBuilder::BuildIntermediateOperand(
+OperandId GraphInfoBuilder::BuildIntermediateOperand(
     const std::vector<uint32_t>& dimensions,
     OperandDataType type) {
   return BuildOperand(dimensions, type, mojom::Operand::Kind::kOutput);
 }
 
-uint64_t GraphInfoBuilder::BuildInput(const std::string& name,
-                                      const std::vector<uint32_t>& dimensions,
-                                      OperandDataType type) {
-  uint64_t operand_id =
+OperandId GraphInfoBuilder::BuildInput(const std::string& name,
+                                       const std::vector<uint32_t>& dimensions,
+                                       OperandDataType type) {
+  OperandId operand_id =
       BuildOperand(dimensions, type, mojom::Operand::Kind::kInput);
   graph_info_->id_to_operand_map[operand_id]->name = name;
   graph_info_->input_operands.push_back(operand_id);
   return operand_id;
 }
 
-uint64_t GraphInfoBuilder::BuildConstant(
+OperandId GraphInfoBuilder::BuildConstant(
     const std::vector<uint32_t>& dimensions,
     OperandDataType type,
     base::span<const uint8_t> values,
     blink::WebNNPendingConstantToken handle) {
-  uint64_t operand_id =
+  OperandId operand_id =
       BuildOperand(dimensions, type, mojom::Operand::Kind::kConstant);
 
   graph_builder_remote_->get()->CreatePendingConstant(
@@ -68,22 +70,23 @@
   return operand_id;
 }
 
-void GraphInfoBuilder::AddOutput(const std::string& name, uint64_t operand_id) {
+void GraphInfoBuilder::AddOutput(const std::string& name,
+                                 OperandId operand_id) {
   graph_info_->id_to_operand_map[operand_id]->name = name;
   graph_info_->output_operands.push_back(operand_id);
 }
 
-uint64_t GraphInfoBuilder::BuildOutput(const std::string& name,
-                                       const std::vector<uint32_t>& dimensions,
-                                       OperandDataType type) {
-  uint64_t operand_id = BuildOperand(dimensions, type);
+OperandId GraphInfoBuilder::BuildOutput(const std::string& name,
+                                        const std::vector<uint32_t>& dimensions,
+                                        OperandDataType type) {
+  OperandId operand_id = BuildOperand(dimensions, type);
   AddOutput(name, operand_id);
   return operand_id;
 }
 
 void GraphInfoBuilder::BuildArgMinMax(mojom::ArgMinMax::Kind kind,
-                                      uint64_t input_operand_id,
-                                      uint64_t output_operand_id,
+                                      OperandId input_operand_id,
+                                      OperandId output_operand_id,
                                       uint32_t axis,
                                       bool keep_dimensions) {
   mojom::ArgMinMaxPtr arg_min_max = mojom::ArgMinMax::New();
@@ -96,8 +99,8 @@
       mojom::Operation::NewArgMinMax(std::move(arg_min_max)));
 }
 
-void GraphInfoBuilder::BuildElu(uint64_t input_operand_id,
-                                uint64_t output_operand_id,
+void GraphInfoBuilder::BuildElu(OperandId input_operand_id,
+                                OperandId output_operand_id,
                                 float alpha) {
   mojom::EluPtr elu = mojom::Elu::New();
   elu->input_operand_id = input_operand_id;
@@ -106,8 +109,8 @@
   graph_info_->operations.push_back(mojom::Operation::NewElu(std::move(elu)));
 }
 
-void GraphInfoBuilder::BuildLeakyRelu(uint64_t input_operand_id,
-                                      uint64_t output_operand_id,
+void GraphInfoBuilder::BuildLeakyRelu(OperandId input_operand_id,
+                                      OperandId output_operand_id,
                                       float alpha) {
   mojom::LeakyReluPtr leaky_relu = mojom::LeakyRelu::New();
   leaky_relu->input_operand_id = input_operand_id;
@@ -117,8 +120,8 @@
       mojom::Operation::NewLeakyRelu(std::move(leaky_relu)));
 }
 
-void GraphInfoBuilder::BuildLinear(uint64_t input_operand_id,
-                                   uint64_t output_operand_id,
+void GraphInfoBuilder::BuildLinear(OperandId input_operand_id,
+                                   OperandId output_operand_id,
                                    float alpha,
                                    float beta) {
   mojom::LinearPtr linear = mojom::Linear::New();
@@ -130,8 +133,8 @@
       mojom::Operation::NewLinear(std::move(linear)));
 }
 
-void GraphInfoBuilder::BuildPad(uint64_t input_operand_id,
-                                uint64_t output_operand_id,
+void GraphInfoBuilder::BuildPad(OperandId input_operand_id,
+                                OperandId output_operand_id,
                                 const std::vector<uint32_t>& beginning_padding,
                                 const std::vector<uint32_t>& ending_padding,
                                 mojom::PaddingMode::Tag mode,
@@ -165,8 +168,8 @@
 }
 
 void GraphInfoBuilder::BuildSplit(
-    uint64_t input_operand_id,
-    const std::vector<uint64_t>& output_operand_ids,
+    OperandId input_operand_id,
+    const std::vector<OperandId>& output_operand_ids,
     uint32_t axis) {
   mojom::SplitPtr split = mojom::Split::New();
   split->input_operand_id = input_operand_id;
@@ -177,8 +180,8 @@
       mojom::Operation::NewSplit(std::move(split)));
 }
 
-void GraphInfoBuilder::BuildClamp(uint64_t input_operand_id,
-                                  uint64_t output_operand_id,
+void GraphInfoBuilder::BuildClamp(OperandId input_operand_id,
+                                  OperandId output_operand_id,
                                   float min_value,
                                   float max_value) {
   mojom::ClampPtr clamp = mojom::Clamp::New();
@@ -190,8 +193,8 @@
       mojom::Operation::NewClamp(std::move(clamp)));
 }
 
-void GraphInfoBuilder::BuildConcat(std::vector<uint64_t> input_operand_ids,
-                                   uint64_t output_operand_id,
+void GraphInfoBuilder::BuildConcat(std::vector<OperandId> input_operand_ids,
+                                   OperandId output_operand_id,
                                    uint32_t axis) {
   mojom::ConcatPtr concat = mojom::Concat::New();
   concat->input_operand_ids = std::move(input_operand_ids);
@@ -201,8 +204,8 @@
       mojom::Operation::NewConcat(std::move(concat)));
 }
 
-void GraphInfoBuilder::BuildCumulativeSum(uint64_t input_operand_id,
-                                          uint64_t output_operand_id,
+void GraphInfoBuilder::BuildCumulativeSum(OperandId input_operand_id,
+                                          OperandId output_operand_id,
                                           uint32_t axis,
                                           std::optional<bool> exclusive,
                                           std::optional<bool> reversed) {
@@ -220,10 +223,10 @@
       mojom::Operation::NewCumulativeSum(std::move(cumulative_sum)));
 }
 
-void GraphInfoBuilder::BuildDequantizeLinear(uint64_t input_operand_id,
-                                             uint64_t scale_operand_id,
-                                             uint64_t zero_point_operand_id,
-                                             uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildDequantizeLinear(OperandId input_operand_id,
+                                             OperandId scale_operand_id,
+                                             OperandId zero_point_operand_id,
+                                             OperandId output_operand_id) {
   mojom::DequantizeLinearPtr dequantize_linear = mojom::DequantizeLinear::New();
   dequantize_linear->input_operand_id = input_operand_id;
   dequantize_linear->scale_operand_id = scale_operand_id;
@@ -235,9 +238,9 @@
 
 void GraphInfoBuilder::BuildElementWiseBinary(
     mojom::ElementWiseBinary::Kind kind,
-    uint64_t lhs_operand,
-    uint64_t rhs_operand,
-    uint64_t output_operand) {
+    OperandId lhs_operand,
+    OperandId rhs_operand,
+    OperandId output_operand) {
   mojom::ElementWiseBinaryPtr binary = mojom::ElementWiseBinary::New();
   binary->kind = kind;
   binary->lhs_operand_id = lhs_operand;
@@ -247,15 +250,15 @@
       mojom::Operation::NewElementWiseBinary(std::move(binary)));
 }
 
-void GraphInfoBuilder::BuildExpand(uint64_t input_operand_id,
-                                   uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildExpand(OperandId input_operand_id,
+                                   OperandId output_operand_id) {
   graph_info_->operations.push_back(mojom::Operation::NewExpand(
       mojom::Expand::New(input_operand_id, output_operand_id, "")));
 }
 
-void GraphInfoBuilder::BuildMatmul(uint64_t a_operand_id,
-                                   uint64_t b_operand_id,
-                                   uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildMatmul(OperandId a_operand_id,
+                                   OperandId b_operand_id,
+                                   OperandId output_operand_id) {
   mojom::MatmulPtr matmul = mojom::Matmul::New();
   matmul->a_operand_id = a_operand_id;
   matmul->b_operand_id = b_operand_id;
@@ -265,8 +268,8 @@
 }
 
 void GraphInfoBuilder::BuildElementWiseUnary(mojom::ElementWiseUnary::Kind kind,
-                                             uint64_t input_operand,
-                                             uint64_t output_operand) {
+                                             OperandId input_operand,
+                                             OperandId output_operand) {
   mojom::ElementWiseUnaryPtr unary = mojom::ElementWiseUnary::New();
   unary->kind = kind;
   unary->input_operand_id = input_operand;
@@ -275,9 +278,9 @@
       mojom::Operation::NewElementWiseUnary(std::move(unary)));
 }
 
-void GraphInfoBuilder::BuildGather(uint64_t input_operand_id,
-                                   uint64_t indices_operand_id,
-                                   uint64_t output_operand_id,
+void GraphInfoBuilder::BuildGather(OperandId input_operand_id,
+                                   OperandId indices_operand_id,
+                                   OperandId output_operand_id,
                                    uint32_t axis) {
   mojom::GatherPtr gather = mojom::Gather::New();
   gather->input_operand_id = input_operand_id;
@@ -288,9 +291,9 @@
       mojom::Operation::NewGather(std::move(gather)));
 }
 
-void GraphInfoBuilder::BuildGatherElements(uint64_t input_operand_id,
-                                           uint64_t indices_operand_id,
-                                           uint64_t output_operand_id,
+void GraphInfoBuilder::BuildGatherElements(OperandId input_operand_id,
+                                           OperandId indices_operand_id,
+                                           OperandId output_operand_id,
                                            uint32_t axis) {
   auto gather_elements = mojom::GatherElements::New();
   gather_elements->input_operand_id = input_operand_id;
@@ -301,24 +304,24 @@
       mojom::Operation::NewGatherElements(std::move(gather_elements)));
 }
 
-void GraphInfoBuilder::BuildGatherND(uint64_t input_operand_id,
-                                     uint64_t indices_operand_id,
-                                     uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildGatherND(OperandId input_operand_id,
+                                     OperandId indices_operand_id,
+                                     OperandId output_operand_id) {
   auto gather_nd = mojom::GatherND::New(input_operand_id, indices_operand_id,
                                         output_operand_id, "");
   graph_info_->operations.push_back(
       mojom::Operation::NewGatherNd(std::move(gather_nd)));
 }
 
-void GraphInfoBuilder::BuildGelu(uint64_t input_operand_id,
-                                 uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildGelu(OperandId input_operand_id,
+                                 OperandId output_operand_id) {
   mojom::GeluPtr gelu =
       mojom::Gelu::New(input_operand_id, output_operand_id, "");
   graph_info_->operations.push_back(mojom::Operation::NewGelu(std::move(gelu)));
 }
 
-void GraphInfoBuilder::BuildHardSigmoid(uint64_t input_operand_id,
-                                        uint64_t output_operand_id,
+void GraphInfoBuilder::BuildHardSigmoid(OperandId input_operand_id,
+                                        OperandId output_operand_id,
                                         std::optional<float> alpha,
                                         std::optional<float> beta) {
   mojom::HardSigmoidPtr hard_sigmoid = mojom::HardSigmoid::New();
@@ -334,8 +337,8 @@
       mojom::Operation::NewHardSigmoid(std::move(hard_sigmoid)));
 }
 
-void GraphInfoBuilder::BuildHardSwish(uint64_t input_operand_id,
-                                      uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildHardSwish(OperandId input_operand_id,
+                                      OperandId output_operand_id) {
   mojom::HardSwishPtr hard_swish = mojom::HardSwish::New();
   hard_swish->input_operand_id = input_operand_id;
   hard_swish->output_operand_id = output_operand_id;
@@ -343,9 +346,9 @@
       mojom::Operation::NewHardSwish(std::move(hard_swish)));
 }
 
-void GraphInfoBuilder::BuildPrelu(uint64_t input_operand_id,
-                                  uint64_t slope_operand_id,
-                                  uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildPrelu(OperandId input_operand_id,
+                                  OperandId slope_operand_id,
+                                  OperandId output_operand_id) {
   mojom::PreluPtr prelu = mojom::Prelu::New();
   prelu->input_operand_id = input_operand_id;
   prelu->slope_operand_id = slope_operand_id;
@@ -354,10 +357,10 @@
       mojom::Operation::NewPrelu(std::move(prelu)));
 }
 
-void GraphInfoBuilder::BuildQuantizeLinear(uint64_t input_operand_id,
-                                           uint64_t scale_operand_id,
-                                           uint64_t zero_point_operand_id,
-                                           uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildQuantizeLinear(OperandId input_operand_id,
+                                           OperandId scale_operand_id,
+                                           OperandId zero_point_operand_id,
+                                           OperandId output_operand_id) {
   mojom::QuantizeLinearPtr quantize_linear = mojom::QuantizeLinear::New();
   quantize_linear->input_operand_id = input_operand_id;
   quantize_linear->scale_operand_id = scale_operand_id;
@@ -368,8 +371,8 @@
 }
 
 void GraphInfoBuilder::BuildReduce(mojom::Reduce::Kind kind,
-                                   uint64_t input_operand_id,
-                                   uint64_t output_operand_id,
+                                   OperandId input_operand_id,
+                                   OperandId output_operand_id,
                                    std::vector<uint32_t> axes,
                                    bool keep_dimensions) {
   mojom::ReducePtr reduce = mojom::Reduce::New();
@@ -382,16 +385,16 @@
       mojom::Operation::NewReduce(std::move(reduce)));
 }
 
-void GraphInfoBuilder::BuildRelu(uint64_t input_operand_id,
-                                 uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildRelu(OperandId input_operand_id,
+                                 OperandId output_operand_id) {
   mojom::ReluPtr relu = mojom::Relu::New();
   relu->input_operand_id = input_operand_id;
   relu->output_operand_id = output_operand_id;
   graph_info_->operations.push_back(mojom::Operation::NewRelu(std::move(relu)));
 }
 
-void GraphInfoBuilder::BuildReshape(uint64_t input_operand_id,
-                                    uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildReshape(OperandId input_operand_id,
+                                    OperandId output_operand_id) {
   mojom::ReshapePtr reshape = mojom::Reshape::New();
   reshape->input_operand_id = input_operand_id;
   reshape->output_operand_id = output_operand_id;
@@ -399,8 +402,8 @@
       mojom::Operation::NewReshape(std::move(reshape)));
 }
 
-void GraphInfoBuilder::BuildReverse(uint64_t input_operand_id,
-                                    uint64_t output_operand_id,
+void GraphInfoBuilder::BuildReverse(OperandId input_operand_id,
+                                    OperandId output_operand_id,
                                     std::vector<uint32_t> axes) {
   auto reverse = mojom::Reverse::New();
   reverse->input_operand_id = input_operand_id;
@@ -410,10 +413,10 @@
       mojom::Operation::NewReverse(std::move(reverse)));
 }
 
-void GraphInfoBuilder::BuildScatterElements(uint64_t input_operand_id,
-                                            uint64_t indices_operand_id,
-                                            uint64_t updates_operand_id,
-                                            uint64_t output_operand_id,
+void GraphInfoBuilder::BuildScatterElements(OperandId input_operand_id,
+                                            OperandId indices_operand_id,
+                                            OperandId updates_operand_id,
+                                            OperandId output_operand_id,
                                             uint32_t axis) {
   mojom::ScatterElementsPtr scatter_elements = mojom::ScatterElements::New(
       input_operand_id, indices_operand_id, updates_operand_id,
@@ -422,10 +425,10 @@
       mojom::Operation::NewScatterElements(std::move(scatter_elements)));
 }
 
-void GraphInfoBuilder::BuildScatterND(uint64_t input_operand_id,
-                                      uint64_t indices_operand_id,
-                                      uint64_t updates_operand_id,
-                                      uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildScatterND(OperandId input_operand_id,
+                                      OperandId indices_operand_id,
+                                      OperandId updates_operand_id,
+                                      OperandId output_operand_id) {
   mojom::ScatterNDPtr scatter_nd =
       mojom::ScatterND::New(input_operand_id, indices_operand_id,
                             updates_operand_id, output_operand_id, "");
@@ -433,8 +436,8 @@
       mojom::Operation::NewScatterNd(std::move(scatter_nd)));
 }
 
-void GraphInfoBuilder::BuildSigmoid(uint64_t input_operand_id,
-                                    uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildSigmoid(OperandId input_operand_id,
+                                    OperandId output_operand_id) {
   mojom::SigmoidPtr sigmoid = mojom::Sigmoid::New();
   sigmoid->input_operand_id = input_operand_id;
   sigmoid->output_operand_id = output_operand_id;
@@ -442,8 +445,8 @@
       mojom::Operation::NewSigmoid(std::move(sigmoid)));
 }
 
-void GraphInfoBuilder::BuildSoftmax(uint64_t input_operand_id,
-                                    uint64_t output_operand_id,
+void GraphInfoBuilder::BuildSoftmax(OperandId input_operand_id,
+                                    OperandId output_operand_id,
                                     uint32_t axis) {
   mojom::SoftmaxPtr softmax =
       mojom::Softmax::New(input_operand_id, output_operand_id, axis, "");
@@ -451,15 +454,15 @@
       mojom::Operation::NewSoftmax(std::move(softmax)));
 }
 
-void GraphInfoBuilder::BuildSoftplus(uint64_t input_operand_id,
-                                     uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildSoftplus(OperandId input_operand_id,
+                                     OperandId output_operand_id) {
   auto softplus = mojom::Softplus::New(input_operand_id, output_operand_id, "");
   graph_info_->operations.push_back(
       mojom::Operation::NewSoftplus(std::move(softplus)));
 }
 
-void GraphInfoBuilder::BuildSoftsign(uint64_t input_operand_id,
-                                     uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildSoftsign(OperandId input_operand_id,
+                                     OperandId output_operand_id) {
   mojom::SoftsignPtr softsign = mojom::Softsign::New();
   softsign->input_operand_id = input_operand_id;
   softsign->output_operand_id = output_operand_id;
@@ -467,16 +470,16 @@
       mojom::Operation::NewSoftsign(std::move(softsign)));
 }
 
-void GraphInfoBuilder::BuildTanh(uint64_t input_operand_id,
-                                 uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildTanh(OperandId input_operand_id,
+                                 OperandId output_operand_id) {
   mojom::TanhPtr tanh = mojom::Tanh::New();
   tanh->input_operand_id = input_operand_id;
   tanh->output_operand_id = output_operand_id;
   graph_info_->operations.push_back(mojom::Operation::NewTanh(std::move(tanh)));
 }
 
-void GraphInfoBuilder::BuildTile(uint64_t input_operand_id,
-                                 uint64_t output_operand_id,
+void GraphInfoBuilder::BuildTile(OperandId input_operand_id,
+                                 OperandId output_operand_id,
                                  std::vector<uint32_t> repetitions) {
   mojom::TilePtr tile = mojom::Tile::New();
   tile->input_operand_id = input_operand_id;
@@ -485,8 +488,8 @@
   graph_info_->operations.push_back(mojom::Operation::NewTile(std::move(tile)));
 }
 
-void GraphInfoBuilder::BuildTranspose(uint64_t input_operand_id,
-                                      uint64_t output_operand_id,
+void GraphInfoBuilder::BuildTranspose(OperandId input_operand_id,
+                                      OperandId output_operand_id,
                                       std::vector<uint32_t> permutation) {
   mojom::TransposePtr transpose = mojom::Transpose::New();
   transpose->input_operand_id = input_operand_id;
@@ -496,8 +499,8 @@
       mojom::Operation::NewTranspose(std::move(transpose)));
 }
 
-void GraphInfoBuilder::BuildTriangular(uint64_t input_operand_id,
-                                       uint64_t output_operand_id,
+void GraphInfoBuilder::BuildTriangular(OperandId input_operand_id,
+                                       OperandId output_operand_id,
                                        bool upper,
                                        int32_t diagonal) {
   mojom::TriangularPtr triangular = mojom::Triangular::New(
@@ -506,10 +509,10 @@
       mojom::Operation::NewTriangular(std::move(triangular)));
 }
 
-void GraphInfoBuilder::BuildWhere(uint64_t condition_operand_id,
-                                  uint64_t true_value_operand_id,
-                                  uint64_t false_value_operand_id,
-                                  uint64_t output_operand_id) {
+void GraphInfoBuilder::BuildWhere(OperandId condition_operand_id,
+                                  OperandId true_value_operand_id,
+                                  OperandId false_value_operand_id,
+                                  OperandId output_operand_id) {
   mojom::WherePtr where = mojom::Where::New();
   where->condition_operand_id = condition_operand_id;
   where->true_value_operand_id = true_value_operand_id;
@@ -519,8 +522,8 @@
       mojom::Operation::NewWhere(std::move(where)));
 }
 
-void GraphInfoBuilder::BuildSlice(uint64_t input_operand_id,
-                                  uint64_t output_operand_id,
+void GraphInfoBuilder::BuildSlice(OperandId input_operand_id,
+                                  OperandId output_operand_id,
                                   base::span<const uint32_t> starts,
                                   base::span<const uint32_t> sizes,
                                   base::span<const uint32_t> strides) {
diff --git a/services/webnn/webnn_test_utils.h b/services/webnn/webnn_test_utils.h
index 2dd4dba5..96d9228 100644
--- a/services/webnn/webnn_test_utils.h
+++ b/services/webnn/webnn_test_utils.h
@@ -12,6 +12,7 @@
 #include "base/memory/stack_allocated.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "services/webnn/public/cpp/operand_descriptor.h"
+#include "services/webnn/public/cpp/webnn_types.h"
 #include "services/webnn/public/mojom/webnn_context_provider.mojom.h"
 #include "services/webnn/public/mojom/webnn_graph.mojom.h"
 #include "services/webnn/public/mojom/webnn_graph_builder.mojom.h"
@@ -36,45 +37,45 @@
   GraphInfoBuilder& operator=(const GraphInfoBuilder&) = delete;
   ~GraphInfoBuilder();
 
-  uint64_t BuildIntermediateOperand(const std::vector<uint32_t>& dimensions,
-                                    OperandDataType type);
+  OperandId BuildIntermediateOperand(const std::vector<uint32_t>& dimensions,
+                                     OperandDataType type);
 
-  uint64_t BuildInput(const std::string& name,
-                      const std::vector<uint32_t>& dimensions,
-                      OperandDataType type);
-
-  // Optionally provide `handle` to identify this constant operand; otherwise a
-  // handle will be generated automatically.
-  uint64_t BuildConstant(const std::vector<uint32_t>& dimensions,
-                         OperandDataType type,
-                         base::span<const uint8_t> values,
-                         blink::WebNNPendingConstantToken handle =
-                             blink::WebNNPendingConstantToken());
-
-  void AddOutput(const std::string& name, uint64_t operand_id);
-
-  uint64_t BuildOutput(const std::string& name,
+  OperandId BuildInput(const std::string& name,
                        const std::vector<uint32_t>& dimensions,
                        OperandDataType type);
 
+  // Optionally provide `handle` to identify this constant operand; otherwise a
+  // handle will be generated automatically.
+  OperandId BuildConstant(const std::vector<uint32_t>& dimensions,
+                          OperandDataType type,
+                          base::span<const uint8_t> values,
+                          blink::WebNNPendingConstantToken handle =
+                              blink::WebNNPendingConstantToken());
+
+  void AddOutput(const std::string& name, OperandId operand_id);
+
+  OperandId BuildOutput(const std::string& name,
+                        const std::vector<uint32_t>& dimensions,
+                        OperandDataType type);
+
   void BuildArgMinMax(mojom::ArgMinMax::Kind kind,
-                      uint64_t input_operand_id,
-                      uint64_t output_operand_id,
+                      OperandId input_operand_id,
+                      OperandId output_operand_id,
                       uint32_t axis,
                       bool keep_dimensions);
 
   // A `BatchNormalizationAttributes` type should have the following members:
   // struct BatchNormalizationAttributes {
-  //  std::optional<uint64_t> scale_operand_id;
-  //  std::optional<uint64_t> bias_operand_id;
+  //  std::optional<OperandId> scale_operand_id;
+  //  std::optional<OperandId> bias_operand_id;
   //  uint32_t axis = 1;
   //  float epsilon = 1e-5;
   // };
   template <typename BatchNormalizationAttributes>
-  void BuildBatchNormalization(uint64_t input_operand_id,
-                               uint64_t mean_operand_id,
-                               uint64_t variance_operand_id,
-                               uint64_t output_operand_id,
+  void BuildBatchNormalization(OperandId input_operand_id,
+                               OperandId mean_operand_id,
+                               OperandId variance_operand_id,
+                               OperandId output_operand_id,
                                const BatchNormalizationAttributes& attributes) {
     mojom::BatchNormalizationPtr batch_normalization =
         mojom::BatchNormalization::New();
@@ -92,13 +93,13 @@
         std::move(batch_normalization)));
   }
 
-  void BuildClamp(uint64_t input_operand_id,
-                  uint64_t output_operand_id,
+  void BuildClamp(OperandId input_operand_id,
+                  OperandId output_operand_id,
                   float min_value,
                   float max_value);
 
-  void BuildConcat(std::vector<uint64_t> input_operand_ids,
-                   uint64_t output_operand_id,
+  void BuildConcat(std::vector<OperandId> input_operand_ids,
+                   OperandId output_operand_id,
                    uint32_t axis);
 
   // A `Conv2dAttributes` type should have the following members:
@@ -107,15 +108,15 @@
   //   std::vector<uint32_t> strides;
   //   std::vector<uint32_t> dilations;
   //   uint32_t groups;
-  //   std::optional<uint64_t> bias_operand_id,
+  //   std::optional<OperandId> bias_operand_id,
   // };
   template <typename Conv2dAttributes>
   void BuildConv2d(mojom::Conv2d::Kind type,
-                   uint64_t input_operand_id,
-                   uint64_t filter_operand_id,
-                   uint64_t output_operand_id,
+                   OperandId input_operand_id,
+                   OperandId filter_operand_id,
+                   OperandId output_operand_id,
                    const Conv2dAttributes& attributes,
-                   std::optional<uint64_t> bias_operand_id) {
+                   std::optional<OperandId> bias_operand_id) {
     mojom::Conv2dPtr conv2d = mojom::Conv2d::New();
     conv2d->input_operand_id = input_operand_id;
     conv2d->filter_operand_id = filter_operand_id;
@@ -142,60 +143,60 @@
         mojom::Operation::NewConv2d(std::move(conv2d)));
   }
 
-  void BuildCumulativeSum(uint64_t input_operand_id,
-                          uint64_t output_operand_id,
+  void BuildCumulativeSum(OperandId input_operand_id,
+                          OperandId output_operand_id,
                           uint32_t axis,
                           std::optional<bool> exclusive,
                           std::optional<bool> reversed);
 
-  void BuildDequantizeLinear(uint64_t input_operand_id,
-                             uint64_t scale_operand_id,
-                             uint64_t zero_point_operand_id,
-                             uint64_t output_operand_id);
+  void BuildDequantizeLinear(OperandId input_operand_id,
+                             OperandId scale_operand_id,
+                             OperandId zero_point_operand_id,
+                             OperandId output_operand_id);
 
   void BuildElementWiseBinary(mojom::ElementWiseBinary::Kind kind,
-                              uint64_t lhs_operand,
-                              uint64_t rhs_operand,
-                              uint64_t output_operand);
+                              OperandId lhs_operand,
+                              OperandId rhs_operand,
+                              OperandId output_operand);
 
-  void BuildElu(uint64_t input_operand_id,
-                uint64_t output_operand_id,
+  void BuildElu(OperandId input_operand_id,
+                OperandId output_operand_id,
                 float alpha);
 
   void BuildElementWiseUnary(mojom::ElementWiseUnary::Kind kind,
-                             uint64_t input_operand,
-                             uint64_t output_operand);
+                             OperandId input_operand,
+                             OperandId output_operand);
 
-  void BuildExpand(uint64_t input_operand_id, uint64_t output_operand_id);
+  void BuildExpand(OperandId input_operand_id, OperandId output_operand_id);
 
-  void BuildGather(uint64_t input_operand_id,
-                   uint64_t indices_operand_id,
-                   uint64_t output_operand_id,
+  void BuildGather(OperandId input_operand_id,
+                   OperandId indices_operand_id,
+                   OperandId output_operand_id,
                    uint32_t axis);
 
-  void BuildGatherElements(uint64_t input_operand_id,
-                           uint64_t indices_operand_id,
-                           uint64_t output_operand_id,
+  void BuildGatherElements(OperandId input_operand_id,
+                           OperandId indices_operand_id,
+                           OperandId output_operand_id,
                            uint32_t axis);
 
-  void BuildGatherND(uint64_t input_operand_id,
-                     uint64_t indices_operand_id,
-                     uint64_t output_operand_id);
+  void BuildGatherND(OperandId input_operand_id,
+                     OperandId indices_operand_id,
+                     OperandId output_operand_id);
 
-  void BuildGelu(uint64_t input_operand_id, uint64_t output_operand_id);
+  void BuildGelu(OperandId input_operand_id, OperandId output_operand_id);
 
   // A `GemmAttributes` type should have the following members:
   // struct GemmAttributes {
-  //   std::optional<uint64_t> c_operand_id,
+  //   std::optional<OperandId> c_operand_id,
   //   float alpha = 1.0;
   //   float beta = 1.0;
   //   bool a_transpose = false;
   //   bool b_transpose = false;
   // };
   template <typename GemmAttributes>
-  void BuildGemm(uint64_t a_operand_id,
-                 uint64_t b_operand_id,
-                 uint64_t output_operand_id,
+  void BuildGemm(OperandId a_operand_id,
+                 OperandId b_operand_id,
+                 OperandId output_operand_id,
                  const GemmAttributes& attributes) {
     mojom::GemmPtr gemm = mojom::Gemm::New();
     gemm->a_operand_id = a_operand_id;
@@ -214,9 +215,9 @@
 
   // A `GruAttributes` type should have the following members:
   // struct GruAttributes {
-  //   std::optional<uint64_t> bias_operand_id;
-  //   std::optional<uint64_t> recurrent_bias_operand_id;
-  //   std::optional<uint64_t> initial_hidden_state_operand_id;
+  //   std::optional<OperandId> bias_operand_id;
+  //   std::optional<OperandId> recurrent_bias_operand_id;
+  //   std::optional<OperandId> initial_hidden_state_operand_id;
   //   bool reset_after;
   //   bool return_sequence;
   //   mojom::RecurrentNetworkDirection direction;
@@ -224,10 +225,10 @@
   //   std::vector<mojom::RecurrentNetworkActivation> activations;
   // };
   template <typename GruAttributes>
-  void BuildGru(uint64_t input_operand_id,
-                uint64_t weight_operand_id,
-                uint64_t recurrent_weight_operand_id,
-                std::vector<uint64_t> output_operand_ids,
+  void BuildGru(OperandId input_operand_id,
+                OperandId weight_operand_id,
+                OperandId recurrent_weight_operand_id,
+                std::vector<OperandId> output_operand_ids,
                 uint32_t steps,
                 uint32_t hidden_size,
                 const GruAttributes& attributes) {
@@ -254,18 +255,18 @@
 
   // A `GruCellAttributes` type should have the following members:
   // struct GruCellAttributes {
-  //   std::optional<uint64_t> bias_operand_id;
-  //   std::optional<uint64_t> recurrent_bias_operand_id;
+  //   std::optional<OperandId> bias_operand_id;
+  //   std::optional<OperandId> recurrent_bias_operand_id;
   //   bool reset_after;
   //   mojom::GruWeightLayout layout;
   //   std::vector<mojom::RecurrentNetworkActivation> activations;
   // };
   template <typename GruCellAttributes>
-  void BuildGruCell(uint64_t input_operand_id,
-                    uint64_t weight_operand_id,
-                    uint64_t recurrent_weight_operand_id,
-                    uint64_t hidden_state_operand_id,
-                    uint64_t output_operand_id,
+  void BuildGruCell(OperandId input_operand_id,
+                    OperandId weight_operand_id,
+                    OperandId recurrent_weight_operand_id,
+                    OperandId hidden_state_operand_id,
+                    OperandId output_operand_id,
                     uint32_t hidden_size,
                     const GruCellAttributes& attributes) {
     mojom::GruCellPtr gru_cell = mojom::GruCell::New(
@@ -278,23 +279,23 @@
         mojom::Operation::NewGruCell(std::move(gru_cell)));
   }
 
-  void BuildHardSigmoid(uint64_t input_operand_id,
-                        uint64_t output_operand_id,
+  void BuildHardSigmoid(OperandId input_operand_id,
+                        OperandId output_operand_id,
                         std::optional<float> alpha,
                         std::optional<float> beta);
 
-  void BuildHardSwish(uint64_t input_operand_id, uint64_t output_operand_id);
+  void BuildHardSwish(OperandId input_operand_id, OperandId output_operand_id);
 
   // A `LayerNormalizationAttributes` type should have the following members:
   // struct LayerNormalizationAttributes {
-  //  std::optional<uint64_t> scale_operand_id;
-  //  std::optional<uint64_t> bias_operand_id;
+  //  std::optional<OperandId> scale_operand_id;
+  //  std::optional<OperandId> bias_operand_id;
   //  std::vector<uint32_t> axes;
   //  float epsilon = 1e-5;
   // };
   template <typename LayerNormalizationAttributes>
-  void BuildLayerNormalization(uint64_t input_operand_id,
-                               uint64_t output_operand_id,
+  void BuildLayerNormalization(OperandId input_operand_id,
+                               OperandId output_operand_id,
                                const LayerNormalizationAttributes& attributes) {
     mojom::LayerNormalizationPtr layer_normalization =
         mojom::LayerNormalization::New();
@@ -312,21 +313,21 @@
 
   // A `LstmAttributes` type should have the following members:
   // struct LstmAttributes {
-  //   std::optional<uint64_t> bias_operand_id;
-  //   std::optional<uint64_t> recurrent_bias_operand_id;
-  //   std::optional<uint64_t> peephole_weight_operand_id;
-  //   std::optional<uint64_t> initial_hidden_state_operand_id;
-  //   std::optional<uint64_t> initial_cell_state_operand_id;
+  //   std::optional<OperandId> bias_operand_id;
+  //   std::optional<OperandId> recurrent_bias_operand_id;
+  //   std::optional<OperandId> peephole_weight_operand_id;
+  //   std::optional<OperandId> initial_hidden_state_operand_id;
+  //   std::optional<OperandId> initial_cell_state_operand_id;
   //   bool return_sequence;
   //   mojom::RecurrentNetworkDirection direction;
   //   mojom::LstmWeightLayout layout;
   //   std::vector<mojom::RecurrentNetworkActivation> activations;
   // };
   template <typename LstmAttributes>
-  void BuildLstm(uint64_t input_operand_id,
-                 uint64_t weight_operand_id,
-                 uint64_t recurrent_weight_operand_id,
-                 std::vector<uint64_t> output_operand_ids,
+  void BuildLstm(OperandId input_operand_id,
+                 OperandId weight_operand_id,
+                 OperandId recurrent_weight_operand_id,
+                 std::vector<OperandId> output_operand_ids,
                  uint32_t steps,
                  uint32_t hidden_size,
                  const LstmAttributes& attributes) {
@@ -356,19 +357,19 @@
 
   // A `LstmCellAttributes` type should have the following members:
   // struct LstmCellAttributes {
-  //   std::optional<uint64_t> bias_operand_id;
-  //   std::optional<uint64_t> recurrent_bias_operand_id;
-  //   std::optional<uint64_t> peephole_weight_operand_id;
+  //   std::optional<OperandId> bias_operand_id;
+  //   std::optional<OperandId> recurrent_bias_operand_id;
+  //   std::optional<OperandId> peephole_weight_operand_id;
   //   mojom::LstmWeightLayout layout;
   //   std::vector<mojom::RecurrentNetworkActivation> activations;
   // };
   template <typename LstmCellAttributes>
-  void BuildLstmCell(uint64_t input_operand_id,
-                     uint64_t weight_operand_id,
-                     uint64_t recurrent_weight_operand_id,
-                     uint64_t hidden_state_operand_id,
-                     uint64_t cell_state_operand_id,
-                     std::vector<uint64_t> output_operand_ids,
+  void BuildLstmCell(OperandId input_operand_id,
+                     OperandId weight_operand_id,
+                     OperandId recurrent_weight_operand_id,
+                     OperandId hidden_state_operand_id,
+                     OperandId cell_state_operand_id,
+                     std::vector<OperandId> output_operand_ids,
                      uint32_t hidden_size,
                      const LstmCellAttributes& attributes) {
     auto lstm_cell = mojom::LstmCell::New(
@@ -385,14 +386,14 @@
 
   // A `InstanceNormalizationAttributes` type should have the following members:
   // struct InstanceNormalizationAttributes {
-  //  std::optional<uint64_t> scale_operand_id;
-  //  std::optional<uint64_t> bias_operand_id;
+  //  std::optional<OperandId> scale_operand_id;
+  //  std::optional<OperandId> bias_operand_id;
   //  float epsilon = 1e-5;
   // };
   template <typename InstanceNormalizationAttributes>
   void BuildInstanceNormalization(
-      uint64_t input_operand_id,
-      uint64_t output_operand_id,
+      OperandId input_operand_id,
+      OperandId output_operand_id,
       const InstanceNormalizationAttributes& attributes) {
     mojom::InstanceNormalizationPtr instance_normalization =
         mojom::InstanceNormalization::New();
@@ -408,21 +409,21 @@
             std::move(instance_normalization)));
   }
 
-  void BuildLeakyRelu(uint64_t input_operand_id,
-                      uint64_t output_operand_id,
+  void BuildLeakyRelu(OperandId input_operand_id,
+                      OperandId output_operand_id,
                       float alpha);
 
-  void BuildLinear(uint64_t input_operand_id,
-                   uint64_t output_operand_id,
+  void BuildLinear(OperandId input_operand_id,
+                   OperandId output_operand_id,
                    float alpha,
                    float beta);
 
-  void BuildMatmul(uint64_t a_operand_id,
-                   uint64_t b_operand_id,
-                   uint64_t output_operand_id);
+  void BuildMatmul(OperandId a_operand_id,
+                   OperandId b_operand_id,
+                   OperandId output_operand_id);
 
-  void BuildPad(uint64_t input_operand_id,
-                uint64_t output_operand_id,
+  void BuildPad(OperandId input_operand_id,
+                OperandId output_operand_id,
                 const std::vector<uint32_t>& beginning_padding,
                 const std::vector<uint32_t>& ending_padding,
                 mojom::PaddingMode::Tag mode,
@@ -437,8 +438,8 @@
   // };
   template <typename Pool2dAttributes>
   void BuildPool2d(mojom::Pool2d::Kind kind,
-                   uint64_t input_operand_id,
-                   uint64_t output_operand_id,
+                   OperandId input_operand_id,
+                   OperandId output_operand_id,
                    const Pool2dAttributes& attributes) {
     mojom::Pool2dPtr pool2d = mojom::Pool2d::New();
     pool2d->kind = kind;
@@ -466,22 +467,22 @@
         mojom::Operation::NewPool2d(std::move(pool2d)));
   }
 
-  void BuildPrelu(uint64_t input_operand_id,
-                  uint64_t slope_operand_id,
-                  uint64_t output_operand_id);
+  void BuildPrelu(OperandId input_operand_id,
+                  OperandId slope_operand_id,
+                  OperandId output_operand_id);
 
-  void BuildQuantizeLinear(uint64_t input_operand_id,
-                           uint64_t scale_operand_id,
-                           uint64_t zero_point_operand_id,
-                           uint64_t output_operand_id);
+  void BuildQuantizeLinear(OperandId input_operand_id,
+                           OperandId scale_operand_id,
+                           OperandId zero_point_operand_id,
+                           OperandId output_operand_id);
 
   void BuildReduce(mojom::Reduce::Kind kind,
-                   uint64_t input_operand_id,
-                   uint64_t output_operand_id,
+                   OperandId input_operand_id,
+                   OperandId output_operand_id,
                    std::vector<uint32_t> axes,
                    bool keep_dimensions);
 
-  void BuildRelu(uint64_t input_operand_id, uint64_t output_operand_id);
+  void BuildRelu(OperandId input_operand_id, OperandId output_operand_id);
 
   // A `Resample2dAttributes` type should have the following members:
   // struct Resample2dAttributes {
@@ -490,8 +491,8 @@
   //   std::optional<std::vector<float>> scales;
   //   std::vector<uint32_t> axes = {2, 3};};
   template <typename Resample2dAttributes>
-  void BuildResample2d(uint64_t input_operand_id,
-                       uint64_t output_operand_id,
+  void BuildResample2d(OperandId input_operand_id,
+                       OperandId output_operand_id,
                        const Resample2dAttributes& attributes) {
     mojom::Resample2dPtr resample2d = mojom::Resample2d::New();
     resample2d->input_operand_id = input_operand_id;
@@ -506,59 +507,59 @@
         mojom::Operation::NewResample2d(std::move(resample2d)));
   }
 
-  void BuildReshape(uint64_t input_operand_id, uint64_t output_operand_id);
+  void BuildReshape(OperandId input_operand_id, OperandId output_operand_id);
 
-  void BuildReverse(uint64_t input_operand_id,
-                    uint64_t output_operand_id,
+  void BuildReverse(OperandId input_operand_id,
+                    OperandId output_operand_id,
                     std::vector<uint32_t> axes);
 
-  void BuildScatterElements(uint64_t input_operand_id,
-                            uint64_t indices_operand_id,
-                            uint64_t updates_operand_id,
-                            uint64_t output_operand_id,
+  void BuildScatterElements(OperandId input_operand_id,
+                            OperandId indices_operand_id,
+                            OperandId updates_operand_id,
+                            OperandId output_operand_id,
                             uint32_t axis);
 
-  void BuildScatterND(uint64_t input_operand_id,
-                      uint64_t indices_operand_id,
-                      uint64_t updates_operand_id,
-                      uint64_t output_operand_id);
+  void BuildScatterND(OperandId input_operand_id,
+                      OperandId indices_operand_id,
+                      OperandId updates_operand_id,
+                      OperandId output_operand_id);
 
-  void BuildSigmoid(uint64_t input_operand_id, uint64_t output_operand_id);
+  void BuildSigmoid(OperandId input_operand_id, OperandId output_operand_id);
 
-  void BuildSoftmax(uint64_t input_operand_id,
-                    uint64_t output_operand_id,
+  void BuildSoftmax(OperandId input_operand_id,
+                    OperandId output_operand_id,
                     uint32_t axis);
 
-  void BuildSoftplus(uint64_t input_operand_id, uint64_t output_operand_id);
+  void BuildSoftplus(OperandId input_operand_id, OperandId output_operand_id);
 
-  void BuildSoftsign(uint64_t input_operand_id, uint64_t output_operand_id);
+  void BuildSoftsign(OperandId input_operand_id, OperandId output_operand_id);
 
-  void BuildSplit(uint64_t input_operand_id,
-                  const std::vector<uint64_t>& output_operand_ids,
+  void BuildSplit(OperandId input_operand_id,
+                  const std::vector<OperandId>& output_operand_ids,
                   uint32_t axis);
 
-  void BuildTanh(uint64_t input_operand_id, uint64_t output_operand_id);
+  void BuildTanh(OperandId input_operand_id, OperandId output_operand_id);
 
-  void BuildTile(uint64_t input_operand_id,
-                 uint64_t output_operand_id,
+  void BuildTile(OperandId input_operand_id,
+                 OperandId output_operand_id,
                  std::vector<uint32_t> repetitions);
 
-  void BuildTranspose(uint64_t input_operand_id,
-                      uint64_t output_operand_id,
+  void BuildTranspose(OperandId input_operand_id,
+                      OperandId output_operand_id,
                       std::vector<uint32_t> permutation);
 
-  void BuildTriangular(uint64_t input_operand_id,
-                       uint64_t output_operand_id,
+  void BuildTriangular(OperandId input_operand_id,
+                       OperandId output_operand_id,
                        bool upper,
                        int32_t diagonal);
 
-  void BuildWhere(uint64_t condition_operand_id,
-                  uint64_t true_value_operand_id,
-                  uint64_t false_value_operand_id,
-                  uint64_t output_operand_id);
+  void BuildWhere(OperandId condition_operand_id,
+                  OperandId true_value_operand_id,
+                  OperandId false_value_operand_id,
+                  OperandId output_operand_id);
 
-  void BuildSlice(uint64_t input_operand_id,
-                  uint64_t output_operand_id,
+  void BuildSlice(OperandId input_operand_id,
+                  OperandId output_operand_id,
                   base::span<const uint32_t> starts,
                   base::span<const uint32_t> sizes,
                   base::span<const uint32_t> strides);
@@ -575,13 +576,13 @@
       const ContextProperties& context_properties);
 
  private:
-  uint64_t BuildOperand(
+  OperandId BuildOperand(
       const std::vector<uint32_t>& dimensions,
       OperandDataType type,
       mojom::Operand::Kind kind = mojom::Operand::Kind::kOutput);
 
   mojom::GraphInfoPtr graph_info_;
-  uint64_t operand_id_ = 1;
+  OperandId next_operand_id_ = 1;
 
   base::raw_ref<mojo::AssociatedRemote<mojom::WebNNGraphBuilder>>
       graph_builder_remote_;
diff --git a/storage/browser/quota/quota_manager_impl.cc b/storage/browser/quota/quota_manager_impl.cc
index d333962e..85ddf5ee 100644
--- a/storage/browser/quota/quota_manager_impl.cc
+++ b/storage/browser/quota/quota_manager_impl.cc
@@ -148,10 +148,11 @@
 
 class QuotaManagerImpl::UsageAndQuotaInfoGatherer : public QuotaTask {
  public:
-  UsageAndQuotaInfoGatherer(QuotaManagerImpl* manager,
-                            const StorageKey& storage_key,
-                            bool is_incognito,
-                            UsageAndQuotaForDevtoolsCallback callback)
+  UsageAndQuotaInfoGatherer(
+      QuotaManagerImpl* manager,
+      const StorageKey& storage_key,
+      bool is_incognito,
+      UsageAndQuotaWithBreakdownAndOverrideFlagCallback callback)
       : QuotaTask(manager),
         storage_key_(storage_key),
         callback_(std::move(callback)),
@@ -161,10 +162,11 @@
     DCHECK(callback_);
   }
 
-  UsageAndQuotaInfoGatherer(QuotaManagerImpl* manager,
-                            const BucketInfo& bucket_info,
-                            bool is_incognito,
-                            UsageAndQuotaForDevtoolsCallback callback)
+  UsageAndQuotaInfoGatherer(
+      QuotaManagerImpl* manager,
+      const BucketInfo& bucket_info,
+      bool is_incognito,
+      UsageAndQuotaWithBreakdownAndOverrideFlagCallback callback)
       : UsageAndQuotaInfoGatherer(manager,
                                   bucket_info.storage_key,
                                   is_incognito,
@@ -331,7 +333,7 @@
   // Non-null iff usage info is to be gathered for an individual bucket. If
   // null, usage is gathered for all buckets in the given host/StorageKey.
   std::optional<BucketInfo> bucket_info_;
-  QuotaManagerImpl::UsageAndQuotaForDevtoolsCallback callback_;
+  QuotaManagerImpl::UsageAndQuotaWithBreakdownAndOverrideFlagCallback callback_;
   const bool is_unlimited_;
   const bool is_incognito_;
 
@@ -1203,7 +1205,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(callback);
 
-  GetUsageAndQuotaForDevtools(
+  HandleGetUsageAndQuotaRequest(
       storage_key,
       base::BindOnce(&DidGetUsageAndQuotaStripOverride, std::move(callback)));
 }
@@ -1216,7 +1218,7 @@
 
   if (base::FeatureList::IsEnabled(storage::features::kStaticStorageQuota) &&
       !IsStorageUnlimited(storage_key)) {
-    GetUsageAndQuotaForDevtools(
+    HandleGetUsageAndQuotaRequest(
         storage_key,
         base::BindOnce(
             [](UsageAndQuotaWithBreakdownCallback callback,
@@ -1232,14 +1234,22 @@
     return;
   }
 
-  GetUsageAndQuotaForDevtools(
+  HandleGetUsageAndQuotaRequest(
       storage_key,
       base::BindOnce(&DidGetUsageAndQuotaStripOverride, std::move(callback)));
 }
 
 void QuotaManagerImpl::GetUsageAndQuotaForDevtools(
     const StorageKey& storage_key,
-    UsageAndQuotaForDevtoolsCallback callback) {
+    UsageAndQuotaWithBreakdownAndOverrideFlagCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(callback);
+  HandleGetUsageAndQuotaRequest(storage_key, std::move(callback));
+}
+
+void QuotaManagerImpl::HandleGetUsageAndQuotaRequest(
+    const StorageKey& storage_key,
+    UsageAndQuotaWithBreakdownAndOverrideFlagCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(callback);
   EnsureDatabaseOpened();
diff --git a/storage/browser/quota/quota_manager_impl.h b/storage/browser/quota/quota_manager_impl.h
index f692b9c..81b986b 100644
--- a/storage/browser/quota/quota_manager_impl.h
+++ b/storage/browser/quota/quota_manager_impl.h
@@ -147,7 +147,7 @@
                               int64_t quota,
                               blink::mojom::UsageBreakdownPtr usage_breakdown)>;
 
-  using UsageAndQuotaForDevtoolsCallback =
+  using UsageAndQuotaWithBreakdownAndOverrideFlagCallback =
       base::OnceCallback<void(blink::mojom::QuotaStatusCode,
                               int64_t usage,
                               int64_t quota,
@@ -269,7 +269,7 @@
   // Called by DevTools.
   virtual void GetUsageAndQuotaForDevtools(
       const blink::StorageKey& storage_key,
-      UsageAndQuotaForDevtoolsCallback callback);
+      UsageAndQuotaWithBreakdownAndOverrideFlagCallback callback);
 
   // Called by storage backends.
   //
@@ -699,6 +699,9 @@
   void GetUsageAndQuotaWithBreakdown(
       const blink::StorageKey& storage_key,
       UsageAndQuotaWithBreakdownCallback callback);
+  void HandleGetUsageAndQuotaRequest(
+      const blink::StorageKey& storage_key,
+      UsageAndQuotaWithBreakdownAndOverrideFlagCallback callback);
   void GetQuotaSettings(QuotaSettingsCallback callback);
   void DidGetSettings(std::optional<QuotaSettings> settings);
   void GetStorageCapacity(StorageCapacityCallback callback);
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 34f157f..30227f02 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -13030,7 +13030,8 @@
                 {
                     "name": "Enabled",
                     "enable_features": [
-                        "LCPPPrefetchSubresource"
+                        "LCPPPrefetchSubresource",
+                        "LCPPPrefetchSubresourceAsync"
                     ]
                 }
             ]
@@ -15945,7 +15946,8 @@
                         "PartitionAllocSchedulerLoopQuarantine",
                         "PartitionAllocWithAdvancedChecks",
                         "PartitionAllocZappingByFreeFlags"
-                    ]
+                    ],
+                    "disable_benchmarking": "true"
                 }
             ]
         }
diff --git a/third_party/angle b/third_party/angle
index 432d1d1..3b77a17 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 432d1d1efdbd0e8d5788529fe193e0eb1e8456bf
+Subproject commit 3b77a177ba09c418046d232e7f16dd402f10ef1c
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index f04610d..8222b48 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -1671,6 +1671,10 @@
              "LCPPPrefetchSubresource",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kLCPPPrefetchSubresourceAsync,
+             "LCPPPrefetchSubresourceAsync",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kHttpDiskCachePrewarming,
              "HttpDiskCachePrewarming",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index bfdbf2c..1095714 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -1140,6 +1140,9 @@
 // cache data.
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kLCPPPrefetchSubresource);
 
+// If enabled, doing prefetch task async after main resource fetching.
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kLCPPPrefetchSubresourceAsync);
+
 // If enabled, prewarm HTTP disk cache based on the previous navigation.
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kHttpDiskCachePrewarming);
 
diff --git a/third_party/blink/public/common/service_worker/service_worker_subresource_load_metrics.h b/third_party/blink/public/common/service_worker/service_worker_subresource_load_metrics.h
index a886c70c..1a843f3c 100644
--- a/third_party/blink/public/common/service_worker/service_worker_subresource_load_metrics.h
+++ b/third_party/blink/public/common/service_worker/service_worker_subresource_load_metrics.h
@@ -136,6 +136,11 @@
   // Routing API, from navigation start till onload event.
   uint32_t matched_race_network_and_fetch_router_source_count = 0;
 
+  // Total number of sub-resources which were matched to the
+  // `RouterSourceEnum.race-network-and-cache` in ServiceWorker Static
+  // Routing API, from navigation start till onload event.
+  uint32_t matched_race_network_and_cache_router_source_count = 0;
+
   // Total router evaluation time of ServiceWorker Static Routing API
   // for sub-resources.
   base::TimeDelta total_router_evaluation_time_for_subresources;
@@ -183,6 +188,8 @@
                other.matched_cache_router_source_count &&
            matched_race_network_and_fetch_router_source_count ==
                other.matched_race_network_and_fetch_router_source_count &&
+           matched_race_network_and_cache_router_source_count ==
+               other.matched_race_network_and_cache_router_source_count &&
            total_router_evaluation_time_for_subresources ==
                other.total_router_evaluation_time_for_subresources &&
            total_cache_lookup_time_for_subresources ==
diff --git a/third_party/blink/renderer/modules/ai/ai.idl b/third_party/blink/renderer/modules/ai/ai.idl
index d024739..9eb32bc4 100644
--- a/third_party/blink/renderer/modules/ai/ai.idl
+++ b/third_party/blink/renderer/modules/ai/ai.idl
@@ -8,6 +8,7 @@
 // for LanguageModel instead.
 [
   Exposed(Window AIPromptAPI, Worker AIPromptAPIForWorkers),
+  RuntimeEnabled=AIPromptAPI,
   SecureContext
 ]
 interface AI {};
diff --git a/third_party/blink/renderer/modules/ai/language_model.idl b/third_party/blink/renderer/modules/ai/language_model.idl
index a55cbed3..95f846e 100644
--- a/third_party/blink/renderer/modules/ai/language_model.idl
+++ b/third_party/blink/renderer/modules/ai/language_model.idl
@@ -15,6 +15,7 @@
 
 [
   Exposed(Window AIPromptAPI, Worker AIPromptAPIForWorkers),
+  RuntimeEnabled=AIPromptAPI,
   SecureContext
 ]
 interface LanguageModel : EventTarget {
diff --git a/third_party/blink/renderer/modules/ai/language_model_factory.idl b/third_party/blink/renderer/modules/ai/language_model_factory.idl
index 64dc38b6..e9abc2d 100644
--- a/third_party/blink/renderer/modules/ai/language_model_factory.idl
+++ b/third_party/blink/renderer/modules/ai/language_model_factory.idl
@@ -9,6 +9,7 @@
 
 [
   Exposed(Window AIPromptAPI, Worker AIPromptAPIForWorkers),
+  RuntimeEnabled=AIPromptAPI,
   SecureContext
 ]
 interface LanguageModelFactory {
diff --git a/third_party/blink/renderer/modules/ai/language_model_params.idl b/third_party/blink/renderer/modules/ai/language_model_params.idl
index 6d0c751..2ab22da 100644
--- a/third_party/blink/renderer/modules/ai/language_model_params.idl
+++ b/third_party/blink/renderer/modules/ai/language_model_params.idl
@@ -6,6 +6,7 @@
 
 [
   Exposed(Window AIPromptAPI, Worker AIPromptAPIForWorkers),
+  RuntimeEnabled=AIPromptAPI,
   SecureContext
 ]
 interface LanguageModelParams {
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index 506cc99..d656a6fe 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -3551,7 +3551,7 @@
       metrics.matched_race_network_and_fetch_router_source_count++;
       break;
     case network::mojom::ServiceWorkerRouterSourceType::kRaceNetworkAndCache:
-      // TODO(crbug.com/370844790): implement race network and cache
+      metrics.matched_race_network_and_cache_router_source_count++;
       break;
   }
 }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 8663620..a9f2490f 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -342,11 +342,7 @@
     {
       name: "AIPromptAPIForWorkers",
       public: true,
-      origin_trial_feature_name: "AIPromptAPIForExtension",
-      origin_trial_allows_third_party: true,
       status: "experimental",
-      base_feature_status: "enabled",
-      copied_from_base_feature_if: "overridden",
     },
     {
       name: "AIPromptAPIMultimodalInput",
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 236d7ef9..d31fbdc 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -9179,7 +9179,7 @@
 # Gardener 2025-04-28
 crbug.com/414234413 [ Mac13-arm64 ] http/tests/devtools/elements/styles-1/edit-value-with-trimmed-url.js [ Failure Pass ]
 
-# Gardener 2025-04-28. Failes on WebKit Linux ASAN
+# Gardener 2025-04-28. Fails on WebKit Linux ASAN
 crbug.com/414243950 [ Linux ] virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.html?cpu [ Crash ]
 crbug.com/414243950 [ Linux ] virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.serviceworker.html?cpu [ Crash ]
 crbug.com/414243950 [ Linux ] virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.sharedworker.html?cpu [ Crash ]
@@ -9187,3 +9187,6 @@
 
 # Gardener 2025-04-30
 crbug.com/324536287 external/wpt/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html [ Failure Pass ]
+
+# Gardener 2025-05-01
+crbug.com/411161567 [ Linux ] accessibility/interest-target.html [ Failure Pass ]
diff --git a/third_party/catapult b/third_party/catapult
index 0f4bac5..1591e81 160000
--- a/third_party/catapult
+++ b/third_party/catapult
@@ -1 +1 @@
-Subproject commit 0f4bac59a38c6aa5ad9874778f822d76ea737d35
+Subproject commit 1591e813b66e04a5b9d9e8fc5d94e74ef4effaa1
diff --git a/third_party/compiler-rt/src b/third_party/compiler-rt/src
index cf8cac9..6558e6a 160000
--- a/third_party/compiler-rt/src
+++ b/third_party/compiler-rt/src
@@ -1 +1 @@
-Subproject commit cf8cac9f64ff4ac6fd2041a699dd86f717c63586
+Subproject commit 6558e6a3eacafd269527cf2595e0e722f117c1e6
diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc
index 03d7cfa..3a6e194 100644
--- a/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc
+++ b/third_party/crashpad/crashpad/snapshot/mac/process_reader_mac_test.cc
@@ -836,7 +836,9 @@
          MacOSVersionNumber() >= 10'07'00;
 }
 
-TEST(ProcessReaderMac, SelfModules) {
+// Disabled to investigate crbug.com/414919209.
+// TODO(crbug.com/414919209): Re-enable or remove if no longer relevant.
+TEST(ProcessReaderMac, DISABLED_SelfModules) {
   ScopedOpenCLNoOpKernel ensure_cl_kernels;
   ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp());
 
@@ -1023,7 +1025,9 @@
   bool ensure_cl_kernels_success_;
 };
 
-TEST(ProcessReaderMac, ChildModules) {
+// Disabled to investigate crbug.com/414919209.
+// TODO(crbug.com/414919209): Re-enable or remove if no longer relevant.
+TEST(ProcessReaderMac, DISABLED_ChildModules) {
   ScopedOpenCLNoOpKernel ensure_cl_kernels;
   ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp());
 
diff --git a/third_party/depot_tools b/third_party/depot_tools
index e659674..354f602 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit e6596746dc95fe658b3a5f0924c10e322c7d8e22
+Subproject commit 354f6026f14f87466040e3e1878c599138a23bce
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 6c841fc..503afe0 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 6c841fc0da4e1eeec9f198fd14167572e1f6656a
+Subproject commit 503afe09a5c33d317399ceb4278bcd59b2f3de0b
diff --git a/third_party/googletest/src b/third_party/googletest/src
index 59c924b..04ee1b4 160000
--- a/third_party/googletest/src
+++ b/third_party/googletest/src
@@ -1 +1 @@
-Subproject commit 59c924bc471cefea25a7cf7eb40b6c101b170b12
+Subproject commit 04ee1b4f2aefdffb0135d7cf2a2c519fe50dabe4
diff --git a/third_party/hunspell/README.chromium b/third_party/hunspell/README.chromium
index 0a76a7f4..af8b153 100644
--- a/third_party/hunspell/README.chromium
+++ b/third_party/hunspell/README.chromium
@@ -1,6 +1,6 @@
 Name: hunspell
 URL: http://hunspell.sourceforge.net/
-Version: v1.7.1
+Version: 18f4056
 CPEPrefix: cpe:/a:hunspell_project:hunspell:1.7.1
 License: MPL-1.1
 License File: COPYING.MPL
@@ -8,7 +8,7 @@
 Shipped: yes
 
 Description:
-This is a partial copy of Hunspell v1.7.1 with the following changes:
+This is a partial copy of Hunspell 18f4056 with the following changes:
 * Move README.md onto the README symlink.
 * Change src/hunspell/filemgr.hxx and src/hunspell/filemgr.cxx to use
   LineIterator.
@@ -34,7 +34,7 @@
 2) Checkout hunspell:
    $ git clone https://github.com/hunspell/hunspell.git
    $ cd hunspell
-   $ git checkout v1.7.1
+   $ git checkout 18f4056
 3) Apply Hunspell patches and make a throwaway commit:
    $ patch -p1 --no-backup-if-mismatch -i $CHROMIUM_HUNSPELL/correction-of-a-knowingly-false-condition.patch
    $ patch -p1 --no-backup-if-mismatch -i $CHROMIUM_HUNSPELL/speed-up-hunspell-inner-loop.patch
diff --git a/third_party/hunspell/README.md b/third_party/hunspell/README.md
index 90ef880..3145e43 100644
--- a/third_party/hunspell/README.md
+++ b/third_party/hunspell/README.md
@@ -307,6 +307,18 @@
     # or better, use pkg-config
     g++ $(pkg-config --cflags --libs hunspell) example.cxx
 
+# Installing Hunspell (vcpkg)
+
+Alternatively, you can build and install hunspell using [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
+
+    git clone https://github.com/Microsoft/vcpkg.git
+    cd vcpkg
+    ./bootstrap-vcpkg.sh
+    ./vcpkg integrate install
+    ./vcpkg install hunspell
+
+The hunspell port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
+
 ## Dictionaries
 
 Hunspell (MySpell) dictionaries:
diff --git a/third_party/hunspell/google.patch b/third_party/hunspell/google.patch
index 553c0fd4..8d0ab01fa6 100644
--- a/third_party/hunspell/google.patch
+++ b/third_party/hunspell/google.patch
@@ -1,5 +1,5 @@
 diff --git a/src/hunspell/affixmgr.cxx b/src/hunspell/affixmgr.cxx
-index 2f38b93..6bdec4e 100644
+index c49be5b..71790df 100644
 --- a/src/hunspell/affixmgr.cxx
 +++ b/src/hunspell/affixmgr.cxx
 @@ -86,11 +86,19 @@
@@ -128,7 +128,7 @@
    }
  
    finishFileMgr(afflst);
-@@ -1271,6 +1328,24 @@ std::string AffixMgr::prefix_check_twosfx_morph(const char* word,
+@@ -1277,6 +1334,24 @@ std::string AffixMgr::prefix_check_twosfx_morph(const char* word,
  // Is word a non-compound with a REP substitution (see checkcompoundrep)?
  int AffixMgr::cpdrep_check(const char* word, int wl) {
  
@@ -144,7 +144,7 @@
 +    while ((r=strstr(r, pattern)) != NULL) {
 +      std::string candidate(word);
 +      candidate.replace(r-word, lenp, pattern2);
-+      if (candidate_check(candidate.c_str(), candidate.size())) return 1;
++      if (candidate_check(candidate)) return 1;
 +      r++; // search for the next letter
 +    }
 +  }
@@ -153,7 +153,7 @@
    if ((wl < 2) || get_reptable().empty())
      return 0;
  
-@@ -1289,6 +1364,7 @@ int AffixMgr::cpdrep_check(const char* word, int wl) {
+@@ -1295,6 +1370,7 @@ int AffixMgr::cpdrep_check(const char* word, int wl) {
        }
      }
    }
@@ -161,15 +161,15 @@
  
   return 0;
  }
-@@ -4470,6 +4546,7 @@ bool AffixMgr::parse_affix(const std::string& line,
+@@ -4480,6 +4556,7 @@ bool AffixMgr::parse_affix(const std::string& line,
        case 1: {
          np++;
-         aflag = pHMgr->decode_flag(std::string(start_piece, iter).c_str());
+         aflag = pHMgr->decode_flag(std::string(start_piece, iter));
 +#ifndef HUNSPELL_CHROME_CLIENT // We don't check for duplicates.
          if (((at == 'S') && (dupflags[aflag] & dupSFX)) ||
              ((at == 'P') && (dupflags[aflag] & dupPFX))) {
            HUNSPELL_WARNING(
-@@ -4478,6 +4555,7 @@ bool AffixMgr::parse_affix(const std::string& line,
+@@ -4488,6 +4565,7 @@ bool AffixMgr::parse_affix(const std::string& line,
                af->getlinenum());
          }
          dupflags[aflag] += (char)((at == 'S') ? dupSFX : dupPFX);
@@ -178,7 +178,7 @@
        }
        // piece 3 - is cross product indicator
 diff --git a/src/hunspell/affixmgr.hxx b/src/hunspell/affixmgr.hxx
-index 450f50a..ea2ee98 100644
+index 836ebe0..1e9c4f1 100644
 --- a/src/hunspell/affixmgr.hxx
 +++ b/src/hunspell/affixmgr.hxx
 @@ -89,6 +89,40 @@
@@ -242,7 +242,7 @@
    ~AffixMgr();
    struct hentry* affix_check(const char* word,
                               int len,
-@@ -334,6 +376,10 @@ class AffixMgr {
+@@ -333,6 +375,10 @@ class AffixMgr {
    int get_fullstrip() const;
  
   private:
@@ -335,10 +335,10 @@
  #endif
 +#endif
 diff --git a/src/hunspell/hashmgr.cxx b/src/hunspell/hashmgr.cxx
-index 3ec263d..2e4ed3a 100644
+index b0a6d74..f1b6583 100644
 --- a/src/hunspell/hashmgr.cxx
 +++ b/src/hunspell/hashmgr.cxx
-@@ -82,8 +82,14 @@
+@@ -82,16 +82,28 @@
  
  // build a hash table from a munched word list
  
@@ -347,15 +347,14 @@
 +    : bdict_reader(reader),
 +#else
  HashMgr::HashMgr(const char* tpath, const char* apath, const char* key)
--    : tablesize(0),
+-    : flag_mode(FLAG_CHAR),
 +    :
 +#endif
-+      tablesize(0),
-       tableptr(NULL),
-       flag_mode(FLAG_CHAR),
++      flag_mode(FLAG_CHAR),
        complexprefixes(0),
-@@ -97,8 +103,14 @@ HashMgr::HashMgr(const char* tpath, const char* apath, const char* key)
-       aliasm(NULL) {
+       utf8(0),
+       forbiddenword(FORBIDDENWORD)  // forbidden word signing flag
+ {
    langnum = 0;
    csconv = 0;
 +#ifdef HUNSPELL_CHROME_CLIENT
@@ -369,7 +368,7 @@
    if (ec) {
      /* error condition - what should we do here */
      HUNSPELL_WARNING(stderr, "Hash Manager Error : %d\n", ec);
-@@ -156,14 +168,57 @@ HashMgr::~HashMgr() {
+@@ -140,14 +152,57 @@ HashMgr::~HashMgr() {
  #endif
  #endif
  
@@ -424,18 +423,18 @@
 +
 +  return AffixIDsToHentry(word_buf, affix_ids, affix_count);
 +#else
-   struct hentry* dp;
-   if (tableptr) {
-     dp = tableptr[hash(word)];
-@@ -175,6 +230,7 @@ struct hentry* HashMgr::lookup(const char* word) const {
-     }
+   struct hentry* dp = tableptr[hash(word)];
+   if (!dp)
+     return NULL;
+@@ -156,6 +211,7 @@ struct hentry* HashMgr::lookup(const char* word) const {
+       return dp;
    }
    return NULL;
 +#endif
  }
  
  // add a word to the hash table (private)
-@@ -185,6 +241,8 @@ int HashMgr::add_word(const std::string& in_word,
+@@ -166,6 +222,8 @@ int HashMgr::add_word(const std::string& in_word,
                        const std::string* in_desc,
                        bool onlyupcase,
                        int captype) {
@@ -444,7 +443,7 @@
    const std::string* word = &in_word;
    const std::string* desc = in_desc;
  
-@@ -416,6 +474,17 @@ int HashMgr::add_word(const std::string& in_word,
+@@ -408,6 +466,17 @@ int HashMgr::add_word(const std::string& in_word,
  
    delete desc_copy;
    delete word_copy;
@@ -462,7 +461,7 @@
    return 0;
  }
  
-@@ -480,6 +549,12 @@ int HashMgr::get_clen_and_captype(const std::string& word, int* captype) {
+@@ -472,6 +541,12 @@ int HashMgr::get_clen_and_captype(const std::string& word, int* captype) {
  
  // remove word (personal dictionary function for standalone applications)
  int HashMgr::remove(const std::string& word) {
@@ -475,7 +474,7 @@
    struct hentry* dp = lookup(word.c_str());
    while (dp) {
      if (dp->alen == 0 || !TESTAFF(dp->astr, forbiddenword, dp->alen)) {
-@@ -497,6 +572,7 @@ int HashMgr::remove(const std::string& word) {
+@@ -489,6 +564,7 @@ int HashMgr::remove(const std::string& word) {
      }
      dp = dp->next_homonym;
    }
@@ -483,7 +482,7 @@
    return 0;
  }
  
-@@ -555,6 +631,44 @@ int HashMgr::add_with_affix(const std::string& word, const std::string& example)
+@@ -547,6 +623,44 @@ int HashMgr::add_with_affix(const std::string& word, const std::string& example)
  // walk the hash table entry by entry - null at end
  // initialize: col=-1; hp = NULL; hp = walk_hashtable(&col, hp);
  struct hentry* HashMgr::walk_hashtable(int& col, struct hentry* hp) const {
@@ -527,8 +526,8 @@
 +#else
    if (hp && hp->next != NULL)
      return hp->next;
-   for (col++; col < tablesize; col++) {
-@@ -564,10 +678,12 @@ struct hentry* HashMgr::walk_hashtable(int& col, struct hentry* hp) const {
+   for (col++; col < (int)tableptr.size(); ++col) {
+@@ -556,10 +670,12 @@ struct hentry* HashMgr::walk_hashtable(int& col, struct hentry* hp) const {
    // null at end and reset to start
    col = -1;
    return NULL;
@@ -541,7 +540,7 @@
    // open dictionary file
    FileMgr* dict = new FileMgr(tpath, key);
    if (dict == NULL)
-@@ -698,12 +814,16 @@ int HashMgr::load_tables(const char* tpath, const char* key) {
+@@ -686,12 +802,16 @@ int HashMgr::load_tables(const char* tpath, const char* key) {
    }
  
    delete dict;
@@ -558,15 +557,15 @@
    unsigned long hv = 0;
    for (int i = 0; i < 4 && *word != 0; i++)
      hv = (hv << 8) | (*word++);
-@@ -712,6 +832,7 @@ int HashMgr::hash(const char* word) const {
+@@ -700,6 +820,7 @@ int HashMgr::hash(const char* word) const {
      hv ^= (*word++);
    }
-   return (unsigned long)hv % tablesize;
+   return (unsigned long)hv % tableptr.size();
 +#endif
  }
  
  int HashMgr::decode_flags(unsigned short** result, const std::string& flags, FileMgr* af) const {
-@@ -921,7 +1042,12 @@ int HashMgr::load_config(const char* affpath, const char* key) {
+@@ -909,7 +1030,12 @@ int HashMgr::load_config(const char* affpath, const char* key) {
    int firstline = 1;
  
    // open the affix file
@@ -579,7 +578,7 @@
    if (!afflst) {
      HUNSPELL_WARNING(
          stderr, "Error - could not open affix description file %s\n", affpath);
-@@ -1159,6 +1285,132 @@ bool HashMgr::parse_aliasf(const std::string& line, FileMgr* af) {
+@@ -1132,6 +1258,132 @@ bool HashMgr::parse_aliasf(const std::string& line, FileMgr* af) {
    return true;
  }
  
@@ -710,10 +709,10 @@
 +#endif
 +
  int HashMgr::is_aliasf() const {
-   return (aliasf != NULL);
+   return !aliasf.empty();
  }
 diff --git a/src/hunspell/hashmgr.hxx b/src/hunspell/hashmgr.hxx
-index 98b09e2..6fa2ba7 100644
+index 401b83c..666074a 100644
 --- a/src/hunspell/hashmgr.hxx
 +++ b/src/hunspell/hashmgr.hxx
 @@ -79,6 +79,13 @@
@@ -740,10 +739,10 @@
 +  std::map<std::string_view, int> custom_word_to_affix_id_map_;
 +  std::vector<std::string*> pointer_to_strings_;
 +#endif
-   int tablesize;
-   struct hentry** tableptr;
+   std::vector<struct hentry*> tableptr;
    flag flag_mode;
-@@ -111,7 +124,23 @@ class HashMgr {
+   int complexprefixes;
+@@ -108,7 +121,23 @@ class HashMgr {
    std::vector<replentry> reptable;
  
   public:
@@ -767,7 +766,7 @@
    ~HashMgr();
  
    struct hentry* lookup(const char*) const;
-@@ -144,6 +173,40 @@ class HashMgr {
+@@ -141,6 +170,40 @@ class HashMgr {
                 int captype);
    int load_config(const char* affpath, const char* key);
    bool parse_aliasf(const std::string& line, FileMgr* af);
@@ -830,7 +829,7 @@
  
  #define ROTATE(v, q) \
 diff --git a/src/hunspell/hunspell.cxx b/src/hunspell/hunspell.cxx
-index 4afafda..f699f5c 100644
+index d136d14..329e297 100644
 --- a/src/hunspell/hunspell.cxx
 +++ b/src/hunspell/hunspell.cxx
 @@ -77,19 +77,28 @@
@@ -1086,23 +1085,23 @@
    /* spell(word) - spellcheck word
     * output: false = bad word, true = good word
 diff --git a/src/hunspell/replist.cxx b/src/hunspell/replist.cxx
-index 1395ade..9d52f33 100644
+index df428be..cc861e4 100644
 --- a/src/hunspell/replist.cxx
 +++ b/src/hunspell/replist.cxx
-@@ -161,6 +161,7 @@ int RepList::add(const std::string& in_pat1, const std::string& pat2) {
+@@ -151,6 +151,7 @@ int RepList::add(const std::string& in_pat1, const std::string& pat2) {
    mystrrep(r->outstrings[type], "_", " ");
-   dat[pos++] = r;
+   dat.push_back(r);
    // sort to the right place in the list
 +#if 0
-   int i;
-   for (i = pos - 1; i > 0; i--) {
+   size_t i;
+   for (i = dat.size() - 1; i > 0; --i) {
      if (strcmp(r->pattern.c_str(), dat[i - 1]->pattern.c_str()) < 0) {
-@@ -169,6 +170,15 @@ int RepList::add(const std::string& in_pat1, const std::string& pat2) {
+@@ -159,6 +160,15 @@ int RepList::add(const std::string& in_pat1, const std::string& pat2) {
        break;
    }
    dat[i] = r;
 +#else
-+  for (int i = pos - 1; i > 0; i--) {
++  for (int i = dat.size() - 1; i > 0; --i) {
 +    r = dat[i];
 +    if (r->pattern < dat[i - 1]->pattern) {
 +      dat[i] = dat[i - 1];
@@ -1114,7 +1113,7 @@
  }
  
 diff --git a/src/hunspell/replist.hxx b/src/hunspell/replist.hxx
-index 08daeb4..2bc8aa7 100644
+index ea0524a..35fe025 100644
 --- a/src/hunspell/replist.hxx
 +++ b/src/hunspell/replist.hxx
 @@ -72,6 +72,12 @@
@@ -1131,7 +1130,7 @@
  
  #include <string>
 diff --git a/src/hunspell/suggestmgr.cxx b/src/hunspell/suggestmgr.cxx
-index 6b363de..2be03dd 100644
+index 6e9d028..c35b491 100644
 --- a/src/hunspell/suggestmgr.cxx
 +++ b/src/hunspell/suggestmgr.cxx
 @@ -82,7 +82,112 @@ const w_char W_VLINE = {'\0', '|'};
@@ -1247,8 +1246,8 @@
    // register affix manager and check in string of chars to
    // try when building candidate suggestions
    pAMgr = aptr;
-@@ -455,6 +560,21 @@ int SuggestMgr::replchars(std::vector<std::string>& wlst,
-   int wl = strlen(word);
+@@ -462,6 +567,21 @@ int SuggestMgr::replchars(std::vector<std::string>& wlst,
+   int wl = word.size();
    if (wl < 2 || !pAMgr)
      return wlst.size();
 +
@@ -1257,27 +1256,27 @@
 +  const char *pattern, *pattern2;
 +  hunspell::ReplacementIterator iterator = bdict_reader->GetReplacementIterator();
 +  while (iterator.GetNext(&pattern, &pattern2)) {
-+    const char* r = word;
++    size_t r = 0;
 +    size_t lenr = strlen(pattern2);
 +    size_t lenp = strlen(pattern);
 +
 +    // search every occurence of the pattern in the word
-+    while ((r=strstr(r, pattern)) != NULL) {
++    while ((r = word.find(pattern, r)) != std::string::npos) {
 +      candidate = word;
-+      candidate.replace(r-word, lenp, pattern2);
++      candidate.replace(r, lenp, pattern2);
 +#else
    const std::vector<replentry>& reptable = pAMgr->get_reptable();
    for (size_t i = 0; i < reptable.size(); ++i) {
-     const char* r = word;
-@@ -474,6 +594,7 @@ int SuggestMgr::replchars(std::vector<std::string>& wlst,
-       candidate.resize(r - word);
+     size_t r = 0;
+@@ -480,6 +600,7 @@ int SuggestMgr::replchars(std::vector<std::string>& wlst,
+       candidate.assign(word, 0, r);
        candidate.append(reptable[i].outstrings[type]);
-       candidate.append(r + reptable[i].pattern.size());
+       candidate.append(word, r + reptable[i].pattern.size(), std::string::npos);
 +#endif
        testsug(wlst, candidate, cpdsuggest, NULL, NULL);
        // check REP suggestions with space
        size_t sp = candidate.find(' ');
-@@ -1131,6 +1252,9 @@ void SuggestMgr::ngsuggest(std::vector<std::string>& wlst,
+@@ -1137,6 +1258,9 @@ void SuggestMgr::ngsuggest(std::vector<std::string>& wlst,
  
    struct hentry* hp = NULL;
    int col = -1;
@@ -1287,7 +1286,7 @@
    phonetable* ph = (pAMgr) ? pAMgr->get_phonetable() : NULL;
    std::string target;
    std::string candidate;
-@@ -1257,7 +1381,11 @@ void SuggestMgr::ngsuggest(std::vector<std::string>& wlst,
+@@ -1263,7 +1387,11 @@ void SuggestMgr::ngsuggest(std::vector<std::string>& wlst,
  
        if (sc > scores[lp]) {
          scores[lp] = sc;
@@ -1299,7 +1298,7 @@
          lval = sc;
          for (int j = 0; j < MAX_ROOTS; j++)
            if (scores[j] < lval) {
-@@ -2197,8 +2325,8 @@ void SuggestMgr::lcs(const char* s,
+@@ -2202,8 +2330,8 @@ void SuggestMgr::lcs(const char* s,
      m = strlen(s);
      n = strlen(s2);
    }
@@ -1310,7 +1309,7 @@
    if (!c || !b) {
      if (c)
        free(c);
-@@ -2207,10 +2335,6 @@ void SuggestMgr::lcs(const char* s,
+@@ -2212,10 +2340,6 @@ void SuggestMgr::lcs(const char* s,
      *result = NULL;
      return;
    }
@@ -1322,7 +1321,7 @@
      for (j = 1; j <= n; j++) {
        if (((utf8) && (su[i - 1] == su2[j - 1])) ||
 diff --git a/src/hunspell/suggestmgr.hxx b/src/hunspell/suggestmgr.hxx
-index 4c2fb69..925c6a0 100644
+index 0c2398a..35c8903 100644
 --- a/src/hunspell/suggestmgr.hxx
 +++ b/src/hunspell/suggestmgr.hxx
 @@ -116,7 +116,11 @@ class SuggestMgr {
diff --git a/third_party/hunspell/src/hunspell/affentry.cxx b/third_party/hunspell/src/hunspell/affentry.cxx
index 2cf4f467..1e33991a 100644
--- a/third_party/hunspell/src/hunspell/affentry.cxx
+++ b/third_party/hunspell/src/hunspell/affentry.cxx
@@ -122,8 +122,9 @@
   return NULL;
 }
 
-inline int PfxEntry::test_condition(const char* st) {
-  const char* pos = NULL;  // group with pos input position
+inline int PfxEntry::test_condition(const std::string& s) {
+  size_t st = 0;
+  size_t pos = std::string::npos;  // group with pos input position
   bool neg = false;        // complementer
   bool ingroup = false;    // character in the group
   if (numconds == 0)
@@ -148,53 +149,56 @@
       case ']': {
         if (bool(neg) == bool(ingroup))
           return 0;
-        pos = NULL;
+        pos = std::string::npos;
         p = nextchar(p);
         // skip the next character
-        if (!ingroup && *st)
-          for (st++; (opts & aeUTF8) && (*st & 0xc0) == 0x80; st++)
-            ;
-        if (*st == '\0' && p)
+        if (!ingroup && st < s.size()) {
+          ++st;
+          while ((opts & aeUTF8) && st < s.size() && (s[st] & 0xc0) == 0x80)
+            ++st;
+        }
+        if (st == s.size() && p)
           return 0;  // word <= condition
         break;
       }
       case '.':
-        if (!pos) {  // dots are not metacharacters in groups: [.]
+        if (pos == std::string::npos) {  // dots are not metacharacters in groups: [.]
           p = nextchar(p);
           // skip the next character
-          for (st++; (opts & aeUTF8) && (*st & 0xc0) == 0x80; st++)
-            ;
-          if (*st == '\0' && p)
+          ++st;
+          while ((opts & aeUTF8) && st < s.size() && (s[st] & 0xc0) == 0x80)
+            ++st;
+          if (st == s.size() && p)
             return 0;  // word <= condition
           break;
         }
       /* FALLTHROUGH */
       default: {
-        if (*st == *p) {
-          st++;
+        if (s[st] == *p) {
+          ++st;
           p = nextchar(p);
-          if ((opts & aeUTF8) && (*(st - 1) & 0x80)) {  // multibyte
+          if ((opts & aeUTF8) && (s[st - 1] & 0x80)) {  // multibyte
             while (p && (*p & 0xc0) == 0x80) {          // character
-              if (*p != *st) {
-                if (!pos)
+              if (*p != s[st]) {
+                if (pos == std::string::npos)
                   return 0;
                 st = pos;
                 break;
               }
               p = nextchar(p);
-              st++;
+              ++st;
             }
-            if (pos && st != pos) {
+            if (pos != std::string::npos && st != pos) {
               ingroup = true;
               while (p && *p != ']' && ((p = nextchar(p)) != NULL)) {
               }
             }
-          } else if (pos) {
+          } else if (pos != std::string::npos) {
             ingroup = true;
             while (p && *p != ']' && ((p = nextchar(p)) != NULL)) {
             }
           }
-        } else if (pos) {  // group
+        } else if (pos != std::string::npos) {  // group
           p = nextchar(p);
         } else
           return 0;
@@ -234,7 +238,7 @@
     // if all conditions are met then check if resulting
     // root word in the dictionary
 
-    if (test_condition(tmpword.c_str())) {
+    if (test_condition(tmpword)) {
       tmpl += strip.size();
       if ((he = pmyMgr->lookup(tmpword.c_str())) != NULL) {
         do {
@@ -266,7 +270,7 @@
 }
 
 // check if this prefix entry matches
-struct hentry* PfxEntry::check_twosfx(const char* word,
+struct hentry* PfxEntry::check_twosfx(const std::string& word,
                                       int len,
                                       char in_compound,
                                       const FLAG needflag) {
@@ -283,7 +287,7 @@
     // back any characters that would have been stripped
 
     std::string tmpword(strip);
-    tmpword.append(word + appnd.size());
+    tmpword.append(word, appnd.size());
 
     // now make sure all of the conditions on characters
     // are met.  Please see the appendix at the end of
@@ -293,7 +297,7 @@
     // if all conditions are met then check if resulting
     // root word in the dictionary
 
-    if (test_condition(tmpword.c_str())) {
+    if (test_condition(tmpword)) {
       tmpl += strip.size();
 
       // prefix matched but no root word was found
@@ -302,7 +306,7 @@
 
       if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) {
         // hash entry of root word or NULL
-        struct hentry* he = pmyMgr->suffix_check_twosfx(tmpword.c_str(), tmpl, aeXPRODUCT, this,
+        struct hentry* he = pmyMgr->suffix_check_twosfx(tmpword, tmpl, aeXPRODUCT, this,
                                                         needflag);
         if (he)
           return he;
@@ -340,7 +344,7 @@
     // if all conditions are met then check if resulting
     // root word in the dictionary
 
-    if (test_condition(tmpword.c_str())) {
+    if (test_condition(tmpword)) {
       tmpl += strip.size();
 
       // prefix matched but no root word was found
@@ -387,7 +391,7 @@
     // if all conditions are met then check if resulting
     // root word in the dictionary
 
-    if (test_condition(tmpword.c_str())) {
+    if (test_condition(tmpword)) {
       tmpl += strip.size();
       struct hentry* he;  // hash entry of root word or NULL
       if ((he = pmyMgr->lookup(tmpword.c_str())) != NULL) {
@@ -689,7 +693,7 @@
 }
 
 // see if two-level suffix is present in the word
-struct hentry* SfxEntry::check_twosfx(const char* word,
+struct hentry* SfxEntry::check_twosfx(const std::string& word,
                                       int len,
                                       int optflags,
                                       PfxEntry* ppfx,
diff --git a/third_party/hunspell/src/hunspell/affentry.hxx b/third_party/hunspell/src/hunspell/affentry.hxx
index b736bf0..6cc8d2a 100644
--- a/third_party/hunspell/src/hunspell/affentry.hxx
+++ b/third_party/hunspell/src/hunspell/affentry.hxx
@@ -99,7 +99,7 @@
                            char in_compound,
                            const FLAG needflag = FLAG_NULL);
 
-  struct hentry* check_twosfx(const char* word,
+  struct hentry* check_twosfx(const std::string& word,
                               int len,
                               char in_compound,
                               const FLAG needflag = FLAG_NULL);
@@ -136,7 +136,7 @@
   inline void setFlgNxt(PfxEntry* ptr) { flgnxt = ptr; }
 
   inline char* nextchar(char* p);
-  inline int test_condition(const char* st);
+  inline int test_condition(const std::string& st);
 };
 
 /* A Suffix Entry */
@@ -171,7 +171,7 @@
                            const FLAG needflag,
                            const FLAG badflag);
 
-  struct hentry* check_twosfx(const char* word,
+  struct hentry* check_twosfx(const std::string& word,
                               int len,
                               int optflags,
                               PfxEntry* ppfx,
diff --git a/third_party/hunspell/src/hunspell/affixmgr.cxx b/third_party/hunspell/src/hunspell/affixmgr.cxx
index 6bdec4e..71790df 100644
--- a/third_party/hunspell/src/hunspell/affixmgr.cxx
+++ b/third_party/hunspell/src/hunspell/affixmgr.cxx
@@ -1083,10 +1083,12 @@
 }
 
 // calculate the character length of the condition
-int AffixMgr::condlen(const char* st) {
+int AffixMgr::condlen(const std::string& s) {
   int l = 0;
   bool group = false;
-  for (; *st; st++) {
+  std::string::const_iterator st = s.begin();
+  std::string::const_iterator end = s.end();
+  while (st != end) {
     if (*st == '[') {
       group = true;
       l++;
@@ -1094,16 +1096,17 @@
       group = false;
     else if (!group && (!utf8 || (!(*st & 0x80) || ((*st & 0xc0) == 0x80))))
       l++;
+    ++st;
   }
   return l;
 }
 
-int AffixMgr::encodeit(AffEntry& entry, const char* cs) {
-  if (strcmp(cs, ".") != 0) {
+int AffixMgr::encodeit(AffEntry& entry, const std::string& cs) {
+  if (cs.compare(".") != 0) {
     entry.numconds = (char)condlen(cs);
-    const size_t cslen = strlen(cs);
+    const size_t cslen = cs.size();
     const size_t short_part = std::min<size_t>(MAXCONDLEN, cslen);
-    memcpy(entry.c.conds, cs, short_part);
+    memcpy(entry.c.conds, cs.data(), short_part);
     if (short_part < MAXCONDLEN) {
       //blank out the remaining space
       memset(entry.c.conds + short_part, 0, MAXCONDLEN - short_part);
@@ -1111,9 +1114,12 @@
       //there is more conditions than fit in fixed space, so its
       //a long condition
       entry.opts |= aeLONGCOND;
-      entry.c.l.conds2 = mystrdup(cs + MAXCONDLEN_1);
+      size_t remaining = cs.size() - MAXCONDLEN_1;
+      entry.c.l.conds2 = (char*)malloc(1 + remaining);
       if (!entry.c.l.conds2)
         return 1;
+      memcpy(entry.c.l.conds2, cs.data() + MAXCONDLEN_1, remaining);
+      entry.c.l.conds2[remaining] = 0;
     }
   } else {
     entry.numconds = 0;
@@ -1197,7 +1203,7 @@
 }
 
 // check word for prefixes and two-level suffixes
-struct hentry* AffixMgr::prefix_check_twosfx(const char* word,
+struct hentry* AffixMgr::prefix_check_twosfx(const std::string& word,
                                              int len,
                                              char in_compound,
                                              const FLAG needflag) {
@@ -1218,11 +1224,11 @@
   }
 
   // now handle the general case
-  unsigned char sp = *((const unsigned char*)word);
+  unsigned char sp = word[0];
   PfxEntry* pptr = pStart[sp];
 
   while (pptr) {
-    if (isSubset(pptr->getKey(), word)) {
+    if (isSubset(pptr->getKey(), word.c_str())) {
       rv = pptr->check_twosfx(word, len, in_compound, needflag);
       if (rv) {
         pfx = pptr;
@@ -1340,7 +1346,7 @@
     while ((r=strstr(r, pattern)) != NULL) {
       std::string candidate(word);
       candidate.replace(r-word, lenp, pattern2);
-      if (candidate_check(candidate.c_str(), candidate.size())) return 1;
+      if (candidate_check(candidate)) return 1;
       r++; // search for the next letter
     }
   }
@@ -1358,7 +1364,7 @@
       while ((r = strstr(r, get_reptable()[i].pattern.c_str())) != NULL) {
         std::string candidate(word);
         candidate.replace(r - word, lenp, get_reptable()[i].outstrings[0]);
-        if (candidate_check(candidate.c_str(), candidate.size()))
+        if (candidate_check(candidate))
           return 1;
         ++r;  // search for the next letter
       }
@@ -1379,7 +1385,7 @@
       if (utf8 && ((word[i] & 0xc0) == 0x80))
           continue;
       candidate.insert(i, 1, ' ');
-      if (candidate_check(candidate.c_str(), candidate.size()))
+      if (candidate_check(candidate))
         return 1;
       candidate.erase(i, 1);
     }
@@ -1407,7 +1413,7 @@
          ((checkcpdtable[i].pattern[0] == '0' && r1->blen <= pos &&
            strncmp(word + pos - r1->blen, r1->word, r1->blen) == 0) ||
           (checkcpdtable[i].pattern[0] != '0' &&
-           ((len = checkcpdtable[i].pattern.size()) != 0) &&
+           ((len = checkcpdtable[i].pattern.size()) != 0) && len <= pos &&
            strncmp(word + pos - len, checkcpdtable[i].pattern.c_str(), len) == 0)))) {
       return 1;
     }
@@ -1578,16 +1584,16 @@
   return 0;
 }
 
-inline int AffixMgr::candidate_check(const char* word, int len) {
+inline int AffixMgr::candidate_check(const std::string& word) {
 
-  struct hentry* rv = lookup(word);
+  struct hentry* rv = lookup(word.c_str());
   if (rv)
     return 1;
 
   //  rv = prefix_check(word,len,1);
   //  if (rv) return 1;
 
-  rv = affix_check(word, len);
+  rv = affix_check(word.c_str(), word.size());
   if (rv)
     return 1;
   return 0;
@@ -1651,7 +1657,6 @@
                                         char hu_mov_rule = 0,
                                         char is_sug = 0,
                                         int* info = NULL) {
-  int i;
   short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
   struct hentry* rv = NULL;
   struct hentry* rv_first;
@@ -1694,7 +1699,7 @@
 
   st.assign(word);
 
-  for (i = cmin; i < cmax; i++) {
+  for (int i = cmin; i < cmax; ++i) {
     // go to end of the UTF-8 character
     if (utf8) {
       for (; (st[i] & 0xc0) == 0x80; i++)
@@ -1795,7 +1800,7 @@
                       st.c_str(), i, 0, NULL, FLAG_NULL, compoundflag,
                       hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
                  (compoundmoresuffixes &&
-                  (rv = suffix_check_twosfx(st.c_str(), i, 0, NULL, compoundflag)))) &&
+                  (rv = suffix_check_twosfx(st, i, 0, NULL, compoundflag)))) &&
                 !hu_mov_rule && sfx->getCont() &&
                 ((compoundforbidflag &&
                   TESTAFF(sfx->getCont(), compoundforbidflag,
@@ -1813,7 +1818,7 @@
                       hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
                  (compoundmoresuffixes &&
                   (rv = suffix_check_twosfx(
-                       st.c_str(), i, 0, NULL,
+                       st, i, 0, NULL,
                        compoundbegin))) ||  // twofold suffixes + compound
                  (rv = prefix_check(st.c_str(), i,
                                     hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN,
@@ -1824,7 +1829,7 @@
                       hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
                  (compoundmoresuffixes &&
                   (rv = suffix_check_twosfx(
-                       st.c_str(), i, 0, NULL,
+                       st, i, 0, NULL,
                        compoundmiddle))) ||  // twofold suffixes + compound
                  (rv = prefix_check(st.c_str(), i,
                                     hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN,
@@ -1969,7 +1974,7 @@
             }
 
             // check FORCEUCASE
-            if (rv && forceucase && (rv) &&
+            if (rv && forceucase &&
                 (TESTAFF(rv->astr, forceucase, rv->alen)) &&
                 !(info && *info & SPELL_ORIGCAP))
               rv = NULL;
@@ -2038,19 +2043,20 @@
             // perhaps second word has prefix or/and suffix
             sfx = NULL;
             sfxflag = FLAG_NULL;
-            rv = (compoundflag && !onlycpdrule)
+            rv = (compoundflag && !onlycpdrule && i < word.size())
                      ? affix_check((word.c_str() + i), strlen(word.c_str() + i), compoundflag,
                                    IN_CPD_END)
                      : NULL;
             if (!rv && compoundend && !onlycpdrule) {
               sfx = NULL;
               pfx = NULL;
-              rv = affix_check((word.c_str() + i), strlen(word.c_str() + i), compoundend,
-                               IN_CPD_END);
+              if (i < word.size())
+                rv = affix_check((word.c_str() + i), strlen(word.c_str() + i), compoundend, IN_CPD_END);
             }
 
             if (!rv && !defcpdtable.empty() && words) {
-              rv = affix_check((word.c_str() + i), strlen(word.c_str() + i), 0, IN_CPD_END);
+              if (i < word.size())
+                rv = affix_check((word.c_str() + i), strlen(word.c_str() + i), 0, IN_CPD_END);
               if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1))
                 return rv_first;
               rv = NULL;
@@ -2078,7 +2084,7 @@
             }
 
             // check FORCEUCASE
-            if (rv && forceucase && (rv) &&
+            if (rv && forceucase &&
                 (TESTAFF(rv->astr, forceucase, rv->alen)) &&
                 !(info && *info & SPELL_ORIGCAP))
               rv = NULL;
@@ -2256,8 +2262,7 @@
 
 // check if compound word is correctly spelled
 // hu_mov_rule = spec. Hungarian rule (XXX)
-int AffixMgr::compound_check_morph(const char* word,
-                                   int len,
+int AffixMgr::compound_check_morph(const std::string& word,
                                    short wordnum,
                                    short numsyllable,
                                    short maxwordnum,
@@ -2267,7 +2272,6 @@
                                    char hu_mov_rule,
                                    std::string& result,
                                    const std::string* partresult) {
-  int i;
   short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
   int ok = 0;
 
@@ -2284,6 +2288,7 @@
 
   char affixed = 0;
   hentry** oldwords = words;
+  size_t len = word.size();
 
   // add a time limit to handle possible
   // combinatorical explosion of the overlapping words
@@ -2303,11 +2308,11 @@
             > static_cast<double>(TIMELIMIT) * CLOCKS_PER_SEC * 1000)
       timelimit_exceeded = true;
 
-  setcminmax(&cmin, &cmax, word, len);
+  setcminmax(&cmin, &cmax, word.c_str(), len);
 
   st.assign(word);
 
-  for (i = cmin; i < cmax; i++) {
+  for (int i = cmin; i < cmax; ++i) {
     // go to end of the UTF-8 character
     if (utf8) {
       for (; (st[i] & 0xc0) == 0x80; i++)
@@ -2393,7 +2398,7 @@
                                   compoundflag,
                                   hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
                (compoundmoresuffixes &&
-                (rv = suffix_check_twosfx(st.c_str(), i, 0, NULL, compoundflag)))) &&
+                (rv = suffix_check_twosfx(st, i, 0, NULL, compoundflag)))) &&
               !hu_mov_rule && sfx->getCont() &&
               ((compoundforbidflag &&
                 TESTAFF(sfx->getCont(), compoundforbidflag,
@@ -2411,7 +2416,7 @@
                                   hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
                (compoundmoresuffixes &&
                 (rv = suffix_check_twosfx(
-                     st.c_str(), i, 0, NULL,
+                     st, i, 0, NULL,
                      compoundbegin))) ||  // twofold suffix+compound
                (rv = prefix_check(st.c_str(), i,
                                   hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN,
@@ -2422,7 +2427,7 @@
                                   hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
                (compoundmoresuffixes &&
                 (rv = suffix_check_twosfx(
-                     st.c_str(), i, 0, NULL,
+                     st, i, 0, NULL,
                      compoundmiddle))) ||  // twofold suffix+compound
                (rv = prefix_check(st.c_str(), i,
                                   hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN,
@@ -2516,8 +2521,8 @@
              (
                  // test CHECKCOMPOUNDPATTERN
                  !checkcpdtable.empty() && !words &&
-                 cpdpat_check(word, i, rv, NULL, affixed)) ||
-             (checkcompoundcase && !words && cpdcase_check(word, i))))
+                 cpdpat_check(word.c_str(), i, rv, NULL, affixed)) ||
+             (checkcompoundcase && !words && cpdcase_check(word.c_str(), i))))
           // LANG_hu section: spec. Hungarian rule
           ||
           ((!rv) && (langnum == LANG_hu) && hu_mov_rule &&
@@ -2541,7 +2546,7 @@
 
         // NEXT WORD(S)
         rv_first = rv;
-        rv = lookup((word + i));  // perhaps without prefix
+        rv = lookup((word.c_str() + i));  // perhaps without prefix
 
         // search homonym with compound flag
         while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
@@ -2558,7 +2563,7 @@
           result.append(presult);
           result.push_back(MSEP_FLD);
           result.append(MORPH_PART);
-          result.append(word + i);
+          result.append(word, i);
           if (complexprefixes && HENTRY_DATA(rv))
             result.append(HENTRY_DATA2(rv));
           if (!HENTRY_FIND(rv, MORPH_STEM)) {
@@ -2615,7 +2620,7 @@
           result.append(presult);
           result.push_back(MSEP_FLD);
           result.append(MORPH_PART);
-          result.append(word + i);
+          result.append(word, i);
 
           if (HENTRY_DATA(rv)) {
             if (complexprefixes)
@@ -2643,30 +2648,30 @@
         sfxflag = FLAG_NULL;
 
         if (compoundflag && !onlycpdrule)
-          rv = affix_check((word + i), strlen(word + i), compoundflag);
+          rv = affix_check((word.c_str() + i), strlen(word.c_str() + i), compoundflag);
         else
           rv = NULL;
 
         if (!rv && compoundend && !onlycpdrule) {
           sfx = NULL;
           pfx = NULL;
-          rv = affix_check((word + i), strlen(word + i), compoundend);
+          rv = affix_check((word.c_str() + i), strlen(word.c_str() + i), compoundend);
         }
 
         if (!rv && !defcpdtable.empty() && words) {
-          rv = affix_check((word + i), strlen(word + i), 0, IN_CPD_END);
+          rv = affix_check((word.c_str() + i), strlen(word.c_str() + i), 0, IN_CPD_END);
           if (rv && words && defcpd_check(&words, wnum + 1, rv, NULL, 1)) {
             std::string m;
             if (compoundflag)
-              m = affix_check_morph((word + i), strlen(word + i), compoundflag);
+              m = affix_check_morph((word.c_str() + i), strlen(word.c_str() + i), compoundflag);
             if (m.empty() && compoundend) {
-              m = affix_check_morph((word + i), strlen(word + i), compoundend);
+              m = affix_check_morph((word.c_str() + i), strlen(word.c_str() + i), compoundend);
             }
             result.append(presult);
             if (!m.empty()) {
               result.push_back(MSEP_FLD);
               result.append(MORPH_PART);
-              result.append(word + i);
+              result.append(word, i);
               line_uniq_app(m, MSEP_REC);
               result.append(m);
             }
@@ -2696,7 +2701,7 @@
 
         if (langnum == LANG_hu) {
           // calculate syllable number of the word
-          numsyllable += get_syllable(word + i);
+          numsyllable += get_syllable(word.c_str() + i);
 
           // - affix syllable num.
           // XXX only second suffix (inflections, not derivations)
@@ -2750,15 +2755,15 @@
             ((!checkcompounddup || (rv != rv_first)))) {
           std::string m;
           if (compoundflag)
-            m = affix_check_morph((word + i), strlen(word + i), compoundflag);
+            m = affix_check_morph((word.c_str() + i), strlen(word.c_str() + i), compoundflag);
           if (m.empty() && compoundend) {
-            m = affix_check_morph((word + i), strlen(word + i), compoundend);
+            m = affix_check_morph((word.c_str() + i), strlen(word.c_str() + i), compoundend);
           }
           result.append(presult);
           if (!m.empty()) {
             result.push_back(MSEP_FLD);
             result.append(MORPH_PART);
-            result.append(word + i);
+            result.append(word, i);
             line_uniq_app(m, MSEP_REC);
             result.push_back(MSEP_FLD);
             result.append(m);
@@ -2772,7 +2777,7 @@
 
         // perhaps second word is a compound word (recursive call)
         if ((wordnum + 2 < maxwordnum) && (ok == 0)) {
-          compound_check_morph((word + i), strlen(word + i), wordnum + 1,
+          compound_check_morph(word.substr(i), wordnum + 1,
                                numsyllable, maxwordnum, wnum + 1, words, rwords, 0,
                                result, &presult);
         } else {
@@ -2923,7 +2928,7 @@
 }
 
 // check word for two-level suffixes
-struct hentry* AffixMgr::suffix_check_twosfx(const char* word,
+struct hentry* AffixMgr::suffix_check_twosfx(const std::string& word,
                                              int len,
                                              int sfxopts,
                                              PfxEntry* ppfx,
@@ -2944,11 +2949,11 @@
   // now handle the general case
   if (len == 0)
     return NULL;  // FULLSTRIP
-  unsigned char sp = *((const unsigned char*)(word + len - 1));
+  unsigned char sp = word[len - 1];
   SfxEntry* sptr = sStart[sp];
 
   while (sptr) {
-    if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
+    if (isRevSubset(sptr->getKey(), word.c_str() + len - 1, len)) {
       if (contclasses[sptr->getFlag()]) {
         rv = sptr->check_twosfx(word, len, sfxopts, ppfx, needflag);
         if (rv) {
@@ -3785,7 +3790,7 @@
   std::string s;
   if (!parse_string(line, s, af->getlinenum()))
     return false;
-  *out = pHMgr->decode_flag(s.c_str());
+  *out = pHMgr->decode_flag(s);
   return true;
 }
 
@@ -4105,7 +4110,7 @@
           if (slash_pos != std::string::npos) {
             std::string chunk(checkcpdtable.back().pattern, slash_pos + 1);
             checkcpdtable.back().pattern.resize(slash_pos);
-            checkcpdtable.back().cond = pHMgr->decode_flag(chunk.c_str());
+            checkcpdtable.back().cond = pHMgr->decode_flag(chunk);
           }
           break;
         }
@@ -4115,7 +4120,7 @@
           if (slash_pos != std::string::npos) {
             std::string chunk(checkcpdtable.back().pattern2, slash_pos + 1);
             checkcpdtable.back().pattern2.resize(slash_pos);
-            checkcpdtable.back().cond2 = pHMgr->decode_flag(chunk.c_str());
+            checkcpdtable.back().cond2 = pHMgr->decode_flag(chunk);
           }
           break;
         }
@@ -4320,6 +4325,11 @@
                 --k;
               }
             }
+            if (chb == che) {
+              HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
+                              af->getlinenum());
+            }
+
             maptable.back().push_back(std::string(chb, che));
           }
           break;
@@ -4545,7 +4555,7 @@
       // piece 2 - is affix char
       case 1: {
         np++;
-        aflag = pHMgr->decode_flag(std::string(start_piece, iter).c_str());
+        aflag = pHMgr->decode_flag(std::string(start_piece, iter));
 #ifndef HUNSPELL_CHROME_CLIENT // We don't check for duplicates.
         if (((at == 'S') && (dupflags[aflag] & dupSFX)) ||
             ((at == 'P') && (dupflags[aflag] & dupPFX))) {
@@ -4636,7 +4646,7 @@
         case 1: {
           np++;
           std::string chunk(start_piece, iter);
-          if (pHMgr->decode_flag(chunk.c_str()) != aflag) {
+          if (pHMgr->decode_flag(chunk) != aflag) {
             char* err = pHMgr->encode_flag(aflag);
             if (err) {
               HUNSPELL_WARNING(stderr,
@@ -4751,14 +4761,14 @@
             reverse_condition(chunk);
           }
           if (!entry->strip.empty() && chunk != "." &&
-              redundant_condition(at, entry->strip.c_str(), entry->strip.size(), chunk.c_str(),
+              redundant_condition(at, entry->strip, chunk,
                                   af->getlinenum()))
             chunk = ".";
           if (at == 'S') {
             reverseword(chunk);
             reverse_condition(chunk);
           }
-          if (encodeit(*entry, chunk.c_str()))
+          if (encodeit(*entry, chunk))
             return false;
           break;
         }
@@ -4838,17 +4848,15 @@
 }
 
 int AffixMgr::redundant_condition(char ft,
-                                  const char* strip,
-                                  int stripl,
-                                  const char* cond,
+                                  const std::string& strip,
+                                  const std::string& cond,
                                   int linenum) {
-  int condl = strlen(cond);
-  int i;
-  int j;
+  int stripl = strip.size(), condl = cond.size();
+  int i, j;
   int neg;
   int in;
   if (ft == 'P') {  // prefix
-    if (strncmp(strip, cond, condl) == 0)
+    if (strip.compare(0, condl, cond) == 0)
       return 1;
     if (utf8) {
     } else {
@@ -4872,7 +4880,7 @@
           if (j == (condl - 1) && (cond[j] != ']')) {
             HUNSPELL_WARNING(stderr,
                              "error: line %d: missing ] in condition:\n%s\n",
-                             linenum, cond);
+                             linenum, cond.c_str());
             return 0;
           }
           if ((!neg && !in) || (neg && in)) {
@@ -4888,7 +4896,7 @@
         return 1;
     }
   } else {  // suffix
-    if ((stripl >= condl) && strcmp(strip + stripl - condl, cond) == 0)
+    if ((stripl >= condl) && strip.compare(stripl - condl, std::string::npos, cond) == 0)
       return 1;
     if (utf8) {
     } else {
@@ -4911,7 +4919,7 @@
           if ((j == 0) && (cond[j] != '[')) {
             HUNSPELL_WARNING(stderr,
                              "error: line: %d: missing ] in condition:\n%s\n",
-                             linenum, cond);
+                             linenum, cond.c_str());
             return 0;
           }
           neg = (cond[j + 1] == '^') ? 1 : 0;
@@ -4933,7 +4941,7 @@
 
 std::vector<std::string> AffixMgr::get_suffix_words(short unsigned* suff,
                                int len,
-                               const char* root_word) {
+                               const std::string& root_word) {
   std::vector<std::string> slst;
   short unsigned* start_ptr = suff;
   for (int j = 0; j < SETSIZE; j++) {
diff --git a/third_party/hunspell/src/hunspell/affixmgr.hxx b/third_party/hunspell/src/hunspell/affixmgr.hxx
index ea2ee98..1e9c4f1 100644
--- a/third_party/hunspell/src/hunspell/affixmgr.hxx
+++ b/third_party/hunspell/src/hunspell/affixmgr.hxx
@@ -227,7 +227,7 @@
                               char in_compound,
                               const FLAG needflag = FLAG_NULL);
   inline int isSubset(const char* s1, const char* s2);
-  struct hentry* prefix_check_twosfx(const char* word,
+  struct hentry* prefix_check_twosfx(const std::string& word,
                                      int len,
                                      char in_compound,
                                      const FLAG needflag = FLAG_NULL);
@@ -239,7 +239,7 @@
                               const FLAG cclass = FLAG_NULL,
                               const FLAG needflag = FLAG_NULL,
                               char in_compound = IN_CPD_NOT);
-  struct hentry* suffix_check_twosfx(const char* word,
+  struct hentry* suffix_check_twosfx(const std::string& word,
                                      int len,
                                      int sfxopts,
                                      PfxEntry* ppfx,
@@ -303,7 +303,7 @@
                    hentry** rwords,
                    char all);
   int cpdcase_check(const char* word, int len);
-  inline int candidate_check(const char* word, int len);
+  inline int candidate_check(const std::string& word);
   void setcminmax(int* cmin, int* cmax, const char* word, int len);
   struct hentry* compound_check(const std::string& word,
                                 short wordnum,
@@ -316,8 +316,7 @@
                                 char is_sug,
                                 int* info);
 
-  int compound_check_morph(const char* word,
-                           int len,
+  int compound_check_morph(const std::string& word,
                            short wordnum,
                            short numsyllable,
                            short maxwordnum,
@@ -330,7 +329,7 @@
 
   std::vector<std::string> get_suffix_words(short unsigned* suff,
                        int len,
-                       const char* root_word);
+                       const std::string& root_word);
 
   struct hentry* lookup(const char* word);
   const std::vector<replentry>& get_reptable() const;
@@ -397,8 +396,8 @@
 
   void reverse_condition(std::string&);
   std::string& debugflag(std::string& result, unsigned short flag);
-  int condlen(const char*);
-  int encodeit(AffEntry& entry, const char* cs);
+  int condlen(const std::string& s);
+  int encodeit(AffEntry& entry, const std::string& cs);
   int build_pfxtree(PfxEntry* pfxptr);
   int build_sfxtree(SfxEntry* sfxptr);
   int process_pfx_order();
@@ -407,7 +406,7 @@
   SfxEntry* process_sfx_in_order(SfxEntry* ptr, SfxEntry* nptr);
   int process_pfx_tree_to_list();
   int process_sfx_tree_to_list();
-  int redundant_condition(char, const char* strip, int stripl, const char* cond, int);
+  int redundant_condition(char, const std::string& strip, const std::string& cond, int);
   void finishFileMgr(FileMgr* afflst);
 };
 
diff --git a/third_party/hunspell/src/hunspell/csutil.cxx b/third_party/hunspell/src/hunspell/csutil.cxx
index fbaa768..df1fc92 100644
--- a/third_party/hunspell/src/hunspell/csutil.cxx
+++ b/third_party/hunspell/src/hunspell/csutil.cxx
@@ -134,6 +134,7 @@
 
 std::string& u16_u8(std::string& dest, const std::vector<w_char>& src) {
   dest.clear();
+  dest.reserve(src.size());
   std::vector<w_char>::const_iterator u2 = src.begin();
   std::vector<w_char>::const_iterator u2_max = src.end();
   while (u2 < u2_max) {
@@ -169,8 +170,11 @@
   return dest;
 }
 
-int u8_u16(std::vector<w_char>& dest, const std::string& src) {
-  dest.clear();
+int u8_u16(std::vector<w_char>& dest, const std::string& src, bool only_convert_first_letter) {
+  // faster to oversize initially, assign to elements and resize to what's used
+  // than to reserve and push_back
+  dest.resize(only_convert_first_letter ? 1 : src.size());
+  std::vector<w_char>::iterator u16 = dest.begin();
   std::string::const_iterator u8 = src.begin();
   std::string::const_iterator u8_max = src.end();
 
@@ -246,21 +250,27 @@
         }
         break;
       }
-      case 0xf0: {  // 4 or more byte UTF-8 codes
+      default: {  // 4 or more byte UTF-8 codes
+        assert(((*u8) & 0xf0) == 0xf0 && "can only be 0xf0");
         HUNSPELL_WARNING(stderr,
                          "This UTF-8 encoding can't convert to UTF-16:\n%s\n",
                          src.c_str());
         u2.h = 0xff;
         u2.l = 0xfd;
-        dest.push_back(u2);
+        *u16++ = u2;
+        dest.resize(u16 - dest.begin());
         return -1;
       }
     }
-    dest.push_back(u2);
+    *u16++ = u2;
+    if (only_convert_first_letter)
+        break;
     ++u8;
   }
 
-  return dest.size();
+  int size = u16 - dest.begin();
+  dest.resize(size);
+  return size;
 }
 
 namespace {
diff --git a/third_party/hunspell/src/hunspell/csutil.hxx b/third_party/hunspell/src/hunspell/csutil.hxx
index c6f03d8..eaab2f7 100644
--- a/third_party/hunspell/src/hunspell/csutil.hxx
+++ b/third_party/hunspell/src/hunspell/csutil.hxx
@@ -78,6 +78,7 @@
 #include <fstream>
 #include <string>
 #include <vector>
+#include <assert.h>
 #include <string.h>
 #include "w_char.hxx"
 #include "htypes.hxx"
@@ -135,7 +136,8 @@
 
 // convert UTF-8 characters to UTF-16
 LIBHUNSPELL_DLL_EXPORTED int u8_u16(std::vector<w_char>& dest,
-                                    const std::string& src);
+                                    const std::string& src,
+                                    bool only_convert_first_letter = false);
 
 // remove end of line char(s)
 LIBHUNSPELL_DLL_EXPORTED void mychomp(std::string& s);
@@ -321,7 +323,8 @@
 
 inline char* HENTRY_FIND(struct hentry* h,
                                                   const char* p) {
-  return (HENTRY_DATA(h) ? strstr(HENTRY_DATA(h), p) : NULL);
+  char* data = HENTRY_DATA(h);
+  return data ? strstr(data, p) : NULL;
 }
 
 #endif
diff --git a/third_party/hunspell/src/hunspell/hashmgr.cxx b/third_party/hunspell/src/hunspell/hashmgr.cxx
index 2e4ed3a..f1b6583 100644
--- a/third_party/hunspell/src/hunspell/hashmgr.cxx
+++ b/third_party/hunspell/src/hunspell/hashmgr.cxx
@@ -89,18 +89,11 @@
 HashMgr::HashMgr(const char* tpath, const char* apath, const char* key)
     :
 #endif
-      tablesize(0),
-      tableptr(NULL),
       flag_mode(FLAG_CHAR),
       complexprefixes(0),
       utf8(0),
       forbiddenword(FORBIDDENWORD)  // forbidden word signing flag
-      ,
-      numaliasf(0),
-      aliasf(NULL),
-      aliasflen(0),
-      numaliasm(0),
-      aliasm(NULL) {
+{
   langnum = 0;
   csconv = 0;
 #ifdef HUNSPELL_CHROME_CLIENT
@@ -114,52 +107,43 @@
   if (ec) {
     /* error condition - what should we do here */
     HUNSPELL_WARNING(stderr, "Hash Manager Error : %d\n", ec);
-    free(tableptr);
-    //keep tablesize to 1 to fix possible division with zero
-    tablesize = 1;
-    tableptr = (struct hentry**)calloc(tablesize, sizeof(struct hentry*));
-    if (!tableptr) {
-      tablesize = 0;
-    }
+    free_table();
+    //keep table size to 1 to fix possible division with zero
+    tableptr.resize(1, nullptr);
   }
 }
 
-HashMgr::~HashMgr() {
-  if (tableptr) {
-    // now pass through hash table freeing up everything
-    // go through column by column of the table
-    for (int i = 0; i < tablesize; i++) {
-      struct hentry* pt = tableptr[i];
-      struct hentry* nt = NULL;
-      while (pt) {
-        nt = pt->next;
-        if (pt->astr &&
-            (!aliasf || TESTAFF(pt->astr, ONLYUPCASEFLAG, pt->alen)))
-          free(pt->astr);
-        free(pt);
-        pt = nt;
-      }
-    }
-    free(tableptr);
-  }
-  tablesize = 0;
+void HashMgr::free_flag(unsigned short* astr, short alen) {
+  if (astr && (aliasf.empty() || TESTAFF(astr, ONLYUPCASEFLAG, alen)))
+    free(astr);
+}
 
-  if (aliasf) {
-    for (int j = 0; j < (numaliasf); j++)
-      free(aliasf[j]);
-    free(aliasf);
-    aliasf = NULL;
-    if (aliasflen) {
-      free(aliasflen);
-      aliasflen = NULL;
+void HashMgr::free_table() {
+  // now pass through hash table freeing up everything
+  // go through column by column of the table
+  for (size_t i = 0; i < tableptr.size(); ++i) {
+    struct hentry* pt = tableptr[i];
+    struct hentry* nt = NULL;
+    while (pt) {
+      nt = pt->next;
+      free_flag(pt->astr, pt->alen);
+      free(pt);
+      pt = nt;
     }
   }
-  if (aliasm) {
-    for (int j = 0; j < (numaliasm); j++)
-      free(aliasm[j]);
-    free(aliasm);
-    aliasm = NULL;
-  }
+  tableptr.clear();
+}
+
+HashMgr::~HashMgr() {
+  free_table();
+
+  for (size_t j = 0, numaliasf = aliasf.size(); j < numaliasf; ++j)
+    free(aliasf[j]);
+  aliasf.clear();
+
+  for (size_t j = 0, numaliasm = aliasm.size(); j < numaliasm; ++j)
+    free(aliasm[j]);
+  aliasm.clear();
 
 #ifndef OPENOFFICEORG
 #ifndef MOZILLA_CLIENT
@@ -219,15 +203,12 @@
 
   return AffixIDsToHentry(word_buf, affix_ids, affix_count);
 #else
-  struct hentry* dp;
-  if (tableptr) {
-    dp = tableptr[hash(word)];
-    if (!dp)
-      return NULL;
-    for (; dp != NULL; dp = dp->next) {
-      if (strcmp(word, dp->word) == 0)
-        return dp;
-    }
+  struct hentry* dp = tableptr[hash(word)];
+  if (!dp)
+    return NULL;
+  for (; dp != NULL; dp = dp->next) {
+    if (strcmp(word, dp->word) == 0)
+      return dp;
   }
   return NULL;
 #endif
@@ -265,7 +246,7 @@
       else
         reverseword(*word_copy);
 
-      if (in_desc && !aliasm) {
+      if (in_desc && aliasm.empty()) {
         desc_copy = new std::string(*in_desc);
 
         if (complexprefixes) {
@@ -281,14 +262,24 @@
     word = word_copy;
   }
 
+  // limit of hp->blen
+  if (word->size() > std::numeric_limits<unsigned char>::max()) {
+    HUNSPELL_WARNING(stderr, "error: word len %ld is over max limit\n", word->size());
+    delete desc_copy;
+    delete word_copy;
+    free_flag(aff, al);
+    return 1;
+  }
+
   bool upcasehomonym = false;
-  int descl = desc ? (aliasm ? sizeof(char*) : desc->size() + 1) : 0;
+  int descl = desc ? (!aliasm.empty() ? sizeof(char*) : desc->size() + 1) : 0;
   // variable-length hash record with word and optional fields
   struct hentry* hp =
       (struct hentry*)malloc(sizeof(struct hentry) + word->size() + descl);
   if (!hp) {
     delete desc_copy;
     delete word_copy;
+    free_flag(aff, al);
     return 1;
   }
 
@@ -308,18 +299,19 @@
   // store the description string or its pointer
   if (desc) {
     hp->var |= H_OPT;
-    if (aliasm) {
+    if (!aliasm.empty()) {
       hp->var |= H_OPT_ALIASM;
       store_pointer(hpw + word->size() + 1, get_aliasm(atoi(desc->c_str())));
     } else {
       strcpy(hpw + word->size() + 1, desc->c_str());
     }
-    if (strstr(HENTRY_DATA(hp), MORPH_PHON)) {
+    if (HENTRY_FIND(hp, MORPH_PHON)) {
       hp->var |= H_OPT_PHON;
       // store ph: fields (pronounciation, misspellings, old orthography etc.)
       // of a morphological description in reptable to use in REP replacements.
-      if (reptable.capacity() < (unsigned int)(tablesize/MORPH_PHON_RATIO))
-          reptable.reserve(tablesize/MORPH_PHON_RATIO);
+      size_t predicted = tableptr.size() / MORPH_PHON_RATIO;
+      if (reptable.capacity() < predicted)
+          reptable.reserve(predicted);
       std::string fields = HENTRY_DATA(hp);
       std::string::const_iterator iter = fields.begin();
       std::string::const_iterator start_piece = mystrsep(fields, iter);
@@ -610,7 +602,7 @@
   if (dp && dp->astr) {
     int captype;
     int wcl = get_clen_and_captype(word, &captype);
-    if (aliasf) {
+    if (!aliasf.empty()) {
       add_word(word, wcl, dp->astr, dp->alen, NULL, false, captype);
     } else {
       unsigned short* flags =
@@ -671,7 +663,7 @@
 #else
   if (hp && hp->next != NULL)
     return hp->next;
-  for (col++; col < tablesize; col++) {
+  for (col++; col < (int)tableptr.size(); ++col) {
     if (tableptr[col])
       return tableptr[col];
   }
@@ -689,7 +681,7 @@
   if (dict == NULL)
     return 1;
 
-  // first read the first line of file to get hash table size */
+  // first read the first line of file to get hash table size
   std::string ts;
   if (!dict->getline(ts)) {
     HUNSPELL_WARNING(stderr, "error: empty dic file %s\n", tpath);
@@ -703,7 +695,7 @@
     ts.erase(0, 3);
   }
 
-  tablesize = atoi(ts.c_str());
+  int tablesize = atoi(ts.c_str());
 
   int nExtra = 5 + USERWORD;
 
@@ -720,11 +712,7 @@
     tablesize++;
 
   // allocate the hash table
-  tableptr = (struct hentry**)calloc(tablesize, sizeof(struct hentry*));
-  if (!tableptr) {
-    delete dict;
-    return 3;
-  }
+  tableptr.resize(tablesize, nullptr);
 
   // loop through all words on much list and add to hash
   // table and create word and affix strings
@@ -781,7 +769,7 @@
     if (ap_pos != std::string::npos && ap_pos != ts.size()) {
       std::string ap(ts.substr(ap_pos + 1));
       ts.resize(ap_pos);
-      if (aliasf) {
+      if (!aliasf.empty()) {
         int index = atoi(ap.c_str());
         al = get_aliasf(index, &flags, dict);
         if (!al) {
@@ -831,7 +819,7 @@
     ROTATE(hv, ROTATE_LEN);
     hv ^= (*word++);
   }
-  return (unsigned long)hv % tablesize;
+  return (unsigned long)hv % tableptr.size();
 #endif
 }
 
@@ -987,7 +975,7 @@
   return true;
 }
 
-unsigned short HashMgr::decode_flag(const char* f) const {
+unsigned short HashMgr::decode_flag(const std::string& f) const {
   unsigned short s = 0;
   int i;
   switch (flag_mode) {
@@ -995,7 +983,7 @@
       s = ((unsigned short)((unsigned char)f[0]) << 8) + (unsigned char)f[1];
       break;
     case FLAG_NUM:
-      i = atoi(f);
+      i = atoi(f.c_str());
       if (i >= DEFAULTFLAGS)
         HUNSPELL_WARNING(stderr, "error: flag id %d is too large (max: %d)\n",
                          i, DEFAULTFLAGS - 1);
@@ -1009,7 +997,7 @@
       break;
     }
     default:
-      s = *(unsigned char*)f;
+      s = (unsigned char)f[0];
   }
   if (s == 0)
     HUNSPELL_WARNING(stderr, "error: 0 is wrong flag id\n");
@@ -1097,7 +1085,7 @@
         delete afflst;
         return 1;
       }
-      forbiddenword = decode_flag(st.c_str());
+      forbiddenword = decode_flag(st);
     }
 
     if (line.compare(0, 3, "SET", 3) == 0) {
@@ -1175,13 +1163,14 @@
 
 /* parse in the ALIAS table */
 bool HashMgr::parse_aliasf(const std::string& line, FileMgr* af) {
-  if (numaliasf != 0) {
+  if (!aliasf.empty()) {
     HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n",
                      af->getlinenum());
     return false;
   }
   int i = 0;
   int np = 0;
+  int numaliasf = 0;
   std::string::const_iterator iter = line.begin();
   std::string::const_iterator start_piece = mystrsep(line, iter);
   while (start_piece != line.end()) {
@@ -1193,27 +1182,14 @@
       case 1: {
         numaliasf = atoi(std::string(start_piece, iter).c_str());
         if (numaliasf < 1) {
-          numaliasf = 0;
-          aliasf = NULL;
-          aliasflen = NULL;
+          aliasf.clear();
+          aliasflen.clear();
           HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n",
                            af->getlinenum());
           return false;
         }
-        aliasf =
-            (unsigned short**)malloc(numaliasf * sizeof(unsigned short*));
-        aliasflen =
-            (unsigned short*)malloc(numaliasf * sizeof(unsigned short));
-        if (!aliasf || !aliasflen) {
-          numaliasf = 0;
-          if (aliasf)
-            free(aliasf);
-          if (aliasflen)
-            free(aliasflen);
-          aliasf = NULL;
-          aliasflen = NULL;
-          return false;
-        }
+        aliasf.reserve(numaliasf);
+        aliasflen.reserve(numaliasf);
         np++;
         break;
       }
@@ -1224,21 +1200,18 @@
     start_piece = mystrsep(line, iter);
   }
   if (np != 2) {
-    numaliasf = 0;
-    free(aliasf);
-    free(aliasflen);
-    aliasf = NULL;
-    aliasflen = NULL;
+    aliasf.clear();
+    aliasflen.clear();
     HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",
                      af->getlinenum());
     return false;
   }
 
   /* now parse the numaliasf lines to read in the remainder of the table */
-  for (int j = 0; j < numaliasf; j++) {
+  for (int j = 0; j < numaliasf; ++j) {
     std::string nl;
-    aliasf[j] = NULL;
-    aliasflen[j] = 0;
+    unsigned short* alias = NULL;
+    unsigned aliaslen = 0;
     i = 0;
     if (af->getline(nl)) {
       mychomp(nl);
@@ -1256,9 +1229,9 @@
           }
           case 1: {
             std::string piece(start_piece, iter);
-            aliasflen[j] =
-                (unsigned short)decode_flags(&(aliasf[j]), piece, af);
-            std::sort(aliasf[j], aliasf[j] + aliasflen[j]);
+            aliaslen =
+                (unsigned short)decode_flags(&alias, piece, af);
+            std::sort(alias, alias + aliaslen);
             break;
           }
           default:
@@ -1268,19 +1241,19 @@
         start_piece = mystrsep(nl, iter);
       }
     }
-    if (!aliasf[j]) {
+    if (!alias) {
       for (int k = 0; k < j; ++k) {
         free(aliasf[k]);
       }
-      free(aliasf);
-      free(aliasflen);
-      aliasf = NULL;
-      aliasflen = NULL;
-      numaliasf = 0;
+      aliasf.clear();
+      aliasflen.clear();
       HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
                        af->getlinenum());
       return false;
     }
+
+    aliasf.push_back(alias);
+    aliasflen.push_back(aliaslen);
   }
   return true;
 }
@@ -1412,11 +1385,11 @@
 #endif
 
 int HashMgr::is_aliasf() const {
-  return (aliasf != NULL);
+  return !aliasf.empty();
 }
 
 int HashMgr::get_aliasf(int index, unsigned short** fvec, FileMgr* af) const {
-  if ((index > 0) && (index <= numaliasf)) {
+  if (index > 0 && static_cast<size_t>(index) <= aliasflen.size()) {
     *fvec = aliasf[index - 1];
     return aliasflen[index - 1];
   }
@@ -1428,13 +1401,14 @@
 
 /* parse morph alias definitions */
 bool HashMgr::parse_aliasm(const std::string& line, FileMgr* af) {
-  if (numaliasm != 0) {
+  if (!aliasm.empty()) {
     HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n",
                      af->getlinenum());
     return false;
   }
   int i = 0;
   int np = 0;
+  int numaliasm = 0;
   std::string::const_iterator iter = line.begin();
   std::string::const_iterator start_piece = mystrsep(line, iter);
   while (start_piece != line.end()) {
@@ -1450,11 +1424,7 @@
                            af->getlinenum());
           return false;
         }
-        aliasm = (char**)malloc(numaliasm * sizeof(char*));
-        if (!aliasm) {
-          numaliasm = 0;
-          return false;
-        }
+        aliasm.reserve(numaliasm);
         np++;
         break;
       }
@@ -1465,18 +1435,16 @@
     start_piece = mystrsep(line, iter);
   }
   if (np != 2) {
-    numaliasm = 0;
-    free(aliasm);
-    aliasm = NULL;
+    aliasm.clear();
     HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",
                      af->getlinenum());
     return false;
   }
 
   /* now parse the numaliasm lines to read in the remainder of the table */
-  for (int j = 0; j < numaliasm; j++) {
+  for (int j = 0; j < numaliasm; ++j) {
     std::string nl;
-    aliasm[j] = NULL;
+    char* alias = NULL;
     if (af->getline(nl)) {
       mychomp(nl);
       iter = nl.begin();
@@ -1502,7 +1470,7 @@
               else
                 reverseword(chunk);
             }
-            aliasm[j] = mystrdup(chunk.c_str());
+            alias = mystrdup(chunk.c_str());
             break;
           }
           default:
@@ -1512,27 +1480,26 @@
         start_piece = mystrsep(nl, iter);
       }
     }
-    if (!aliasm[j]) {
-      numaliasm = 0;
+    if (!alias) {
       for (int k = 0; k < j; ++k) {
         free(aliasm[k]);
       }
-      free(aliasm);
-      aliasm = NULL;
+      aliasm.clear();
       HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
                        af->getlinenum());
       return false;
     }
+    aliasm.push_back(alias);
   }
   return true;
 }
 
 int HashMgr::is_aliasm() const {
-  return (aliasm != NULL);
+  return !aliasm.empty();
 }
 
 char* HashMgr::get_aliasm(int index) const {
-  if ((index > 0) && (index <= numaliasm))
+  if (index > 0 && static_cast<size_t>(index) <= aliasm.size())
     return aliasm[index - 1];
   HUNSPELL_WARNING(stderr, "error: bad morph. alias index: %d\n", index);
   return NULL;
diff --git a/third_party/hunspell/src/hunspell/hashmgr.hxx b/third_party/hunspell/src/hunspell/hashmgr.hxx
index 6fa2ba71..666074a6 100644
--- a/third_party/hunspell/src/hunspell/hashmgr.hxx
+++ b/third_party/hunspell/src/hunspell/hashmgr.hxx
@@ -101,8 +101,7 @@
   std::map<std::string_view, int> custom_word_to_affix_id_map_;
   std::vector<std::string*> pointer_to_strings_;
 #endif
-  int tablesize;
-  struct hentry** tableptr;
+  std::vector<struct hentry*> tableptr;
   flag flag_mode;
   int complexprefixes;
   int utf8;
@@ -113,11 +112,9 @@
   struct cs_info* csconv;
   std::string ignorechars;
   std::vector<w_char> ignorechars_utf16;
-  int numaliasf;  // flag vector `compression' with aliases
-  unsigned short** aliasf;
-  unsigned short* aliasflen;
-  int numaliasm;  // morphological desciption `compression' with aliases
-  char** aliasm;
+  std::vector<unsigned short*> aliasf; // flag vector `compression' with aliases
+  std::vector<unsigned short> aliasflen;
+  std::vector<char*> aliasm; // morphological desciption `compression' with aliases
   // reptable created from REP table of aff file and from "ph:" fields
   // of the dic file. It contains phonetic and other common misspellings
   // (letters, letter groups and words) for better suggestions
@@ -152,7 +149,7 @@
   int remove(const std::string& word);
   int decode_flags(unsigned short** result, const std::string& flags, FileMgr* af) const;
   bool decode_flags(std::vector<unsigned short>& result, const std::string& flags, FileMgr* af) const;
-  unsigned short decode_flag(const char* flag) const;
+  unsigned short decode_flag(const std::string& flag) const;
   char* encode_flag(unsigned short flag) const;
   int is_aliasf() const;
   int get_aliasf(int index, unsigned short** fvec, FileMgr* af) const;
@@ -216,6 +213,8 @@
   bool parse_aliasm(const std::string& line, FileMgr* af);
   bool parse_reptable(const std::string& line, FileMgr* af);
   int remove_forbidden_flag(const std::string& word);
+  void free_table();
+  void free_flag(unsigned short* astr, short alen);
 };
 
 #endif
diff --git a/third_party/hunspell/src/hunspell/hunspell.cxx b/third_party/hunspell/src/hunspell/hunspell.cxx
index f699f5c..329e297 100644
--- a/third_party/hunspell/src/hunspell/hunspell.cxx
+++ b/third_party/hunspell/src/hunspell/hunspell.cxx
@@ -1969,7 +1969,7 @@
     he = m_HMgrs[i]->lookup(word);
   }
   if (he) {
-    slst = pAMgr->get_suffix_words(he->astr, he->alen, root_word.c_str());
+    slst = pAMgr->get_suffix_words(he->astr, he->alen, root_word);
   }
   return slst;
 }
diff --git a/third_party/hunspell/src/hunspell/phonet.cxx b/third_party/hunspell/src/hunspell/phonet.cxx
index 69601a2..fd4b6b6 100644
--- a/third_party/hunspell/src/hunspell/phonet.cxx
+++ b/third_party/hunspell/src/hunspell/phonet.cxx
@@ -108,9 +108,10 @@
           if (myisalpha(word[i + k])  // ...could be implied?
               && strchr(s + 1, word[i + k]) != NULL) {
             k++;
-            while (*s != ')')
+            while (*s && *s != ')')
               s++;
-            s++;
+            if (*s == ')')
+              s++;
           }
         }
         p0 = (int)*s;
diff --git a/third_party/hunspell/src/hunspell/replist.cxx b/third_party/hunspell/src/hunspell/replist.cxx
index 9d52f33..cc861e4d 100644
--- a/third_party/hunspell/src/hunspell/replist.cxx
+++ b/third_party/hunspell/src/hunspell/replist.cxx
@@ -77,28 +77,18 @@
 #include "csutil.hxx"
 
 RepList::RepList(int n) {
-  dat = (replentry**)malloc(sizeof(replentry*) * n);
-  if (dat == 0)
-    size = 0;
-  else
-    size = n;
-  pos = 0;
+  dat.reserve(n);
 }
 
 RepList::~RepList() {
-  for (int i = 0; i < pos; i++) {
+  for (size_t i = 0, pos = dat.size(); i < pos; ++i) {
     delete dat[i];
   }
-  free(dat);
-}
-
-replentry* RepList::item(int n) {
-  return dat[n];
 }
 
 int RepList::find(const char* word) {
   int p1 = 0;
-  int p2 = pos - 1;
+  int p2 = dat.size() - 1;
   int ret = -1;
   while (p1 <= p2) {
     int m = ((unsigned)p1 + (unsigned)p2) >> 1;
@@ -115,11 +105,11 @@
   return ret;
 }
 
-std::string RepList::replace(const char* word, int ind, bool atstart) {
+std::string RepList::replace(const size_t wordlen, int ind, bool atstart) {
   int type = atstart ? 1 : 0;
   if (ind < 0)
     return std::string();
-  if (strlen(word) == dat[ind]->pattern.size())
+  if (wordlen == dat[ind]->pattern.size())
     type = atstart ? 3 : 2;
   while (type && dat[ind]->outstrings[type].empty())
     type = (type == 2 && !atstart) ? 0 : type - 1;
@@ -127,7 +117,7 @@
 }
 
 int RepList::add(const std::string& in_pat1, const std::string& pat2) {
-  if (pos >= size || in_pat1.empty() || pat2.empty()) {
+  if (in_pat1.empty() || pat2.empty()) {
     return 1;
   }
   // analyse word context
@@ -159,11 +149,11 @@
   r->pattern = pat1;
   r->outstrings[type] = pat2;
   mystrrep(r->outstrings[type], "_", " ");
-  dat[pos++] = r;
+  dat.push_back(r);
   // sort to the right place in the list
 #if 0
-  int i;
-  for (i = pos - 1; i > 0; i--) {
+  size_t i;
+  for (i = dat.size() - 1; i > 0; --i) {
     if (strcmp(r->pattern.c_str(), dat[i - 1]->pattern.c_str()) < 0) {
       dat[i] = dat[i - 1];
     } else
@@ -171,7 +161,7 @@
   }
   dat[i] = r;
 #else
-  for (int i = pos - 1; i > 0; i--) {
+  for (int i = dat.size() - 1; i > 0; --i) {
     r = dat[i];
     if (r->pattern < dat[i - 1]->pattern) {
       dat[i] = dat[i - 1];
@@ -191,7 +181,7 @@
   bool change = false;
   for (size_t i = 0; i < wordlen; ++i) {
     int n = find(word + i);
-    std::string l = replace(word + i, n, i == 0);
+    std::string l = replace(wordlen - i, n, i == 0);
     if (!l.empty()) {
       dest.append(l);
       i += dat[n]->pattern.size() - 1;
diff --git a/third_party/hunspell/src/hunspell/replist.hxx b/third_party/hunspell/src/hunspell/replist.hxx
index 2bc8aa74..35fe025 100644
--- a/third_party/hunspell/src/hunspell/replist.hxx
+++ b/third_party/hunspell/src/hunspell/replist.hxx
@@ -88,19 +88,15 @@
   RepList(const RepList&);
   RepList& operator=(const RepList&);
 
- protected:
-  replentry** dat;
-  int size;
-  int pos;
+  std::vector<replentry*> dat;
 
  public:
   explicit RepList(int n);
   ~RepList();
 
   int add(const std::string& pat1, const std::string& pat2);
-  replentry* item(int n);
   int find(const char* word);
-  std::string replace(const char* word, int n, bool atstart);
+  std::string replace(const size_t wordlen, int n, bool atstart);
   bool conv(const std::string& word, std::string& dest);
 };
 #endif
diff --git a/third_party/hunspell/src/hunspell/suggestmgr.cxx b/third_party/hunspell/src/hunspell/suggestmgr.cxx
index 2be03dd..c35b491 100644
--- a/third_party/hunspell/src/hunspell/suggestmgr.cxx
+++ b/third_party/hunspell/src/hunspell/suggestmgr.cxx
@@ -499,18 +499,19 @@
   timelimit = clock();
   timer = MINTIMER;
   return map_related(word, candidate, 0, wlst, cpdsuggest,
-                     maptable, &timer, &timelimit);
+                     maptable, &timer, &timelimit, 0);
 }
 
-int SuggestMgr::map_related(const char* word,
+int SuggestMgr::map_related(const std::string& word,
                             std::string& candidate,
-                            int wn,
+                            size_t wn,
                             std::vector<std::string>& wlst,
                             int cpdsuggest,
                             const std::vector<mapentry>& maptable,
                             int* timer,
-                            clock_t* timelimit) {
-  if (*(word + wn) == '\0') {
+                            clock_t* timelimit,
+                            int depth) {
+  if (word.size() == wn) {
     int cwrd = 1;
     for (size_t m = 0; m < wlst.size(); ++m) {
       if (wlst[m] == candidate) {
@@ -525,18 +526,24 @@
     }
     return wlst.size();
   }
+
+  if (depth > 16384) {
+    *timer = 0;
+    return wlst.size();
+  }
+
   int in_map = 0;
   for (size_t j = 0; j < maptable.size(); ++j) {
     for (size_t k = 0; k < maptable[j].size(); ++k) {
       size_t len = maptable[j][k].size();
-      if (strncmp(maptable[j][k].c_str(), word + wn, len) == 0) {
+      if (len && word.compare(wn, len, maptable[j][k]) == 0) {
         in_map = 1;
         size_t cn = candidate.size();
         for (size_t l = 0; l < maptable[j].size(); ++l) {
           candidate.resize(cn);
           candidate.append(maptable[j][l]);
           map_related(word, candidate, wn + len, wlst,
-                           cpdsuggest, maptable, timer, timelimit);
+                           cpdsuggest, maptable, timer, timelimit, depth + 1);
           if (!(*timer))
             return wlst.size();
         }
@@ -544,9 +551,9 @@
     }
   }
   if (!in_map) {
-    candidate.push_back(*(word + wn));
+    candidate.push_back(word[wn]);
     map_related(word, candidate, wn + 1, wlst, cpdsuggest,
-                maptable, timer, timelimit);
+                maptable, timer, timelimit, depth + 1);
   }
   return wlst.size();
 }
@@ -554,10 +561,10 @@
 // suggestions for a typical fault of spelling, that
 // differs with more, than 1 letter from the right form.
 int SuggestMgr::replchars(std::vector<std::string>& wlst,
-                          const char* word,
+                          const std::string& word,
                           int cpdsuggest) {
   std::string candidate;
-  int wl = strlen(word);
+  int wl = word.size();
   if (wl < 2 || !pAMgr)
     return wlst.size();
 
@@ -566,34 +573,33 @@
   const char *pattern, *pattern2;
   hunspell::ReplacementIterator iterator = bdict_reader->GetReplacementIterator();
   while (iterator.GetNext(&pattern, &pattern2)) {
-    const char* r = word;
+    size_t r = 0;
     size_t lenr = strlen(pattern2);
     size_t lenp = strlen(pattern);
 
     // search every occurence of the pattern in the word
-    while ((r=strstr(r, pattern)) != NULL) {
+    while ((r = word.find(pattern, r)) != std::string::npos) {
       candidate = word;
-      candidate.replace(r-word, lenp, pattern2);
+      candidate.replace(r, lenp, pattern2);
 #else
   const std::vector<replentry>& reptable = pAMgr->get_reptable();
   for (size_t i = 0; i < reptable.size(); ++i) {
-    const char* r = word;
+    size_t r = 0;
     // search every occurence of the pattern in the word
-    while ((r = strstr(r, reptable[i].pattern.c_str())) != NULL) {
-      int type = (r == word) ? 1 : 0;
-      if (r - word + reptable[i].pattern.size() == strlen(word))
+    while ((r = word.find(reptable[i].pattern, r)) != std::string::npos) {
+      int type = (r == 0) ? 1 : 0;
+      if (r + reptable[i].pattern.size() == word.size())
         type += 2;
       while (type && reptable[i].outstrings[type].empty())
-        type = (type == 2 && r != word) ? 0 : type - 1;
+        type = (type == 2 && r != 0) ? 0 : type - 1;
       const std::string&out = reptable[i].outstrings[type];
       if (out.empty()) {
         ++r;
         continue;
       }
-      candidate.assign(word);
-      candidate.resize(r - word);
+      candidate.assign(word, 0, r);
       candidate.append(reptable[i].outstrings[type]);
-      candidate.append(r + reptable[i].pattern.size());
+      candidate.append(word, r + reptable[i].pattern.size(), std::string::npos);
 #endif
       testsug(wlst, candidate, cpdsuggest, NULL, NULL);
       // check REP suggestions with space
@@ -1814,9 +1820,9 @@
     }
 
     if (!rv && pAMgr->have_contclass()) {
-      rv = pAMgr->suffix_check_twosfx(word.c_str(), word.size(), 0, NULL, FLAG_NULL);
+      rv = pAMgr->suffix_check_twosfx(word, word.size(), 0, NULL, FLAG_NULL);
       if (!rv)
-        rv = pAMgr->prefix_check_twosfx(word.c_str(), word.size(), 0, FLAG_NULL);
+        rv = pAMgr->prefix_check_twosfx(word, word.size(), 0, FLAG_NULL);
     }
 
     // check forbidden words
@@ -1901,8 +1907,7 @@
 
   if (pAMgr->get_compound() && result.empty()) {
     struct hentry* rwords[100];  // buffer for COMPOUND pattern checking
-    pAMgr->compound_check_morph(w.c_str(), w.size(), 0, 0, 100, 0, NULL, (hentry**)&rwords, 0, result,
-                                NULL);
+    pAMgr->compound_check_morph(w, 0, 0, 100, 0, NULL, (hentry**)&rwords, 0, result, NULL);
   }
 
   line_uniq(result, MSEP_REC);
diff --git a/third_party/hunspell/src/hunspell/suggestmgr.hxx b/third_party/hunspell/src/hunspell/suggestmgr.hxx
index 925c6a0..35c8903 100644
--- a/third_party/hunspell/src/hunspell/suggestmgr.hxx
+++ b/third_party/hunspell/src/hunspell/suggestmgr.hxx
@@ -143,7 +143,7 @@
   int check_forbidden(const char*, int);
 
   void capchars(std::vector<std::string>&, const char*, int);
-  int replchars(std::vector<std::string>&, const char*, int);
+  int replchars(std::vector<std::string>&, const std::string&, int);
   int doubletwochars(std::vector<std::string>&, const char*, int);
   int forgotchar(std::vector<std::string>&, const char*, int);
   int swapchar(std::vector<std::string>&, const char*, int);
@@ -165,14 +165,15 @@
   int movechar_utf(std::vector<std::string>&, const w_char*, int, int);
 
   int mapchars(std::vector<std::string>&, const char*, int);
-  int map_related(const char*,
+  int map_related(const std::string&,
                   std::string&,
-                  int,
+                  size_t,
                   std::vector<std::string>& wlst,
                   int,
                   const std::vector<mapentry>&,
                   int*,
-                  clock_t*);
+                  clock_t*,
+                  int depth);
   int ngram(int n, const std::vector<w_char>& su1,
             const std::vector<w_char>& su2, int opt);
   int ngram(int n, const std::string& s1, const std::string& s2, int opt);
diff --git a/third_party/hunspell/src/parsers/latexparser.cxx b/third_party/hunspell/src/parsers/latexparser.cxx
index ea0335c..1188ed9 100644
--- a/third_party/hunspell/src/parsers/latexparser.cxx
+++ b/third_party/hunspell/src/parsers/latexparser.cxx
@@ -217,13 +217,13 @@
         break;
       case 1:  // wordchar
         apostrophe = 0;
-        if ((is_wordchar((char*)APOSTROPHE) ||
-             (is_utf8() && is_wordchar((char*)UTF8_APOS))) &&
+        if ((is_wordchar(APOSTROPHE) ||
+             (is_utf8() && is_wordchar(UTF8_APOS))) &&
             !line[actual].empty() && line[actual][head] == '\'' &&
             is_wordchar(line[actual].c_str() + head + 1)) {
           head++;
         } else if (is_utf8() &&
-                   is_wordchar((char*)APOSTROPHE) &&  // add Unicode apostrophe
+                   is_wordchar(APOSTROPHE) &&  // add Unicode apostrophe
                                                       // to the WORDCHARS, if
                                                       // needed
                    strncmp(line[actual].c_str() + head, UTF8_APOS, strlen(UTF8_APOS)) ==
diff --git a/third_party/hunspell/src/parsers/textparser.cxx b/third_party/hunspell/src/parsers/textparser.cxx
index fb1bc9a..d798075 100644
--- a/third_party/hunspell/src/parsers/textparser.cxx
+++ b/third_party/hunspell/src/parsers/textparser.cxx
@@ -77,18 +77,19 @@
 int TextParser::is_wordchar(const char* w) {
   if (*w == '\0')
     return 0;
+  size_t cache_index = (*w + 256) % 256;
   if (utf8) {
-    std::vector<w_char> wc;
-    unsigned short idx;
-    u8_u16(wc, w);
-    if (wc.empty())
+    const bool use_cache = cache_index < 0x80;
+    if (use_cache)
+      return wordcharacters[cache_index];
+    if (u8_u16(wc, w, true) < 1)
         return 0;
-    idx = (wc[0].h << 8) + wc[0].l;
-    return (unicodeisalpha(idx) ||
-            (wordchars_utf16 &&
-             std::binary_search(wordchars_utf16, wordchars_utf16 + wclen, wc[0])));
+    unsigned short idx = (wc[0].h << 8) + wc[0].l;
+    return unicodeisalpha(idx) ||
+           (wordchars_utf16 &&
+            std::binary_search(wordchars_utf16, wordchars_utf16 + wclen, wc[0]));
   } else {
-    return wordcharacters[(*w + 256) % 256];
+    return wordcharacters[cache_index];
   }
 }
 
@@ -120,15 +121,27 @@
   }
 }
 
-void TextParser::init(const w_char* wc, int len) {
+void TextParser::init(const w_char* wc_utf8, int len) {
   actual = 0;
   head = 0;
   token = 0;
   state = 0;
   utf8 = 1;
   checkurl = 0;
-  wordchars_utf16 = wc;
+  wordchars_utf16 = wc_utf8;
   wclen = len;
+
+  // build a cache for the simple cases
+  wordcharacters.resize(0x80);
+  w_char wc2;
+  wc2.h = 0;
+  for (unsigned char idx = 0; idx < 0x80; ++idx) {
+    wc2.l = idx;
+    int cache = unicodeisalpha(idx) ||
+                (wordchars_utf16 &&
+                 std::binary_search(wordchars_utf16, wordchars_utf16 + wclen, wc2));
+    wordcharacters[idx] = cache;
+  }
 }
 
 int TextParser::next_char(const char* ln, size_t* pos) {
@@ -181,13 +194,13 @@
       case 1:  // wordchar
         if ((latin1 = get_latin1(line[actual].c_str() + head))) {
           head += strlen(latin1);
-        } else if ((is_wordchar((char*)APOSTROPHE) ||
-                    (is_utf8() && is_wordchar((char*)UTF8_APOS))) &&
+        } else if ((is_wordchar(APOSTROPHE) ||
+                    (is_utf8() && is_wordchar(UTF8_APOS))) &&
                    !line[actual].empty() && line[actual][head] == '\'' &&
                    is_wordchar(line[actual].c_str() + head + 1)) {
           head++;
         } else if (is_utf8() &&
-                   is_wordchar((char*)APOSTROPHE) &&  // add Unicode apostrophe
+                   is_wordchar(APOSTROPHE) &&  // add Unicode apostrophe
                                                       // to the WORDCHARS, if
                                                       // needed
                    strncmp(line[actual].c_str() + head, UTF8_APOS, strlen(UTF8_APOS)) ==
diff --git a/third_party/hunspell/src/parsers/textparser.hxx b/third_party/hunspell/src/parsers/textparser.hxx
index 94a1f77..06e7a68 100644
--- a/third_party/hunspell/src/parsers/textparser.hxx
+++ b/third_party/hunspell/src/parsers/textparser.hxx
@@ -59,6 +59,7 @@
   std::vector<int> wordcharacters;// for detection of the word boundaries
   std::string line[MAXPREVLINE];  // parsed and previous lines
   std::vector<bool> urlline;      // mask for url detection
+  std::vector<w_char> wc;
   int checkurl;
   int actual;  // actual line
   size_t head; // head position
diff --git a/third_party/hunspell/src/parsers/xmlparser.cxx b/third_party/hunspell/src/parsers/xmlparser.cxx
index 83a20c9..aacc977 100644
--- a/third_party/hunspell/src/parsers/xmlparser.cxx
+++ b/third_party/hunspell/src/parsers/xmlparser.cxx
@@ -135,14 +135,14 @@
       case ST_WORD:  // wordchar
         if ((latin1 = get_latin1(line[actual].c_str() + head))) {
           head += strlen(latin1);
-        } else if ((is_wordchar((char*)APOSTROPHE) ||
-                    (is_utf8() && is_wordchar((char*)UTF8_APOS))) &&
+        } else if ((is_wordchar(APOSTROPHE) ||
+                    (is_utf8() && is_wordchar(UTF8_APOS))) &&
                    strncmp(line[actual].c_str() + head, ENTITY_APOS,
                            strlen(ENTITY_APOS)) == 0 &&
                    is_wordchar(line[actual].c_str() + head + strlen(ENTITY_APOS))) {
           head += strlen(ENTITY_APOS) - 1;
         } else if (is_utf8() &&
-                   is_wordchar((char*)APOSTROPHE) &&  // add Unicode apostrophe
+                   is_wordchar(APOSTROPHE) &&  // add Unicode apostrophe
                                                       // to the WORDCHARS, if
                                                       // needed
                    strncmp(line[actual].c_str() + head, UTF8_APOS, strlen(UTF8_APOS)) ==
diff --git a/tools/crates/run_cargo.py b/tools/crates/run_cargo.py
index c83086a..518c0ff 100755
--- a/tools/crates/run_cargo.py
+++ b/tools/crates/run_cargo.py
@@ -14,9 +14,10 @@
 import platform
 import subprocess
 import sys
+import pathlib
 
-DEFAULT_SYSROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..',
-                               '..', 'third_party', 'rust-toolchain')
+DEFAULT_SYSROOT = pathlib.Path(__file__).parents[2].joinpath(
+    'third_party', 'rust-toolchain')
 
 # Determine the cargo executable name based on whether `subprocess` thinks
 # we're on a Windows platform or not, which is more accurate than checking it
@@ -25,12 +26,12 @@
 
 
 def RunCargo(rust_sysroot, home_dir, cargo_args):
-    if not os.path.exists(rust_sysroot):
+    rust_sysroot = pathlib.Path(rust_sysroot)
+    if not rust_sysroot.exists():
         print(f'WARNING: Rust sysroot missing at "{rust_sysroot}"')
 
-    abs_rust_sysroot = os.path.abspath(rust_sysroot)
-    bin_dir = os.path.join(abs_rust_sysroot, 'bin')
-    cargo_path = os.path.join(bin_dir, _CARGO_EXE)
+    bin_dir = rust_sysroot.absolute().joinpath('bin')
+    cargo_path = bin_dir.joinpath(_CARGO_EXE)
 
     cargo_env = dict(os.environ)
     if home_dir:
@@ -66,6 +67,7 @@
     parser = argparse.ArgumentParser(description='run cargo')
     parser.add_argument('--rust-sysroot',
                         default=DEFAULT_SYSROOT,
+                        type=pathlib.Path,
                         help='use cargo and rustc from here')
     (args, cargo_args) = parser.parse_known_args()
     return RunCargo(args.rust_sysroot, None, cargo_args)
diff --git a/tools/crates/run_cargo_vet.py b/tools/crates/run_cargo_vet.py
index 2719de0..16a9e8d 100755
--- a/tools/crates/run_cargo_vet.py
+++ b/tools/crates/run_cargo_vet.py
@@ -14,6 +14,7 @@
 
 import argparse
 import os
+import pathlib
 import platform
 import subprocess
 import sys
@@ -46,6 +47,7 @@
         'run `cargo vet` against `//third_party/rust/chromium_crates_io`')
     parser.add_argument('--rust-sysroot',
                         default=DEFAULT_SYSROOT,
+                        type=pathlib.Path,
                         help='use cargo and rustc from here')
     (args, unrecognized_args) = parser.parse_known_args()
 
diff --git a/tools/crates/run_gnrt.py b/tools/crates/run_gnrt.py
index 9254cf0f..ed074743 100755
--- a/tools/crates/run_gnrt.py
+++ b/tools/crates/run_gnrt.py
@@ -9,6 +9,7 @@
 
 import argparse
 import os
+import pathlib
 import platform
 import subprocess
 import sys
@@ -22,6 +23,7 @@
     parser = argparse.ArgumentParser(description='build and run gnrt')
     parser.add_argument('--rust-sysroot',
                         default=DEFAULT_SYSROOT,
+                        type=pathlib.Path,
                         help='use cargo and rustc from here')
     parser.add_argument('--out-dir',
                         default='out/gnrt',
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 32f393a0..f631a44 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -319,7 +319,7 @@
     ],
 
     'asan_lsan_release_trybot_remoteexec': [
-      'asan', 'lsan', 'release_trybot_minimal_symbols_remoteexec', 'use_siso', 'reclient',
+      'asan', 'lsan', 'release_trybot_minimal_symbols_remoteexec', 'use_siso',
     ],
 
     'cast_binary_size_remoteexec': [
@@ -387,7 +387,7 @@
     ],
 
     'debug_bot_blink': [
-      'debug_bot_blink', 'use_siso', 'reclient',
+      'debug_bot_blink', 'use_siso',
     ],
 
     'debug_bot_remoteexec': [
@@ -412,15 +412,15 @@
     ],
 
     'gpu_tests_release_trybot_minimal_symbol_x86_resource_allowlisting_remoteexec': [
-      'gpu_tests', 'release_trybot_minimal_symbols_remoteexec', 'x86', 'resource_allowlisting', 'use_siso', 'reclient',
+      'gpu_tests', 'release_trybot_minimal_symbols_remoteexec', 'x86', 'resource_allowlisting', 'use_siso',
     ],
 
     'gpu_tests_release_trybot_minimal_symbols_remoteexec': [
-      'gpu_tests', 'release_trybot_minimal_symbols_remoteexec', 'use_siso', 'reclient',
+      'gpu_tests', 'release_trybot_minimal_symbols_remoteexec', 'use_siso',
     ],
 
     'gpu_tests_release_trybot_ptr_comp_remoteexec': [
-      'gpu_tests', 'release_trybot_minimal_symbols_remoteexec', 'v8_pointer_compression', 'use_siso', 'reclient',
+      'gpu_tests', 'release_trybot_minimal_symbols_remoteexec', 'v8_pointer_compression', 'use_siso',
     ],
 
     'gpu_tests_release_trybot_remoteexec_siso': [
@@ -637,15 +637,15 @@
     ],
 
     'release_bot_blink': [
-      'release_bot_blink', 'use_siso', 'reclient',
+      'release_bot_blink', 'use_siso',
     ],
 
     'release_bot_blink_v8_debug_remoteexec': [
-      'release_bot_blink', 'v8_enable_debugging_features', 'use_siso', 'reclient'
+      'release_bot_blink', 'v8_enable_debugging_features', 'use_siso'
     ],
 
     'release_bot_remoteexec': [
-      'release_bot_remoteexec', 'reclient',
+      'release_bot_remoteexec', 'use_siso',
     ],
 
     'release_trybot_blink_do_typecheck_siso': [
@@ -1013,11 +1013,6 @@
       'gn_args': 'proprietary_codecs=true',
     },
 
-    # TODO: crbug.com/379584977 - Remove this mixin after dropping reclient.
-    'reclient': {
-      'gn_args': 'use_reclient=true',
-    },
-
     # Historically, a 'release' bot had DCHECKs turned off. DCHECKs are now
     # enabled by default, but explicitly turning them off here preserves
     # backwards compatibility. TODO: We should probably come up with better
diff --git a/tools/mb/mb_config_expectations/client.v8.chromium.json b/tools/mb/mb_config_expectations/client.v8.chromium.json
index 2c1ae46..cc3b8d2a 100644
--- a/tools/mb/mb_config_expectations/client.v8.chromium.json
+++ b/tools/mb/mb_config_expectations/client.v8.chromium.json
@@ -4,8 +4,9 @@
       "dcheck_always_on": false,
       "is_component_build": false,
       "is_debug": false,
-      "use_reclient": true,
-      "use_remoteexec": true
+      "use_reclient": false,
+      "use_remoteexec": true,
+      "use_siso": true
     }
   }
 }
\ No newline at end of file
diff --git a/tools/mb/mb_config_expectations/client.v8.fyi.json b/tools/mb/mb_config_expectations/client.v8.fyi.json
index 41d02f0..be49792 100644
--- a/tools/mb/mb_config_expectations/client.v8.fyi.json
+++ b/tools/mb/mb_config_expectations/client.v8.fyi.json
@@ -25,7 +25,7 @@
       "is_debug": false,
       "is_lsan": true,
       "symbol_level": 1,
-      "use_reclient": true,
+      "use_reclient": false,
       "use_remoteexec": true,
       "use_siso": true
     }
@@ -37,7 +37,7 @@
       "is_debug": true,
       "proprietary_codecs": true,
       "symbol_level": 1,
-      "use_reclient": true,
+      "use_reclient": false,
       "use_remoteexec": true,
       "use_siso": true
     }
@@ -50,7 +50,7 @@
       "is_debug": false,
       "proprietary_codecs": true,
       "symbol_level": 1,
-      "use_reclient": true,
+      "use_reclient": false,
       "use_remoteexec": true,
       "use_siso": true
     }
@@ -63,7 +63,7 @@
       "is_debug": false,
       "proprietary_codecs": true,
       "symbol_level": 1,
-      "use_reclient": true,
+      "use_reclient": false,
       "use_remoteexec": true,
       "use_siso": true,
       "v8_enable_pointer_compression": false
@@ -77,7 +77,7 @@
       "is_debug": false,
       "proprietary_codecs": true,
       "symbol_level": 1,
-      "use_reclient": true,
+      "use_reclient": false,
       "use_remoteexec": true,
       "use_siso": true
     }
@@ -89,7 +89,7 @@
       "is_component_build": false,
       "is_debug": false,
       "proprietary_codecs": true,
-      "use_reclient": true,
+      "use_reclient": false,
       "use_remoteexec": true,
       "use_siso": true
     }
@@ -101,7 +101,7 @@
       "is_component_build": false,
       "is_debug": false,
       "proprietary_codecs": true,
-      "use_reclient": true,
+      "use_reclient": false,
       "use_remoteexec": true,
       "use_siso": true,
       "v8_enable_debugging_features": true
@@ -114,7 +114,7 @@
       "is_component_build": false,
       "is_debug": false,
       "proprietary_codecs": true,
-      "use_reclient": true,
+      "use_reclient": false,
       "use_remoteexec": true,
       "use_siso": true
     }
@@ -126,7 +126,7 @@
       "is_component_build": false,
       "is_debug": false,
       "proprietary_codecs": true,
-      "use_reclient": true,
+      "use_reclient": false,
       "use_remoteexec": true,
       "use_siso": true
     }
@@ -138,7 +138,7 @@
       "is_component_build": false,
       "is_debug": false,
       "proprietary_codecs": true,
-      "use_reclient": true,
+      "use_reclient": false,
       "use_remoteexec": true,
       "use_siso": true
     }
@@ -153,7 +153,7 @@
       "proprietary_codecs": true,
       "symbol_level": 1,
       "target_cpu": "x86",
-      "use_reclient": true,
+      "use_reclient": false,
       "use_remoteexec": true,
       "use_siso": true
     }
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index e691575..d336106 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -8745,6 +8745,7 @@
   <int value="-2134333982" label="ShowArcFilesApp:enabled"/>
   <int value="-2134244069" label="HttpFormWarning:enabled"/>
   <int value="-2133892372" label="ResamplingInputEvents:disabled"/>
+  <int value="-2133694243" label="AllowTabClosingUponMinimization:disabled"/>
   <int value="-2133277113" label="CCTModuleCustomHeader:disabled"/>
   <int value="-2133276662" label="EduCoexistenceConsentLog:disabled"/>
   <int value="-2132591642" label="enable-input-view"/>
@@ -14193,6 +14194,7 @@
   <int value="-67176255" label="PeripheralNotification:disabled"/>
   <int value="-66834375" label="SharesheetCopyToClipboard:enabled"/>
   <int value="-66535194" label="DesktopMinimalUI:enabled"/>
+  <int value="-65929089" label="CpaSpecUpdate:enabled"/>
   <int value="-64839201" label="SyncUSSAutofillWalletData:disabled"/>
   <int value="-64824628" label="VizHitTestSurfaceLayer:disabled"/>
   <int value="-64747770" label="IncognitoDownloadsWarning:disabled"/>
@@ -17215,6 +17217,7 @@
   <int value="1101421005" label="DownloadLater:disabled"/>
   <int value="1102035446"
       label="AutofillUpstreamAuthenticatePreflightCall:enabled"/>
+  <int value="1102164603" label="AllowTabClosingUponMinimization:enabled"/>
   <int value="1102309668" label="EnableKeyboardUsedPalmSuppression:disabled"/>
   <int value="1102780374" label="ClickToCallDetectionV2:disabled"/>
   <int value="1103037724" label="AiSettingsPageEnterpriseDisabledUi:enabled"/>
@@ -18046,6 +18049,7 @@
   <int value="1408331660" label="enhanced-bookmarks-experiment"/>
   <int value="1409120591" label="PassiveMixedContentWarning:disabled"/>
   <int value="1409437197" label="OfflinePagesLimitlessPrefetching:enabled"/>
+  <int value="1409625676" label="CpaSpecUpdate:disabled"/>
   <int value="1409653883"
       label="GetUserMediaDeferredDeviceSettingsSelection:disabled"/>
   <int value="1410403456" label="AndroidBrowserControlsInViz:enabled"/>
diff --git a/tools/metrics/histograms/metadata/contextual_cueing/histograms.xml b/tools/metrics/histograms/metadata/contextual_cueing/histograms.xml
index 5a60929..6243506 100644
--- a/tools/metrics/histograms/metadata/contextual_cueing/histograms.xml
+++ b/tools/metrics/histograms/metadata/contextual_cueing/histograms.xml
@@ -55,6 +55,27 @@
 </histogram>
 
 <histogram
+    name="ContextualCueing.GlicSuggestions.SuggestionsFetchLatency.{ResultType}"
+    units="ms" expires_after="2025-08-30">
+  <owner>zekunjiang@google.com</owner>
+  <owner>rajendrant@google.com</owner>
+  <owner>sophiechang@chromium.org</owner>
+  <summary>
+    Recorded every time something is returned as a result of invoking the
+    suggestions fetch API. The results are split by the type of results returned
+    (e.g. results with no suggestions vs results with valid suggestions).
+  </summary>
+  <token key="ResultType">
+    <variant name="EmptySuggestions"
+        summary="When no suggestions are returned as a result of a
+                 suggestions fetch"/>
+    <variant name="ValidSuggestions"
+        summary="When valid suggestions are returned as a result of a
+                 suggestions fetch"/>
+  </token>
+</histogram>
+
+<histogram
     name="ContextualCueing.NudgeDecision.{ContextualCueingOptimizationType}"
     enum="ContextualCueingNudgeDecision" expires_after="2025-08-30">
   <owner>sophiechang@chromium.org</owner>
diff --git a/ui/accessibility/ax_bit_map.h b/ui/accessibility/ax_bit_map.h
index 271722d..a0175925 100644
--- a/ui/accessibility/ax_bit_map.h
+++ b/ui/accessibility/ax_bit_map.h
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include <array>
 
diff --git a/ui/android/java/src/org/chromium/ui/base/DeviceInput.java b/ui/android/java/src/org/chromium/ui/base/DeviceInput.java
index a96c8ca8..3aa7602 100644
--- a/ui/android/java/src/org/chromium/ui/base/DeviceInput.java
+++ b/ui/android/java/src/org/chromium/ui/base/DeviceInput.java
@@ -52,7 +52,9 @@
         for (int i = 0; i < deviceIds.length; i++) {
             int deviceId = deviceIds[i];
             InputDevice device = InputDevice.getDevice(deviceId);
-            if (device != null) mDeviceSnapshotsById.put(deviceId, DeviceSnapshot.from(device));
+            if (device != null) {
+                mDeviceSnapshotsById.put(deviceId, DeviceSnapshot.from(device));
+            }
         }
 
         // Register listener to perform cache updates.
@@ -89,7 +91,9 @@
             return sSupportsAlphabeticKeyboardForTesting;
         }
         for (int i = 0; i < mDeviceSnapshotsById.size(); i++) {
-            if (mDeviceSnapshotsById.valueAt(i).supportsAlphabeticKeyboard) return true;
+            if (mDeviceSnapshotsById.valueAt(i).supportsAlphabeticKeyboard) {
+                return true;
+            }
         }
         return false;
     }
@@ -117,7 +121,9 @@
             return sSupportsPrecisionPointerForTesting;
         }
         for (int i = 0; i < mDeviceSnapshotsById.size(); i++) {
-            if (mDeviceSnapshotsById.valueAt(i).supportsPrecisionPointer) return true;
+            if (mDeviceSnapshotsById.valueAt(i).supportsPrecisionPointer) {
+                return true;
+            }
         }
         return false;
     }
@@ -126,15 +132,20 @@
     public void onInputDeviceAdded(int deviceId) {
         ThreadUtils.assertOnUiThread();
         InputDevice device = InputDevice.getDevice(deviceId);
-        if (device != null) mDeviceSnapshotsById.put(deviceId, DeviceSnapshot.from(device));
+        if (device != null) {
+            mDeviceSnapshotsById.put(deviceId, DeviceSnapshot.from(device));
+        }
     }
 
     @Override
     public void onInputDeviceChanged(int deviceId) {
         ThreadUtils.assertOnUiThread();
         InputDevice device = InputDevice.getDevice(deviceId);
-        if (device != null) mDeviceSnapshotsById.put(deviceId, DeviceSnapshot.from(device));
-        else mDeviceSnapshotsById.remove(deviceId);
+        if (device != null) {
+            mDeviceSnapshotsById.put(deviceId, DeviceSnapshot.from(device));
+        } else {
+            mDeviceSnapshotsById.remove(deviceId);
+        }
     }
 
     @Override
@@ -171,6 +182,7 @@
             return new DeviceSnapshot(
                     /* supportsAlphabeticKeyboard= */ isPhysical
                             && device.getKeyboardType() == KEYBOARD_TYPE_ALPHABETIC,
+                    // SOURCE_MOUSE applies to pointer devices, including mouse and touchpad
                     /* supportsPrecisionPointer= */ isPhysical
                             && device.supportsSource(SOURCE_MOUSE));
         }
diff --git a/ui/base/webui/web_ui_util.cc b/ui/base/webui/web_ui_util.cc
index 46a4ec9..c2cd68ef 100644
--- a/ui/base/webui/web_ui_util.cc
+++ b/ui/base/webui/web_ui_util.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "ui/base/webui/web_ui_util.h"
 
diff --git a/ui/compositor/debug_utils.cc b/ui/compositor/debug_utils.cc
index 256386e..70ace71d0 100644
--- a/ui/compositor/debug_utils.cc
+++ b/ui/compositor/debug_utils.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "ui/compositor/debug_utils.h"
 
diff --git a/ui/display/util/edid_parser.cc b/ui/display/util/edid_parser.cc
index 8f6a91f..939fe32 100644
--- a/ui/display/util/edid_parser.cc
+++ b/ui/display/util/edid_parser.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "ui/display/util/edid_parser.h"
 
diff --git a/ui/gfx/color_transform_unittest.cc b/ui/gfx/color_transform_unittest.cc
index 9514c654..9177fdf 100644
--- a/ui/gfx/color_transform_unittest.cc
+++ b/ui/gfx/color_transform_unittest.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/354829279): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "ui/gfx/color_transform.h"
 
diff --git a/ui/message_center/message_center_stats_collector.cc b/ui/message_center/message_center_stats_collector.cc
index db52080..45f35e7 100644
--- a/ui/message_center/message_center_stats_collector.cc
+++ b/ui/message_center/message_center_stats_collector.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "ui/message_center/message_center_stats_collector.h"
 
diff --git a/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc b/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc
index 5db1e212..8bfa9bd 100644
--- a/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc
+++ b/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "ui/ozone/demo/skia/skia_surfaceless_gl_renderer.h"
 
diff --git a/ui/ozone/demo/surfaceless_gl_renderer.cc b/ui/ozone/demo/surfaceless_gl_renderer.cc
index 6926973..2487fd0b 100644
--- a/ui/ozone/demo/surfaceless_gl_renderer.cc
+++ b/ui/ozone/demo/surfaceless_gl_renderer.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "ui/ozone/demo/surfaceless_gl_renderer.h"
 
diff --git a/ui/ozone/demo/vulkan_overlay_renderer.cc b/ui/ozone/demo/vulkan_overlay_renderer.cc
index 416aa9923..236782b2 100644
--- a/ui/ozone/demo/vulkan_overlay_renderer.cc
+++ b/ui/ozone/demo/vulkan_overlay_renderer.cc
@@ -2,10 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
 
 #include "ui/ozone/demo/vulkan_overlay_renderer.h"