diff --git a/DEPS b/DEPS
index d23c9a8..f94216a 100644
--- a/DEPS
+++ b/DEPS
@@ -299,7 +299,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '9ba1169cd85206cb6113e43356b639c5d1913b49',
+  'skia_revision': '9100700840668a8a3276c05f114463f9b8c7a264',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -307,11 +307,11 @@
   # 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': '3d81e45d2bb674c7ba81cf2facc31336571b8a2d',
+  'angle_revision': '3a15fab06cff664a04188959f35fff33a37c5edb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '11dc1b167af4369aafa058d77f204700c0ecbb1c',
+  'swiftshader_revision': 'a857503547cb01a294df9a2c2df6bee18693b05c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -371,7 +371,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': 'aa341ec41f6d475102eee85ddec60d403ef575cd',
+  'catapult_revision': '304d8b75253e53388a57530f579fbd1049538fa6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
@@ -391,7 +391,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': '6ab44879f40f97dfb7bb2093300e3263b474198d',
+  'devtools_frontend_revision': 'e9953f0394b4f7f1c2a61a0827ed8b0fded4ad60',
   # 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.
@@ -758,7 +758,7 @@
       'packages': [
           {
               'package': 'chrome_internal/third_party/google3/data_sharing_sdk',
-              'version': 'vM0mNrZJfPV7ee7JhcjUvIrbPZ90opPqlNbV1DHxNJwC',
+              'version': 'UIfm6JqoDwxBPA4coc_W6eoc3FQ7wzG6f66DOgsuU3MC',
           },
       ],
       'condition': 'checkout_src_internal and non_git_source',
@@ -1521,7 +1521,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'c96cd8b39560ced5458b17bd824fac671f8622f3',
+    'b59fa7eada24f104539a738e2e81ff70154b14e0',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1784,7 +1784,7 @@
       'packages': [
           {
                'package': 'chromium/third_party/android_build_tools/manifest_merger',
-               'version': '1QtZ_gZ5icFNOEvVrY34lJgcC2XoXxBfgPmz-eBrk7UC',
+               'version': 'VcONzD64-9oyADpPb46XY3qQ0bWCJVaFIKA2dNziAY4C',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -2937,7 +2937,7 @@
     Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '4cbaeac5a15340612960eeaf14842b3faf09f0b5',
+    Var('webrtc_git') + '/src.git' + '@' + 'a424cbdcd65b2729417eaf8827a812a0d6ad2b00',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -4657,7 +4657,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        '667312c22e24f5b3ad4bd09f229d837cd880a5db',
+        'a06298498a2ca3f88c5c894a54076e970152c035',
       'condition': 'checkout_src_internal',
   },
 
@@ -4723,7 +4723,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        'f93575738db651614557a7f7c6841142661b9e21',
+        'f570cdfd61b09e2357cc640bb2de2ba7455fad1f',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/ash/system/time/calendar_view_unittest.cc b/ash/system/time/calendar_view_unittest.cc
index faf35d8..51cf1864 100644
--- a/ash/system/time/calendar_view_unittest.cc
+++ b/ash/system/time/calendar_view_unittest.cc
@@ -355,7 +355,6 @@
   std::unique_ptr<views::Widget> widget_;
   // Owned by `widget_`.
   raw_ptr<CalendarView> calendar_view_ = nullptr;
-  std::unique_ptr<CalendarEventListView> event_list_view_;
   static base::Time fake_time_;
 };
 
@@ -1290,8 +1289,7 @@
 }
 
 // Tests multiple scenarios that should record the metric when scrolling.
-// TODO(crbug.com/333283676): Re-enable once the test failure is fixed.
-TEST_F(CalendarViewTest, DISABLED_RecordDwellTimeMetricWhenScrolling) {
+TEST_F(CalendarViewTest, RecordDwellTimeMetricWhenScrolling) {
   base::HistogramTester histogram_tester;
   CreateCalendarView();
 
@@ -1315,9 +1313,12 @@
 
   // Opening and closing the CalendarEventListView through a date cell within
   // the current month does not record the metric.
-  auto* first_of_month_date_cell =
-      GetDateCell(/*month=*/current_month(), /*day=*/u"1");
-  ClickDateCell(first_of_month_date_cell);
+  //
+  // A specific date (today's date) is used here to avoid test flakiness. For
+  // more details, see https://crrev.com/c/6586142/5..6//COMMIT_MSG#b15.
+  auto* todays_date_cell =
+      calendar_view()->calendar_view_controller()->todays_date_cell_view();
+  ClickDateCell(todays_date_cell);
   CloseEventList();
   histogram_tester.ExpectTotalCount("Ash.Calendar.MonthDwellTime",
                                     /*expected_count=*/6);
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 3e8c3be..05e3a101 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -638,6 +638,7 @@
     "strings/stringprintf.cc",
     "strings/stringprintf.h",
     "strings/sys_string_conversions.h",
+    "strings/to_string.cc",
     "strings/to_string.h",
     "strings/utf_offset_string_conversions.cc",
     "strings/utf_offset_string_conversions.h",
diff --git a/base/containers/span.h b/base/containers/span.h
index 1320eb00..483523a 100644
--- a/base/containers/span.h
+++ b/base/containers/span.h
@@ -17,7 +17,6 @@
 #include <concepts>
 #include <functional>
 #include <initializer_list>
-#include <iosfwd>
 #include <iterator>
 #include <limits>
 #include <memory>
@@ -35,7 +34,6 @@
 #include "base/numerics/integral_constant_like.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/cstring_view.h"
-#include "base/strings/to_string.h"
 #include "base/types/to_address.h"
 
 // A span is a view of contiguous elements that can be accessed like an array,
@@ -261,9 +259,6 @@
 //   instead of `std::byte` as the returned element type.
 // - For convenience, provides `as_[writable_]chars()` and `as_string_view()`
 //   to convert to other "view of bytes"-like objects.
-// - For convenience, provides an `operator<<()` overload that accepts a span
-//   and prints a byte representation. Also provides a `PrintTo()` overload to
-//   convince GoogleTest to use this operator to print.
 // - For convenience, provides `[byte_]span_from_ref()` to convert single
 //   (non-range) objects to spans.
 // - For convenience, provides `[byte_]span_[with_nul_]from_cstring()` to
@@ -1486,73 +1481,6 @@
   return std::wstring_view(s.begin(), s.end());
 }
 
-namespace internal {
-
-template <typename T>
-concept SpanConvertsToStringView = requires {
-  { as_string_view(span<T>()) };
-};
-
-}  // namespace internal
-
-// Stream output that prints a byte representation.
-//
-// (Not in `std::`; convenient for debugging.)
-template <typename ElementType, size_t Extent, typename InternalPtrType>
-  requires(internal::SpanConvertsToStringView<ElementType> ||
-           requires(const ElementType& t) {
-             { ToString(t) };
-           })
-constexpr std::ostream& operator<<(
-    std::ostream& l,
-    span<ElementType, Extent, InternalPtrType> r) {
-  l << '[';
-  if constexpr (internal::SpanConvertsToStringView<ElementType>) {
-    const auto sv = as_string_view(r);
-    if constexpr (requires { l << sv; }) {
-      using T = std::remove_cvref_t<ElementType>;
-      if constexpr (std::same_as<wchar_t, T>) {
-        l << 'L';
-      } else if constexpr (std::same_as<char16_t, T>) {
-        l << 'u';
-      } else if constexpr (std::same_as<char32_t, T>) {
-        l << 'U';
-      }
-      l << '\"' << sv << '\"';
-    } else {
-      // base/strings/utf_ostream_operators.h provides streaming support for
-      // wchar_t/char16_t, so branching on whether streaming is available will
-      // give different results depending on whether code has included that,
-      // which can lead to UB due to violating the ODR. We don't want to
-      // unconditionally include this header above for compile time reasons, so
-      // instead force the rare caller that wants this to do it themselves.
-      static_assert(
-          requires { l << sv; },
-          "include base/strings/utf_ostream_operators.h when streaming spans "
-          "of wide chars");
-    }
-  } else if constexpr (Extent != 0) {
-    // It would be nice to use `JoinString()` here, but making that `constexpr`
-    // is more trouble than it's worth.
-    if (!r.empty()) {
-      l << ToString(r.front());
-      for (const ElementType& e : r.template subspan<1>()) {
-        l << ", " << ToString(e);
-      }
-    }
-  }
-  return l << ']';
-}
-
-// Because `span` meets the GoogleTest "container" criteria, explicitly
-// overloading `PrintTo()` is necessary to make GoogleTest print spans using the
-// `operator<<()` overload above, and not its own container printer.
-template <typename ElementType, size_t Extent, typename InternalPtrType>
-constexpr void PrintTo(span<ElementType, Extent, InternalPtrType> s,
-                       std::ostream* os) {
-  *os << s;
-}
-
 // Converts a `T&` to a `span<T, 1>`.
 //
 // (Not in `std::`; inspired by Rust's `slice::from_ref()`.)
diff --git a/base/containers/span_unittest.cc b/base/containers/span_unittest.cc
index d5288f7..506b2dbc7 100644
--- a/base/containers/span_unittest.cc
+++ b/base/containers/span_unittest.cc
@@ -25,6 +25,7 @@
 #include "base/memory/raw_span.h"
 #include "base/numerics/byte_conversions.h"
 #include "base/strings/cstring_view.h"
+#include "base/strings/to_string.h"
 #include "base/strings/utf_ostream_operators.h"
 #include "base/test/gtest_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -3286,44 +3287,6 @@
   }
 }
 
-TEST(SpanTest, Printing) {
-  struct S {
-    std::string ToString() const { return "S()"; }
-  };
-
-  // Gtest prints values in the spans. Chars are special.
-  EXPECT_EQ(testing::PrintToString(span<const int>({1, 2, 3})), "[1, 2, 3]");
-  EXPECT_EQ(testing::PrintToString(span<const S>({S(), S()})), "[S(), S()]");
-  EXPECT_EQ(testing::PrintToString(span<const char>({'a', 'b', 'c'})),
-            "[\"abc\"]");
-  EXPECT_EQ(testing::PrintToString(span<const char>({'a', 'b', 'c', '\0'})),
-            std::string_view("[\"abc\0\"]", 8u));
-  EXPECT_EQ(
-      testing::PrintToString(span<const char>({'a', 'b', '\0', 'c', '\0'})),
-      std::string_view("[\"ab\0c\0\"]", 9u));
-  EXPECT_EQ(testing::PrintToString(span<int>()), "[]");
-  EXPECT_EQ(testing::PrintToString(span<char>()), "[\"\"]");
-
-  EXPECT_EQ(testing::PrintToString(span<const char16_t>({u'a', u'b', u'c'})),
-            "[u\"abc\"]");
-  EXPECT_EQ(testing::PrintToString(span<const wchar_t>({L'a', L'b', L'c'})),
-            "[L\"abc\"]");
-
-  // Base prints values in spans. Chars are special.
-  EXPECT_EQ(ToString(span<const int>({1, 2, 3})), "[1, 2, 3]");
-  EXPECT_EQ(ToString(span<const S>({S(), S()})), "[S(), S()]");
-  EXPECT_EQ(ToString(span<const char>({'a', 'b', 'c'})), "[\"abc\"]");
-  EXPECT_EQ(ToString(span<const char>({'a', 'b', 'c', '\0'})),
-            std::string_view("[\"abc\0\"]", 8u));
-  EXPECT_EQ(ToString(span<const char>({'a', 'b', '\0', 'c', '\0'})),
-            std::string_view("[\"ab\0c\0\"]", 9u));
-  EXPECT_EQ(ToString(span<int>()), "[]");
-  EXPECT_EQ(ToString(span<char>()), "[\"\"]");
-
-  EXPECT_EQ(ToString(span<const char16_t>({u'a', u'b', u'c'})), "[u\"abc\"]");
-  EXPECT_EQ(ToString(span<const wchar_t>({L'a', L'b', L'c'})), "[L\"abc\"]");
-}
-
 }  // namespace base
 
 // Test for compatibility with std::span<>, in case some third-party
diff --git a/base/i18n/break_iterator.h b/base/i18n/break_iterator.h
index 7a506b7..5445925 100644
--- a/base/i18n/break_iterator.h
+++ b/base/i18n/break_iterator.h
@@ -12,7 +12,6 @@
 #include <string_view>
 
 #include "base/i18n/base_i18n_export.h"
-#include "base/memory/raw_ptr.h"
 
 // The BreakIterator class iterates through the words, word breaks, and
 // line breaks in a UTF-16 string.
diff --git a/base/strings/to_string.cc b/base/strings/to_string.cc
new file mode 100644
index 0000000..a8a09ac
--- /dev/null
+++ b/base/strings/to_string.cc
@@ -0,0 +1,26 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/to_string.h"
+
+#include <string>
+#include <string_view>
+
+#include "base/strings/utf_string_conversions.h"
+
+namespace base {
+
+std::string ToString(std::string_view sv) {
+  return std::string(sv);
+}
+
+std::string ToString(std::u16string_view sv) {
+  return UTF16ToUTF8(sv);
+}
+
+std::string ToString(std::wstring_view sv) {
+  return WideToUTF8(sv);
+}
+
+}  // namespace base
diff --git a/base/strings/to_string.h b/base/strings/to_string.h
index 464d776..d89ad402 100644
--- a/base/strings/to_string.h
+++ b/base/strings/to_string.h
@@ -9,10 +9,13 @@
 #include <memory>
 #include <sstream>
 #include <string>
+#include <string_view>
 #include <tuple>
 #include <type_traits>
 #include <utility>
 
+#include "base/base_export.h"
+#include "base/containers/span.h"
 #include "base/types/supports_ostream_operator.h"
 #include "base/types/supports_to_string.h"
 
@@ -134,6 +137,56 @@
   return ss.str();
 }
 
+BASE_EXPORT std::string ToString(std::string_view sv);
+BASE_EXPORT std::string ToString(std::u16string_view sv);
+BASE_EXPORT std::string ToString(std::wstring_view sv);
+
+namespace to_string_internal {
+
+template <typename T>
+concept SpanConvertsToStringView = requires {
+  { as_string_view(span<T>()) };
+};
+
+}  // namespace to_string_internal
+
+// Stringify base::span, hopefully in a way that's useful for tests.
+template <typename ElementType, size_t Extent, typename InternalPtrType>
+  requires(to_string_internal::SpanConvertsToStringView<ElementType> ||
+           requires(const ElementType& t) {
+             { ToString(t) };
+           })
+constexpr std::string ToString(span<ElementType, Extent, InternalPtrType> r) {
+  std::string out = "[";
+  if constexpr (to_string_internal::SpanConvertsToStringView<ElementType>) {
+    const auto sv = as_string_view(r);
+    using T = std::remove_cvref_t<ElementType>;
+    if constexpr (std::same_as<wchar_t, T>) {
+      out += "L\"";
+      out += ToString(sv);
+    } else if constexpr (std::same_as<char16_t, T>) {
+      out += "u\"";
+      out += ToString(sv);
+    } else {
+      out += "\"";
+      out += sv;
+    }
+    out += '\"';
+  } else if constexpr (Extent != 0) {
+    // It would be nice to use `JoinString()` here, but making that `constexpr`
+    // is more trouble than it's worth.
+    if (!r.empty()) {
+      out += ToString(r.front());
+      for (const ElementType& e : r.template subspan<1>()) {
+        out += ", ";
+        out += ToString(e);
+      }
+    }
+  }
+  out += "]";
+  return out;
+}
+
 }  // namespace base
 
 #endif  // BASE_STRINGS_TO_STRING_H_
diff --git a/base/strings/to_string_unittest.cc b/base/strings/to_string_unittest.cc
index 0c27937..1e50baa 100644
--- a/base/strings/to_string_unittest.cc
+++ b/base/strings/to_string_unittest.cc
@@ -145,5 +145,24 @@
             ToString(static_cast<OverloadsAddressOp*>(nullptr)));
 }
 
+TEST(ToStringTest, Span) {
+  struct S {
+    std::string ToString() const { return "S()"; }
+  };
+
+  EXPECT_EQ(ToString(span<const int>({1, 2, 3})), "[1, 2, 3]");
+  EXPECT_EQ(ToString(span<const S>({S(), S()})), "[S(), S()]");
+  EXPECT_EQ(ToString(span<const char>({'a', 'b', 'c'})), "[\"abc\"]");
+  EXPECT_EQ(ToString(span<const char>({'a', 'b', 'c', '\0'})),
+            std::string_view("[\"abc\0\"]", 8u));
+  EXPECT_EQ(ToString(span<const char>({'a', 'b', '\0', 'c', '\0'})),
+            std::string_view("[\"ab\0c\0\"]", 9u));
+  EXPECT_EQ(ToString(span<int>()), "[]");
+  EXPECT_EQ(ToString(span<char>()), "[\"\"]");
+
+  EXPECT_EQ(ToString(span<const char16_t>({u'a', u'b', u'c'})), "[u\"abc\"]");
+  EXPECT_EQ(ToString(span<const wchar_t>({L'a', L'b', L'c'})), "[L\"abc\"]");
+}
+
 }  // namespace
 }  // namespace base
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index ad582c2..74463a6c 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -713,12 +713,12 @@
       "android/javatests/src/org/chromium/base/test/transit/ScrollableFacility.java",
       "android/javatests/src/org/chromium/base/test/transit/SimpleConditions.java",
       "android/javatests/src/org/chromium/base/test/transit/Station.java",
+      "android/javatests/src/org/chromium/base/test/transit/StationToStationTrip.java",
       "android/javatests/src/org/chromium/base/test/transit/StatusStore.java",
       "android/javatests/src/org/chromium/base/test/transit/TrafficControl.java",
       "android/javatests/src/org/chromium/base/test/transit/TransitAsserts.java",
       "android/javatests/src/org/chromium/base/test/transit/Transition.java",
       "android/javatests/src/org/chromium/base/test/transit/TravelException.java",
-      "android/javatests/src/org/chromium/base/test/transit/Trip.java",
       "android/javatests/src/org/chromium/base/test/transit/UiThreadCondition.java",
       "android/javatests/src/org/chromium/base/test/transit/ViewConditions.java",
       "android/javatests/src/org/chromium/base/test/transit/ViewElement.java",
@@ -737,9 +737,7 @@
       "//third_party/junit",
     ]
 
-    sources = [
-      "android/javatests/src/org/chromium/base/test/transit/TripUnitTest.java",
-    ]
+    sources = [ "android/javatests/src/org/chromium/base/test/transit/ElementFactoryUnitTest.java" ]
   }
 }
 
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
index 64efb164..7ba07773 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
@@ -191,7 +191,7 @@
                 "org.mockito.android.target",
                 sInMemorySharedPreferencesContext.getCacheDir().getPath());
         // Reduce the time Espresso waits before failing to be less than the Python test timeout.
-        IdlingPolicies.setMasterPolicyTimeout(20, TimeUnit.SECONDS);
+        IdlingPolicies.setMasterPolicyTimeout(5, TimeUnit.SECONDS);
         if (arguments.getString(IS_UNIT_TEST_FLAG) != null) {
             LibraryLoader.setBrowserProcessStartupBlockedForTesting();
         }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/TripUnitTest.java b/base/test/android/javatests/src/org/chromium/base/test/transit/ElementFactoryUnitTest.java
similarity index 98%
rename from base/test/android/javatests/src/org/chromium/base/test/transit/TripUnitTest.java
rename to base/test/android/javatests/src/org/chromium/base/test/transit/ElementFactoryUnitTest.java
index 92c3ef8a..882f3439 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/TripUnitTest.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/ElementFactoryUnitTest.java
@@ -19,11 +19,11 @@
 
 import java.util.concurrent.atomic.AtomicReference;
 
-/** Unit Tests for {@link Trip}. */
+/** Unit Tests for {@link ElementFactory}. */
 @RunWith(BaseRobolectricTestRunner.class)
-public class TripUnitTest {
+public class ElementFactoryUnitTest {
 
-    private static final String TAG = "TripUnitTest";
+    private static final String TAG = "TransitUnitTest";
 
     private static class NestedFactoryStation extends Station<Activity> {
         public final CallbackHelper mDeclareElementsCallbackHelper = new CallbackHelper();
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/Facility.java b/base/test/android/javatests/src/org/chromium/base/test/transit/Facility.java
index 95f1a74..6cee792e 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/Facility.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/Facility.java
@@ -19,7 +19,7 @@
  * that determine its enter and exit {@link Condition}s.
  *
  * <p>Leaving the host {@link Station} causes this state to be left as well, and exit {@link
- * Condition}s will be waited upon for the {@link Trip} to be complete.
+ * Condition}s will be waited upon for the {@link StationToStationTrip} to be complete.
  *
  * <p>Transitions into and out of a Facility while the host {@link Station} is ACTIVE should be done
  * with {@link Station#enterFacilitySync(Facility, Trigger)} and {@link
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/Station.java b/base/test/android/javatests/src/org/chromium/base/test/transit/Station.java
index 6be79b0..ea6fcddc 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/Station.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/Station.java
@@ -124,7 +124,8 @@
      * Starts a transition from this origin {@link Station} to another destination {@link Station}.
      * Runs the transition |trigger|, and blocks until the destination {@link Station} is considered
      * ACTIVE (enter Conditions are fulfilled), the origin {@link Station} is considered FINISHED
-     * (exit Conditions are fulfilled), and the {@link Trip}'s transition conditions are fulfilled.
+     * (exit Conditions are fulfilled), and the {@link StationToStationTrip}'s transition conditions
+     * are fulfilled.
      *
      * @param destination the {@link Facility} to arrive at.
      * @param trigger the trigger to start the transition (e.g. clicking a view).
@@ -133,8 +134,9 @@
      */
     public final <T extends Station<?>> T travelToSync(T destination, @Nullable Trigger trigger) {
         destination.requireToBeInSameTask(this);
-        Trip trip =
-                new Trip(List.of(this), List.of(destination), TransitionOptions.DEFAULT, trigger);
+        StationToStationTrip trip =
+                new StationToStationTrip(
+                        List.of(this), List.of(destination), TransitionOptions.DEFAULT, trigger);
         trip.transitionSync();
         return destination;
     }
@@ -143,7 +145,8 @@
     public final <T extends Station<?>> T travelToSync(
             T destination, TransitionOptions options, @Nullable Trigger trigger) {
         destination.requireToBeInSameTask(this);
-        Trip trip = new Trip(List.of(this), List.of(destination), options, trigger);
+        StationToStationTrip trip =
+                new StationToStationTrip(List.of(this), List.of(destination), options, trigger);
         trip.transitionSync();
         return destination;
     }
@@ -326,8 +329,8 @@
      * <p>Useful for opening a new window.
      *
      * <p>Runs the transition |trigger|, and blocks until the destination {@link Station} is
-     * considered ACTIVE (enter Conditions are fulfilled) and the {@link Trip}'s transition
-     * conditions are fulfilled.
+     * considered ACTIVE (enter Conditions are fulfilled) and the {@link StationToStationTrip}'s
+     * transition conditions are fulfilled.
      *
      * @param destination the {@link Facility} to arrive at.
      * @param trigger the trigger to start the transition (e.g. clicking a view).
@@ -342,7 +345,8 @@
     public static <T extends Station<?>> T spawnSync(
             T destination, TransitionOptions options, @Nullable Trigger trigger) {
         destination.requireToBeInNewTask();
-        Trip trip = new Trip(List.of(), List.of(destination), options, trigger);
+        StationToStationTrip trip =
+                new StationToStationTrip(List.of(), List.of(destination), options, trigger);
         trip.transitionSync();
         return destination;
     }
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/Trip.java b/base/test/android/javatests/src/org/chromium/base/test/transit/StationToStationTrip.java
similarity index 96%
rename from base/test/android/javatests/src/org/chromium/base/test/transit/Trip.java
rename to base/test/android/javatests/src/org/chromium/base/test/transit/StationToStationTrip.java
index 9c62a0e..91aa61aa 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/Trip.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/StationToStationTrip.java
@@ -16,7 +16,7 @@
  * point.
  */
 @NullMarked
-class Trip extends Transition {
+class StationToStationTrip extends Transition {
     private final List<Station<?>> mOrigins;
     private final List<Station<?>> mDestinations;
 
@@ -34,7 +34,7 @@
      * @param options the {@link TransitionOptions}.
      * @param trigger the action that triggers the transition. e.g. clicking a View.
      */
-    Trip(
+    StationToStationTrip(
             List<Station<?>> origins,
             List<Station<?>> destinations,
             TransitionOptions options,
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/TransitAsserts.java b/base/test/android/javatests/src/org/chromium/base/test/transit/TransitAsserts.java
index f2cdc07..ecfbce1 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/TransitAsserts.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/TransitAsserts.java
@@ -21,7 +21,7 @@
      * happened.
      *
      * <p>A different instance of the same subclass {@link Station} does not count; it must be the
-     * {@link Station} instance returned by the last {@link Trip} transition.
+     * {@link Station} instance returned by the last transition that traveled to a Station.
      *
      * @param expectedStation the {@link Station} expected to be the last
      * @param expectedFacilities the {@link Facility}'s expected to be the active
diff --git a/build/android/pylib/gtest/gtest_test_instance.py b/build/android/pylib/gtest/gtest_test_instance.py
index edb9360..c70205d 100644
--- a/build/android/pylib/gtest/gtest_test_instance.py
+++ b/build/android/pylib/gtest/gtest_test_instance.py
@@ -32,8 +32,8 @@
     'weblayer_browsertests',
 ]
 
-# The max number of tests to run on a shard during the test run.
-MAX_SHARDS = 256
+# The max number of tests to run on a batch during the test run.
+MAX_BATCH_SIZE = 256
 
 RUN_IN_SUB_THREAD_TEST_SUITES = [
     # Multiprocess tests should be run outside of the main thread.
@@ -82,6 +82,10 @@
     'org.chromium.native_test.NativeTestInstrumentationTestRunner.'
         'ShardSizeLimit')
 
+_PREFIX_DISABLED = 'DISABLED_'
+_PREFIX_FLAKY = 'FLAKY_'
+_PREFIX_PRE = 'PRE_'
+
 # TODO(jbudorick): Remove these once we're no longer parsing stdout to generate
 # results.
 _RE_TEST_STATUS = re.compile(
@@ -105,8 +109,6 @@
 _RE_TEST_CURRENTLY_RUNNING = re.compile(
     r'\[.*ERROR:.*?\] Currently running: (.*)')
 _RE_TEST_DCHECK_FATAL = re.compile(r'\[.*:FATAL:.*\] (.*)')
-_RE_DISABLED = re.compile(r'DISABLED_')
-_RE_FLAKY = re.compile(r'FLAKY_')
 
 # Detect a new launcher invocation. When encountered, the output parser will
 # stop recording logs for a suddenly crashed test (if one was running) in the
@@ -315,19 +317,59 @@
   return results
 
 
-def TestNameWithoutDisabledPrefix(test_name):
-  """Modify the test name without disabled prefix if prefix 'DISABLED_' or
-  'FLAKY_' presents.
+def _TestNameWithoutPrefix(full_test_name, prefixes):
+  """Get full test name without any prefix from the given list of prefixes.
 
   Args:
-    test_name: The name of a test.
+    full_test_name: A string containing the full name of a test, e.g.
+        TestSuite1.TestName1
+    prefixes: A list of prefixes to remove from the test name.
   Returns:
-    A test name without prefix 'DISABLED_' or 'FLAKY_'.
+    A full test name without any given prefix.
   """
-  disabled_prefixes = [_RE_DISABLED, _RE_FLAKY]
-  for dp in disabled_prefixes:
-    test_name = dp.sub('', test_name)
-  return test_name
+  for prefix in prefixes:
+    full_test_name = full_test_name.replace(prefix, '')
+  return full_test_name
+
+
+def TestNameWithoutDisabledPrefix(full_test_name):
+  """Get full test name without disabled prefixes 'DISABLED_' or 'FLAKY_'."""
+  return _TestNameWithoutPrefix(full_test_name,
+                                [_PREFIX_DISABLED, _PREFIX_FLAKY])
+
+
+def TestNameWithoutPrefixes(full_test_name):
+  """Get full test name without prefixes 'DISABLED_', 'FLAKY_', or 'PRE_'."""
+  return _TestNameWithoutPrefix(full_test_name,
+                                [_PREFIX_DISABLED, _PREFIX_FLAKY, _PREFIX_PRE])
+
+
+def TestNameWithPrePrefix(full_test_name):
+  """Get full test name with PRE_ added to the test name.
+
+  Note that the DISABLED_ prefix will be stripped if present.
+
+  For example:
+   - TestSuite1.TestName1 -> TestSuite1.PRE_TestName1
+   - TestSuite1.PRE_TestName2 -> TestSuite1.PRE_PRE_TestName2
+   - TestSuite1.DISABLED_TestName3 -> TestSuite1.PRE_TestName3
+  """
+  full_test_name = TestNameWithoutDisabledPrefix(full_test_name)
+  test_suite, test_name = full_test_name.split('.', maxsplit=1)
+  return f'{test_suite}.{_PREFIX_PRE}{test_name}'
+
+
+def IsPreTest(full_test_name):
+  """Check if a full test name is a PRE_ test.
+
+  Since both of the following are valid PRE_ tests, we just check if PRE_ exists
+  in the test name:
+   - TestSuite1.PRE_TestName2
+   - TestSuite1.DISABLED_PRE_TestName3
+  """
+  _, test_name = full_test_name.split('.', maxsplit=1)
+  return _PREFIX_PRE in test_name
+
 
 class GtestTestInstance(test_instance.TestInstance):
 
@@ -371,9 +413,9 @@
     if args.test_apk_incremental_install_json:
       incremental_part = '_incremental'
 
-    self._test_launcher_batch_limit = MAX_SHARDS
+    self._test_launcher_batch_limit = MAX_BATCH_SIZE
     if (args.test_launcher_batch_limit
-        and 0 < args.test_launcher_batch_limit < MAX_SHARDS):
+        and 0 < args.test_launcher_batch_limit < MAX_BATCH_SIZE):
       self._test_launcher_batch_limit = args.test_launcher_batch_limit
 
     apk_path = os.path.join(
diff --git a/build/android/pylib/gtest/gtest_test_instance_test.py b/build/android/pylib/gtest/gtest_test_instance_test.py
index 815730d..c65230b6 100755
--- a/build/android/pylib/gtest/gtest_test_instance_test.py
+++ b/build/android/pylib/gtest/gtest_test_instance_test.py
@@ -4,8 +4,14 @@
 # found in the LICENSE file.
 
 
+import os
 import unittest
 
+import sys
+
+sys.path.append(
+    os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
+
 from pylib.base import base_test_result
 from pylib.gtest import gtest_test_instance
 
@@ -352,8 +358,7 @@
       'DISABLED_A.DISABLED_B',
     ]
     for test_name in test_name_list:
-      actual = gtest_test_instance \
-          .TestNameWithoutDisabledPrefix(test_name)
+      actual = gtest_test_instance.TestNameWithoutDisabledPrefix(test_name)
       expected = 'A.B'
       self.assertEqual(expected, actual)
 
@@ -364,18 +369,60 @@
       'FLAKY_A.FLAKY_B',
     ]
     for test_name in test_name_list:
-      actual = gtest_test_instance \
-          .TestNameWithoutDisabledPrefix(test_name)
+      actual = gtest_test_instance.TestNameWithoutDisabledPrefix(test_name)
       expected = 'A.B'
       self.assertEqual(expected, actual)
 
   def testTestNameWithoutDisabledPrefix_notDisabledOrFlaky(self):
     test_name = 'A.B'
-    actual = gtest_test_instance \
-        .TestNameWithoutDisabledPrefix(test_name)
+    actual = gtest_test_instance.TestNameWithoutDisabledPrefix(test_name)
     expected = 'A.B'
     self.assertEqual(expected, actual)
 
+  def testTestNameWithoutPrefix(self):
+    test_name_list = [
+        'A.DISABLED_B',
+        'DISABLED_A.B',
+        'DISABLED_A.DISABLED_B',
+        'A.FLAKY_B',
+        'FLAKY_A.B',
+        'FLAKY_A.FLAKY_B',
+        'A.PRE_B',
+        'A.PRE_PRE_B',
+        'A.DISABLED_PRE_B',
+        'A.DISABLED_PRE_PRE_B',
+        'DISABLED_A.DISABLED_PRE_B',
+        'DISABLED_A.DISABLED_PRE_PRE_B',
+    ]
+    for test_name in test_name_list:
+      actual = gtest_test_instance.TestNameWithoutPrefixes(test_name)
+      expected = 'A.B'
+      self.assertEqual(expected, actual)
+
+  def testTestNameWithPrePrefix(self):
+    test_data = [
+        ('A.B', 'A.PRE_B'),
+        ('A.PRE_B', 'A.PRE_PRE_B'),
+        ('A.DISABLED_B', 'A.PRE_B'),
+        ('A.DISABLED_PRE_B', 'A.PRE_PRE_B'),
+    ]
+    for test_name, expected in test_data:
+      actual = gtest_test_instance.TestNameWithPrePrefix(test_name)
+      self.assertEqual(expected, actual)
+
+  def testIsPreTest(self):
+    test_data = [
+        ('A.PRE_B', True),
+        ('A.PRE_PRE_B', True),
+        ('A.DISABLED_PRE_B', True),
+        ('A.DISABLED_PRE_PRE_B', True),
+        ('A.B', False),
+        ('A.DISABLED_B', False),
+    ]
+    for test_name, expected in test_data:
+      actual = gtest_test_instance.IsPreTest(test_name)
+      self.assertEqual(expected, actual)
+
 
 if __name__ == '__main__':
   unittest.main(verbosity=2)
diff --git a/build/android/pylib/local/device/local_device_gtest_run.py b/build/android/pylib/local/device/local_device_gtest_run.py
index acf0d364..88d57e6 100644
--- a/build/android/pylib/local/device/local_device_gtest_run.py
+++ b/build/android/pylib/local/device/local_device_gtest_run.py
@@ -164,28 +164,44 @@
 
 
 def _GroupPreTests(tests):
-  pre_tests = dict()
+  """Separate a list of tests to two groups, depending on if having PRE_ tests.
+
+  PRE_ tests will be put in the same subgroup, in the order like
+  [PRE_PRE_foo, PRE_foo, foo].
+  """
+  pre_tests = []
   other_tests = []
-  for test in tests:
-    test_name_start = max(test.find('.') + 1, 0)
-    test_name = test[test_name_start:]
-    if test_name_start > 0 and test_name.startswith(_GTEST_PRETEST_PREFIX):
-      test_suite = test[:test_name_start - 1]
-      trim_test = test
-      trim_tests = [test]
 
-      while test_name.startswith(_GTEST_PRETEST_PREFIX):
-        test_name = test_name[len(_GTEST_PRETEST_PREFIX):]
-        trim_test = '%s.%s' % (test_suite, test_name)
-        trim_tests.append(trim_test)
+  pre_test_dict = {}
+  tests = set(tests)
+  # Preprocess pre tests. The key is the full test name without any disabled
+  # prefixes, and the value is the original full test name.
+  for t in tests:
+    if gtest_test_instance.IsPreTest(t):
+      pre_test_dict[gtest_test_instance.TestNameWithoutDisabledPrefix(t)] = t
 
-      # The trim test should exist at first place. For example, if a test has
-      # been disabled, there is no need to run PRE_ test with this test.
-      if trim_test in tests and (not trim_test in pre_tests or len(
-          pre_tests[trim_test]) < len(trim_tests)):
-        pre_tests[trim_test] = trim_tests
+  for t in tests:
+    # Skip PRE tests as they will be processed below.
+    if gtest_test_instance.IsPreTest(t):
+      continue
+
+    t_group = [t]
+    while True:
+      test_with_pre = gtest_test_instance.TestNameWithPrePrefix(t_group[0])
+      if test_with_pre in pre_test_dict:
+        # Remove pre test from dict, and add its original full test name to
+        # test group, in the order like PRE_PRE_foo, PRE_foo, foo.
+        t_group.insert(0, pre_test_dict.pop(test_with_pre))
+      else:
+        break
+
+    if len(t_group) > 1:
+      pre_tests.append(t_group)
     else:
-      other_tests.append(test)
+      other_tests.append(t_group[0])
+
+  for pre_test in pre_test_dict.values():
+    logging.error('%s is an orphaned pre test', pre_test)
   return pre_tests, other_tests
 
 
@@ -615,31 +631,52 @@
     """Create shards of tests to run on devices.
 
     Args:
-      tests: List containing tests or test batches.
+      tests: List containing tests or test groups.
 
     Returns:
-      List of test batches.
+      List of test groups.
     """
     # _crashes are tests that might crash and make the tests in the same shard
     # following the crashed testcase not run.
     # Thus we need to create separate shards for each crashed testcase,
     # so that other tests can be run.
     device_count = len(self._env.devices)
+    batch_size = self._test_instance.test_launcher_batch_limit
     shards = []
 
-    # Add shards with only one suspect testcase.
-    shards += [[crash] for crash in self._crashes if crash in tests]
+    for i in range(device_count):
+      tests_on_device = tests[i::device_count]
+      single_tests = []
+      for test in tests_on_device:
+        if isinstance(test, list):
+          # Any existing list from "tests" shall be a PRE test group.
+          assert self._IsPreTestGroup(test), (
+              f'Expecting a PRE test group, got {test}')
+          # A test subgroup will run together even if it has a crashed test
+          shards.append(test)
+        elif test in self._crashes:
+          # Put a crashed test in its own group.
+          shards.append([test])
+        else:
+          single_tests.append(test)
+          if len(single_tests) == batch_size:
+            shards.append(list(single_tests))
+            single_tests.clear()
 
-    # Delete suspect testcase from tests.
-    tests = [test for test in tests if not test in self._crashes]
+      if single_tests:
+        shards.append(list(single_tests))
 
-    max_shard_size = self._test_instance.test_launcher_batch_limit
-
-    shards.extend(self._PartitionTests(tests, device_count, max_shard_size))
     return shards
 
   #override
   def _GetTests(self):
+    """Get the tests to run on the current shard.
+
+    It either:
+     - gets the tests from the given filters.
+     - or retrieves the full tests, applies filters and sharding, and return
+       the tests to run on the current shard.
+    """
     if self._test_instance.extract_test_list_from_filter:
       # When the exact list of tests to run is given via command-line (e.g. when
       # locally iterating on a specific test), skip querying the device (which
@@ -706,52 +743,10 @@
     return tests
 
   #override
-  def _AppendPreTestsForRetry(self, failed_tests, tests):
-    if not self._test_instance.run_pre_tests:
-      return failed_tests
-
-    pre_tests, _ = _GroupPreTests(tests)
-    trim_failed_tests = set()
-    for failed_test in failed_tests:
-      failed_test_name_start = max(failed_test.find('.') + 1, 0)
-      failed_test_name = failed_test[failed_test_name_start:]
-
-      if failed_test_name_start > 0 and failed_test_name.startswith(
-          _GTEST_PRETEST_PREFIX):
-        failed_test_suite = failed_test[:failed_test_name_start - 1]
-        while failed_test_name.startswith(_GTEST_PRETEST_PREFIX):
-          failed_test_name = failed_test_name[len(_GTEST_PRETEST_PREFIX):]
-        failed_test = '%s.%s' % (failed_test_suite, failed_test_name)
-      trim_failed_tests.add(failed_test)
-
-    all_tests = []
-    for trim_failed_test in trim_failed_tests:
-      if trim_failed_test in tests:
-        if trim_failed_test in pre_tests:
-          all_tests.extend(pre_tests[trim_failed_test])
-        else:
-          all_tests.append(trim_failed_test)
-    return all_tests
-
-  #override
   def _GroupTests(self, tests):
     pre_tests, other_tests = _GroupPreTests(tests)
-
-    all_tests = []
-    for other_test in other_tests:
-      if not other_test in pre_tests:
-        all_tests.append(other_test)
-
-    # TODO(crbug.com/40200835): Add logic to support grouping tests.
-    # Once grouping logic is added, switch to 'append' from 'extend'.
-    for _, test_list in pre_tests.items():
-      all_tests.extend(test_list)
-
-    return all_tests
-
-  #override
-  def _GroupTestsAfterSharding(self, tests):
-    return self._GroupTests(tests)
+    all_tests = pre_tests + other_tests
+    return self._SortTests(all_tests)
 
   def _UploadTestArtifacts(self, device, test_artifacts_device_dir):
     # TODO(jbudorick): Reconcile this with the output manager once
@@ -836,8 +831,30 @@
 
   #override
   def _GetUniqueTestName(self, test):
+    if isinstance(test, list):
+      # Pick the last test which doesn't have PRE_ prefix.
+      test = test[-1]
     return gtest_test_instance.TestNameWithoutDisabledPrefix(test)
 
+  def _IsPreTestGroup(self, test_group):
+    """Check if a test group has one and only one PRE test group."""
+    # pylint: disable=no-self-use
+    test_set = set()
+    has_pre_test = False
+    for test in test_group:
+      if not has_pre_test and gtest_test_instance.IsPreTest(test):
+        has_pre_test = True
+      test_set.add(gtest_test_instance.TestNameWithoutPrefixes(test))
+    return has_pre_test and len(test_set) == 1
+
+  #override
+  def _ShouldRetryFullGroup(self, test_group):
+    """A group in gtest shall be a PRE test group and retry in full."""
+    # Ensure the given test group is a PRE test group
+    assert self._IsPreTestGroup(test_group), (
+        f'Expecting a PRE test group, got {test_group}')
+    return True
+
   #override
   def _RunTest(self, device, test):
     # Run the test.
diff --git a/build/android/pylib/local/device/local_device_gtest_run_test.py b/build/android/pylib/local/device/local_device_gtest_run_test.py
index bc78184..5d514993 100755
--- a/build/android/pylib/local/device/local_device_gtest_run_test.py
+++ b/build/android/pylib/local/device/local_device_gtest_run_test.py
@@ -7,6 +7,7 @@
 # pylint: disable=protected-access
 
 
+import random
 import os
 import unittest
 
@@ -14,13 +15,12 @@
 sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
     '../../..')))
 
+from pylib.base import base_test_result
 from pylib.gtest import gtest_test_instance
 from pylib.local.device import local_device_environment
 from pylib.local.device import local_device_gtest_run
-from pylib.local.device import local_device_test_run
 
 import mock  # pylint: disable=import-error
-from unittest.mock import MagicMock
 
 
 def isSliceInList(s, l):
@@ -62,193 +62,138 @@
     path = local_device_gtest_run._GetLLVMProfilePath('test_dir', 'sr71', '5')
     self.assertEqual(path, os.path.join('test_dir', 'sr71_5_%2m%c.profraw'))
 
-  def testGroupTests(self):
-    test = [
-        'TestClass1.testcase1',
-        'TestClass1.otherTestCase',
-        'TestClass1.PRE_testcase1',
-        'TestClass1.abc_testcase2',
-        'TestClass1.PRE_PRE_testcase1',
-        'TestClass1.PRE_abc_testcase2',
-        'TestClass1.PRE_PRE_abc_testcase2',
+  def testGroupPreTests(self):
+    pre_test_group1 = [
+        'TestSuite1.PRE_PRE_TestName',
+        'TestSuite1.PRE_TestName',
+        'TestSuite1.TestName',
     ]
-    expectedTestcase1 = [
-        'TestClass1.PRE_PRE_testcase1',
-        'TestClass1.PRE_testcase1',
-        'TestClass1.testcase1',
+    # pre test group with "abc_" added to pre_test_group1
+    pre_test_group2 = [
+        'TestSuite1.PRE_PRE_abc_TestName',
+        'TestSuite1.PRE_abc_TestName',
+        'TestSuite1.abc_TestName',
     ]
-    expectedTestcase2 = [
-        'TestClass1.PRE_PRE_abc_testcase2',
-        'TestClass1.PRE_abc_testcase2',
-        'TestClass1.abc_testcase2',
+    # pre test group with disabled prefixes in test name
+    pre_test_group3 = [
+        'TestSuite2.DISABLED_PRE_PRE_TestName',
+        'TestSuite2.DISABLED_PRE_TestName',
+        'TestSuite2.DISABLED_TestName',
     ]
-    expectedOtherTestcase = [
-        'TestClass1.otherTestCase',
+    # pre test group with disabled prefixes in test suite
+    pre_test_group4 = [
+        'DISABLED_TestSuite3.PRE_PRE_TestName',
+        'DISABLED_TestSuite3.PRE_TestName',
+        'DISABLED_TestSuite3.TestName',
     ]
-    actualTestCase = self._obj._GroupTests(test)
-    self.assertTrue(isSliceInList(expectedTestcase1, actualTestCase))
-    self.assertTrue(isSliceInList(expectedTestcase2, actualTestCase))
-    self.assertTrue(isSliceInList(expectedOtherTestcase, actualTestCase))
 
-  def testAppendPreTests(self):
-    failed_tests = [
-        'TestClass1.PRE_PRE_testcase1',
-        'TestClass1.abc_testcase2',
-        'TestClass1.PRE_def_testcase3',
-        'TestClass1.otherTestCase',
-    ]
     tests = [
-        'TestClass1.testcase1',
-        'TestClass1.otherTestCase',
-        'TestClass1.def_testcase3',
-        'TestClass1.PRE_testcase1',
-        'TestClass1.abc_testcase2',
-        'TestClass1.PRE_PRE_testcase1',
-        'TestClass1.PRE_abc_testcase2',
-        'TestClass1.PRE_def_testcase3',
-        'TestClass1.PRE_PRE_abc_testcase2',
-    ]
-    expectedTestcase1 = [
-        'TestClass1.PRE_PRE_testcase1',
-        'TestClass1.PRE_testcase1',
-        'TestClass1.testcase1',
-    ]
-    expectedTestcase2 = [
-        'TestClass1.PRE_PRE_abc_testcase2',
-        'TestClass1.PRE_abc_testcase2',
-        'TestClass1.abc_testcase2',
-    ]
-    expectedTestcase3 = [
-        'TestClass1.PRE_def_testcase3',
-        'TestClass1.def_testcase3',
-    ]
-    expectedOtherTestcase = [
-        'TestClass1.otherTestCase',
-    ]
-    actualTestCase = self._obj._AppendPreTestsForRetry(failed_tests, tests)
-    self.assertTrue(isSliceInList(expectedTestcase1, actualTestCase))
-    self.assertTrue(isSliceInList(expectedTestcase2, actualTestCase))
-    self.assertTrue(isSliceInList(expectedTestcase3, actualTestCase))
-    self.assertTrue(isSliceInList(expectedOtherTestcase, actualTestCase))
+        'TestSuite1.NormalTestName1',
+        'TestSuite1.NormalTestName2',
+    ] + pre_test_group1 + pre_test_group2 + pre_test_group3 + pre_test_group4
+    # shuffle the tests to test if each pre test group is still in order.
+    random.shuffle(tests)
+    pre_tests, _ = local_device_gtest_run._GroupPreTests(tests)
+    self.assertIn(pre_test_group1, pre_tests)
+    self.assertIn(pre_test_group2, pre_tests)
+    self.assertIn(pre_test_group3, pre_tests)
+    self.assertIn(pre_test_group4, pre_tests)
 
-
-class LocalDeviceGtestTestRunShardingTest(unittest.TestCase):
-  def setUp(self):
-    self._env = mock.MagicMock(
-        spec=local_device_environment.LocalDeviceEnvironment)
-    self._test_instance = mock.MagicMock(
-        spec=gtest_test_instance.GtestTestInstance)
-    self._obj = local_device_gtest_run.LocalDeviceGtestRun(
-        self._env, self._test_instance)
-
-    self.test_list = [
-        'TestClass1.testcase1',
-        'TestClass2.testcase1',
-        'TestClass1.def_testcase3',
-        'TestClass1.abc_testcase2',
-        'TestClass3.testcase1'
+  def test_ApplyExternalSharding(self):
+    tests = [
+        'TestSuite1.TestName1',
+        'TestSuite1.TestName2',
+        'TestSuite1.PRE_TestName3',
+        'TestSuite1.TestName3',
+        'TestSuite2.PRE_TestName1',
+        'TestSuite2.TestName1',
+        'TestSuite2.TestName2',
     ]
-
-    # Mock these methods called in RunTests
-    self._env.ResetCurrentTry = MagicMock(side_effect = self.reset_try)
-    self._env.IncrementCurrentTry = MagicMock(side_effect = self.increment_try)
-    self._obj._GetTests = MagicMock(return_value=self.test_list)
-
-  def reset_try(self):
-    self._env.current_try = 0
-
-  def increment_try(self):
-    self._env.current_try += 1
+    expected_shard0 = [
+        'TestSuite1.TestName2',
+        'TestSuite1.TestName1',
+    ]
+    expected_shard1 = [
+        ['TestSuite1.PRE_TestName3', 'TestSuite1.TestName3'],
+        ['TestSuite2.PRE_TestName1', 'TestSuite2.TestName1'],
+        'TestSuite2.TestName2',
+    ]
+    # Shuffle the tests two times to check if the output is deterministic.
+    random.shuffle(tests)
+    self.assertListEqual(self._obj._ApplyExternalSharding(tests, 0, 2),
+                         expected_shard0)
+    self.assertListEqual(self._obj._ApplyExternalSharding(tests, 1, 2),
+                         expected_shard1)
+    random.shuffle(tests)
+    self.assertListEqual(self._obj._ApplyExternalSharding(tests, 0, 2),
+                         expected_shard0)
+    self.assertListEqual(self._obj._ApplyExternalSharding(tests, 1, 2),
+                         expected_shard1)
 
   def test_CreateShardsForDevices(self):
     self._obj._env.devices = [1]
     self._obj._test_instance.test_launcher_batch_limit = 2
-    expected_shards = [
-        ['TestClass1.testcase1', 'TestClass2.testcase1'],
-        ['TestClass1.def_testcase3', 'TestClass1.abc_testcase2'],
-        ['TestClass3.testcase1']
-    ]
-    actual_shards = self._obj._CreateShardsForDevices(self.test_list)
-    self.assertEqual(expected_shards, actual_shards)
-
-  def test_ApplyExternalSharding_1_shard(self):
+    self._obj._crashes = set(
+        ['TestSuite1.CrashedTest1', 'TestSuite1.CrashedTest2'])
     tests = [
-        'TestClass1.testcase1', 'TestClass1.testcase2', 'TestClass2.testcase1',
-        'TestClass3.testcase1'
+        ['TestSuite1.PRE_TestName1', 'TestSuite1.TestName1'],
+        'TestSuite1.TestName2',
+        'TestSuite1.TestName3',
+        'TestSuite1.CrashedTest1',
+        'TestSuite2.TestName1',
+        ['TestSuite1.PRE_CrashedTest2', 'TestSuite1.CrashedTest2'],
+        'TestSuite2.TestName2',
+        'TestSuite2.TestName3',
     ]
-    expected_tests = [
-        'TestClass1.testcase2', 'TestClass1.testcase1', 'TestClass3.testcase1',
-        'TestClass2.testcase1'
-    ]
-    actual_tests = self._obj._ApplyExternalSharding(
-        tests, 0, 1)
-    self.assertEqual(expected_tests, actual_tests)
-
-  def test_ApplyExternalSharding_2_shards(self):
-    tests = [
-        'TestClass1.testcase1', 'TestClass1.testcase2', 'TestClass2.testcase1',
-        'TestClass3.testcase1'
-    ]
-    expected_shard0 = ['TestClass1.testcase2', 'TestClass1.testcase1']
-    expected_shard1 = ['TestClass3.testcase1', 'TestClass2.testcase1']
-    actual_shard0 = self._obj._ApplyExternalSharding(
-        tests, 0, 2)
-    actual_shard1 = self._obj._ApplyExternalSharding(
-        tests, 1, 2)
-    self.assertEqual(expected_shard0, expected_shard0)
-    self.assertEqual(expected_shard1, expected_shard1)
-    self.assertSetEqual(set(actual_shard0 + actual_shard1), set(tests))
-
-  def test_deterministic_sharding_grouped_tests(self):
-    self._test_instance.external_shard_index = 0
-    self._test_instance.total_external_shards = 1
-    self._test_instance.test_launcher_batch_limit = 2
-    self._env.devices = [1]
-    self._env.recover_devices = False
-    # 1 try and just the last try of mock_RunTestsOnDevice call is asserted
-    self._env.max_tries = 1
-
     expected_shards = [
-        ['TestClass1.def_testcase3', 'TestClass1.abc_testcase2'],
-        ['TestClass1.testcase1', 'TestClass3.testcase1'],
-        ['TestClass2.testcase1']
+        ['TestSuite1.PRE_TestName1', 'TestSuite1.TestName1'],
+        ['TestSuite1.TestName2', 'TestSuite1.TestName3'],
+        ['TestSuite1.CrashedTest1'],
+        ['TestSuite1.PRE_CrashedTest2', 'TestSuite1.CrashedTest2'],
+        ['TestSuite2.TestName1', 'TestSuite2.TestName2'],
+        ['TestSuite2.TestName3'],
     ]
+    actual_shards = self._obj._CreateShardsForDevices(tests)
+    self.assertListEqual(actual_shards, expected_shards)
 
-    # Mock pMap to call the provided function
-    def mock_pMap(func, *args):
-      for _ in self._env.devices:
-        func(*args)
-      return MagicMock()
+  def test_IsPreTestGroup(self):
+    self.assertTrue(self._obj._IsPreTestGroup([
+        'TestSuite1.TestName1',
+        'TestSuite1.PRE_TestName1',
+        'TestSuite1.PRE_PRE_TestName1',
+    ]))
+    self.assertFalse(self._obj._IsPreTestGroup([
+        'TestSuite1.TestName2',
+    ]))
+    self.assertFalse(self._obj._IsPreTestGroup([
+        'TestSuite1.TestName1',
+        'TestSuite1.PRE_TestName1',
+        'TestSuite1.TestName2',
+    ]))
 
-    # Process test collection arg of last mock_RunTestsOnDevice call
-    def get_actual_shards():
-      actual_shards = []
-      # mock_RunTestsOnDevice must be called for call_args to not be None
-      mock_RunTestsOnDevice.assert_called()
-      tc = mock_RunTestsOnDevice.call_args.args[0]
-      for group in tc:
-        actual_shards.append(group)
-        tc.test_completed()
-      return actual_shards
-
-    mock_RunTestsOnDevice = MagicMock(
-        autospec='local_device_test_run.LocalDeviceTestRun._RunTestsOnDevice')
-    # Monkey patch needed to call mock. Decorator patch calls original method
-    # instead of mock possibly due to decorator or pMap on original method.
-    (local_device_test_run.LocalDeviceTestRun
-        ._RunTestsOnDevice) = mock_RunTestsOnDevice
-
-    with mock.patch.object(
-        self._env.parallel_devices, "pMap", side_effect=mock_pMap
-    ):
-      self._obj.RunTests(results=[])
-      actual_shards = get_actual_shards()
-      self.assertEqual(actual_shards, expected_shards)
-
-      # Check "Retry shards with patch" has deterministic test ordering
-      self._obj.RunTests(results=[])
-      actual_shards = get_actual_shards()
-      self.assertEqual(actual_shards, expected_shards)
+  def test_GetTestsToRetry(self):
+    test_data = [
+        ('TestSuite1.TestName1', base_test_result.ResultType.PASS),
+        ('TestSuite1.TestName2', base_test_result.ResultType.FAIL),
+        ('TestSuite1.PRE_TestName3', base_test_result.ResultType.PASS),
+        ('TestSuite1.TestName3', base_test_result.ResultType.FAIL),
+    ]
+    all_tests = [
+        'TestSuite1.TestName1',
+        'TestSuite1.TestName2',
+        ['TestSuite1.PRE_TestName3', 'TestSuite1.TestName3'],
+    ]
+    try_results = base_test_result.TestRunResults()
+    for test, test_result in test_data:
+      try_results.AddResult(
+          base_test_result.BaseTestResult(self._obj._GetUniqueTestName(test),
+                                          test_result))
+    actual_retry = self._obj._GetTestsToRetry(all_tests, try_results)
+    expected_retry = [
+        'TestSuite1.TestName2',
+        ['TestSuite1.PRE_TestName3', 'TestSuite1.TestName3'],
+    ]
+    self.assertListEqual(actual_retry, expected_retry)
 
 
 if __name__ == '__main__':
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index c9f89a80..0e4f84d 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -820,12 +820,12 @@
     """Create shards of tests to run on devices.
 
     Args:
-      tests: List containing tests or test batches.
+      tests: List containing tests or test groups.
 
     Returns:
-      List of tests or batches.
+      List of tests or groups.
     """
-    # Each test or test batch will be a single shard.
+    # Each test or test group will be a single shard.
     return tests
 
   def _GetTestsFromPickle(self, pickle_extras):
@@ -897,8 +897,6 @@
     batched_tests_split = self._SplitBatchesAboveMaxSize(batched_tests)
     all_tests = batched_tests_split + other_tests
 
-    # Sort all tests by hash.
-    # TODO(crbug.com/40200835): Add sorting logic back to _PartitionTests.
     return self._SortTests(all_tests)
 
   def _GroupTestsIntoBatchesAndOthers(self, tests):
@@ -958,17 +956,9 @@
     return batched_tests_split
 
   #override
-  def _GroupTestsAfterSharding(self, tests):
-    # pylint: disable=no-self-use
-    batched_tests, other_tests = self._GroupTestsIntoBatchesAndOthers(tests)
-    all_tests = list(batched_tests.values()) + other_tests
-
-    # Sort all tests by hash.
-    # TODO(crbug.com/40200835): Add sorting logic back to _PartitionTests.
-    return self._SortTests(all_tests)
-
-  #override
   def _GetUniqueTestName(self, test):
+    if isinstance(test, list):
+      test = test[-1]
     return instrumentation_test_instance.GetUniqueTestName(test)
 
   #override
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run_test.py b/build/android/pylib/local/device/local_device_instrumentation_test_run_test.py
index 5f5e746..ce1b68aa 100755
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run_test.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run_test.py
@@ -10,6 +10,7 @@
 
 import unittest
 
+import random
 import os
 import sys
 sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
@@ -19,10 +20,6 @@
 from pylib.base import mock_environment
 from pylib.base import mock_test_instance
 from pylib.local.device import local_device_instrumentation_test_run
-from pylib.local.device import local_device_test_run
-
-import mock  # pylint: disable=import-error
-from unittest.mock import MagicMock
 
 
 class LocalDeviceInstrumentationTestRunTest(unittest.TestCase):
@@ -164,166 +161,73 @@
     with self.assertRaises(ValueError):
       local_device_instrumentation_test_run._ReplaceUncommonChars(original)
 
-
-class LocalDeviceInstrumentationTestRunShardingTest(unittest.TestCase):
-  def setUp(self):
-    super().setUp()
-    self._env = mock_environment.MockEnvironment()
-    self._ti = mock_test_instance.MockTestInstance()
-    self._obj = (
-        local_device_instrumentation_test_run.LocalDeviceInstrumentationTestRun(
-            self._env, self._ti))
-
-    # Create test data
-    self.test1_batch1 = create_test({'Batch': {'value': 'batch1'}},
-        'com.example.TestA', 'test1')
-    self.test2 = create_test(
+  def test_ApplyExternalSharding(self):
+    test1_batch1 = create_test(
+        {'Batch': {'value': 'batch1'}}, 'com.example.TestA', 'test1')
+    test2 = create_test(
         {'Features$EnableFeatures': {'value': 'defg'}},
         'com.example.TestB', 'test2')
-    self.test3 = create_test({}, 'com.example.TestC', 'test3')
-    self.test3_multiprocess = create_test({}, 'com.example.TestC',
-        'test3__multiprocess_mode')
-    self.test4_batch1 = create_test({'Batch': {'value': 'batch1'}},
-        'com.example.TestD', 'test4')
-    self.test5_batch1 = create_test({'Batch': {'value': 'batch1'}},
-        'com.example.TestE', 'test5')
-    self.test6 = create_test({}, 'com.example.TestF', 'test6')
-    self.test7 = create_test({}, 'com.example.TestG', 'test7')
-    self.test8 = create_test({}, 'com.example.TestH', 'test8')
+    test3 = create_test({}, 'com.example.TestC', 'test3')
+    test3_multiprocess = create_test(
+        {}, 'com.example.TestC', 'test3__multiprocess_mode')
+    test4_batch1 = create_test(
+        {'Batch': {'value': 'batch1'}}, 'com.example.TestD', 'test4')
+    test5_batch1 = create_test(
+        {'Batch': {'value': 'batch1'}}, 'com.example.TestE', 'test5')
+    test6 = create_test({}, 'com.example.TestF', 'test6')
+    test7 = create_test({}, 'com.example.TestG', 'test7')
+    test8 = create_test({}, 'com.example.TestH', 'test8')
 
-    self.test_list = [
-        self.test1_batch1, self.test2, self.test3, self.test3_multiprocess,
-        self.test4_batch1, self.test5_batch1, self.test6, self.test7,
-        self.test8
+    tests = [
+        test1_batch1, test2, test3, test3_multiprocess, test4_batch1,
+        test5_batch1, test6, test7, test8
     ]
-
-    # Mock these methods called in RunTests
-    self._env.ResetCurrentTry = MagicMock(side_effect = self.reset_try)
-    self._env.IncrementCurrentTry = MagicMock(side_effect = self.increment_try)
-    self._obj._GetTests = MagicMock(return_value=self.test_list)
-
-  def reset_try(self):
-    self._env.current_try = 0
-
-  def increment_try(self):
-    self._env.current_try += 1
-
-  def test_GroupTestsIntoBatchesAndOthers(self):
-    expected_batched_tests = {
-        'batch1': [self.test1_batch1, self.test4_batch1, self.test5_batch1]
-    }
-    expected_other_tests = [
-        self.test2, self.test3, self.test3_multiprocess, self.test6,
-        self.test7, self.test8
-    ]
-    batched_tests, other_tests = self._obj._GroupTestsIntoBatchesAndOthers(
-        self.test_list)
-    self.assertEqual(batched_tests, expected_batched_tests)
-    self.assertEqual(other_tests, expected_other_tests)
-
-  @mock.patch(('pylib.local.device.local_device_instrumentation_test_run.'
-      '_NON_UNIT_TEST_MAX_GROUP_SIZE'), 2)
-  def test_GroupTests(self):
-    expected_tests = [
-        [self.test1_batch1, self.test4_batch1],
-        self.test3_multiprocess, self.test8, self.test2,
-        self.test7, self.test6,
-        [self.test5_batch1],
-        self.test3
-    ]
-    actual_tests = self._obj._GroupTests(self.test_list)
-    self.assertEqual(actual_tests, expected_tests)
-
-  def test_GroupTestsAfterSharding(self):
-    expected_tests = [
-        [self.test1_batch1, self.test4_batch1, self.test5_batch1],
-        self.test3_multiprocess, self.test8, self.test2,
-        self.test7, self.test6, self.test3
-    ]
-    actual_tests = self._obj._GroupTestsAfterSharding(self.test_list)
-    self.assertEqual(actual_tests, expected_tests)
-
-  def test_ApplyExternalSharding_1_shard(self):
-      expected_tests = [
-          self.test1_batch1, self.test4_batch1, self.test5_batch1,
-          self.test3_multiprocess, self.test8, self.test2, self.test7,
-          self.test6, self.test3
-      ]
-      actual_tests = self._obj._ApplyExternalSharding(
-          self.test_list, 0, 1)
-      self.assertEqual(actual_tests, expected_tests)
-
-  def test_ApplyExternalSharding_2_shards(self):
-    expected_shard0 = [
-        self.test1_batch1, self.test4_batch1, self.test5_batch1,
-        self.test3_multiprocess
-    ]
+    expected_shard0 = [test8]
     expected_shard1 = [
-        self.test8, self.test2, self.test7, self.test6, self.test3
+        [test1_batch1, test4_batch1, test5_batch1],
+        test3_multiprocess,
+        test7,
+        test3,
+        test6,
+        test2,
     ]
-    expected_union = str(sorted(self.test_list, key=lambda d: d['method']))
+    # Shuffle the tests two times to check if the output is deterministic.
+    random.shuffle(tests)
+    self.assertListEqual(self._obj._ApplyExternalSharding(tests, 0, 2),
+                         expected_shard0)
+    self.assertListEqual(self._obj._ApplyExternalSharding(tests, 1, 2),
+                         expected_shard1)
+    random.shuffle(tests)
+    self.assertListEqual(self._obj._ApplyExternalSharding(tests, 0, 2),
+                         expected_shard0)
+    self.assertListEqual(self._obj._ApplyExternalSharding(tests, 1, 2),
+                         expected_shard1)
 
-    actual_shard0 = self._obj._ApplyExternalSharding(
-        self.test_list, 0, 2)
-    actual_shard1 = self._obj._ApplyExternalSharding(
-        self.test_list, 1, 2)
-    # Serialize the sorted list of test dicts for comparison
-    actual_union = str(sorted(actual_shard0 + actual_shard1,
-        key=lambda d: d['method']))
-    self.assertEqual(actual_shard0, expected_shard0)
-    self.assertEqual(actual_shard1, expected_shard1)
-    self.assertEqual(actual_union, expected_union)
-
-  def test_CreateShardsForDevices(self):
-    actual_tests = self._obj._CreateShardsForDevices(self.test_list)
-    self.assertEqual(actual_tests, self.test_list)
-
-  def test_deterministic_sharding_grouped_tests(self):
-    self._env.devices = [1]
-    # 1 try and just the last try of mock_RunTestsOnDevice call is asserted
-    self._env.max_tries = 1
-
-    expected_shards = [
-        [self.test1_batch1, self.test4_batch1, self.test5_batch1],
-        self.test3_multiprocess, self.test8, self.test2,
-        self.test7, self.test6, self.test3
+  def test_GetTestsToRetry(self):
+    test1_batch1 = create_test(
+        {'Batch': {'value': 'batch1'}}, 'com.example.TestA', 'test1')
+    test2_batch1 = create_test(
+        {'Batch': {'value': 'batch1'}}, 'com.example.TestB', 'test2')
+    test3 = create_test({}, 'com.example.TestC', 'test3')
+    test4 = create_test({}, 'com.example.TestD', 'test4')
+    test_data = [
+        (test1_batch1, base_test_result.ResultType.PASS),
+        (test2_batch1, base_test_result.ResultType.FAIL),
+        (test3, base_test_result.ResultType.PASS),
+        (test4, base_test_result.ResultType.FAIL),
     ]
-
-    # Mock pMap to call the provided function
-    def mock_pMap(func, *args):
-      for _ in self._env.devices:
-        func(*args)
-      return MagicMock()
-
-    # Store test collection arg as actual_shards
-    def get_actual_shards():
-      actual_shards = []
-      # mock_RunTestsOnDevice must be called for call_args to not be None
-      mock_RunTestsOnDevice.assert_called()
-      tc = mock_RunTestsOnDevice.call_args.args[0]
-      for group in tc:
-        actual_shards.append(group)
-        tc.test_completed()
-      return actual_shards
-
-    mock_RunTestsOnDevice = MagicMock(
-        autospec='local_device_test_run.LocalDeviceTestRun._RunTestsOnDevice')
-    # Monkey patch needed to call mock. Decorator patch calls original method
-    # instead of mock possibly due to decorator or pMap on original method.
-    (local_device_test_run.LocalDeviceTestRun
-        ._RunTestsOnDevice) = mock_RunTestsOnDevice
-
-    with mock.patch.object(
-        self._env.parallel_devices, "pMap", side_effect=mock_pMap
-    ):
-      self._obj.RunTests(results=[])
-      actual_shards = get_actual_shards()
-      self.assertEqual(actual_shards, expected_shards)
-
-      # Check "Retry shards with patch" has deterministic test ordering
-      self._obj.RunTests(results=[])
-      actual_shards = get_actual_shards()
-      self.assertEqual(actual_shards, expected_shards)
+    all_tests = [[test1_batch1, test2_batch1], test3, test4]
+    try_results = base_test_result.TestRunResults()
+    for test, test_result in test_data:
+      try_results.AddResult(
+          base_test_result.BaseTestResult(self._obj._GetUniqueTestName(test),
+                                          test_result))
+    actual_retry = self._obj._GetTestsToRetry(all_tests, try_results)
+    expected_retry = [
+        [test2_batch1],
+        test4,
+    ]
+    self.assertListEqual(actual_retry, expected_retry)
 
 
 def create_test(annotation_dict, class_name, method_name):
diff --git a/build/android/pylib/local/device/local_device_test_run.py b/build/android/pylib/local/device/local_device_test_run.py
index a7350c9..3ecdbcb 100644
--- a/build/android/pylib/local/device/local_device_test_run.py
+++ b/build/android/pylib/local/device/local_device_test_run.py
@@ -142,7 +142,7 @@
   #override
   def RunTests(self, results, raw_logs_fh=None):
     tests = self._GetTests()
-    total_test_count = len(tests)
+    total_test_count = len(FlattenTestList(tests))
 
     exit_now = threading.Event()
 
@@ -156,12 +156,11 @@
         self._env.ResetCurrentTry()
         while self._env.current_try < self._env.max_tries and tests:
           tries = self._env.current_try
-          tests = self._SortTests(tests)
-          grouped_tests = self._GroupTestsAfterSharding(tests)
+          flatten_tests = FlattenTestList(tests)
           logging.info('STARTING TRY #%d/%d', tries + 1, self._env.max_tries)
           if tries > 0 and self._env.recover_devices:
             # The variable "tests" is reused to store the failed tests.
-            failed_test_pct = 100 * len(tests) // total_test_count
+            failed_test_pct = 100 * len(flatten_tests) // total_test_count
             if failed_test_pct > FAILED_TEST_PCT_MAX:
               logging.info(
                   'Attempting to recover devices as the percentage of failed '
@@ -174,13 +173,13 @@
               self._RecoverDevices()
           logging.info(
               'Will run %d tests, grouped into %d groups, on %d devices: %s',
-              len(tests), len(grouped_tests), len(self._env.devices),
+              len(flatten_tests), len(tests), len(self._env.devices),
               ', '.join(str(d) for d in self._env.devices))
           for t in tests:
             logging.debug('  %s', t)
 
           try_results = base_test_result.TestRunResults()
-          test_names = (self._GetUniqueTestName(t) for t in tests)
+          test_names = (self._GetUniqueTestName(t) for t in flatten_tests)
           try_results.AddResults(
               base_test_result.BaseTestResult(
                   t, base_test_result.ResultType.NOTRUN)
@@ -194,15 +193,13 @@
           try:
             if self._ShouldShardTestsForDevices():
               tc = test_collection.TestCollection(
-                  self._CreateShardsForDevices(grouped_tests))
+                  self._CreateShardsForDevices(tests))
               self._env.parallel_devices.pMap(
                   self._RunTestsOnDevice,
                   tc, try_results, exit_now).pGet(None)
             else:
-              self._env.parallel_devices.pMap(self._RunTestsOnDevice,
-                                              grouped_tests,
-                                              try_results,
-                                              exit_now).pGet(None)
+              self._env.parallel_devices.pMap(self._RunTestsOnDevice, tests,
+                                              try_results, exit_now).pGet(None)
           except TestsTerminated:
             for unknown_result in try_results.GetUnknown():
               try_results.AddResult(
@@ -227,6 +224,12 @@
     self._env.parallel_devices.pMap(device_recovery.RecoverDevice, None)
 
   def _GetTestsToRetry(self, tests, try_results):
+    """Get the tests to retry.
+
+    Note "tests" can have groups of test. For gtest, we would like to add
+    the entire group to retry as they are PRE test group. For instrumentation,
+    we only keep the failed tests in that group.
+    """
 
     def is_failure_result(test_result):
       if isinstance(test_result, list):
@@ -237,27 +240,32 @@
               base_test_result.ResultType.PASS,
               base_test_result.ResultType.SKIP))
 
+    def get_tests_to_retry(test_list, all_test_results):
+      failed_tests = []
+      for test in test_list:
+        if isinstance(test, list):
+          failed_test = get_tests_to_retry(test, all_test_results)
+          if failed_test:
+            failed_tests.append(
+                test if self._ShouldRetryFullGroup(test) else failed_test)
+        else:
+          name = self._GetUniqueTestName(test)
+          # When specifying a test filter, names can contain trailing wildcards.
+          # See local_device_gtest_run._ExtractTestsFromFilters()
+          if name.endswith('*'):
+            result = [
+                r for n, r in all_test_results.items()
+                if fnmatch.fnmatch(n, name)
+            ]
+          else:
+            result = all_test_results.get(name)
+          if is_failure_result(result) and self._ShouldRetry(test, result):
+            failed_tests.append(test)
+      return failed_tests
+
     all_test_results = {r.GetName(): r for r in try_results.GetAll()}
+    return get_tests_to_retry(tests, all_test_results)
 
-    tests_and_names = ((t, self._GetUniqueTestName(t)) for t in tests)
-
-    tests_and_results = {}
-    for test, name in tests_and_names:
-      if name.endswith('*'):
-        tests_and_results[name] = (test, [
-            r for n, r in all_test_results.items() if fnmatch.fnmatch(n, name)
-        ])
-      else:
-        tests_and_results[name] = (test, all_test_results.get(name))
-
-    failed_tests_and_results = ((test, result)
-                                for test, result in tests_and_results.values()
-                                if is_failure_result(result))
-
-    failed_tests = [
-        t for t, r in failed_tests_and_results if self._ShouldRetry(t, r)
-    ]
-    return self._AppendPreTestsForRetry(failed_tests, tests)
 
   def _ApplyExternalSharding(self, tests, shard_index, total_shards):
     logging.info('Using external sharding settings. This is shard %d/%d',
@@ -268,103 +276,27 @@
 
     sharded_tests = []
 
-    # Sort tests by hash.
-    # TODO(crbug.com/40200835): Add sorting logic back to _PartitionTests.
-    tests = self._SortTests(tests)
-
-    # Group tests by tests that should run in the same test invocation - either
-    # unit tests or batched tests.
     grouped_tests = self._GroupTests(tests)
+    for test in grouped_tests:
+      test_name = self._GetUniqueTestName(test)
+      if self._DeterministicHash(test_name) % total_shards == shard_index:
+        sharded_tests.append(test)
 
-    # Partition grouped tests approximately evenly across shards.
-    partitioned_tests = self._PartitionTests(grouped_tests, total_shards,
-                                             float('inf'))
-    if len(partitioned_tests) <= shard_index:
-      return []
-    for t in partitioned_tests[shard_index]:
-      if isinstance(t, list):
-        sharded_tests.extend(t)
-      else:
-        sharded_tests.append(t)
     return sharded_tests
 
-  # Sort by hash so we don't put all tests in a slow suite in the same
-  # partition.
+  def _DeterministicHash(self, test_name):
+    """Return the deterministic hash for a test name, as an integer."""
+    # pylint: disable=no-self-use
+    assert isinstance(test_name, str), 'Expecting a string.'
+    hash_bytes = hashlib.sha256(test_name.encode('utf-8')).digest()
+    # To speed thing up, only take the last 3 bytes
+    return int.from_bytes(hash_bytes[-3:])
+
+  # Sort by hash so we don't put all tests in a slow suite in the same shard.
   def _SortTests(self, tests):
-    return sorted(tests,
-                  key=lambda t: hashlib.sha256(
-                      self._GetUniqueTestName(t[0] if isinstance(t, list) else t
-                                              ).encode()).hexdigest())
-
-  # Partition tests evenly into |num_desired_partitions| partitions where
-  # possible. However, many constraints make partitioning perfectly impossible.
-  # If the max_partition_size isn't large enough, extra partitions may be
-  # created (infinite max size should always return precisely the desired
-  # number of partitions). Even if the |max_partition_size| is technically large
-  # enough to hold all of the tests in |num_desired_partitions|, we attempt to
-  # keep test order relatively stable to minimize flakes, so when tests are
-  # grouped (eg. batched tests), we cannot perfectly fill all paritions as that
-  # would require breaking up groups.
-  def _PartitionTests(self, tests, num_desired_partitions, max_partition_size):
-    # pylint: disable=no-self-use
-    partitions = []
-
-
-    num_not_yet_allocated = sum(
-        [len(test) - 1 for test in tests if self._CountTestsIndividually(test)])
-    num_not_yet_allocated += len(tests)
-
-    # Fast linear partition approximation capped by max_partition_size. We
-    # cannot round-robin or otherwise re-order tests dynamically because we want
-    # test order to remain stable.
-    partition_size = min(num_not_yet_allocated // num_desired_partitions,
-                         max_partition_size)
-    partitions.append([])
-    last_partition_size = 0
-    for test in tests:
-      test_count = len(test) if self._CountTestsIndividually(test) else 1
-      # Make a new shard whenever we would overfill the previous one. However,
-      # if the size of the test group is larger than the max partition size on
-      # its own, just put the group in its own shard instead of splitting up the
-      # group.
-      # TODO(crbug.com/40200835): Add logic to support PRE_ test recognition but
-      # it may hurt performance in most scenarios. Currently all PRE_ tests are
-      # partitioned into the last shard. Unless the number of PRE_ tests are
-      # larger than the partition size, the PRE_ test may get assigned into a
-      # different shard and cause test failure.
-      if (last_partition_size + test_count > partition_size
-          and last_partition_size > 0):
-        num_desired_partitions -= 1
-        if num_desired_partitions <= 0:
-          # Too many tests for number of partitions, just fill all partitions
-          # beyond num_desired_partitions.
-          partition_size = max_partition_size
-        else:
-          # Re-balance remaining partitions.
-          partition_size = min(num_not_yet_allocated // num_desired_partitions,
-                               max_partition_size)
-        partitions.append([])
-        partitions[-1].append(test)
-        last_partition_size = test_count
-      else:
-        partitions[-1].append(test)
-        last_partition_size += test_count
-
-      num_not_yet_allocated -= test_count
-
-    if not partitions[-1]:
-      partitions.pop()
-    return partitions
-
-  def _CountTestsIndividually(self, test):
-    # pylint: disable=no-self-use
-    if not isinstance(test, list):
-      return False
-    annotations = test[0]['annotations']
-    # UnitTests tests are really fast, so to balance shards better, count
-    # UnitTests Batches as single tests.
-    return ('Batch' not in annotations
-            or annotations['Batch']['value'] != 'UnitTests')
+    return sorted(
+        tests,
+        key=lambda t: self._DeterministicHash(self._GetUniqueTestName(t)))
 
   def _CreateShardsForDevices(self, tests):
     raise NotImplementedError
@@ -377,6 +309,11 @@
     # pylint: disable=no-self-use,unused-argument
     return True
 
+  def _ShouldRetryFullGroup(self, test_group):
+    """Whether should retry the full group if the group has test failure."""
+    # pylint: disable=no-self-use,unused-argument
+    return False
+
   #override
   def GetTestsForListing(self):
     ret = self._GetTests()
@@ -395,20 +332,28 @@
     return sorted(f'{d} <- {os.path.relpath(h)}' for h, d in host_device_tuples)
 
   def _GetTests(self):
+    """Get the tests to run on the assigned shard index.
+
+    Shall be implemented by the subclasses.
+    """
     raise NotImplementedError
 
   def _GroupTests(self, tests):
+    """Group tests by tests that should run in the same test invocation.
+
+    Can be override by subclasses if needed. Examples are:
+      - gtest: PRE_ tests
+      - instrumentation test: unit tests and batched tests.
+
+    Args:
+      tests: a flatten list of tests. The element can be a string for gtest,
+        or a dict for instrumentation tests.
+
+    Return a list whose element can be a test, or a list of tests.
+    """
     # pylint: disable=no-self-use
     return tests
 
-  def _GroupTestsAfterSharding(self, tests):
-    # pylint: disable=no-self-use
-    return tests
-
-  def _AppendPreTestsForRetry(self, failed_tests, tests):
-    # pylint: disable=no-self-use,unused-argument
-    return failed_tests
-
   def _RunTest(self, device, test):
     raise NotImplementedError
 
diff --git a/build/android/pylib/local/device/local_device_test_run_test.py b/build/android/pylib/local/device/local_device_test_run_test.py
index 086d05f..0db4dc83 100755
--- a/build/android/pylib/local/device/local_device_test_run_test.py
+++ b/build/android/pylib/local/device/local_device_test_run_test.py
@@ -67,8 +67,10 @@
 
   def testSortTests(self):
     test_run = TestLocalDeviceTestRun()
+    # Note that the expected result purely depends on the implementation of
+    # the function "_DeterministicHash" and "_GetUniqueTestName".
     self.assertEqual(test_run._SortTests(['a', 'b', 'c', 'd', 'e', 'f', 'g']),
-                     ['d', 'f', 'c', 'b', 'e', 'a', 'g'])
+                     ['e', 'c', 'g', 'b', 'f', 'a', 'd'])
 
   def testGetTestsToRetry_allTestsPassed(self):
     results = [
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index fa3aec4e..80a4585d 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -49,8 +49,6 @@
 using VizBreakdown = CompositorFrameReporter::VizBreakdown;
 using FrameFinalState = FrameInfo::FrameFinalState;
 
-constexpr int kFrameReportTypeCount =
-    static_cast<int>(FrameReportType::kMaxValue) + 1;
 constexpr int kStageTypeCount = static_cast<int>(StageType::kStageTypeCount);
 constexpr int kAllBreakdownCount =
     static_cast<int>(VizBreakdown::kBreakdownCount) +
@@ -71,25 +69,12 @@
 // also means too much memory usage.
 constexpr size_t kMaxOwnedPartialUpdateDependents = 300u;
 
-// Names for CompositorFrameReporter::FrameReportType, which should be
-// updated in case of changes to the enum.
-constexpr auto kReportTypeNames = std::to_array<const char*>({
-    "",
-    "MissedDeadlineFrame.",
-    "DroppedFrame.",
-    "CompositorOnlyFrame.",
-});
-
-static_assert(std::size(kReportTypeNames) == kFrameReportTypeCount,
-              "Compositor latency report types has changed.");
-
 // This value should be recalculated in case of changes to the number of values
 // in CompositorFrameReporter::DroppedFrameReportType or in
 // CompositorFrameReporter::StageType.
 constexpr int kStagesWithBreakdownCount = kStageTypeCount + kAllBreakdownCount;
 constexpr int kMaxCompositorLatencyHistogramIndex =
-    kFrameReportTypeCount *
-    (kFrameSequenceTrackerTypeCount + kStagesWithBreakdownCount);
+    kFrameSequenceTrackerTypeCount * kStagesWithBreakdownCount;
 
 constexpr base::TimeDelta kCompositorLatencyHistogramMin =
     base::Microseconds(1);
@@ -140,7 +125,6 @@
 constexpr double kEpsilon = 0.001;
 
 std::string GetCompositorLatencyHistogramName(
-    FrameReportType report_type,
     FrameSequenceTrackerType frame_sequence_tracker_type,
     StageType stage_type,
     std::optional<VizBreakdown> viz_breakdown,
@@ -150,12 +134,10 @@
       FrameSequenceTracker::GetFrameSequenceTrackerTypeName(
           frame_sequence_tracker_type);
   DCHECK(tracker_type_name);
-  bool impl_only_frame = report_type == FrameReportType::kCompositorOnlyFrame;
-  return base::StrCat(
-      {"CompositorLatency.", kReportTypeNames[static_cast<int>(report_type)],
-       tracker_type_name, *tracker_type_name ? "." : "",
-       CompositorFrameReporter::GetStageName(
-           stage_type, viz_breakdown, blink_breakdown, impl_only_frame)});
+  return base::StrCat({"CompositorLatency2.", tracker_type_name,
+                       *tracker_type_name ? "." : "",
+                       CompositorFrameReporter::GetStageName(
+                           stage_type, viz_breakdown, blink_breakdown)});
 }
 
 // Helper function to record UMA histogram for an EventLatency metric. There
@@ -634,8 +616,7 @@
 const char* CompositorFrameReporter::GetStageName(
     StageType stage_type,
     std::optional<VizBreakdown> viz_breakdown,
-    std::optional<BlinkBreakdown> blink_breakdown,
-    bool impl_only) {
+    std::optional<BlinkBreakdown> blink_breakdown) {
   DCHECK(!viz_breakdown ||
          stage_type ==
              StageType::kSubmitCompositorFrameToPresentationCompositorFrame);
@@ -643,12 +624,10 @@
          stage_type == StageType::kSendBeginMainFrameToCommit);
   switch (stage_type) {
     case StageType::kBeginImplFrameToSendBeginMainFrame:
-      return impl_only ? "BeginImplFrameToFinishImpl"
-                       : "BeginImplFrameToSendBeginMainFrame";
+      return "BeginImplFrameToSendBeginMainFrame";
     case StageType::kSendBeginMainFrameToCommit:
       if (!blink_breakdown) {
-        return impl_only ? "SendBeginMainFrameToBeginMainAbort"
-                         : "SendBeginMainFrameToCommit";
+        return "SendBeginMainFrameToCommit";
       }
       switch (*blink_breakdown) {
         case BlinkBreakdown::kHandleInputEvents:
@@ -683,8 +662,7 @@
     case StageType::kActivation:
       return "Activation";
     case StageType::kEndActivateToSubmitCompositorFrame:
-      return impl_only ? "ImplFrameDoneToSubmitCompositorFrame"
-                       : "EndActivateToSubmitCompositorFrame";
+      return "EndActivateToSubmitCompositorFrame";
     case StageType::kSubmitCompositorFrameToPresentationCompositorFrame:
       if (!viz_breakdown)
         return "SubmitCompositorFrameToPresentationCompositorFrame";
@@ -1032,100 +1010,16 @@
     return;
 
   for (const StageData& stage : stage_history_) {
-    ReportStageHistogramWithBreakdown(stage);
-
-    if (stage.stage_type == StageType::kTotalLatency) {
-      for (size_t type = 0; type < active_trackers_.size(); ++type) {
-        if (active_trackers_.test(type)) {
-          // Report stage breakdowns.
-          ReportStageHistogramWithBreakdown(
-              stage, static_cast<FrameSequenceTrackerType>(type));
-        }
-      }
+    // Top-level breakdowns are only reported for presented frames.
+    if (TestReportType(FrameReportType::kNonDroppedFrame)) {
+      ReportStageHistogramWithBreakdown(stage);
     }
-  }
-
-  for (size_t type = 0; type < report_types_.size(); ++type) {
-    if (!report_types_.test(type))
-      continue;
-    FrameReportType report_type = static_cast<FrameReportType>(type);
-    UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type", report_type);
-    bool any_active_interaction = false;
-    for (size_t fst_type = 0; fst_type < active_trackers_.size(); ++fst_type) {
-      const auto tracker_type = static_cast<FrameSequenceTrackerType>(fst_type);
-      if (!active_trackers_.test(fst_type) ||
-          tracker_type == FrameSequenceTrackerType::kCustom ||
-          tracker_type == FrameSequenceTrackerType::kMaxType) {
-        continue;
+    for (size_t type = 0; type < active_trackers_.size(); ++type) {
+      if (active_trackers_.test(type)) {
+        // Report stage breakdowns for each `FrameSequenceTrackerType`
+        ReportStageHistogramWithBreakdown(
+            stage, static_cast<FrameSequenceTrackerType>(type));
       }
-      any_active_interaction = true;
-      switch (tracker_type) {
-        case FrameSequenceTrackerType::kCompositorAnimation:
-          UMA_HISTOGRAM_ENUMERATION(
-              "CompositorLatency.Type.CompositorAnimation", report_type);
-          break;
-        case FrameSequenceTrackerType::kMainThreadAnimation:
-          UMA_HISTOGRAM_ENUMERATION(
-              "CompositorLatency.Type.MainThreadAnimation", report_type);
-          break;
-        case FrameSequenceTrackerType::kPinchZoom:
-          UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.PinchZoom",
-                                    report_type);
-          break;
-        case FrameSequenceTrackerType::kRAF:
-          UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.RAF", report_type);
-          break;
-        case FrameSequenceTrackerType::kTouchScroll:
-          UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.TouchScroll",
-                                    report_type);
-          break;
-        case FrameSequenceTrackerType::kVideo:
-          UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.Video",
-                                    report_type);
-          break;
-        case FrameSequenceTrackerType::kWheelScroll:
-          UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.WheelScroll",
-                                    report_type);
-          break;
-        case FrameSequenceTrackerType::kScrollbarScroll:
-          UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.ScrollbarScroll",
-                                    report_type);
-          break;
-        case FrameSequenceTrackerType::kCanvasAnimation:
-          UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.CanvasAnimation",
-                                    report_type);
-          break;
-        case FrameSequenceTrackerType::kJSAnimation:
-          UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.JSAnimation",
-                                    report_type);
-          break;
-        case FrameSequenceTrackerType::kSETCompositorAnimation:
-          UMA_HISTOGRAM_ENUMERATION(
-              "CompositorLatency.Type.SETCompositorAnimation", report_type);
-          break;
-        case FrameSequenceTrackerType::kSETMainThreadAnimation:
-          UMA_HISTOGRAM_ENUMERATION(
-              "CompositorLatency.Type.SETMainThreadAnimation", report_type);
-          break;
-        case FrameSequenceTrackerType::kCompositorNativeAnimation:
-          UMA_HISTOGRAM_ENUMERATION(
-              "CompositorLatency.Type.NativePropertyAnimation", report_type);
-          break;
-        case FrameSequenceTrackerType::kCompositorRasterAnimation:
-          UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.RasterAnimation",
-                                    report_type);
-          break;
-        case FrameSequenceTrackerType::kCustom:
-        case FrameSequenceTrackerType::kMaxType:
-          NOTREACHED();
-      }
-    }
-    if (any_active_interaction) {
-      UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.AnyInteraction",
-                                report_type);
-    } else {
-      UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type.NoInteraction",
-                                report_type);
     }
   }
 
@@ -1228,50 +1122,37 @@
              StageType::kSubmitCompositorFrameToPresentationCompositorFrame);
   DCHECK(!blink_breakdown ||
          stage_type == StageType::kSendBeginMainFrameToCommit);
-  for (size_t type = 0; type < report_types_.size(); ++type) {
-    if (!report_types_.test(type))
-      continue;
-    FrameReportType report_type = static_cast<FrameReportType>(type);
-    const int report_type_index = static_cast<int>(report_type);
-    const int frame_sequence_tracker_type_index =
-        static_cast<int>(frame_sequence_tracker_type);
-    const int stage_type_index =
-        blink_breakdown
-            ? kBlinkBreakdownInitialIndex + static_cast<int>(*blink_breakdown)
-        : viz_breakdown
-            ? kVizBreakdownInitialIndex + static_cast<int>(*viz_breakdown)
-            : static_cast<int>(stage_type);
-    const int histogram_index =
-        (stage_type_index == static_cast<int>(StageType::kTotalLatency)
-             ? kStagesWithBreakdownCount + frame_sequence_tracker_type_index
-             : stage_type_index) *
-            kFrameReportTypeCount +
-        report_type_index;
+  const int frame_sequence_tracker_type_index =
+      static_cast<int>(frame_sequence_tracker_type);
+  const int stage_type_index =
+      blink_breakdown
+          ? kBlinkBreakdownInitialIndex + static_cast<int>(*blink_breakdown)
+      : viz_breakdown
+          ? kVizBreakdownInitialIndex + static_cast<int>(*viz_breakdown)
+          : static_cast<int>(stage_type);
+  const int histogram_index =
+      stage_type_index * kFrameSequenceTrackerTypeCount +
+      frame_sequence_tracker_type_index;
 
-    CHECK_LT(stage_type_index, kStagesWithBreakdownCount);
-    CHECK_GE(stage_type_index, 0);
-    CHECK_LT(report_type_index, kFrameReportTypeCount);
-    CHECK_GE(report_type_index, 0);
-    CHECK_LT(histogram_index, kMaxCompositorLatencyHistogramIndex);
-    CHECK_GE(histogram_index, 0);
+  CHECK_LT(stage_type_index, kStagesWithBreakdownCount);
+  CHECK_GE(stage_type_index, 0);
+  CHECK_LT(histogram_index, kMaxCompositorLatencyHistogramIndex);
+  CHECK_GE(histogram_index, 0);
 
-    // Note: There's a 1:1 mapping between `histogram_index` and the name
-    // returned by `GetCompositorLatencyHistogramName()` which allows the use of
-    // `STATIC_HISTOGRAM_POINTER_GROUP()` to cache histogram objects.
-    STATIC_HISTOGRAM_POINTER_GROUP(
-        GetCompositorLatencyHistogramName(
-            report_type, frame_sequence_tracker_type, stage_type, viz_breakdown,
-            blink_breakdown),
-        histogram_index, kMaxCompositorLatencyHistogramIndex,
-        AddTimeMicrosecondsGranularity(time_delta),
-        base::Histogram::FactoryMicrosecondsTimeGet(
-            GetCompositorLatencyHistogramName(
-                report_type, frame_sequence_tracker_type, stage_type,
-                viz_breakdown, blink_breakdown),
-            kCompositorLatencyHistogramMin, kCompositorLatencyHistogramMax,
-            kCompositorLatencyHistogramBucketCount,
-            base::HistogramBase::kUmaTargetedHistogramFlag));
-  }
+  auto histogram_name = GetCompositorLatencyHistogramName(
+      frame_sequence_tracker_type, stage_type, viz_breakdown, blink_breakdown);
+
+  // Note: There's a 1:1 mapping between `histogram_index` and the name
+  // returned by `GetCompositorLatencyHistogramName()` which allows the use
+  // of `STATIC_HISTOGRAM_POINTER_GROUP()` to cache histogram objects.
+  STATIC_HISTOGRAM_POINTER_GROUP(
+      histogram_name, histogram_index, kMaxCompositorLatencyHistogramIndex,
+      AddTimeMicrosecondsGranularity(time_delta),
+      base::Histogram::FactoryMicrosecondsTimeGet(
+          histogram_name, kCompositorLatencyHistogramMin,
+          kCompositorLatencyHistogramMax,
+          kCompositorLatencyHistogramBucketCount,
+          base::HistogramBase::kUmaTargetedHistogramFlag));
 }
 
 void CompositorFrameReporter::ReportEventLatencyMetrics() const {
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h
index 61ea132..3fb8ee9 100644
--- a/cc/metrics/compositor_frame_reporter.h
+++ b/cc/metrics/compositor_frame_reporter.h
@@ -325,8 +325,7 @@
   static const char* GetStageName(
       StageType stage_type,
       std::optional<VizBreakdown> viz_breakdown = std::nullopt,
-      std::optional<BlinkBreakdown> blink_breakdown = std::nullopt,
-      bool impl_only = false);
+      std::optional<BlinkBreakdown> blink_breakdown = std::nullopt);
 
   // Name for the viz breakdowns which are shown in traces as substages under
   // PipelineReporter -> SubmitCompositorFrameToPresentationCompositorFrame or
diff --git a/cc/metrics/compositor_frame_reporter_unittest.cc b/cc/metrics/compositor_frame_reporter_unittest.cc
index e78a5ea7..a136a71f 100644
--- a/cc/metrics/compositor_frame_reporter_unittest.cc
+++ b/cc/metrics/compositor_frame_reporter_unittest.cc
@@ -288,16 +288,16 @@
 
   pipeline_reporter_ = nullptr;
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 1);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 0);
-  histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation",
+      "CompositorLatency2.SendBeginMainFrameToCommit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 0);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
                                     0);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 1);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
+      "CompositorLatency2.SubmitCompositorFrameToPresentationCompositorFrame",
       1);
 }
 
@@ -320,8 +320,8 @@
   EXPECT_EQ(2u, pipeline_reporter_->stage_history_size_for_testing());
 
   pipeline_reporter_ = nullptr;
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 0);
-  histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation",
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 0);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
                                     0);
 }
 
@@ -344,58 +344,21 @@
   EXPECT_EQ(2u, pipeline_reporter_->stage_history_size_for_testing());
 
   pipeline_reporter_ = nullptr;
-  histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.TotalLatency", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.DroppedFrame.Activation",
-                                    0);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.TotalLatency", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.EndActivateToSubmitCompositorFrame", 0);
+      "CompositorLatency2.DroppedFrame.Activation", 0);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.TotalLatency", 0);
+      "CompositorLatency2.DroppedFrame.EndActivateToSubmitCompositorFrame", 0);
+  histogram_tester.ExpectTotalCount(
+      "CompositorLatency2.DroppedFrame.TotalLatency", 0);
 
-  histogram_tester.ExpectBucketCount("CompositorLatency.Activation", 3, 1);
+  histogram_tester.ExpectBucketCount("CompositorLatency2.Activation", 3, 1);
   histogram_tester.ExpectBucketCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 2, 1);
-  histogram_tester.ExpectBucketCount("CompositorLatency.TotalLatency", 5, 1);
-}
-
-TEST_F(CompositorFrameReporterTest, SubmittedDroppedFrameReportingTest) {
-  base::HistogramTester histogram_tester;
-
-  pipeline_reporter_->StartStage(
-      CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now());
-  EXPECT_EQ(0u, pipeline_reporter_->stage_history_size_for_testing());
-
-  AdvanceNowByUs(3);
-  pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit,
-                                 Now());
-  EXPECT_EQ(1u, pipeline_reporter_->stage_history_size_for_testing());
-
-  AdvanceNowByUs(2);
-  pipeline_reporter_->TerminateFrame(
-      CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame,
-      Now());
-  EXPECT_EQ(2u, pipeline_reporter_->stage_history_size_for_testing());
-
-  pipeline_reporter_ = nullptr;
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.SendBeginMainFrameToCommit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.DroppedFrame.Commit", 1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.TotalLatency", 1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 0);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 0);
-  histogram_tester.ExpectTotalCount("CompositorLatency.TotalLatency", 0);
-
-  histogram_tester.ExpectBucketCount(
-      "CompositorLatency.DroppedFrame.SendBeginMainFrameToCommit", 3, 1);
-  histogram_tester.ExpectBucketCount("CompositorLatency.DroppedFrame.Commit", 2,
-                                     1);
-  histogram_tester.ExpectBucketCount(
-      "CompositorLatency.DroppedFrame.TotalLatency", 5, 1);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 2, 1);
+  histogram_tester.ExpectBucketCount("CompositorLatency2.TotalLatency", 5, 1);
 }
 
 // Tests that when a frame is presented to the user, total event latency metrics
diff --git a/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
index c80aa3f..8822a92b 100644
--- a/cc/metrics/compositor_frame_reporting_controller_unittest.cc
+++ b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
@@ -460,7 +460,7 @@
   reporting_controller_.OnStoppedRequestingBeginFrames();
   reporting_controller_.ResetReporters();
   histogram_tester.ExpectBucketCount(
-      "CompositorLatency.Type",
+      "CompositorLatency2.Type",
       CompositorFrameReporter::FrameReportType::kDroppedFrame, 0);
 }
 
@@ -476,54 +476,30 @@
   SimulatePresentCompositorFrame();
 
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.SendBeginMainFrameToCommit", 0);
-  histogram_tester.ExpectTotalCount("CompositorLatency.DroppedFrame.Commit", 0);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.EndCommitToActivation", 0);
-  histogram_tester.ExpectTotalCount("CompositorLatency.DroppedFrame.Activation",
-                                    0);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.EndActivateToSubmitCompositorFrame", 0);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation",
+      "CompositorLatency2.SendBeginMainFrameToCommit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
                                     1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 1);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 1);
 
   // Submitting the next reporter will be replaced as a result of a new commit.
   // And this will be reported for all stage before activate as a missed frame.
   SimulateCommit(nullptr);
   // Non Missed frame histogram counts should not change.
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 1);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation",
+      "CompositorLatency2.SendBeginMainFrameToCommit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
                                     1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 1);
-
-  // Other histograms should be reported updated.
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.SendBeginMainFrameToCommit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.DroppedFrame.Commit", 1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.EndCommitToActivation", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.DroppedFrame.Activation",
-                                    0);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.EndActivateToSubmitCompositorFrame", 0);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 1);
 }
 
 TEST_F(CompositorFrameReportingControllerTest, MainFrameCausedNoDamage) {
@@ -557,9 +533,9 @@
   reporting_controller_.WillBeginMainFrame(args_3);
 
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
+      "CompositorLatency2.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.SendBeginMainFrameToCommit", 0);
+      "CompositorLatency2.DroppedFrame.SendBeginMainFrameToCommit", 0);
 }
 
 TEST_F(CompositorFrameReportingControllerTest, DidNotProduceFrame) {
@@ -591,91 +567,22 @@
   reporting_controller_.DidPresentCompositorFrame(1, details);
 
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
+      "CompositorLatency2.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.SendBeginMainFrameToCommit", 0);
+      "CompositorLatency2.DroppedFrame.SendBeginMainFrameToCommit", 0);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 2);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 2);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation",
+      "CompositorLatency2.SendBeginMainFrameToCommit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
                                     1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 2);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 2);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
+      "CompositorLatency2.SubmitCompositorFrameToPresentationCompositorFrame",
       2);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame.BeginImplFrameToFinishImpl", 1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "ImplFrameDoneToSubmitCompositorFrame",
-      1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "SubmitCompositorFrameToPresentationCompositorFrame",
-      1);
-}
-
-TEST_F(CompositorFrameReportingControllerTest,
-       DidNotProduceFrameDueToWaitingOnMain) {
-  base::HistogramTester histogram_tester;
-
-  viz::BeginFrameId current_id_1(1, 1);
-  viz::BeginFrameArgs args_1 = SimulateBeginFrameArgs(current_id_1);
-
-  viz::BeginFrameId current_id_2(1, 2);
-  viz::BeginFrameArgs args_2 = SimulateBeginFrameArgs(current_id_2);
-  args_2.frame_time = args_1.frame_time + args_1.interval;
-
-  viz::BeginFrameId current_id_3(1, 3);
-  viz::BeginFrameArgs args_3 = SimulateBeginFrameArgs(current_id_3);
-  args_3.frame_time = args_2.frame_time + args_2.interval;
-
-  reporting_controller_.WillBeginImplFrame(args_1);
-  reporting_controller_.WillBeginMainFrame(args_1);
-  reporting_controller_.OnFinishImplFrame(current_id_1);
-  reporting_controller_.DidNotProduceFrame(current_id_1,
-                                           FrameSkippedReason::kWaitingOnMain);
-
-  reporting_controller_.WillBeginImplFrame(args_2);
-  reporting_controller_.OnFinishImplFrame(current_id_2);
-  reporting_controller_.DidNotProduceFrame(current_id_2,
-                                           FrameSkippedReason::kWaitingOnMain);
-
-  reporting_controller_.WillBeginImplFrame(args_3);
-  reporting_controller_.NotifyReadyToCommit(nullptr);
-  reporting_controller_.WillCommit();
-  reporting_controller_.DidCommit();
-  reporting_controller_.WillActivate();
-  reporting_controller_.DidActivate();
-  reporting_controller_.OnFinishImplFrame(current_id_3);
-  SubmitInfo submit_info = {1u, AdvanceNowByMs(10)};
-  reporting_controller_.DidSubmitCompositorFrame(submit_info, current_id_3,
-                                                 current_id_1);
-  viz::FrameTimingDetails details;
-  details.presentation_feedback = {args_3.frame_time + args_3.interval,
-                                   args_3.interval, 0};
-  reporting_controller_.DidPresentCompositorFrame(1, details);
-
-  // Frames for |args_1| and |args_2| were dropped waiting on the main-thread.
-  histogram_tester.ExpectBucketCount(
-      "CompositorLatency.Type",
-      CompositorFrameReporter::FrameReportType::kDroppedFrame, 2);
-
-  // Frames for |args_1| and |args_3| were presented with |args_3|, and |args_1|
-  // missed its deadline.
-  histogram_tester.ExpectBucketCount(
-      "CompositorLatency.Type",
-      CompositorFrameReporter::FrameReportType::kNonDroppedFrame, 2);
-  histogram_tester.ExpectBucketCount(
-      "CompositorLatency.Type",
-      CompositorFrameReporter::FrameReportType::kMissedDeadlineFrame, 1);
-  histogram_tester.ExpectBucketCount(
-      "CompositorLatency.Type",
-      CompositorFrameReporter::FrameReportType::kCompositorOnlyFrame, 1);
 }
 
 TEST_F(CompositorFrameReportingControllerTest, MainFrameAborted) {
@@ -694,29 +601,15 @@
   reporting_controller_.DidPresentCompositorFrame(1, details);
 
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 1);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 0);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 0);
+      "CompositorLatency2.SendBeginMainFrameToCommit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 0);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 0);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 1);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
-      1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame.BeginImplFrameToFinishImpl", 1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "SendBeginMainFrameToBeginMainAbort",
-      1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "ImplFrameDoneToSubmitCompositorFrame",
-      1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "SubmitCompositorFrameToPresentationCompositorFrame",
+      "CompositorLatency2.SubmitCompositorFrameToPresentationCompositorFrame",
       1);
 }
 
@@ -750,38 +643,38 @@
   viz::FrameTimingDetails details = {};
   reporting_controller_.DidPresentCompositorFrame(1, details);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
+      "CompositorLatency2.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 2);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 2);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 2);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation",
+      "CompositorLatency2.SendBeginMainFrameToCommit", 2);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
                                     1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 2);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 2);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
+      "CompositorLatency2.SubmitCompositorFrameToPresentationCompositorFrame",
       2);
   SubmitInfo submit_info2 = {2u, AdvanceNowByMs(10)};
   reporting_controller_.DidSubmitCompositorFrame(submit_info2, current_id_2,
                                                  current_id_1);
   reporting_controller_.DidPresentCompositorFrame(2, details);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
+      "CompositorLatency2.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 2);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 2);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 2);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation",
+      "CompositorLatency2.SendBeginMainFrameToCommit", 2);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
                                     1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 2);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 2);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
+      "CompositorLatency2.SubmitCompositorFrameToPresentationCompositorFrame",
       2);
   reporting_controller_.WillBeginImplFrame(args_3);
   reporting_controller_.OnFinishImplFrame(current_id_3);
@@ -790,19 +683,19 @@
                                                  current_id_1);
   reporting_controller_.DidPresentCompositorFrame(3, details);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
+      "CompositorLatency2.DroppedFrame.BeginImplFrameToSendBeginMainFrame", 0);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 3);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 3);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 2);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation",
+      "CompositorLatency2.SendBeginMainFrameToCommit", 2);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
                                     1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 3);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 3);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
+      "CompositorLatency2.SubmitCompositorFrameToPresentationCompositorFrame",
       3);
 }
 
@@ -832,17 +725,17 @@
   reporting_controller_.DidPresentCompositorFrame(1, details);
 
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 1);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation",
+      "CompositorLatency2.SendBeginMainFrameToCommit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
                                     1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 1);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
+      "CompositorLatency2.SubmitCompositorFrameToPresentationCompositorFrame",
       1);
 
   // Second frame will not have the main frame update ready and will only submit
@@ -858,32 +751,18 @@
   // The reporting for the second frame is delayed until the main-thread
   // responds back.
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 1);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation",
+      "CompositorLatency2.SendBeginMainFrameToCommit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
                                     1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 1);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
+      "CompositorLatency2.SubmitCompositorFrameToPresentationCompositorFrame",
       1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame.BeginImplFrameToFinishImpl", 0);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "SendBeginMainFrameToBeginMainAbort",
-      0);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "ImplFrameDoneToSubmitCompositorFrame",
-      0);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "SubmitCompositorFrameToPresentationCompositorFrame",
-      0);
 
   reporting_controller_.WillBeginImplFrame(args_3);
   reporting_controller_.OnFinishImplFrame(current_id_3);
@@ -900,32 +779,18 @@
   // The main-thread responded, so the metrics for |args_2| should now be
   // reported.
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 4);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 4);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 2);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 2);
-  histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation",
+      "CompositorLatency2.SendBeginMainFrameToCommit", 2);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 2);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
                                     2);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 2);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 2);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 4);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 4);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
+      "CompositorLatency2.SubmitCompositorFrameToPresentationCompositorFrame",
       4);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame.BeginImplFrameToFinishImpl", 2);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "SendBeginMainFrameToBeginMainAbort",
-      0);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "ImplFrameDoneToSubmitCompositorFrame",
-      2);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "SubmitCompositorFrameToPresentationCompositorFrame",
-      2);
 }
 
 TEST_F(CompositorFrameReportingControllerTest, LongMainFrame2) {
@@ -951,17 +816,17 @@
   reporting_controller_.DidPresentCompositorFrame(1, details);
 
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 1);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation",
+      "CompositorLatency2.SendBeginMainFrameToCommit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
                                     1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 1);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
+      "CompositorLatency2.SubmitCompositorFrameToPresentationCompositorFrame",
       1);
 
   // The reporting for the second frame is delayed until activation happens.
@@ -977,32 +842,18 @@
   reporting_controller_.DidPresentCompositorFrame(2, details);
 
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 1);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation",
+      "CompositorLatency2.SendBeginMainFrameToCommit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
                                     1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 1);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
+      "CompositorLatency2.SubmitCompositorFrameToPresentationCompositorFrame",
       1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame.BeginImplFrameToFinishImpl", 0);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "SendBeginMainFrameToBeginMainAbort",
-      0);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "ImplFrameDoneToSubmitCompositorFrame",
-      0);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "SubmitCompositorFrameToPresentationCompositorFrame",
-      0);
 
   viz::BeginFrameId current_id_3(1, 3);
   viz::BeginFrameArgs args_3 = SimulateBeginFrameArgs(current_id_3);
@@ -1018,32 +869,18 @@
                                                  current_id_2);
   reporting_controller_.DidPresentCompositorFrame(3, details);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 4);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 4);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 2);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 2);
-  histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation",
+      "CompositorLatency2.SendBeginMainFrameToCommit", 2);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Commit", 2);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.EndCommitToActivation",
                                     2);
-  histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 2);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.Activation", 2);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.EndActivateToSubmitCompositorFrame", 4);
+      "CompositorLatency2.EndActivateToSubmitCompositorFrame", 4);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SubmitCompositorFrameToPresentationCompositorFrame",
+      "CompositorLatency2.SubmitCompositorFrameToPresentationCompositorFrame",
       4);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame.BeginImplFrameToFinishImpl", 2);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "SendBeginMainFrameToBeginMainAbort",
-      0);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "ImplFrameDoneToSubmitCompositorFrame",
-      2);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.CompositorOnlyFrame."
-      "SubmitCompositorFrameToPresentationCompositorFrame",
-      2);
 }
 
 TEST_F(CompositorFrameReportingControllerTest, BlinkBreakdown) {
@@ -1056,36 +893,37 @@
   SimulatePresentCompositorFrame();
 
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit", 1);
+      "CompositorLatency2.SendBeginMainFrameToCommit", 1);
   histogram_tester.ExpectUniqueSample(
-      "CompositorLatency.SendBeginMainFrameToCommit.HandleInputEvents",
+      "CompositorLatency2.SendBeginMainFrameToCommit.HandleInputEvents",
       base::Microseconds(10).InMilliseconds(), 1);
   histogram_tester.ExpectUniqueSample(
-      "CompositorLatency.SendBeginMainFrameToCommit.Animate",
+      "CompositorLatency2.SendBeginMainFrameToCommit.Animate",
       base::Microseconds(9).InMilliseconds(), 1);
   histogram_tester.ExpectUniqueSample(
-      "CompositorLatency.SendBeginMainFrameToCommit.StyleUpdate",
+      "CompositorLatency2.SendBeginMainFrameToCommit.StyleUpdate",
       base::Microseconds(8).InMilliseconds(), 1);
   histogram_tester.ExpectUniqueSample(
-      "CompositorLatency.SendBeginMainFrameToCommit.LayoutUpdate",
+      "CompositorLatency2.SendBeginMainFrameToCommit.LayoutUpdate",
       base::Microseconds(7).InMilliseconds(), 1);
   histogram_tester.ExpectUniqueSample(
-      "CompositorLatency.SendBeginMainFrameToCommit.CompositingInputs",
+      "CompositorLatency2.SendBeginMainFrameToCommit.CompositingInputs",
       base::Microseconds(6).InMilliseconds(), 1);
   histogram_tester.ExpectUniqueSample(
-      "CompositorLatency.SendBeginMainFrameToCommit.Prepaint",
+      "CompositorLatency2.SendBeginMainFrameToCommit.Prepaint",
       base::Microseconds(5).InMilliseconds(), 1);
   histogram_tester.ExpectUniqueSample(
-      "CompositorLatency.SendBeginMainFrameToCommit.Paint",
+      "CompositorLatency2.SendBeginMainFrameToCommit.Paint",
       base::Microseconds(3).InMilliseconds(), 1);
   histogram_tester.ExpectUniqueSample(
-      "CompositorLatency.SendBeginMainFrameToCommit.CompositeCommit",
+      "CompositorLatency2.SendBeginMainFrameToCommit.CompositeCommit",
       base::Microseconds(2).InMilliseconds(), 1);
   histogram_tester.ExpectUniqueSample(
-      "CompositorLatency.SendBeginMainFrameToCommit.UpdateLayers",
+      "CompositorLatency2.SendBeginMainFrameToCommit.UpdateLayers",
       base::Microseconds(1).InMilliseconds(), 1);
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.SendBeginMainFrameToCommit.BeginMainSentToStarted", 1);
+      "CompositorLatency2.SendBeginMainFrameToCommit.BeginMainSentToStarted",
+      1);
 }
 
 // If the presentation of the frame happens before deadline.
@@ -1109,23 +947,8 @@
   reporting_controller_.DidPresentCompositorFrame(1, details);
 
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.TotalLatency", 1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.MissedDeadlineFrame."
-      "BeginImplFrameToSendBeginMainFrame",
-      0);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.MissedDeadlineFrame.TotalLatency", 0);
-
-  // Non-dropped cases.
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type", 0, 1);
-  // Missed-deadline cases.
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type", 1, 0);
-  // Dropped cases.
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type", 2, 0);
-  // Impl only cases.
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type", 3, 0);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.TotalLatency", 1);
 }
 
 // If the presentation of the frame happens after deadline.
@@ -1149,21 +972,8 @@
   reporting_controller_.DidPresentCompositorFrame(1, details);
 
   histogram_tester.ExpectTotalCount(
-      "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 1);
-  histogram_tester.ExpectTotalCount("CompositorLatency.TotalLatency", 1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.MissedDeadlineFrame."
-      "BeginImplFrameToSendBeginMainFrame",
-      1);
-  histogram_tester.ExpectTotalCount(
-      "CompositorLatency.MissedDeadlineFrame.TotalLatency", 1);
-
-  // Non-dropped cases.
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type", 0, 1);
-  // Missed-deadline cases.
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type", 1, 1);
-  // Dropped cases.
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type", 2, 0);
+      "CompositorLatency2.BeginImplFrameToSendBeginMainFrame", 1);
+  histogram_tester.ExpectTotalCount("CompositorLatency2.TotalLatency", 1);
 }
 
 // If a compositor animation takes too long and throttles draw
@@ -1195,36 +1005,6 @@
   EXPECT_EQ(1u, frame_sorter_.total_dropped());
 }
 
-// Testing CompositorLatency.Type metrics
-TEST_F(CompositorFrameReportingControllerTest, ReportingLatencyType) {
-  base::HistogramTester histogram_tester;
-
-  SimulatePresentCompositorFrame();
-  tracker_collection_.StartSequence(
-      FrameSequenceTrackerType::kCompositorAnimation);
-  SimulatePresentCompositorFrame();
-  tracker_collection_.StartScrollSequence(
-      FrameSequenceTrackerType::kWheelScroll,
-      SmoothEffectDrivingThread::kCompositor);
-  SimulatePresentCompositorFrame();
-  tracker_collection_.StopSequence(
-      FrameSequenceTrackerType::kCompositorAnimation);
-  SimulatePresentCompositorFrame();
-  tracker_collection_.StopSequence(FrameSequenceTrackerType::kWheelScroll);
-  SimulatePresentCompositorFrame();
-
-  // All frames are presented so only test on-dropped cases.
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type", 0, 5);
-  histogram_tester.ExpectBucketCount(
-      "CompositorLatency.Type.CompositorAnimation", 0, 2);
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type.WheelScroll", 0,
-                                     2);
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type.AnyInteraction", 0,
-                                     3);
-  histogram_tester.ExpectBucketCount("CompositorLatency.Type.NoInteraction", 0,
-                                     2);
-}
-
 // Tests that EventLatency total latency histograms are reported properly when a
 // frame is presented to the user.
 TEST_F(CompositorFrameReportingControllerTest,
diff --git a/cc/metrics/frame_sorter.cc b/cc/metrics/frame_sorter.cc
index f8b48b18..559d718 100644
--- a/cc/metrics/frame_sorter.cc
+++ b/cc/metrics/frame_sorter.cc
@@ -193,7 +193,8 @@
   size_t good_frames = 0;
   for (auto it = End(); it; --it) {
     if (**it == FrameInfo::FrameFinalState::kPresentedAll ||
-        **it == FrameInfo::FrameFinalState::kPresentedPartialOldMain) {
+        **it == FrameInfo::FrameFinalState::kPresentedPartialOldMain ||
+        **it == FrameInfo::FrameFinalState::kPresentedPartialNewMain) {
       ++good_frames;
     }
   }
diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc
index c6748c0..c910a54 100644
--- a/cc/trees/layer_tree_host_unittest_animation.cc
+++ b/cc/trees/layer_tree_host_unittest_animation.cc
@@ -1081,9 +1081,7 @@
     EXPECT_GT(received_token_, request_token_);
     EXPECT_GE(received_token_, 5u);
     EXPECT_TRUE(base::StatisticsRecorder::FindHistogram(
-        "CompositorLatency.TotalLatency"));
-    EXPECT_FALSE(base::StatisticsRecorder::FindHistogram(
-        "CompositorLatency.Universal.TotalLatency"));
+        "CompositorLatency2.TotalLatency"));
   }
 
  private:
diff --git a/chrome/android/features/tab_ui/java/res/values/colors.xml b/chrome/android/features/tab_ui/java/res/values/colors.xml
index b43e0e0..fe9c107 100644
--- a/chrome/android/features/tab_ui/java/res/values/colors.xml
+++ b/chrome/android/features/tab_ui/java/res/values/colors.xml
@@ -23,7 +23,6 @@
     <color name="incognito_tab_group_hovered_bg_color">@color/default_bg_color_dark_elev_1_baseline</color>
     <color name="incognito_tab_group_hovered_bg_selected_color">@color/baseline_primary_80_alpha_10</color>
 
-    <color name="incognito_tab_tile_number_color">@color/baseline_neutral_90</color>
     <color name="incognito_tab_tile_number_selected_color">@color/baseline_primary_20</color>
 
     <color name="incognito_tab_grid_dialog_ungroup_bar_bg_hovered_color">@color/baseline_primary_80</color>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
index df91921..e56d434 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
@@ -41,8 +41,10 @@
 import org.chromium.chrome.browser.tab_ui.TabUiThemeUtils;
 import org.chromium.chrome.browser.tab_ui.ThumbnailProvider;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
+import org.chromium.components.tab_groups.TabGroupColorId;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -65,6 +67,7 @@
 
     private final float mRadius;
     private final float mFaviconFrameCornerRadius;
+    private final Paint mColordEmptyThumbnailPaint;
     private final Paint mEmptyThumbnailPaint;
     private final Paint mThumbnailFramePaint;
     private final Paint mThumbnailBasePaint;
@@ -97,6 +100,8 @@
         private final List<RectF> mFaviconBackgroundRects = new ArrayList<>(MAX_THUMBNAIL_COUNT);
         private final int mThumbnailWidth;
         private final int mThumbnailHeight;
+        private final @ColorInt int mResolvedEmptyPlaceholderColor;
+        private final @ColorInt int mResolvedTextColor;
 
         /**
          * Fetcher that get the thumbnail drawable depending on if the tab is selected.
@@ -129,6 +134,19 @@
                 mThumbnailWidth = thumbnailSize.getWidth();
                 mThumbnailHeight = thumbnailSize.getHeight();
             }
+
+            TabGroupModelFilter filter = mCurrentTabGroupModelFilterSupplier.get();
+            @TabGroupColorId Integer actualColorId = null;
+            boolean isIncognito = initialTab.isIncognitoBranded();
+            if (filter != null && filter.isTabInTabGroup(initialTab)) {
+                actualColorId = filter.getTabGroupColorWithFallback(initialTab.getRootId());
+            }
+            mResolvedEmptyPlaceholderColor =
+                    TabUiThemeUtils.getMiniThumbnailPlaceholderColor(
+                            mContext, isIncognito, mIsTabSelected, actualColorId);
+            mResolvedTextColor =
+                    TabUiThemeUtils.getTitleTextColor(
+                            mContext, isIncognito, mIsTabSelected, actualColorId);
         }
 
         /** Initialize rects used for thumbnails. */
@@ -274,6 +292,16 @@
         private void drawThumbnailBitmapOnCanvasWithFrame(@Nullable Bitmap thumbnail, int index) {
             final RectF rect = mThumbnailRects.get(index);
             if (thumbnail == null) {
+                if (SurfaceColorUpdateUtils.useNewGm3GtsTabGroupColors()) {
+                    mTextPaint.setColor(mResolvedTextColor);
+                    mColordEmptyThumbnailPaint.setColor(mResolvedEmptyPlaceholderColor);
+                    Paint emptyThumbnailPaint =
+                            mIsTabSelected
+                                    ? mSelectedEmptyThumbnailPaint
+                                    : mColordEmptyThumbnailPaint;
+                    mCanvas.drawRoundRect(rect, mRadius, mRadius, emptyThumbnailPaint);
+                    return;
+                }
                 Paint emptyThumbnailPaint =
                         mIsTabSelected ? mSelectedEmptyThumbnailPaint : mEmptyThumbnailPaint;
                 mCanvas.drawRoundRect(rect, mRadius, mRadius, emptyThumbnailPaint);
@@ -362,11 +390,15 @@
         mEmptyThumbnailPaint.setStyle(Paint.Style.FILL);
         mEmptyThumbnailPaint.setAntiAlias(true);
         mEmptyThumbnailPaint.setColor(
-                TabUiThemeUtils.getMiniThumbnailPlaceholderColor(context, false, false));
+                TabUiThemeUtils.getMiniThumbnailPlaceholderColor(
+                        context, false, false, /* colorId */ null));
 
         mSelectedEmptyThumbnailPaint = new Paint(mEmptyThumbnailPaint);
         mSelectedEmptyThumbnailPaint.setColor(
-                TabUiThemeUtils.getMiniThumbnailPlaceholderColor(context, false, true));
+                TabUiThemeUtils.getMiniThumbnailPlaceholderColor(
+                        context, false, true, /* colorId */ null));
+
+        mColordEmptyThumbnailPaint = new Paint(mEmptyThumbnailPaint);
 
         // Paint used to set base for thumbnails, in case mEmptyThumbnailPaint has transparency.
         mThumbnailBasePaint = new Paint(mEmptyThumbnailPaint);
@@ -388,11 +420,14 @@
         mTextPaint.setFakeBoldText(true);
         mTextPaint.setAntiAlias(true);
         mTextPaint.setTextAlign(Paint.Align.CENTER);
-        mTextPaint.setColor(TabUiThemeProvider.getTabGroupNumberTextColor(context, false, false));
+        mTextPaint.setColor(
+                TabUiThemeProvider.getTabGroupNumberTextColor(
+                        context, false, false, /* colorId */ null));
 
         mSelectedTextPaint = new Paint(mTextPaint);
         mSelectedTextPaint.setColor(
-                TabUiThemeProvider.getTabGroupNumberTextColor(context, false, true));
+                TabUiThemeProvider.getTabGroupNumberTextColor(
+                        context, false, true, /* colorId */ null));
 
         mFaviconBackgroundPaintColor = context.getColor(R.color.favicon_background_color);
         mFaviconBackgroundPaint = new Paint();
@@ -419,21 +454,25 @@
         assert filter != null;
         boolean isIncognito = filter.getTabModel().isIncognitoBranded();
         mMiniThumbnailPlaceholderColor =
-                TabUiThemeUtils.getMiniThumbnailPlaceholderColor(mContext, isIncognito, false);
+                TabUiThemeUtils.getMiniThumbnailPlaceholderColor(
+                        mContext, isIncognito, false, /* colorId */ null);
         if (mGroupTintedMiniThumbnailPlaceholderColor == null) {
             mEmptyThumbnailPaint.setColor(mMiniThumbnailPlaceholderColor);
         }
         mTextPaint.setColor(
-                TabUiThemeProvider.getTabGroupNumberTextColor(mContext, isIncognito, false));
+                TabUiThemeProvider.getTabGroupNumberTextColor(
+                        mContext, isIncognito, false, /* colorId */ null));
         mThumbnailFramePaint.setColor(
                 TabUiThemeProvider.getMiniThumbnailFrameColor(mContext, isIncognito));
         mFaviconBackgroundPaint.setColor(
                 TabUiThemeProvider.getFaviconBackgroundColor(mContext, isIncognito));
 
         mSelectedEmptyThumbnailPaint.setColor(
-                TabUiThemeUtils.getMiniThumbnailPlaceholderColor(mContext, isIncognito, true));
+                TabUiThemeUtils.getMiniThumbnailPlaceholderColor(
+                        mContext, isIncognito, true, /* colorId */ null));
         mSelectedTextPaint.setColor(
-                TabUiThemeProvider.getTabGroupNumberTextColor(mContext, isIncognito, true));
+                TabUiThemeProvider.getTabGroupNumberTextColor(
+                        mContext, isIncognito, true, /* colorId */ null));
     }
 
     /**
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index 507ce7d8..945ded7 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -671,6 +671,7 @@
                     updateTabGroupColorViewProvider(model, tab, newColor);
                     updateDescriptionString(tab, model);
                     updateActionButtonDescriptionString(tab, model);
+                    updateThumbnailFetcher(model, rootId);
                 }
 
                 @Override
@@ -2140,7 +2141,6 @@
 
     private String getDomainForTab(Tab tab) {
         if (!mActionsOnAllRelatedTabs) return getDomain(tab);
-
         List<Tab> relatedTabs = getRelatedTabsForId(tab.getId());
 
         List<String> domainNames = new ArrayList<>();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
index f028b017..0568cfb 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
@@ -367,21 +367,19 @@
                 () -> {
                     final RecyclerView tab_list_recycler_view =
                             hubContainerView.findViewById(R.id.tab_list_recycler_view);
-                    if (tab_list_recycler_view != null) {
-                        GridLayoutManager lm =
-                                (GridLayoutManager) tab_list_recycler_view.getLayoutManager();
-                        int first = lm.findFirstVisibleItemPosition();
-                        int last = lm.findLastVisibleItemPosition();
+                    if (tab_list_recycler_view == null) return;
 
-                        List<View> views = new ArrayList<>();
-                        for (int index = first; index <= last; index++) {
-                            View view = lm.findViewByPosition(index);
-                            if (view != null) views.add(view);
-                        }
-                        animationDataSupplier.set(views.isEmpty() ? null : views);
-                    } else {
-                        animationDataSupplier.set(null);
+                    GridLayoutManager lm =
+                            (GridLayoutManager) tab_list_recycler_view.getLayoutManager();
+                    int first = lm.findFirstVisibleItemPosition();
+                    int last = lm.findLastVisibleItemPosition();
+
+                    List<View> views = new ArrayList<>();
+                    for (int index = first; index <= last; index++) {
+                        View view = lm.findViewByPosition(index);
+                        if (view != null) views.add(view);
                     }
+                    if (!views.isEmpty()) animationDataSupplier.set(views);
                 });
         return animationDataSupplier;
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java
index debf4dd..496b4ce 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java
@@ -15,10 +15,12 @@
 import com.google.android.material.color.MaterialColors;
 
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.tasks.tab_management.TabListEditorCoordinator.CreationMode;
 import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
+import org.chromium.components.tab_groups.TabGroupColorId;
 
 /** Utility class that provides theme related attributes for Tab UI. */
 @NullMarked
@@ -41,22 +43,21 @@
      * @param context {@link Context} used to retrieve color.
      * @param isIncognito Whether the color is used for incognito mode.
      * @param isSelected Whether the tab is currently selected.
+     * @param colorId Color chosen by user for the TabGroup, null if not a tab group.
      * @return The text color for the number used on the tab group cards.
      */
     public static @ColorInt int getTabGroupNumberTextColor(
-            Context context, boolean isIncognito, boolean isSelected) {
-        if (isIncognito) {
-            @ColorRes
-            int colorRes =
-                    isSelected
-                            ? R.color.incognito_tab_tile_number_selected_color
-                            : R.color.incognito_tab_tile_number_color;
-            return context.getColor(colorRes);
-        } else {
-            return isSelected
-                    ? MaterialColors.getColor(context, R.attr.colorOnPrimary, TAG)
-                    : MaterialColors.getColor(context, R.attr.colorOnSurface, TAG);
+            Context context,
+            boolean isIncognito,
+            boolean isSelected,
+            @Nullable @TabGroupColorId Integer colorId) {
+        if (isSelected) {
+            return isIncognito
+                    ? context.getColor(R.color.incognito_tab_tile_number_selected_color)
+                    : MaterialColors.getColor(context, R.attr.colorOnPrimary, TAG);
         }
+        return SurfaceColorUpdateUtils.getCardViewGroupNumberTextColor(
+                context, isIncognito, colorId);
     }
 
     /**
diff --git a/chrome/android/java/res/drawable/custom_tabs_url_bar_omnibox_bg.xml b/chrome/android/java/res/drawable/custom_tabs_url_bar_omnibox_bg.xml
index 81db86e..7a213d4 100644
--- a/chrome/android/java/res/drawable/custom_tabs_url_bar_omnibox_bg.xml
+++ b/chrome/android/java/res/drawable/custom_tabs_url_bar_omnibox_bg.xml
@@ -7,6 +7,6 @@
 
 <inset
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/modern_toolbar_text_box_background_with_primary_color"
+    android:drawable="@drawable/modern_toolbar_text_box_background"
     android:insetLeft="@dimen/custom_tabs_url_bar_bg_horizontal_padding"
     android:insetRight="@dimen/custom_tabs_url_bar_bg_horizontal_padding"/>
diff --git a/chrome/android/java/res/layout/search_widget_template.xml b/chrome/android/java/res/layout/search_widget_template.xml
index 389c0bc..9b30f573 100644
--- a/chrome/android/java/res/layout/search_widget_template.xml
+++ b/chrome/android/java/res/layout/search_widget_template.xml
@@ -17,7 +17,7 @@
         android:layout_height="wrap_content"
         android:layout_gravity="center"
         android:alpha="0.9"
-        android:background="@drawable/modern_toolbar_text_box_background_with_primary_color"
+        android:background="@drawable/modern_toolbar_text_box_background"
         android:gravity="center_vertical"
         android:orientation="horizontal"
         android:theme="@style/Theme.Chromium.Widget"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/BrowsingDataBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/BrowsingDataBridge.java
index 002a7dd..6af5496e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/BrowsingDataBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/BrowsingDataBridge.java
@@ -19,6 +19,7 @@
 import org.chromium.chrome.browser.tab.CurrentTabObserver;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabwindow.TabWindowManager;
 import org.chromium.components.browsing_data.content.BrowsingDataModel;
 import org.chromium.content_public.browser.WebContents;
@@ -282,27 +283,23 @@
         removeTabModelObservers();
 
         TabWindowManager tabWindowManager = TabWindowManagerSingleton.getInstance();
-        for (int i = 0; i < tabWindowManager.getMaxSimultaneousSelectors(); i++) {
-            var selector = tabWindowManager.getTabModelSelectorById(i);
-            if (selector != null) {
-                mCurrentTabObservers.add(
-                        new CurrentTabObserver(
-                                selector.getCurrentTabSupplier(),
-                                new EmptyTabObserver() {
-                                    @Override
-                                    public void onLoadStarted(
-                                            Tab tab, boolean toDifferentDocument) {
-                                        WebContents webContents = tab.getWebContents();
-                                        if (!tab.isOffTheRecord() && webContents != null) {
-                                            BrowsingDataBridgeJni.get()
-                                                    .triggerHatsSurvey(
-                                                            mProfile, webContents, quickDelete);
-                                            removeTabModelObservers();
-                                        }
+        for (TabModelSelector selector : tabWindowManager.getAllTabModelSelectors()) {
+            mCurrentTabObservers.add(
+                    new CurrentTabObserver(
+                            selector.getCurrentTabSupplier(),
+                            new EmptyTabObserver() {
+                                @Override
+                                public void onLoadStarted(Tab tab, boolean toDifferentDocument) {
+                                    WebContents webContents = tab.getWebContents();
+                                    if (!tab.isOffTheRecord() && webContents != null) {
+                                        BrowsingDataBridgeJni.get()
+                                                .triggerHatsSurvey(
+                                                        mProfile, webContents, quickDelete);
+                                        removeTabModelObservers();
                                     }
-                                },
-                                /* swapCallback= */ null));
-            }
+                                }
+                            },
+                            /* swapCallback= */ null));
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayout.java
index a891267..08b9514 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayout.java
@@ -46,6 +46,7 @@
 import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
 import org.chromium.chrome.browser.compositor.scene_layer.SolidColorSceneLayer;
 import org.chromium.chrome.browser.compositor.scene_layer.StaticTabSceneLayer;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.layouts.EventFilter;
 import org.chromium.chrome.browser.layouts.LayoutManager;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
@@ -317,6 +318,11 @@
     @Override
     public void show(long time, boolean animate) {
         if (isStartingToShow()) return;
+        if (mXrSessionManager != null
+                && animate
+                && !ChromeFeatureList.sShowTabListAnimations.isEnabled()) {
+            animate = false;
+        }
 
         try (TraceEvent e = TraceEvent.scoped("HubLayout.show")) {
             super.show(time, animate);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
index 5316860..6f4c2f4a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceManagerApi31.java
@@ -383,8 +383,7 @@
         List<InstanceInfo> result = new ArrayList<>();
         SparseBooleanArray visibleTasks = MultiWindowUtils.getVisibleTasks();
         int currentItemPos = -1;
-        for (int i = 0; i < mMaxInstances; ++i) {
-            if (!instanceEntryExists(i)) continue;
+        for (int i : getPersistedInstanceIds()) {
             @InstanceInfo.Type int type = InstanceInfo.Type.OTHER;
             Activity a = getActivityById(i);
             if (a != null) {
@@ -695,8 +694,8 @@
 
         List<Integer> instancesRemoved = new ArrayList<>();
         // Remove persistent data for unrecoverable instances.
-        for (int i = 0; i < mMaxInstances; ++i) {
-            if (instanceEntryExists(i) && !MultiWindowUtils.isRestorableInstance(i)) {
+        for (int i : getPersistedInstanceIds()) {
+            if (!MultiWindowUtils.isRestorableInstance(i)) {
                 instancesRemoved.add(i);
                 removeInstanceInfo(i);
             }
@@ -762,7 +761,7 @@
     }
 
     private int getInstanceByTask(int taskId) {
-        for (int i = 0; i < mMaxInstances; ++i) {
+        for (int i : getPersistedInstanceIds()) {
             if (taskId == getTaskFromMap(i)) return i;
         }
         return INVALID_WINDOW_ID;
@@ -1252,8 +1251,7 @@
     InstanceInfo getInstanceInfoFor(Activity activity) {
         // Loop thru all instances to determine if the destination activity is present.
         int destinationWindowTaskId = INVALID_TASK_ID;
-        for (int i = 0; i < mMaxInstances; ++i) {
-            if (!instanceEntryExists(i)) continue;
+        for (int i : getPersistedInstanceIds()) {
             Activity activityById = getActivityById(i);
             if (activityById != null) {
                 // The task for the activity must match the one found in our mapping.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
index 13f9749..5915e60 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java
@@ -817,7 +817,7 @@
 
         SparseIntArray windowIdsOfRunningTabbedActivities =
                 MultiInstanceManagerApi31.getWindowIdsOfRunningTabbedActivities();
-        for (int i = 0; i < maxInstances; i++) {
+        for (int i : MultiInstanceManagerApi31.getPersistedInstanceIds()) {
             // Exclude instance IDs of non-running activities.
             if (windowIdsOfRunningTabbedActivities.indexOfValue(i) < 0) continue;
             if (MultiWindowUtils.readLastAccessedTime(i)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerErrorMessageHelperBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerErrorMessageHelperBridge.java
index c66f008..ac2be93b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerErrorMessageHelperBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerErrorMessageHelperBridge.java
@@ -125,10 +125,14 @@
     }
 
     /**
-     * Starts the Android process to retrieve encryption keys in Chrome. This method will only work for users that have been previously syncing in Chrome.
+     * Starts the Android process to retrieve encryption keys in Chrome. This method will only work
+     * for users that have been previously syncing in Chrome.
      */
     @CalledByNative
-    static void startTrustedVaultKeyRetrievalFlow(WindowAndroid windowAndroid, Profile profile) {
+    static void startTrustedVaultKeyRetrievalFlow(
+            WindowAndroid windowAndroid,
+            Profile profile,
+            @TrustedVaultUserActionTriggerForUMA int trustedVaultUserActionTriggerForUMA) {
         final CoreAccountInfo primaryAccountInfo =
                 SyncServiceFactory.getForProfile(profile).getAccountInfo();
         // If the account has been removed before calling this method, there is nothing to do.
@@ -139,12 +143,9 @@
                 .createKeyRetrievalIntent(primaryAccountInfo)
                 .then(
                         (intent) -> {
-                            var action =
-                                    TrustedVaultUserActionTriggerForUMA
-                                            .PASSWORD_MANAGER_ERROR_MESSAGE;
                             var proxyIntent =
                                     SyncTrustedVaultProxyActivity.createKeyRetrievalProxyIntent(
-                                            intent, action);
+                                            intent, trustedVaultUserActionTriggerForUMA);
                             IntentUtils.safeStartActivity(activity, proxyIntent);
                         });
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicy.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicy.java
index 9069ace1..db019026 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabbedModeTabPersistencePolicy.java
@@ -108,8 +108,7 @@
             mOtherMetadataFileName = null;
         }
         mMergeTabsOnStartup = mergeTabsOnStartup;
-        TabWindowManager tabWindowManager = TabWindowManagerSingleton.getInstance();
-        mMaxSelectors = tabWindowManager.getMaxSimultaneousSelectors();
+        mMaxSelectors = TabWindowManager.MAX_SELECTORS;
     }
 
     /**
@@ -264,7 +263,7 @@
             File otherStateDir =
                     new File(
                             TabStateDirectory.getOrCreateBaseStateDirectory(), Integer.toString(i));
-            if (otherStateDir == null || !otherStateDir.exists()) continue;
+            if (!otherStateDir.exists()) continue;
 
             // Rename tab state file.
             oldMetadataFile = new File(otherStateDir, LEGACY_SAVED_STATE_FILE);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
index 4dd72f7..6379d70 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarTest.java
@@ -55,6 +55,7 @@
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.locale.LocaleManagerDelegate;
+import org.chromium.chrome.browser.omnibox.status.StatusProperties.StatusIconResource;
 import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -772,6 +773,13 @@
         startActivityNormally();
 
         mActivityTestRule.loadUrl(UrlConstants.NTP_URL);
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    mSearchEngineUtils.setSearchEngineIcon(
+                            new StatusIconResource(R.drawable.ic_search, 0));
+                });
+
         onView(withId(R.id.location_bar_status_icon)).check(matches(isDisplayed()));
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabArchiverTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabArchiverTest.java
index 56ed93e..26e1c0fe 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabArchiverTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/TabArchiverTest.java
@@ -133,7 +133,6 @@
         mRegularTabModel = sActivityTestRule.getActivity().getCurrentTabModel();
         mRegularTabCreator = sActivityTestRule.getActivity().getTabCreator(false);
 
-        doReturn(1).when(mTabWindowManager).getMaxSimultaneousSelectors();
         doReturn(mSelector).when(mTabWindowManager).getTabModelSelectorById(anyInt());
         doReturn(mRegularTabModel).when(mSelector).getModel(anyBoolean());
         doReturn(true).when(mSelector).isTabStateInitialized();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/MultiInstanceMigrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/MultiInstanceMigrationTest.java
index 94ff973..4933519 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/MultiInstanceMigrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/MultiInstanceMigrationTest.java
@@ -236,7 +236,7 @@
     /**
      * Tests that the metadata file migration skips unrelated files. Also tests that migration works
      * if the number of tab state subdirectories to migrate is less than {@code
-     * TabWindowManagerSingleton.getMaxSimultaneousSelectors()}
+     * TabWindowManager.MAX_SELECTORS}.
      */
     @Test
     @MediumTest
@@ -365,11 +365,7 @@
     @Feature({"TabPersistentStore"})
     public void testNewMetataFileExists() throws Exception {
         // Set up two old metadata files.
-        int maxCount =
-                ThreadUtils.runOnUiThreadBlocking(
-                        () ->
-                                TabWindowManagerSingleton.getInstance()
-                                        .getMaxSimultaneousSelectors());
+        int maxCount = TabWindowManager.MAX_SELECTORS;
         File[] stateDirs = createOldStateDirs(maxCount, true);
         File metadataFile0 =
                 new File(stateDirs[0], TabbedModeTabPersistencePolicy.LEGACY_SAVED_STATE_FILE);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerErrorMessageHelperBridgeTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerErrorMessageHelperBridgeTest.java
index 95d2b081..29db7d8 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerErrorMessageHelperBridgeTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerErrorMessageHelperBridgeTest.java
@@ -52,6 +52,7 @@
 import org.chromium.components.signin.identitymanager.IdentityManager;
 import org.chromium.components.signin.test.util.FakeAccountManagerFacade;
 import org.chromium.components.sync.SyncService;
+import org.chromium.components.sync.TrustedVaultUserActionTriggerForUMA;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.components.user_prefs.UserPrefsJni;
 import org.chromium.ui.base.WindowAndroid;
@@ -294,7 +295,7 @@
         when(mTrustedVaultBackend.createKeyRetrievalIntent(any())).thenReturn(intentPromise);
 
         PasswordManagerErrorMessageHelperBridge.startTrustedVaultKeyRetrievalFlow(
-                mWindowAndroidMock, mProfile);
+                mWindowAndroidMock, mProfile, TrustedVaultUserActionTriggerForUMA.ACCOUNT_MENU);
 
         intentPromise.fulfill(mPendingIntent);
         ShadowLooper.idleMainLooper();
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt
index 77bfe3e4..9e61667 100644
--- a/chrome/android/profiles/arm.newest.txt
+++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-139.0.7216.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-arm-139.0.7221.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index d49816d9..143dc58 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10854,6 +10854,9 @@
         <message name="IDS_PASSWORD_MANAGER_ACCESSORY_PASSWORD_LIST_EMPTY_MESSAGE" desc="Message indicating that the list of saved passwords for this site is empty.">
           No saved passwords for <ph name="domain">$1<ex>example.com</ex></ph>
         </message>
+        <message name="IDS_PASSWORD_MANAGER_ACCESSORY_TRUSTED_VAULT_KEY_RETRIEVAL_REQUIRED_MESSAGE" desc="Message indicating that trusted vault key retrieval is required before passwords can be used.">
+          You need to verify it's you before you can access your passwords
+        </message>
         <message name="IDS_PASSWORD_MANAGER_ACCESSORY_PASSWORD_LIST_TITLE" desc="The title of a list of saved passwords for the currently visible site." translateable="false">
           Saved passwords for <ph name="domain">$1<ex>example.com</ex></ph>
         </message>
@@ -10866,6 +10869,9 @@
         <message name="IDS_PASSWORD_MANAGER_ACCESSORY_SELECT_PASSWORD" desc="The text of the link in the password sheet that opens a bottom sheet which shows all saved credentials from any origin if the user is about to fill a password or username field. Allows user to pick one of the saved passwords to be filled in.">
           Select password
         </message>
+        <message name="IDS_PASSWORD_MANAGER_ACCESSORY_RETRIEVE_TRUSTED_VAULT_KEY" desc="The text of the link in the password sheet that opens the trusted vault key retrieval flow.">
+          Verify it's you
+        </message>
         <message name="IDS_PASSWORD_MANAGER_ACCESSORY_SELECT_PASSKEY" desc="The text of the link in the password sheet that opens the system's selection for available Passkeys.">
           Select passkey
         </message>
diff --git a/chrome/app/generated_resources_grd/IDS_PASSWORD_MANAGER_ACCESSORY_RETRIEVE_TRUSTED_VAULT_KEY.png.sha1 b/chrome/app/generated_resources_grd/IDS_PASSWORD_MANAGER_ACCESSORY_RETRIEVE_TRUSTED_VAULT_KEY.png.sha1
new file mode 100644
index 0000000..ce0c328
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_PASSWORD_MANAGER_ACCESSORY_RETRIEVE_TRUSTED_VAULT_KEY.png.sha1
@@ -0,0 +1 @@
+9a63079450efd4b745f4cf096b405d9b1b658d73
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_PASSWORD_MANAGER_ACCESSORY_TRUSTED_VAULT_KEY_RETRIEVAL_REQUIRED_MESSAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_PASSWORD_MANAGER_ACCESSORY_TRUSTED_VAULT_KEY_RETRIEVAL_REQUIRED_MESSAGE.png.sha1
new file mode 100644
index 0000000..ce0c328
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_PASSWORD_MANAGER_ACCESSORY_TRUSTED_VAULT_KEY_RETRIEVAL_REQUIRED_MESSAGE.png.sha1
@@ -0,0 +1 @@
+9a63079450efd4b745f4cf096b405d9b1b658d73
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 9beaa44..321adf5 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -988,6 +988,9 @@
       <message name="IDS_INSTALLER_DOWNLOADER_BUTTON_LABEL" desc="Button label for the installer downloader infobar to download the Chrome installer.">
         Download Chrome Installer
       </message>
+      <message name="IDS_INSTALLER_DOWNLOADER_LINK" desc="Text for the link in the installer downloader infobar to learn more about installing Chrome">
+        Learn more about installing Chrome
+      </message>
 
       <!-- Enterprise sign-in dialog -->
       <message name="IDS_ENTERPRISE_VALUE_PROPOSITION_PROFILE_REQUIRED_BY_ORG_TITLE" desc="Title of the screen in the profile creation flow where the user is asked whether they want to confirm signing in to Chrome with their enterprise account when it is mandatory to sign in to Chrome with the enterprise account.">
diff --git a/chrome/app/google_chrome_strings_grd/IDS_INSTALLER_DOWNLOADER_LINK.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_INSTALLER_DOWNLOADER_LINK.png.sha1
new file mode 100644
index 0000000..596ecfa
--- /dev/null
+++ b/chrome/app/google_chrome_strings_grd/IDS_INSTALLER_DOWNLOADER_LINK.png.sha1
@@ -0,0 +1 @@
+6b849c101b5af6e79e4d31ca69a745dbdd6fc0e6
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 7a765cd..1910cc0 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -12733,6 +12733,12 @@
          supervised_user::kPropagateDeviceContentFiltersToSupervisedUser)},
 #endif  // BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(IS_ANDROID)
+    {"show-tab-list-animations", flag_descriptions::kShowTabListAnimationsName,
+     flag_descriptions::kShowTabListAnimationsDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kShowTabListAnimations)},
+#endif  // BUILDFLAG(IS_ANDROID)
+
     // Add new entries above this line.
 
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
diff --git a/chrome/browser/ash/policy/DEPS b/chrome/browser/ash/policy/DEPS
index b210214..ed926ff 100644
--- a/chrome/browser/ash/policy/DEPS
+++ b/chrome/browser/ash/policy/DEPS
@@ -26,7 +26,7 @@
   "+remoting/host/curtain_mode_chromeos.h",
 
   # Allow includes for error codes.
-  "+remoting/protocol/errors.h",
+  "+remoting/base/errors.h",
 ]
 
 specific_include_rules = {
diff --git a/chrome/browser/ash/policy/remote_commands/crd/BUILD.gn b/chrome/browser/ash/policy/remote_commands/crd/BUILD.gn
index fb2bb1c8..a867652 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/BUILD.gn
+++ b/chrome/browser/ash/policy/remote_commands/crd/BUILD.gn
@@ -51,12 +51,12 @@
     "//components/user_manager",
     "//google_apis",
     "//mojo/public/cpp/bindings",
+    "//remoting/base:errors",
     "//remoting/host:common",
     "//remoting/host:enterprise_params",
     "//remoting/host/chromeos:features",
     "//remoting/host/chromeos:remoting_service",
     "//remoting/host/mojom",
-    "//remoting/protocol:errors",
     "//ui/base",
   ]
 
@@ -116,10 +116,10 @@
     "//components/session_manager/core",
     "//content/test:test_support",
     "//mojo/public/cpp/bindings",
+    "//remoting/base:errors",
     "//remoting/host:enterprise_params",
     "//remoting/host/chromeos:features",
     "//remoting/host/mojom",
-    "//remoting/protocol:errors",
     "//testing/gmock",
     "//testing/gtest",
     "//ui/base",
diff --git a/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller_unittest.cc b/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller_unittest.cc
index c0add66..ce45f0a 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller_unittest.cc
+++ b/chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller_unittest.cc
@@ -39,9 +39,9 @@
 #include "content/public/test/browser_task_environment.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "remoting/base/errors.h"
 #include "remoting/host/chromeos/chromeos_enterprise_params.h"
 #include "remoting/host/mojom/remote_support.mojom.h"
-#include "remoting/protocol/errors.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -56,12 +56,12 @@
     crosapi::mojom::Remoting::StartSupportSessionCallback;
 
 using base::test::TestFuture;
+using remoting::ErrorCode;
 using remoting::SessionId;
 using remoting::mojom::StartSupportSessionResponse;
 using remoting::mojom::StartSupportSessionResponsePtr;
 using remoting::mojom::SupportHostObserver;
 using remoting::mojom::SupportSessionParamsPtr;
-using remoting::protocol::ErrorCode;
 using ::testing::Eq;
 
 constexpr char kTestUserName[] = "test-username";
@@ -955,8 +955,8 @@
   InitWithNoReconnectableSession(session_controller());
   SupportHostObserver& observer = StartCrdHostAndBindObserver();
 
-  observer.OnHostStateError(static_cast<int64_t>(
-      remoting::protocol::ErrorCode::DISALLOWED_BY_POLICY));
+  observer.OnHostStateError(
+      static_cast<int64_t>(remoting::ErrorCode::DISALLOWED_BY_POLICY));
 
   Response response = WaitForResponse();
   ASSERT_TRUE(response.HasError());
@@ -1280,8 +1280,8 @@
       InitWithReconnectableSession(session_controller());
   ASSERT_TRUE(curtain_controller().IsEnabled());
 
-  observer.OnHostStateError(static_cast<int64_t>(
-      remoting::protocol::ErrorCode::AUTHENTICATION_FAILED));
+  observer.OnHostStateError(
+      static_cast<int64_t>(remoting::ErrorCode::AUTHENTICATION_FAILED));
   FlushForTesting(observer);
 
   EXPECT_FALSE(curtain_controller().IsEnabled());
diff --git a/chrome/browser/ash/policy/remote_commands/crd/crd_support_host_observer_proxy.cc b/chrome/browser/ash/policy/remote_commands/crd/crd_support_host_observer_proxy.cc
index 48149ed..d833f06a 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/crd_support_host_observer_proxy.cc
+++ b/chrome/browser/ash/policy/remote_commands/crd/crd_support_host_observer_proxy.cc
@@ -8,10 +8,10 @@
 #include "chrome/browser/ash/policy/remote_commands/crd/crd_logging.h"
 #include "chrome/browser/ash/policy/remote_commands/crd/crd_session_observer.h"
 #include "chrome/browser/ash/policy/remote_commands/crd/public/crd_session_result_codes.h"
-#include "remoting/protocol/errors.h"
+#include "remoting/base/errors.h"
 
-using remoting::protocol::ErrorCode;
-using remoting::protocol::ErrorCodeToString;
+using remoting::ErrorCode;
+using remoting::ErrorCodeToString;
 
 namespace policy {
 
diff --git a/chrome/browser/ash/policy/remote_commands/crd/public/BUILD.gn b/chrome/browser/ash/policy/remote_commands/crd/public/BUILD.gn
index 54e60d8..04af28d 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/public/BUILD.gn
+++ b/chrome/browser/ash/policy/remote_commands/crd/public/BUILD.gn
@@ -30,6 +30,6 @@
   deps = [
     "//base",
     "//components/policy/proto:",
-    "//remoting/protocol:errors",
+    "//remoting/base:errors",
   ]
 }
diff --git a/chrome/browser/ash/policy/remote_commands/crd/public/crd_session_result_codes.cc b/chrome/browser/ash/policy/remote_commands/crd/public/crd_session_result_codes.cc
index c7eee95..f4331bef 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/public/crd_session_result_codes.cc
+++ b/chrome/browser/ash/policy/remote_commands/crd/public/crd_session_result_codes.cc
@@ -5,12 +5,12 @@
 #include "chrome/browser/ash/policy/remote_commands/crd/public/crd_session_result_codes.h"
 
 #include "base/notreached.h"
-#include "remoting/protocol/errors.h"
+#include "remoting/base/errors.h"
 
 namespace policy {
 
 namespace {
-using remoting::protocol::ErrorCode;
+using remoting::ErrorCode;
 }  // namespace
 
 ExtendedStartCrdSessionResultCode ToExtendedStartCrdSessionResultCode(
diff --git a/chrome/browser/ash/policy/remote_commands/crd/public/crd_session_result_codes.h b/chrome/browser/ash/policy/remote_commands/crd/public/crd_session_result_codes.h
index 86b2e9a5..eafc62a 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/public/crd_session_result_codes.h
+++ b/chrome/browser/ash/policy/remote_commands/crd/public/crd_session_result_codes.h
@@ -6,7 +6,7 @@
 #define CHROME_BROWSER_ASH_POLICY_REMOTE_COMMANDS_CRD_PUBLIC_CRD_SESSION_RESULT_CODES_H_
 
 #include "components/policy/proto/device_management_backend.pb.h"
-#include "remoting/protocol/errors.h"
+#include "remoting/base/errors.h"
 
 namespace policy {
 
@@ -155,7 +155,7 @@
 
 // Translates the error code.
 ExtendedStartCrdSessionResultCode ToExtendedStartCrdSessionResultCode(
-    remoting::protocol::ErrorCode error_code);
+    remoting::ErrorCode error_code);
 
 StartCrdSessionResultCode ToStartCrdSessionResultCode(
     ExtendedStartCrdSessionResultCode error_code);
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 131b7d6..6729716 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -1476,10 +1476,6 @@
       /*default_value=*/false);
 
   registry->RegisterBooleanPref(
-      policy::policy_prefs::kSelectParserRelaxationEnabled,
-      /*default_value=*/true);
-
-  registry->RegisterBooleanPref(
       policy::policy_prefs::kStandardizedBrowserZoomEnabled, true);
 
   registry->RegisterBooleanPref(
@@ -2792,11 +2788,6 @@
         command_line->AppendSwitch(
             blink::switches::kCSSCustomStateDeprecatedSyntaxEnabled);
       }
-      if (!prefs->GetBoolean(
-              policy::policy_prefs::kSelectParserRelaxationEnabled)) {
-        command_line->AppendSwitch(
-            blink::switches::kDisableSelectParserRelaxation);
-      }
 
       if (prefs->GetBoolean(policy::policy_prefs::
                                 kForcePermissionPolicyUnloadDefaultEnabled)) {
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_info.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_info.cc
index b137272f..1fb8919 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_info.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_info.cc
@@ -7,6 +7,7 @@
 #include "base/containers/fixed_flat_set.h"
 #include "base/feature_list.h"
 #include "base/strings/string_number_conversions.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "components/enterprise/connectors/core/features.h"
 #include "components/enterprise/connectors/core/reporting_utils.h"
 #include "components/safe_browsing/core/common/features.h"
@@ -147,4 +148,64 @@
   return accounts.GetAllAccounts()[user_index].email;
 }
 
+// static
+std::string ContentAreaUserProvider::GetUser(Profile* profile,
+                                             const GURL& tab_url) {
+  return ContentAreaUserProvider(IdentityManagerFactory::GetForProfile(profile),
+                                 tab_url)
+      .GetContentAreaAccountEmail();
+}
+
+const GURL& ContentAreaUserProvider::tab_url() const {
+  return *tab_url_;
+}
+
+signin::IdentityManager* ContentAreaUserProvider::identity_manager() const {
+  return im_;
+}
+
+const enterprise_connectors::AnalysisSettings&
+ContentAreaUserProvider::settings() const {
+  NOTREACHED();
+}
+
+int ContentAreaUserProvider::user_action_requests_count() const {
+  NOTREACHED();
+}
+
+std::string ContentAreaUserProvider::tab_title() const {
+  NOTREACHED();
+}
+
+std::string ContentAreaUserProvider::user_action_id() const {
+  NOTREACHED();
+}
+
+std::string ContentAreaUserProvider::email() const {
+  NOTREACHED();
+}
+
+std::string ContentAreaUserProvider::url() const {
+  NOTREACHED();
+}
+
+enterprise_connectors::ContentAnalysisRequest::Reason
+ContentAreaUserProvider::reason() const {
+  NOTREACHED();
+}
+
+google::protobuf::RepeatedPtrField<::safe_browsing::ReferrerChainEntry>
+ContentAreaUserProvider::referrer_chain() const {
+  NOTREACHED();
+}
+
+google::protobuf::RepeatedPtrField<std::string>
+ContentAreaUserProvider::frame_url_chain() const {
+  NOTREACHED();
+}
+
+ContentAreaUserProvider::ContentAreaUserProvider(signin::IdentityManager* im,
+                                                 const GURL& tab_url)
+    : im_(im), tab_url_(tab_url) {}
+
 }  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_info.h b/chrome/browser/enterprise/connectors/analysis/content_analysis_info.h
index d3689aa..43f98d6 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_info.h
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_info.h
@@ -56,6 +56,34 @@
   std::string GetContentAreaAccountEmail() const;
 };
 
+// Simple implementation of `ContentAnalysisInfo` meant to be used for
+// `GetContentAreaAccountEmail` only
+class ContentAreaUserProvider : public ContentAnalysisInfo {
+ public:
+  static std::string GetUser(Profile* profile, const GURL& tab_url);
+
+ private:
+  const AnalysisSettings& settings() const override;
+  signin::IdentityManager* identity_manager() const override;
+  int user_action_requests_count() const override;
+  std::string tab_title() const override;
+  std::string user_action_id() const override;
+  std::string email() const override;
+  std::string url() const override;
+  const GURL& tab_url() const override;
+  ContentAnalysisRequest::Reason reason() const override;
+  google::protobuf::RepeatedPtrField<::safe_browsing::ReferrerChainEntry>
+  referrer_chain() const override;
+  google::protobuf::RepeatedPtrField<std::string> frame_url_chain()
+      const override;
+
+  explicit ContentAreaUserProvider(signin::IdentityManager* im,
+                                   const GURL& tab_url);
+
+  raw_ptr<signin::IdentityManager> im_;
+  raw_ref<const GURL> tab_url_;
+};
+
 }  // namespace enterprise_connectors
 
 #endif  // CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_CONTENT_ANALYSIS_INFO_H_
diff --git a/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.cc b/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.cc
index 61d7b76..c418e2a1 100644
--- a/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.cc
+++ b/chrome/browser/enterprise/data_protection/data_protection_clipboard_utils.cc
@@ -128,7 +128,10 @@
 
             bool text_blocked =
                 !result.text_results.empty() && !result.text_results[0];
+
+            // Image scan results are ignore for non local scans.
             bool image_blocked =
+                data.settings.cloud_or_local_settings.is_local_analysis() &&
                 !clipboard_paste_data.png.empty() && !result.image_result;
             if (text_blocked || image_blocked) {
               std::move(callback).Run(std::nullopt);
diff --git a/chrome/browser/enterprise/watermark/watermark_browsertest.cc b/chrome/browser/enterprise/watermark/watermark_browsertest.cc
index ea375a4..bf019e2 100644
--- a/chrome/browser/enterprise/watermark/watermark_browsertest.cc
+++ b/chrome/browser/enterprise/watermark/watermark_browsertest.cc
@@ -79,17 +79,7 @@
 
 }  // namespace
 
-// The test is enabled only for Windows since this is the standard for pixel
-// golden tests in general (single platform to account for variability in
-// rendering).
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_WatermarkShownAfterNavigation WatermarkShownAfterNavigation
-#else
-#define MAYBE_WatermarkShownAfterNavigation WatermarkShownAfterNavigation
-#endif
-
-IN_PROC_BROWSER_TEST_P(WatermarkBrowserTest,
-                       MAYBE_WatermarkShownAfterNavigation) {
+IN_PROC_BROWSER_TEST_P(WatermarkBrowserTest, WatermarkShownAfterNavigation) {
   NavigateToTestPage();
   ASSERT_TRUE(SetWatermark(GetParam()));
   ShowAndVerifyUi();
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 710ef0d2..ebe37691 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -481,6 +481,7 @@
     "//chrome/browser/safe_browsing",
     "//chrome/browser/safe_browsing:metrics_collector",
     "//chrome/browser/ui/autofill",
+    "//chrome/browser/ui/extensions",
     "//chrome/browser/ui/safety_hub",
     "//chrome/common",
     "//chrome/common/safe_browsing:proto",
diff --git a/chrome/browser/extensions/api/BUILD.gn b/chrome/browser/extensions/api/BUILD.gn
index 6f9af09e..c044555 100644
--- a/chrome/browser/extensions/api/BUILD.gn
+++ b/chrome/browser/extensions/api/BUILD.gn
@@ -34,6 +34,7 @@
     "//chrome/browser/extensions/api/module",
     "//chrome/browser/extensions/api/notifications",
     "//chrome/browser/extensions/api/permissions",
+    "//chrome/browser/extensions/api/processes",
     "//chrome/browser/extensions/api/runtime",
     "//chrome/browser/extensions/api/scripting",
   ]
@@ -58,7 +59,6 @@
       "//chrome/browser/extensions/api/omnibox",
       "//chrome/browser/extensions/api/page_capture",
       "//chrome/browser/extensions/api/preference",
-      "//chrome/browser/extensions/api/processes",
       "//chrome/browser/extensions/api/proxy",
       "//chrome/browser/extensions/api/reading_list",
       "//chrome/browser/extensions/api/resources_private",
diff --git a/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc b/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc
index 08bfeac..6b57f49 100644
--- a/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc
+++ b/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/extensions/api/history/history_api.h"
 #include "chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h"
 #include "chrome/browser/extensions/api/permissions/permissions_event_router_factory.h"
+#include "chrome/browser/extensions/api/processes/processes_api.h"
 #include "chrome/browser/extensions/commands/command_service.h"
 #include "chrome/common/buildflags.h"
 #include "extensions/buildflags/buildflags.h"
@@ -34,7 +35,6 @@
 #include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h"
 #include "chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.h"
 #include "chrome/browser/extensions/api/preference/preference_api.h"
-#include "chrome/browser/extensions/api/processes/processes_api.h"
 #include "chrome/browser/extensions/api/reading_list/reading_list_event_router_factory.h"
 #include "chrome/browser/extensions/api/sessions/sessions_api.h"
 #include "chrome/browser/extensions/api/settings_overrides/settings_overrides_api.h"
@@ -87,6 +87,7 @@
   extensions::FontSettingsAPI::GetFactoryInstance();
   extensions::HistoryAPI::GetFactoryInstance();
   extensions::PermissionsEventRouterFactory::GetInstance();
+  extensions::ProcessesAPI::GetFactoryInstance();
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   extensions::ActivityLogAPI::GetFactoryInstance();
@@ -121,7 +122,6 @@
 #if BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(USE_CUPS)
   extensions::PrintingAPIHandler::GetFactoryInstance();
 #endif
-  extensions::ProcessesAPI::GetFactoryInstance();
   extensions::ReadingListEventRouterFactory::GetInstance();
 #if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
   extensions::SafeBrowsingPrivateEventRouterFactory::GetInstance();
diff --git a/chrome/browser/extensions/api/declarative_content/content_action.cc b/chrome/browser/extensions/api/declarative_content/content_action.cc
index be74608..7de34cb 100644
--- a/chrome/browser/extensions/api/declarative_content/content_action.cc
+++ b/chrome/browser/extensions/api/declarative_content/content_action.cc
@@ -9,6 +9,7 @@
 #include "base/lazy_instance.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/strings/escape.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/extension_action_dispatcher.h"
@@ -331,13 +332,13 @@
                 kMatchForAboutSchemeAndClimbTree
           : mojom::MatchOriginAsFallbackBehavior::kNever);
   for (const auto& css_file_name : script_data.css_file_names) {
-    GURL url = extension->ResolveExtensionURL(css_file_name);
+    GURL url = extension->ResolveExtensionURL(base::EscapePath(css_file_name));
     ExtensionResource resource = extension->GetResource(css_file_name);
     script_.css_scripts().push_back(UserScript::Content::CreateFile(
         resource.extension_root(), resource.relative_path(), url));
   }
   for (const auto& js_file_name : script_data.js_file_names) {
-    GURL url = extension->ResolveExtensionURL(js_file_name);
+    GURL url = extension->ResolveExtensionURL(base::EscapePath(js_file_name));
     ExtensionResource resource = extension->GetResource(js_file_name);
     script_.js_scripts().push_back(UserScript::Content::CreateFile(
         resource.extension_root(), resource.relative_path(), url));
diff --git a/chrome/browser/extensions/api/extension_action/page_action_browsertest.cc b/chrome/browser/extensions/api/extension_action/page_action_browsertest.cc
index 064b7211..b8287add 100644
--- a/chrome/browser/extensions/api/extension_action/page_action_browsertest.cc
+++ b/chrome/browser/extensions/api/extension_action/page_action_browsertest.cc
@@ -137,6 +137,8 @@
   EXPECT_EQ(0u, extension_action_test_util::GetTotalPageActionCount(tab));
 }
 
+// TODO(crbug.com/417057394): Remove log statements once the flakiness has been
+// figured out.
 // Regression test for crbug.com/44415.
 IN_PROC_BROWSER_TEST_P(PageActionBrowserTest, PageActionRefreshCrash) {
   ExtensionRegistry* registry =
@@ -159,13 +161,16 @@
   ASSERT_EQ(size_before + 2, registry->enabled_extensions().size());
 
   std::string idA = extensionA->id();
+  LOG(INFO) << "Reloading extensionA";
   ReloadExtension(extensionA->id());
   // ExtensionA has changed, so refetch it.
   ASSERT_EQ(size_before + 2, registry->enabled_extensions().size());
   extensionA = registry->enabled_extensions().GetByID(idA);
 
+  LOG(INFO) << "Reloading extensionB";
   ReloadExtension(extensionB->id());
 
+  LOG(INFO) << "Reloading extensionA again";
   // This is where it would crash, before http://crbug.com/44415 was fixed.
   ReloadExtension(extensionA->id());
 }
diff --git a/chrome/browser/extensions/api/i18n/i18n_apitest.cc b/chrome/browser/extensions/api/i18n/i18n_apitest.cc
index 0383dbe..8865b830 100644
--- a/chrome/browser/extensions/api/i18n/i18n_apitest.cc
+++ b/chrome/browser/extensions/api/i18n/i18n_apitest.cc
@@ -15,6 +15,7 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
 static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace extensions {
 
 using ExtensionI18nTest = ExtensionApiTest;
diff --git a/chrome/browser/extensions/api/notifications/notifications_api.h b/chrome/browser/extensions/api/notifications/notifications_api.h
index 12a1f1b..dd57851 100644
--- a/chrome/browser/extensions/api/notifications/notifications_api.h
+++ b/chrome/browser/extensions/api/notifications/notifications_api.h
@@ -9,8 +9,11 @@
 
 #include "chrome/common/extensions/api/notifications.h"
 #include "extensions/browser/extension_function.h"
+#include "extensions/buildflags/buildflags.h"
 #include "ui/message_center/public/cpp/notification_types.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 class Profile;
 
 namespace message_center {
diff --git a/chrome/browser/extensions/api/notifications/notifications_apitest.cc b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
index 577a7bb..2b89bcb7 100644
--- a/chrome/browser/extensions/api/notifications/notifications_apitest.cc
+++ b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
@@ -55,6 +55,8 @@
 #include "chrome/browser/apps/platform_apps/app_browsertest_util.h"
 #endif  // BUILDFLAG(ENABLE_PLATFORM_APPS)
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 using extensions::AppWindow;
 using extensions::AppWindowRegistry;
 using extensions::Extension;
diff --git a/chrome/browser/extensions/api/permissions/permissions_apitest.cc b/chrome/browser/extensions/api/permissions/permissions_apitest.cc
index 1f7289cd..c57d384 100644
--- a/chrome/browser/extensions/api/permissions/permissions_apitest.cc
+++ b/chrome/browser/extensions/api/permissions/permissions_apitest.cc
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 #include "base/files/file_util.h"
+#include "base/path_service.h"
 #include "base/test/scoped_feature_list.h"
+#include "build/android_buildflags.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/extensions/api/permissions/permissions_api.h"
@@ -11,8 +13,7 @@
 #include "chrome/browser/extensions/extension_management_test_util.h"
 #include "chrome/browser/extensions/extension_with_management_policy_apitest.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/ui_test_utils.h"
+#include "chrome/common/chrome_paths.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "content/public/test/browser_test.h"
@@ -22,6 +23,10 @@
 #include "extensions/common/switches.h"
 #include "net/dns/mock_host_resolver.h"
 
+#if BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/net/chrome_network_delegate.h"
+#endif
+
 namespace extensions {
 
 namespace {
@@ -112,11 +117,15 @@
   ASSERT_TRUE(RunExtensionTest("permissions/favicon")) << message_;
 }
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 // Test functions and APIs that are always allowed (even if you ask for no
 // permissions).
+// TODO(crbug.com/371432155): Port to desktop Android when chrome.tabs API
+// is available.
 IN_PROC_BROWSER_TEST_F(PermissionsApiTest, AlwaysAllowed) {
   ASSERT_TRUE(RunExtensionTest("permissions/always_allowed")) << message_;
 }
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 // Tests that the optional permissions API works correctly.
 IN_PROC_BROWSER_TEST_P(PermissionsApiTestWithContextType,
@@ -127,7 +136,7 @@
   URLPatternSet explicit_hosts;
   AddPattern(&explicit_hosts, "http://*.c.com/*");
 
-  ExtensionPrefs* prefs = ExtensionPrefs::Get(browser()->profile());
+  ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
   prefs->AddRuntimeGrantedPermissions(
       "kjmkgkdkpedkejedfhmfcenooemhbpbo",
       PermissionSet(std::move(apis), ManifestPermissionSet(),
@@ -158,7 +167,7 @@
   APIPermissionSet apis;
   apis.insert(mojom::APIPermissionID::kManagement);
 
-  ExtensionPrefs* prefs = ExtensionPrefs::Get(browser()->profile());
+  ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
   prefs->AddRuntimeGrantedPermissions(
       "kjmkgkdkpedkejedfhmfcenooemhbpbo",
       PermissionSet(std::move(apis), ManifestPermissionSet(), URLPatternSet(),
@@ -220,7 +229,7 @@
           PermissionsRequestFunction::DialogAction::kAutoReject);
   PermissionsRequestFunction::SetIgnoreUserGestureForTests(true);
 
-  ExtensionPrefs* prefs = ExtensionPrefs::Get(browser()->profile());
+  ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
 
   EXPECT_TRUE(RunExtensionTest("permissions/file_access_no")) << message_;
   EXPECT_FALSE(prefs->AllowFileAccess(last_loaded_extension_id()));
@@ -233,13 +242,21 @@
 // Tests loading of files or directory listings when an extension has file
 // access.
 IN_PROC_BROWSER_TEST_F(PermissionsApiTest, FileLoad) {
+#if BUILDFLAG(IS_DESKTOP_ANDROID)
+  // Enable access to arbitrary files via file: schema. Ordinarily Chrome on
+  // Android blocks access to many directories, which affects the built-in
+  // web server this test extension accesses.
+  ChromeNetworkDelegate::EnableAccessToAllFilesForTesting(true);
+#endif
   base::ScopedTempDir temp_dir;
   {
     base::ScopedAllowBlockingForTesting allow_blocking;
     ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
     base::FilePath empty_file = temp_dir.GetPath().AppendASCII("empty.html");
-    base::FilePath original_empty_file = ui_test_utils::GetTestFilePath(
-        base::FilePath(), base::FilePath().AppendASCII("empty.html"));
+    base::FilePath dir_test_data;
+    base::PathService::Get(chrome::DIR_TEST_DATA, &dir_test_data);
+    base::FilePath original_empty_file =
+        dir_test_data.AppendASCII("empty.html");
 
     EXPECT_TRUE(base::PathExists(original_empty_file));
     EXPECT_TRUE(base::CopyFile(original_empty_file, empty_file));
@@ -265,18 +282,25 @@
   EXPECT_TRUE(RunExtensionTest("permissions/host_subsets")) << message_;
 }
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
 // Tests that requesting an optional permission from a background page, with
 // another window open, grants the permission and updates the bindings
 // (chrome.whatever, in this case chrome.alarms). Regression test for
 // crbug.com/435141, see details there for trickiness.
+// TODO(crbug.com/371432155): Port to desktop Android when chrome.tabs API
+// is available.
 IN_PROC_BROWSER_TEST_F(PermissionsApiTest, OptionalPermissionsUpdatesBindings) {
   ASSERT_TRUE(RunExtensionTest("permissions/optional_updates_bindings"))
       << message_;
 }
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
+// Desktop Android only supports manifest V3 / service worker.
+#if !BUILDFLAG(IS_ANDROID)
 INSTANTIATE_TEST_SUITE_P(PersistentBackground,
                          PermissionsApiTestWithContextType,
                          testing::Values(ContextType::kPersistentBackground));
+#endif
 INSTANTIATE_TEST_SUITE_P(ServiceWorker,
                          PermissionsApiTestWithContextType,
                          testing::Values(ContextType::kServiceWorker));
diff --git a/chrome/browser/extensions/api/processes/BUILD.gn b/chrome/browser/extensions/api/processes/BUILD.gn
index 9eb49274..2d30d47 100644
--- a/chrome/browser/extensions/api/processes/BUILD.gn
+++ b/chrome/browser/extensions/api/processes/BUILD.gn
@@ -4,7 +4,7 @@
 
 import("//extensions/buildflags/buildflags.gni")
 
-assert(enable_extensions)
+assert(enable_extensions_core)
 
 source_set("processes") {
   sources = [
diff --git a/chrome/browser/extensions/api/processes/processes_api.h b/chrome/browser/extensions/api/processes/processes_api.h
index e7c298d..f073202 100644
--- a/chrome/browser/extensions/api/processes/processes_api.h
+++ b/chrome/browser/extensions/api/processes/processes_api.h
@@ -14,6 +14,9 @@
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_event_histogram_value.h"
 #include "extensions/browser/extension_function.h"
+#include "extensions/buildflags/buildflags.h"
+
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
 
 class ProcessesApiTest;
 
diff --git a/chrome/browser/extensions/api/processes/processes_apitest.cc b/chrome/browser/extensions/api/processes/processes_apitest.cc
index 4d1ac47..8e0b9b7 100644
--- a/chrome/browser/extensions/api/processes/processes_apitest.cc
+++ b/chrome/browser/extensions/api/processes/processes_apitest.cc
@@ -2,20 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/command_line.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/api/processes/processes_api.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/task_manager/task_manager_interface.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_dialogs.h"
-#include "chrome/browser/ui/browser_window.h"
 #include "chrome/common/extensions/api/processes.h"
 #include "content/public/test/browser_test.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/switches.h"
 #include "extensions/test/extension_test_message_listener.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 class ProcessesApiTest : public extensions::ExtensionApiTest {
  public:
   ProcessesApiTest() = default;
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
index a7cda72..373b076 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc
@@ -15,6 +15,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/enterprise/connectors/analysis/content_analysis_info.h"
 #include "chrome/browser/enterprise/connectors/common.h"
 #include "chrome/browser/enterprise/connectors/connectors_service.h"
 #include "chrome/browser/policy/chrome_browser_policy_connector.h"
@@ -490,6 +491,12 @@
   if (!content_transfer_method.empty()) {
     event.Set(kKeyContentTransferMethod, content_transfer_method);
   }
+  std::string content_area_account_email =
+      enterprise_connectors::ContentAreaUserProvider::GetUser(
+          Profile::FromBrowserContext(context_), tab_url);
+  if (!content_area_account_email.empty()) {
+    event.Set(kKeyWebAppSignedInAccount, content_area_account_email);
+  }
 
   AddAnalysisConnectorVerdictToEvent(result, event);
 
@@ -550,6 +557,12 @@
   if (!content_transfer_method.empty()) {
     event.Set(kKeyContentTransferMethod, content_transfer_method);
   }
+  std::string content_area_account_email =
+      enterprise_connectors::ContentAreaUserProvider::GetUser(
+          Profile::FromBrowserContext(context_), tab_url);
+  if (!content_area_account_email.empty()) {
+    event.Set(kKeyWebAppSignedInAccount, content_area_account_email);
+  }
 
   AddAnalysisConnectorVerdictToEvent(result, event);
 
@@ -774,6 +787,12 @@
     event.Set(kKeyContentSize, base::Int64ToValue(content_size));
   }
   event.Set(kKeyTrigger, trigger);
+  std::string content_area_account_email =
+      enterprise_connectors::ContentAreaUserProvider::GetUser(
+          Profile::FromBrowserContext(context_), tab_url);
+  if (!content_area_account_email.empty()) {
+    event.Set(kKeyWebAppSignedInAccount, content_area_account_email);
+  }
   event.Set(kKeyEventResult,
             enterprise_connectors::EventResultToString(event_result));
 
diff --git a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
index d78c2441..a08c829 100644
--- a/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
+++ b/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h
@@ -78,6 +78,7 @@
   static const char kKeyTabUrl[];
   static constexpr char kKeyContentTransferMethod[] = "contentTransferMethod";
   static constexpr char kKeyHasWatermarking[] = "hasWatermarking";
+  static constexpr char kKeyWebAppSignedInAccount[] = "webAppSignedInAccount";
   static const char kKeyUnscannedReason[];
 
   // String constants for the "trigger" event field.  This corresponds to
diff --git a/chrome/browser/extensions/api/scripting/scripting_api.cc b/chrome/browser/extensions/api/scripting/scripting_api.cc
index ad62fd41..c77cf85 100644
--- a/chrome/browser/extensions/api/scripting/scripting_api.cc
+++ b/chrome/browser/extensions/api/scripting/scripting_api.cc
@@ -7,6 +7,7 @@
 #include "base/check.h"
 #include "base/containers/contains.h"
 #include "base/json/json_writer.h"
+#include "base/strings/escape.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -116,9 +117,10 @@
   std::vector<mojom::JSSourcePtr> js_sources;
   js_sources.reserve(file_sources.size());
   for (auto& file_source : file_sources) {
-    js_sources.push_back(mojom::JSSource::New(
-        std::move(*file_source.data),
-        extension.ResolveExtensionURL(file_source.file_name)));
+    js_sources.push_back(
+        mojom::JSSource::New(std::move(*file_source.data),
+                             extension.ResolveExtensionURL(
+                                 base::EscapePath(file_source.file_name))));
   }
 
   return js_sources;
@@ -134,8 +136,9 @@
   for (auto& file_source : file_sources) {
     css_sources.push_back(mojom::CSSSource::New(
         std::move(*file_source.data),
-        InjectionKeyForFile(
-            host_id, extension.ResolveExtensionURL(file_source.file_name))));
+        InjectionKeyForFile(host_id,
+                            extension.ResolveExtensionURL(
+                                base::EscapePath(file_source.file_name)))));
   }
 
   return css_sources;
@@ -574,8 +577,9 @@
 
     for (const auto& file : *injection.files) {
       sources.push_back(mojom::CSSSource::New(
-          empty_code, InjectionKeyForFile(
-                          host_id, extension()->ResolveExtensionURL(file))));
+          empty_code,
+          InjectionKeyForFile(host_id, extension()->ResolveExtensionURL(
+                                           base::EscapePath(file)))));
     }
   } else {
     DCHECK(injection.css);
diff --git a/chrome/browser/extensions/api/scripting/scripting_apitest.cc b/chrome/browser/extensions/api/scripting/scripting_apitest.cc
index 162366a5..d12d68a 100644
--- a/chrome/browser/extensions/api/scripting/scripting_apitest.cc
+++ b/chrome/browser/extensions/api/scripting/scripting_apitest.cc
@@ -380,6 +380,11 @@
       << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(ScriptingAPITest, ExecuteScriptSpecialCharacters) {
+  ASSERT_TRUE(RunExtensionTest("scripting/execute_script_special_characters"))
+      << message_;
+}
+
 // Tests that calling scripting.executeScript works on a newly created tab
 // before the initial commit has happened. Regression for crbug.com/1191971.
 // TODO(crbug.com/391921606): Port to desktop Android when we have test
diff --git a/chrome/browser/extensions/component_extension_browsertest.cc b/chrome/browser/extensions/component_extension_browsertest.cc
new file mode 100644
index 0000000..ef284da
--- /dev/null
+++ b/chrome/browser/extensions/component_extension_browsertest.cc
@@ -0,0 +1,27 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "content/public/test/browser_test.h"
+#include "extensions/test/result_catcher.h"
+
+namespace extensions {
+
+using ComponentExtensionBrowserTest = ExtensionBrowserTest;
+
+// Tests that MojoJS is enabled for component extensions that need it.
+// Note the test currently only runs for ChromeOS because the test extension
+// uses `mojoPrivate` to test and `mojoPrivate` is ChromeOS only.
+IN_PROC_BROWSER_TEST_F(ComponentExtensionBrowserTest, MojoJS) {
+  ResultCatcher result_catcher;
+
+  auto* extension =
+      LoadExtension(test_data_dir_.AppendASCII("service_worker/mojo"),
+                    {.load_as_component = true});
+  ASSERT_TRUE(extension);
+
+  ASSERT_TRUE(result_catcher.GetNextResult());
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/extension_incognito_apitest.cc b/chrome/browser/extensions/extension_incognito_apitest.cc
index e027c25..b707939 100644
--- a/chrome/browser/extensions/extension_incognito_apitest.cc
+++ b/chrome/browser/extensions/extension_incognito_apitest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/test/run_until.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/profiles/profile.h"
@@ -10,8 +11,10 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "extensions/browser/event_router.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
+#include "extensions/test/test_extension_dir.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
@@ -85,6 +88,66 @@
   // No crash.
 }
 
+// Tests that when listeners in the `incognito` service worker are not removed
+// when the `regular` service worker stops.
+IN_PROC_BROWSER_TEST_F(IncognitoApiTest, IncognitoSplitKeepListener) {
+  constexpr char kEvent[] = "tabs.onCreated";
+
+  // Prepare a test extension.
+  TestExtensionDir test_dir;
+  constexpr char kManifest[] =
+      R"({
+           "name": "Test Extension",
+           "version": "0.1",
+           "manifest_version": 3,
+           "background": {
+             "service_worker": "background.js"
+           },
+           "incognito": "split",
+           "permissions": ["tabs"]
+         })";
+  test_dir.WriteManifest(kManifest);
+  constexpr char kBackgroundJs[] =
+      R"(
+        chrome.tabs.onCreated.addListener(() => {});
+
+        self.addEventListener('install', e => e.waitUntil(skipWaiting()));
+        self.addEventListener('activate', e => {
+          chrome.test.sendMessage(
+              chrome.extension.inIncognitoContext ? "waiting_incognito"
+                                                  : "waiting");
+        });
+      )";
+  test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundJs);
+
+  ExtensionTestMessageListener listener("waiting", ReplyBehavior::kWontReply);
+  ExtensionTestMessageListener listener_incognito("waiting_incognito",
+                                                  ReplyBehavior::kWontReply);
+
+  PlatformOpenURLOffTheRecord(
+      profile(), embedded_test_server()->GetURL("/extensions/test_file.html"));
+
+  const Extension* extension = LoadExtension(
+      test_dir.UnpackedPath(),
+      {.allow_in_incognito = true, .wait_for_registration_stored = true});
+  ASSERT_TRUE(extension);
+
+  // Waits for both `regular` and `incognito` instances.
+  EXPECT_TRUE(listener.WaitUntilSatisfied());
+  EXPECT_TRUE(listener_incognito.WaitUntilSatisfied());
+
+  EventRouter* event_router = EventRouter::Get(profile());
+  ASSERT_TRUE(base::test::RunUntil([&] {
+    return event_router->ExtensionHasEventListener(extension->id(), kEvent);
+  }));
+
+  // Stops the `regular` service worker.
+  browsertest_util::StopServiceWorkerForExtensionGlobalScope(profile(),
+                                                             extension->id());
+  // The `incognito` service worker should have active listeners.
+  EXPECT_TRUE(event_router->HasNonLazyEventListenerForTesting(kEvent));
+}
+
 #if !BUILDFLAG(IS_ANDROID)
 // Tests that an extension which is enabled for incognito mode doesn't
 // accidentally create an incognito profile.
diff --git a/chrome/browser/extensions/extension_tab_util.h b/chrome/browser/extensions/extension_tab_util.h
index 361c002..ae2ffd3 100644
--- a/chrome/browser/extensions/extension_tab_util.h
+++ b/chrome/browser/extensions/extension_tab_util.h
@@ -54,6 +54,8 @@
 // Provides various utility functions that help manipulate tabs.
 class ExtensionTabUtil {
  public:
+  static constexpr char kTabNotFoundError[] = "No tab with id: *.";
+
 #if !BUILDFLAG(IS_ANDROID)
   // This file is slowly being ported to Android. For now, most of it is
   // ifdef'd out.
@@ -65,7 +67,6 @@
       "Tabs can only be moved between windows in the same profile.";
   static constexpr char kNoCurrentWindowError[] = "No current window";
   static constexpr char kWindowNotFoundError[] = "No window with id: *.";
-  static constexpr char kTabNotFoundError[] = "No tab with id: *.";
   static constexpr char kTabStripNotEditableError[] =
       "Tabs cannot be edited right now (user may be dragging a tab).";
   static constexpr char kTabStripDoesNotSupportTabGroupsError[] =
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 09fd6cb8..2cf4de8 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -8942,6 +8942,11 @@
     "expiry_milestone": 140
   },
   {
+    "name": "show-tab-list-animations",
+    "owners": [ "dregalo@google.com", "gurmeetk@google.com", "desiatyrikov@google.com" ],
+    "expiry_milestone": 150
+  },
+  {
     "name": "show-taps",
     "owners": [ "//ash/OWNERS" ],
     // This is a debug flag, so that video bug reports can show input taps to
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 6e3be12..95eed14 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -5313,6 +5313,11 @@
     "that Chrome sends to Android payment applications: app's package name, "
     "service name, payment method name, and method specific data.";
 
+const char kShowTabListAnimationsName[] =
+    "Show Tab List Animations (Android XR)";
+const char kShowTabListAnimationsDescription[] =
+    "Shows animations for each tab on the tab switcher on Android XR.";
+
 const char kSecurePaymentConfirmationAndroidName[] =
     "Secure Payment Confirmation on Android";
 const char kSecurePaymentConfirmationAndroidDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index b461424..2aca533 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -3123,6 +3123,9 @@
 extern const char kShowReadyToPayDebugInfoName[];
 extern const char kShowReadyToPayDebugInfoDescription[];
 
+extern const char kShowTabListAnimationsName[];
+extern const char kShowTabListAnimationsDescription[];
+
 extern const char kSetMarketUrlForTestingName[];
 extern const char kSetMarketUrlForTestingDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 490e1505..79fd008 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -347,6 +347,7 @@
     &kShortCircuitUnfocusAnimation,
     &kShowHomeButtonPolicyAndroid,
     &kShowNewTabAnimations,
+    &kShowTabListAnimations,
     &kPartnerCustomizationsUma,
     &kQuickDeleteAndroidSurvey,
     &kReadAloud,
@@ -578,7 +579,7 @@
 
 BASE_FEATURE(kAndroidPdfAssistContent,
              "AndroidPdfAssistContent",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 BASE_FEATURE(kAndroidTabGroupsColorUpdateGM3,
              "AndroidTabGroupsColorUpdateGM3",
@@ -1135,6 +1136,10 @@
              "ShowNewTabAnimations",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kShowTabListAnimations,
+             "ShowTabListAnimations",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kPartnerCustomizationsUma,
              "PartnerCustomizationsUma",
              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 5cd738d1..526b16b 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -178,6 +178,7 @@
 BASE_DECLARE_FEATURE(kShortCircuitUnfocusAnimation);
 BASE_DECLARE_FEATURE(kShowHomeButtonPolicyAndroid);
 BASE_DECLARE_FEATURE(kShowNewTabAnimations);
+BASE_DECLARE_FEATURE(kShowTabListAnimations);
 BASE_DECLARE_FEATURE(kOptimizeGeolocationHeaderGeneration);
 BASE_DECLARE_FEATURE(kPageAnnotationsService);
 BASE_DECLARE_FEATURE(kPageContentProvider);
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 45d160c..707a118 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
@@ -570,6 +570,7 @@
     public static final String SHARE_CUSTOM_ACTIONS_IN_CCT = "ShareCustomActionsInCCT";
     public static final String SHOW_HOME_BUTTON_POLICY_ANDROID = "ShowHomeButtonPolicyAndroid";
     public static final String SHOW_NEW_TAB_ANIMATIONS = "ShowNewTabAnimations";
+    public static final String SHOW_TAB_LIST_ANIMATIONS = "ShowTabListAnimations";
     public static final String SHOW_WARNINGS_FOR_SUSPICIOUS_NOTIFICATIONS =
             "ShowWarningsForSuspiciousNotifications";
     public static final String SKIP_ISOLATED_SPLIT_PRELOAD = "SkipIsolatedSplitPreload";
@@ -1180,6 +1181,8 @@
             newMutableFlagWithSafeDefault(SAFETY_HUB_FOLLOWUP, true);
     public static final MutableFlagWithSafeDefault sShowNewTabAnimations =
             newMutableFlagWithSafeDefault(SHOW_NEW_TAB_ANIMATIONS, false);
+    public static final MutableFlagWithSafeDefault sShowTabListAnimations =
+            newMutableFlagWithSafeDefault(SHOW_TAB_LIST_ANIMATIONS, false);
     public static final MutableFlagWithSafeDefault sSuppressToolbarCapturesAtGestureEnd =
             newMutableFlagWithSafeDefault(SUPPRESS_TOOLBAR_CAPTURES_AT_GESTURE_END, false);
     public static final MutableFlagWithSafeDefault sSwapNewTabAndNewTabInGroupAndroid =
diff --git a/chrome/browser/glic/host/glic.mojom b/chrome/browser/glic/host/glic.mojom
index f6a3048..4ce47d9 100644
--- a/chrome/browser/glic/host/glic.mojom
+++ b/chrome/browser/glic/host/glic.mojom
@@ -128,6 +128,8 @@
   kInvalidActionProto = 2,
   // Action target is not found.
   kTargetNotFound = 3,
+  // Failed to start a new task.
+  [MinVersion=1] kFailedToStartTask = 4,
 };
 // LINT.ThenChange(//tools/metrics/histograms/metadata/glic/enums.xml:ActInFocusedTabErrorReason)
 
diff --git a/chrome/browser/glic/host/glic_actor_controller.cc b/chrome/browser/glic/host/glic_actor_controller.cc
index fce867bc..dafad2d7 100644
--- a/chrome/browser/glic/host/glic_actor_controller.cc
+++ b/chrome/browser/glic/host/glic_actor_controller.cc
@@ -21,6 +21,7 @@
 #include "chrome/common/actor/action_result.h"
 #include "chrome/common/chrome_features.h"
 #include "components/optimization_guide/proto/features/actions_data.pb.h"
+#include "components/optimization_guide/proto/features/model_prototyping.pb.h"
 #include "components/tabs/public/tab_interface.h"
 
 namespace glic {
@@ -101,42 +102,31 @@
     const optimization_guide::proto::BrowserAction& action,
     const mojom::GetTabContextOptions& options,
     mojom::WebClientHandler::ActInFocusedTabCallback callback) {
-  tabs::TabHandle handle(action.tab_id());
-  // Create a new task if it one doesn't exist already.
-  if (!actor_task_ ||
-      actor_task_->GetState() == actor::ActorTask::State::kFinished) {
-    // As part of the new task we currently always create a new tab.
-    // TODO(crbug.com/411462297): This is a short term hack. This code will be
-    // deleted soon once tab_id is removed.
-
-    // Get the most recently active browser for this profile.
-    Browser* browser = chrome::FindBrowserWithProfile(profile_.get());
-    // If no browser exists create one.
-    if (!browser) {
-      browser = Browser::Create(
-          Browser::CreateParams(profile_.get(), /*user_gesture=*/false));
-    }
-    // Create a new tab.
-    browser->OpenGURL(GURL(url::kAboutBlankURL),
-                      WindowOpenDisposition::NEW_FOREGROUND_TAB);
-    tabs::TabInterface* tab = browser->GetActiveTabInterface();
-    handle = tab->GetHandle();
-
-    auto task = std::make_unique<actor::ActorTask>(
-        std::make_unique<actor::ActorCoordinator>(profile_, tab));
-    actor_task_ = task.get();
-    actor::ActorKeyedService::Get(profile_.get())->AddTask(std::move(task));
-
-    // HACK: wait one second to let navigation to about:blank finish. All of
-    // this code will be deleted soon.
-    base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
-        FROM_HERE,
-        base::BindOnce(&GlicActorController::ActImpl, GetWeakPtr(),
-                       tab->GetWeakPtr(), action, options, std::move(callback)),
-        base::Seconds(1));
+  // A task is in the process of being started. This means Act() was called
+  // twice in a row without waiting for the first one to finish.
+  if (starting_task_) {
+    PostTaskForActCallback(
+        std::move(callback),
+        mojom::ActInFocusedTabErrorReason::kFailedToStartTask);
     return;
   }
 
+  // Create a new task if it one doesn't exist already.
+  if (!actor_task_ ||
+      actor_task_->GetState() == actor::ActorTask::State::kFinished) {
+    starting_task_ = true;
+    optimization_guide::proto::BrowserStartTask start_task;
+    start_task.set_tab_id(action.tab_id());
+    // Glic doesn't know about tab IDs yet, so we set it in `start_task` but
+    // it's always 0. This will cause `StartTask` to create a new tab.
+    actor::ActorKeyedService::Get(profile_)->StartTask(
+        std::move(start_task),
+        base::BindOnce(&GlicActorController::OnTaskStartedForAct, GetWeakPtr(),
+                       action, options, std::move(callback)));
+    return;
+  }
+
+  tabs::TabHandle handle(action.tab_id());
   tabs::TabInterface* tab = handle.Get();
   if (!tab && focused_tab_data.is_focus()) {
     // The glic actor does not yet specify tab IDs. Just use the focused tab
@@ -147,6 +137,32 @@
           std::move(callback));
 }
 
+void GlicActorController::OnTaskStartedForAct(
+    const optimization_guide::proto::BrowserAction& action,
+    const mojom::GetTabContextOptions& options,
+    mojom::WebClientHandler::ActInFocusedTabCallback callback,
+    optimization_guide::proto::BrowserStartTaskResult result) {
+  starting_task_ = false;
+  if (result.status() !=
+      optimization_guide::proto::BrowserStartTaskResult::SUCCESS) {
+    PostTaskForActCallback(
+        std::move(callback),
+        mojom::ActInFocusedTabErrorReason::kFailedToStartTask);
+    return;
+  }
+
+  actor_task_ = actor::ActorKeyedService::Get(profile_)->GetTask(
+      actor::TaskId(result.task_id()));
+  CHECK(actor_task_);
+
+  // This will always grab the newly created tab (temporary hack).
+  tabs::TabHandle handle(result.tab_id());
+  tabs::TabInterface* tab = handle.Get();
+
+  ActImpl(tab ? tab->GetWeakPtr() : nullptr, action, options,
+          std::move(callback));
+}
+
 // TODO(mcnee): Determine if we need additional mechanisms, within the browser,
 // to stop a task.
 void GlicActorController::StopTask(actor::TaskId task_id) {
diff --git a/chrome/browser/glic/host/glic_actor_controller.h b/chrome/browser/glic/host/glic_actor_controller.h
index 5e330a7..bd38d292 100644
--- a/chrome/browser/glic/host/glic_actor_controller.h
+++ b/chrome/browser/glic/host/glic_actor_controller.h
@@ -13,6 +13,11 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/actor.mojom-forward.h"
 #include "components/optimization_guide/proto/features/actions_data.pb.h"
+#include "components/optimization_guide/proto/features/model_prototyping.pb.h"
+
+namespace optimization_guide::proto {
+class BrowserStartTaskResult;
+}
 
 namespace actor {
 class ActorCoordinator;
@@ -37,6 +42,10 @@
   GlicActorController& operator=(const GlicActorController&) = delete;
   ~GlicActorController();
 
+  // ActorKeyedService, the underlying framework, supports multi-tab actuation.
+  // But this class does not because it does not expose the concept of
+  // start/stop task. Instead it keeps track of any ongoing task, and implicitly
+  // creates one for Act() if one does not already exist.
   // Invokes the actor to complete an action.
   void Act(const FocusedTabData& focused_tab_data,
            const optimization_guide::proto::BrowserAction& action,
@@ -56,6 +65,12 @@
       tabs::TabInterface* tab);
 
  private:
+  void OnTaskStartedForAct(
+      const optimization_guide::proto::BrowserAction& action,
+      const mojom::GetTabContextOptions& options,
+      mojom::WebClientHandler::ActInFocusedTabCallback callback,
+      optimization_guide::proto::BrowserStartTaskResult result);
+
   // Core logic to execute an action.
   void ActImpl(base::WeakPtr<tabs::TabInterface> tab,
                const optimization_guide::proto::BrowserAction& action,
@@ -78,6 +93,8 @@
   // The most recently created task, or nullptr if no task has ever been
   // created.
   raw_ptr<actor::ActorTask> actor_task_ = nullptr;
+  // True if and only if a task is in the process of being started.
+  bool starting_task_ = false;
   base::WeakPtrFactory<GlicActorController> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/glic/host/webui_contents_container.cc b/chrome/browser/glic/host/webui_contents_container.cc
index 8e364b12..51d8a3c 100644
--- a/chrome/browser/glic/host/webui_contents_container.cc
+++ b/chrome/browser/glic/host/webui_contents_container.cc
@@ -66,7 +66,9 @@
 
 void WebUIContentsContainer::PrimaryMainFrameRenderProcessGone(
     base::TerminationStatus status) {
-  base::RecordAction(base::UserMetricsAction("GlicSessionWebUiCrash"));
+  if (status != base::TERMINATION_STATUS_NORMAL_TERMINATION) {
+    base::RecordAction(base::UserMetricsAction("GlicSessionWebUiCrash"));
+  }
   auto* keyed_service = GlicKeyedServiceFactory::GetGlicKeyedService(
       web_contents_->GetBrowserContext());
   keyed_service->CloseUI();
diff --git a/chrome/browser/glic/widget/glic_window_controller_interactive_uitest.cc b/chrome/browser/glic/widget/glic_window_controller_interactive_uitest.cc
index 2b710a6..12d0386 100644
--- a/chrome/browser/glic/widget/glic_window_controller_interactive_uitest.cc
+++ b/chrome/browser/glic/widget/glic_window_controller_interactive_uitest.cc
@@ -310,6 +310,7 @@
 
 IN_PROC_BROWSER_TEST_F(GlicWindowControllerUiTest,
                        ClientUnresponsiveThenError) {
+  base::HistogramTester histogram_tester;
   RunTestSequence(
       OpenGlicWindow(GlicWindowMode::kAttached),
       ClickMockGlicElement(kMockGlicClientHangButton, true),
@@ -318,6 +319,16 @@
                    mojom::WebUiState::kUnresponsive),
       // Client should show error after showing the unresponsive UI for 5s.
       WaitForState(test::internal::kGlicAppState, mojom::WebUiState::kError));
+  histogram_tester.ExpectTotalCount(
+      "Glic.Host.WebClientUnresponsiveState.Duration", 1);
+  histogram_tester.ExpectTotalCount("Glic.Host.WebClientUnresponsiveState", 2);
+  // One sample in the WebClientUnresponsiveState.ENTERED_FROM_CUSTOM_HEARTBEAT
+  // (1) bucket.
+  histogram_tester.ExpectBucketCount("Glic.Host.WebClientUnresponsiveState", 1,
+                                     1);
+  // One sample in the WebClientUnresponsiveState.EXITED (4) bucket.
+  histogram_tester.ExpectBucketCount("Glic.Host.WebClientUnresponsiveState", 4,
+                                     1);
 }
 
 IN_PROC_BROWSER_TEST_F(GlicWindowControllerUiTest,
diff --git a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/TabListHubLayoutAnimatorProvider.java b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/TabListHubLayoutAnimatorProvider.java
index 92ff261..647cc343 100644
--- a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/TabListHubLayoutAnimatorProvider.java
+++ b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/TabListHubLayoutAnimatorProvider.java
@@ -95,8 +95,6 @@
     }
 
     private void supplyFallbackAnimator() {
-        resetState(true);
-
         if (mAnimationType == HubLayoutAnimationType.FADE_IN) {
             mAnimatorSupplier.set(
                     FadeHubLayoutAnimationFactory.createFadeInAnimator(
@@ -114,11 +112,7 @@
     }
 
     private void supplyAnimator() {
-        if (mAnimatorSupplier.hasValue()
-                || !mAnimationDataSupplier.hasValue()
-                || mAnimationDataSupplier.get().isEmpty()) {
-            return;
-        }
+        assert !mAnimatorSupplier.hasValue() && mAnimationDataSupplier.hasValue();
 
         List<View> views = mAnimationDataSupplier.get();
         AnimatorSet animatorSet = buildAnimatorSet(views);
diff --git a/chrome/browser/keyboard_accessory/android/BUILD.gn b/chrome/browser/keyboard_accessory/android/BUILD.gn
index 09ba6c8f..b369458 100644
--- a/chrome/browser/keyboard_accessory/android/BUILD.gn
+++ b/chrome/browser/keyboard_accessory/android/BUILD.gn
@@ -52,6 +52,7 @@
     "//chrome/app:generated_resources_grit",
     "//chrome/browser",
     "//chrome/browser/autofill",
+    "//chrome/browser/password_manager/android:unit_tests",
     "//chrome/browser/password_manager/android/access_loss:test_support",
     "//chrome/browser/password_manager/android/grouped_affiliations:test_utils",
     "//chrome/browser/plus_addresses",
diff --git a/chrome/browser/keyboard_accessory/android/accessory_sheet_enums.h b/chrome/browser/keyboard_accessory/android/accessory_sheet_enums.h
index 84ba2ad..5b0e6e87 100644
--- a/chrome/browser/keyboard_accessory/android/accessory_sheet_enums.h
+++ b/chrome/browser/keyboard_accessory/android/accessory_sheet_enums.h
@@ -92,6 +92,7 @@
   SELECT_PLUS_ADDRESS_FROM_PASSWORD_SHEET = 14,
   MANAGE_PLUS_ADDRESS_FROM_PASSWORD_SHEET = 15,
   MANAGE_LOYALTY_CARDS = 16,
+  RETRIEVE_TRUSTED_VAULT_KEY = 17,
   COUNT,
 };
 // LINT.ThenChange(/tools/metrics/histograms/metadata/password/enums.xml)
diff --git a/chrome/browser/keyboard_accessory/android/manual_filling_controller_impl.cc b/chrome/browser/keyboard_accessory/android/manual_filling_controller_impl.cc
index fe0e7e31..7590ec0 100644
--- a/chrome/browser/keyboard_accessory/android/manual_filling_controller_impl.cc
+++ b/chrome/browser/keyboard_accessory/android/manual_filling_controller_impl.cc
@@ -125,10 +125,11 @@
   }
 
   // Whenever the focus changes, reset the accessory.
-  if (ShouldShowAccessory())
+  if (ShouldShowAccessory()) {
     view_->SwapSheetWithKeyboard();
-  else
+  } else {
     view_->CloseAccessorySheet();
+  }
 
   UpdateVisibility();
 }
@@ -153,8 +154,9 @@
 void ManualFillingControllerImpl::UpdateSourceAvailability(
     FillingSource source,
     bool has_suggestions) {
-  if (has_suggestions == available_sources_.contains(source))
+  if (has_suggestions == available_sources_.contains(source)) {
     return;
+  }
 
   if (has_suggestions) {
     available_sources_.insert(source);
@@ -163,8 +165,9 @@
   }
 
   available_sources_.erase(source);
-  if (!ShouldShowAccessory())
+  if (!ShouldShowAccessory()) {
     UpdateVisibility();
+  }
 }
 
 void ManualFillingControllerImpl::Hide() {
@@ -175,8 +178,9 @@
     AccessoryTabType type,
     const autofill::AccessorySheetField& selection) {
   AccessoryController* controller = GetControllerForTabType(type);
-  if (!controller)
+  if (!controller) {
     return;  // Controller not available anymore.
+  }
   controller->OnFillingTriggered(last_focused_field_id_, selection);
   view_->SwapSheetWithKeyboard();  // Soft-close the keyboard.
 }
@@ -197,8 +201,9 @@
   UMA_HISTOGRAM_ENUMERATION("KeyboardAccessory.AccessoryActionSelected",
                             selected_action, AccessoryAction::COUNT);
   AccessoryController* controller = GetControllerForAction(selected_action);
-  if (!controller)
+  if (!controller) {
     return;  // Controller not available anymore.
+  }
   controller->OnOptionSelected(selected_action);
 }
 
@@ -206,8 +211,9 @@
     AccessoryAction toggled_action,
     bool enabled) const {
   AccessoryController* controller = GetControllerForAction(toggled_action);
-  if (!controller)
+  if (!controller) {
     return;  // Controller not available anymore.
+  }
   controller->OnToggleChanged(toggled_action, enabled);
 }
 
@@ -245,8 +251,9 @@
 void ManualFillingControllerImpl::Initialize() {
   DCHECK(FromWebContents(&GetWebContents())) << "Don't call from constructor!";
   RegisterObserverForAllowedSources();
-  if (address_controller_)
+  if (address_controller_) {
     address_controller_->RefreshSuggestions();
+  }
 }
 
 ManualFillingControllerImpl::ManualFillingControllerImpl(
@@ -348,8 +355,9 @@
   TRACE_EVENT0("passwords", "ManualFillingControllerImpl::UpdateVisibility");
   if (ShouldShowAccessory()) {
     for (const FillingSource& source : available_sources_) {
-      if (source == FillingSource::AUTOFILL)
+      if (source == FillingSource::AUTOFILL) {
         continue;  // Autofill suggestions have no sheet.
+      }
       AccessoryController* controller = GetControllerForFillingSource(source);
       if (!controller) {
         continue;  // Most-likely, the controller was cleaned up already.
@@ -378,8 +386,9 @@
   for (FillingSource source : kAllowedFillingSources) {
     AccessoryController* sheet_controller =
         GetControllerForFillingSource(source);
-    if (!sheet_controller)
+    if (!sheet_controller) {
       continue;  // Ignore disallowed sheets.
+    }
     sheet_controller->RegisterFillingSourceObserver(base::BindRepeating(
         &ManualFillingControllerImpl::OnSourceAvailabilityChanged,
         weak_factory_.GetWeakPtr(), source));
@@ -432,6 +441,7 @@
     case AccessoryAction::CREATE_PLUS_ADDRESS_FROM_PASSWORD_SHEET:
     case AccessoryAction::SELECT_PLUS_ADDRESS_FROM_PASSWORD_SHEET:
     case AccessoryAction::MANAGE_PLUS_ADDRESS_FROM_PASSWORD_SHEET:
+    case AccessoryAction::RETRIEVE_TRUSTED_VAULT_KEY:
       return pwd_controller_.get();
     case AccessoryAction::MANAGE_ADDRESSES:
     case AccessoryAction::CREATE_PLUS_ADDRESS_FROM_ADDRESS_SHEET:
diff --git a/chrome/browser/keyboard_accessory/android/password_accessory_controller_impl.cc b/chrome/browser/keyboard_accessory/android/password_accessory_controller_impl.cc
index 7b8333f..cbb05d0 100644
--- a/chrome/browser/keyboard_accessory/android/password_accessory_controller_impl.cc
+++ b/chrome/browser/keyboard_accessory/android/password_accessory_controller_impl.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/password_manager/android/all_passwords_bottom_sheet_controller.h"
 #include "chrome/browser/password_manager/android/grouped_affiliations/acknowledge_grouped_credential_sheet_controller.h"
 #include "chrome/browser/password_manager/android/password_generation_controller.h"
+#include "chrome/browser/password_manager/android/password_manager_error_message_helper_bridge_impl.h"
 #include "chrome/browser/password_manager/android/password_manager_launcher_android.h"
 #include "chrome/browser/password_manager/android/password_manager_ui_util_android.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
@@ -55,11 +56,13 @@
 #include "components/password_manager/core/browser/password_manager_client.h"
 #include "components/password_manager/core/browser/password_manager_driver.h"
 #include "components/password_manager/core/browser/password_manager_util.h"
+#include "components/password_manager/core/browser/password_store/password_store_backend_error.h"
 #include "components/password_manager/core/browser/webauthn_credentials_delegate.h"
 #include "components/plus_addresses/grit/plus_addresses_strings.h"
 #include "components/plus_addresses/plus_address_service.h"
 #include "components/plus_addresses/plus_address_types.h"
 #include "components/resources/android/theme_resources.h"
+#include "components/sync/service/sync_service_utils.h"
 #include "components/url_formatter/elide_url.h"
 #include "components/webauthn/android/webauthn_cred_man_delegate.h"
 #include "content/public/browser/render_frame_host.h"
@@ -79,6 +82,7 @@
 using webauthn::WebAuthnCredManDelegate;
 using BlocklistedStatus =
     password_manager::OriginCredentialStore::BlocklistedStatus;
+using BackendErrorType = password_manager::PasswordStoreBackendErrorType;
 using FillingSource = ManualFillingController::FillingSource;
 using IsExactMatch = autofill::UserInfo::IsExactMatch;
 using ShouldShowAction = ManualFillingController::ShouldShowAction;
@@ -86,6 +90,19 @@
 
 namespace {
 
+constexpr auto kRequiresTrustedVaultRetrievalErrorTypes =
+    base::MakeFixedFlatSet<BackendErrorType>(
+        {BackendErrorType::kKeyRetrievalRequired,
+         BackendErrorType::kEmptySecurityDomain,
+         BackendErrorType::kIrretrievableSecurityDomain});
+
+bool RequiresTrustedVaultRetrieval(
+    const std::optional<password_manager::PasswordStoreBackendError>&
+        backend_error) {
+  return backend_error &&
+         kRequiresTrustedVaultRetrievalErrorTypes.contains(backend_error->type);
+}
+
 autofill::UserInfo TranslateCredentials(const UiCredential& credential,
                                         const url::Origin& frame_origin,
                                         bool current_field_is_password,
@@ -122,12 +139,18 @@
   return user_info;
 }
 
-std::u16string GetPasswordTitle(bool has_credentials,
-                                bool has_standalone_plus_addresses,
-                                const url::Origin& origin) {
+std::u16string GetPasswordTitle(
+    bool has_credentials,
+    bool has_standalone_plus_addresses,
+    std::optional<password_manager::PasswordStoreBackendError> backend_error,
+    const url::Origin& origin) {
   const std::u16string elided_url =
       url_formatter::FormatOriginForSecurityDisplay(
           origin, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
+  if (RequiresTrustedVaultRetrieval(backend_error)) {
+    return l10n_util::GetStringUTF16(
+        IDS_PASSWORD_MANAGER_ACCESSORY_TRUSTED_VAULT_KEY_RETRIEVAL_REQUIRED_MESSAGE);
+  }
   if (!has_credentials) {
     return l10n_util::GetStringFUTF16(
         IDS_PASSWORD_MANAGER_ACCESSORY_PASSWORD_LIST_EMPTY_MESSAGE, elided_url);
@@ -290,7 +313,7 @@
   AccessorySheetData data = autofill::CreateAccessorySheetData(
       autofill::AccessoryTabType::PASSWORDS,
       GetPasswordTitle(has_suggestions, !plus_address_info_to_add.empty(),
-                       origin),
+                       credential_cache_->backend_error(), origin),
       GetPlusAddressTitle(!plus_address_info_to_add.empty(), origin),
       std::move(info_to_add), CreateManagePasswordsFooter());
   std::ranges::for_each(std::move(passkeys_to_add),
@@ -366,7 +389,8 @@
             ChromePasswordManagerClient::FromWebContents(web_contents),
             base::BindRepeating(GetPasswordManagerDriver),
             std::make_unique<AcknowledgeGroupedCredentialSheetController>(),
-            std::make_unique<PasswordAccessLossWarningBridgeImpl>())));
+            std::make_unique<PasswordAccessLossWarningBridgeImpl>(),
+            std::make_unique<PasswordManagerErrorMessageHelperBridgeImpl>())));
   }
 }
 
@@ -379,8 +403,9 @@
     PasswordDriverSupplierForFocusedFrame driver_supplier,
     std::unique_ptr<AcknowledgeGroupedCredentialSheetController>
         grouped_credential_sheet_controller,
-    std::unique_ptr<PasswordAccessLossWarningBridge>
-        access_loss_warning_bridge) {
+    std::unique_ptr<PasswordAccessLossWarningBridge> access_loss_warning_bridge,
+    std::unique_ptr<PasswordManagerErrorMessageHelperBridge>
+        password_manager_error_message_helper_bridge) {
   DCHECK(web_contents) << "Need valid WebContents to attach controller to!";
   DCHECK(!FromWebContents(web_contents)) << "Controller already attached!";
   DCHECK(manual_filling_controller);
@@ -392,7 +417,8 @@
           web_contents, credential_cache, std::move(manual_filling_controller),
           password_client, std::move(driver_supplier),
           std::move(grouped_credential_sheet_controller),
-          std::move(access_loss_warning_bridge))));
+          std::move(access_loss_warning_bridge),
+          std::move(password_manager_error_message_helper_bridge))));
 }
 
 void PasswordAccessoryControllerImpl::OnOptionSelected(
@@ -484,6 +510,12 @@
       base::RecordAction(base::UserMetricsAction(
           "PlusAddresses.ManageOptionOnPasswordManualFallbackSelected"));
       return;
+    case autofill::AccessoryAction::RETRIEVE_TRUSTED_VAULT_KEY:
+      password_manager_error_message_helper_bridge_
+          ->StartTrustedVaultKeyRetrievalFlow(
+              &GetWebContents(), syncer::TrustedVaultUserActionTriggerForUMA::
+                                     kPasswordManagerKeyboardAccessory);
+      return;
     default:
       NOTREACHED() << "Unhandled selected action: "
                    << static_cast<int>(selected_action);
@@ -598,7 +630,9 @@
     PasswordDriverSupplierForFocusedFrame driver_supplier,
     std::unique_ptr<AcknowledgeGroupedCredentialSheetController>
         grouped_credential_sheet_controller,
-    std::unique_ptr<PasswordAccessLossWarningBridge> access_loss_warning_bridge)
+    std::unique_ptr<PasswordAccessLossWarningBridge> access_loss_warning_bridge,
+    std::unique_ptr<PasswordManagerErrorMessageHelperBridge>
+        password_manager_error_message_helper_bridge)
     : content::WebContentsObserver(web_contents),
       content::WebContentsUserData<PasswordAccessoryControllerImpl>(
           *web_contents),
@@ -606,6 +640,8 @@
       manual_filling_controller_(std::move(manual_filling_controller)),
       password_client_(password_client),
       driver_supplier_(std::move(driver_supplier)),
+      password_manager_error_message_helper_bridge_(
+          std::move(password_manager_error_message_helper_bridge)),
       grouped_credential_sheet_controller_(
           std::move(grouped_credential_sheet_controller)),
       access_loss_warning_bridge_(std::move(access_loss_warning_bridge)),
@@ -633,6 +669,13 @@
     }
   }
 
+  if (RequiresTrustedVaultRetrieval(credential_cache_->backend_error())) {
+    footer_commands_to_add.emplace_back(
+        l10n_util::GetStringUTF16(
+            IDS_PASSWORD_MANAGER_ACCESSORY_RETRIEVE_TRUSTED_VAULT_KEY),
+        autofill::AccessoryAction::RETRIEVE_TRUSTED_VAULT_KEY);
+  }
+
   if (all_passwords_helper_.available_credentials().has_value() &&
       IsSecureSite() &&
       GetFocusedFrameOrigin().GetURL().SchemeIsCryptographic() &&
diff --git a/chrome/browser/keyboard_accessory/android/password_accessory_controller_impl.h b/chrome/browser/keyboard_accessory/android/password_accessory_controller_impl.h
index 54a9db7a3..d67610e 100644
--- a/chrome/browser/keyboard_accessory/android/password_accessory_controller_impl.h
+++ b/chrome/browser/keyboard_accessory/android/password_accessory_controller_impl.h
@@ -18,6 +18,7 @@
 #include "chrome/browser/password_manager/android/access_loss/password_access_loss_warning_bridge.h"
 #include "chrome/browser/password_manager/android/all_passwords_bottom_sheet_helper.h"
 #include "chrome/browser/password_manager/android/grouped_affiliations/acknowledge_grouped_credential_sheet_controller.h"
+#include "chrome/browser/password_manager/android/password_manager_error_message_helper_bridge.h"
 #include "components/autofill/core/common/mojom/autofill_types.mojom-forward.h"
 #include "components/autofill/core/common/password_generation_util.h"
 #include "components/password_manager/core/browser/credential_cache.h"
@@ -100,7 +101,9 @@
       std::unique_ptr<AcknowledgeGroupedCredentialSheetController>
           grouped_credential_sheet_controller,
       std::unique_ptr<PasswordAccessLossWarningBridge>
-          access_loss_warning_bridge);
+          access_loss_warning_bridge,
+      std::unique_ptr<PasswordManagerErrorMessageHelperBridge>
+          password_manager_error_message_helper_bridge);
 
   // Returns true if the current site attached to `web_contents_` has a SECURE
   // security level.
@@ -131,7 +134,9 @@
       std::unique_ptr<AcknowledgeGroupedCredentialSheetController>
           grouped_credential_sheet_controller,
       std::unique_ptr<PasswordAccessLossWarningBridge>
-          access_loss_warning_bridge);
+          access_loss_warning_bridge,
+      std::unique_ptr<PasswordManagerErrorMessageHelperBridge>
+          password_manager_error_message_helper_bridge);
 
  private:
   friend class content::WebContentsUserData<PasswordAccessoryControllerImpl>;
@@ -290,6 +295,9 @@
   std::unique_ptr<AllPasswordsBottomSheetController>
       all_passords_bottom_sheet_controller_;
 
+  std::unique_ptr<PasswordManagerErrorMessageHelperBridge>
+      password_manager_error_message_helper_bridge_;
+
   // Helper for determining whether a bottom sheet showing passwords is useful.
   AllPasswordsBottomSheetHelper all_passwords_helper_{
       password_client_->GetProfilePasswordStore(),
diff --git a/chrome/browser/keyboard_accessory/android/password_accessory_controller_impl_unittest.cc b/chrome/browser/keyboard_accessory/android/password_accessory_controller_impl_unittest.cc
index 54a69087..b9c39d8 100644
--- a/chrome/browser/keyboard_accessory/android/password_accessory_controller_impl_unittest.cc
+++ b/chrome/browser/keyboard_accessory/android/password_accessory_controller_impl_unittest.cc
@@ -28,6 +28,7 @@
 #include "chrome/browser/keyboard_accessory/test_utils/android/mock_manual_filling_controller.h"
 #include "chrome/browser/password_manager/android/access_loss/mock_password_access_loss_warning_bridge.h"
 #include "chrome/browser/password_manager/android/grouped_affiliations/acknowledge_grouped_credential_sheet_controller_test_helper.h"
+#include "chrome/browser/password_manager/android/mock_password_manager_error_message_helper_bridge.h"
 #include "chrome/browser/password_manager/android/password_generation_controller.h"
 #include "chrome/browser/password_manager/android/password_generation_controller_impl.h"
 #include "chrome/browser/password_manager/password_manager_test_util.h"
@@ -63,6 +64,7 @@
 #include "components/resources/android/theme_resources.h"
 #include "components/security_state/core/security_state.h"
 #include "components/strings/grit/components_strings.h"
+#include "components/sync/service/sync_service_utils.h"
 #include "components/webauthn/android/cred_man_support.h"
 #include "components/webauthn/android/webauthn_cred_man_delegate.h"
 #include "components/webauthn/android/webauthn_cred_man_delegate_factory.h"
@@ -255,6 +257,11 @@
       IDS_PASSWORD_MANAGER_ACCESSORY_PASSWORD_LIST_TITLE, domain);
 }
 
+std::u16string trusted_vault_key_retrieval_required_title() {
+  return l10n_util::GetStringUTF16(
+      IDS_PASSWORD_MANAGER_ACCESSORY_TRUSTED_VAULT_KEY_RETRIEVAL_REQUIRED_MESSAGE);
+}
+
 std::u16string plus_address_title(const std::u16string& domain) {
   return l10n_util::GetStringFUTF16(
       IDS_PLUS_ADDRESS_FALLBACK_MANUAL_FILLING_SHEET_TITLE, domain);
@@ -269,6 +276,11 @@
       IDS_PASSWORD_MANAGER_ACCESSORY_SELECT_PASSWORD);
 }
 
+std::u16string retrieve_trusted_vault_key_str() {
+  return l10n_util::GetStringUTF16(
+      IDS_PASSWORD_MANAGER_ACCESSORY_RETRIEVE_TRUSTED_VAULT_KEY);
+}
+
 std::u16string manage_passwords_str() {
   return l10n_util::GetStringUTF16(
       IDS_PASSWORD_MANAGER_ACCESSORY_ALL_PASSWORDS_LINK);
@@ -341,8 +353,7 @@
  public:
   PasswordAccessoryControllerTest()
       : ChromeRenderViewHostTestHarness(
-            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
-  }
+            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
 
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
@@ -403,13 +414,18 @@
     auto access_loss_bridge =
         std::make_unique<MockPasswordAccessLossWarningBridge>();
     mock_access_loss_warning_bridge_ = access_loss_bridge.get();
+
+    auto error_message_helper_bridge =
+        std::make_unique<MockPasswordManagerErrorMessageHelperBridge>();
+    mock_error_message_helper_bridge_ = error_message_helper_bridge.get();
+
     PasswordAccessoryControllerImpl::CreateForWebContentsForTesting(
         web_contents(), cache(), mock_manual_filling_controller_.AsWeakPtr(),
         mock_pwd_manager_client_.get(),
         base::BindRepeating(&PasswordAccessoryControllerTest::GetBaseDriver,
                             base::Unretained(this)),
         grouped_credential_sheet_test_helper.CreateController(),
-        std::move(access_loss_bridge));
+        std::move(access_loss_bridge), std::move(error_message_helper_bridge));
 
     controller()->RegisterFillingSourceObserver(filling_source_observer_.Get());
     controller()->SetSecurityLevelForTesting(security_level);
@@ -469,6 +485,8 @@
   AcknowledgeGroupedCredentialSheetControllerTestHelper
       grouped_credential_sheet_test_helper;
   raw_ptr<MockPasswordAccessLossWarningBridge> mock_access_loss_warning_bridge_;
+  raw_ptr<MockPasswordManagerErrorMessageHelperBridge>
+      mock_error_message_helper_bridge_;
   scoped_refptr<MockPasswordStoreInterface> mock_account_password_store_;
   scoped_refptr<MockPasswordStoreInterface> mock_profile_password_store_;
 
@@ -507,7 +525,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -529,7 +547,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -559,7 +577,7 @@
       CreateEntry("Cat", "M1@u", GURL(kExampleSite),
                   PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -596,7 +614,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   // Pretend that any input in the same frame was focused.
@@ -617,7 +635,7 @@
 TEST_F(PasswordAccessoryControllerTest, ProvidesEmptySuggestionsMessage) {
   CreateSheetController();
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(false),
+      {}, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -638,7 +656,7 @@
       CreateEntry("", "p455w0rd", GURL(kExampleSite),
                   PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   // Pretend a username field was focused. This should result in non-interactive
@@ -690,7 +708,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
   EXPECT_CALL(filling_source_observer_,
               Run(controller(), IsFillingSourceAvailable(true)));
@@ -710,7 +728,7 @@
       "Alf", "M3lm4k", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
       changed_matches, CredentialCache::IsOriginBlocklisted(false),
-      url::Origin::Create(GURL(kExampleSite)));
+      std::nullopt, url::Origin::Create(GURL(kExampleSite)));
   EXPECT_CALL(filling_source_observer_,
               Run(controller(), IsFillingSourceAvailable(true)));
   controller()->RefreshSuggestionsForField(
@@ -734,7 +752,7 @@
       CreateEntry("Alf", "R4nd0m", GURL(kExampleSiteMobile),
                   PasswordForm::MatchType::kPSL)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   EXPECT_CALL(filling_source_observer_,
@@ -767,7 +785,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   // Pretend a username field was focused. This should result in non-emtpy
@@ -804,7 +822,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -850,7 +868,7 @@
 TEST_F(PasswordAccessoryControllerTest, AddsGenerationCommandWhenAvailable) {
   CreateSheetController();
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(false),
+      {}, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   ON_CALL(password_manager(), HaveFormManagersReceivedData)
@@ -880,7 +898,7 @@
        AddsGenerationCommandWhenAvailableOnTextField) {
   CreateSheetController();
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(false),
+      {}, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   ON_CALL(password_manager(), HaveFormManagersReceivedData)
@@ -908,7 +926,7 @@
        NoGenerationCommandIfGenerationIsNotEnabled) {
   CreateSheetController();
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(false),
+      {}, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   ON_CALL(password_manager(), HaveFormManagersReceivedData)
@@ -928,7 +946,7 @@
 TEST_F(PasswordAccessoryControllerTest, NoGenerationCommandIfNoFormsReceived) {
   CreateSheetController();
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(false),
+      {}, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   ON_CALL(password_manager(), HaveFormManagersReceivedData)
@@ -961,7 +979,7 @@
 TEST_F(PasswordAccessoryControllerTest, AddsSaveToggleIfIsBlocklisted) {
   CreateSheetController();
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(true),
+      {}, CredentialCache::IsOriginBlocklisted(true), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
   ON_CALL(*password_client(), IsSavingAndFillingEnabled(GURL(kExampleSite)))
       .WillByDefault(Return(true));
@@ -991,7 +1009,7 @@
       .WillByDefault(Return(false));
 
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(true),
+      {}, CredentialCache::IsOriginBlocklisted(true), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   EXPECT_CALL(filling_source_observer_,
@@ -1009,11 +1027,11 @@
 TEST_F(PasswordAccessoryControllerTest, AddsSaveToggleIfWasBlocklisted) {
   CreateSheetController();
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(true),
+      {}, CredentialCache::IsOriginBlocklisted(true), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
   // Simulate unblocklisting.
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(false),
+      {}, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
   ON_CALL(*password_client(), IsSavingAndFillingEnabled(GURL(kExampleSite)))
       .WillByDefault(Return(true));
@@ -1036,7 +1054,7 @@
 TEST_F(PasswordAccessoryControllerTest, AddsSaveToggleOnAnyFieldIfBlocked) {
   CreateSheetController();
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(true),
+      {}, CredentialCache::IsOriginBlocklisted(true), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
   ON_CALL(*password_client(), IsSavingAndFillingEnabled(GURL(kExampleSite)))
       .WillByDefault(Return(true));
@@ -1094,7 +1112,7 @@
       CreateEntry("example@gmail", "S3cur3", GURL(kExampleSite),
                   PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   MockAffiliatedPlusProfilesProvider provider;
@@ -1139,7 +1157,7 @@
       CreateEntry("foo.bar@gmail", "S3cur3", GURL(kExampleSite),
                   PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   MockAffiliatedPlusProfilesProvider provider;
@@ -1304,7 +1322,7 @@
   base::HistogramTester histogram_tester;
 
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(true),
+      {}, CredentialCache::IsOriginBlocklisted(true), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
   ON_CALL(*password_client(), IsSavingAndFillingEnabled(GURL(kExampleSite)))
       .WillByDefault(Return(true));
@@ -1324,11 +1342,11 @@
   base::HistogramTester histogram_tester;
 
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(true),
+      {}, CredentialCache::IsOriginBlocklisted(true), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
   // Simulate unblocklisting.
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(false),
+      {}, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   ON_CALL(*password_client(), IsSavingAndFillingEnabled(GURL(kExampleSite)))
@@ -1410,7 +1428,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -1439,7 +1457,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -1477,7 +1495,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -1520,7 +1538,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -1565,7 +1583,7 @@
   form.app_display_name = "Example android app";
   std::vector<PasswordForm> matches = {form};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -1601,7 +1619,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kGrouped)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -1636,7 +1654,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kGrouped)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -1664,7 +1682,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kGrouped)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -1705,7 +1723,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -1845,7 +1863,7 @@
   cred_man_delegate()->OnCredManConditionalRequestPending(
       /*has_results=*/true, cred_man_callback.Get());
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(false),
+      {}, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -1877,7 +1895,7 @@
       .WillByDefault(Return(true));
   CreateSheetController();
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(false),
+      {}, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -1946,7 +1964,7 @@
       .WillByDefault(Return(base::ok(&kTestPasskeys)));
   CreateSheetController();
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(false),
+      {}, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -1977,7 +1995,7 @@
        HybridPasskeyOptionNotShownWhenUnavailable) {
   CreateSheetController();
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      {}, CredentialCache::IsOriginBlocklisted(false),
+      {}, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
 
   controller()->RefreshSuggestionsForField(
@@ -2015,7 +2033,7 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, CredentialCache::IsOriginBlocklisted(false),
+      matches, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
       url::Origin::Create(GURL(kExampleSite)));
   controller()->RefreshSuggestionsForField(
       FocusedFieldType::kFillableUsernameField,
@@ -2085,6 +2103,39 @@
             1);
 }
 
+TEST_F(PasswordAccessoryControllerTest, ShowTrustedVaultError) {
+  CreateSheetController();
+  cache()->SaveCredentialsAndBlocklistedForOrigin(
+      {}, CredentialCache::IsOriginBlocklisted(false),
+      password_manager::PasswordStoreBackendErrorType::kKeyRetrievalRequired,
+      url::Origin::Create(GURL(kExampleSite)));
+
+  controller()->RefreshSuggestionsForField(
+      FocusedFieldType::kFillableUsernameField,
+      /*is_field_eligible_for_manual_generation=*/false);
+
+  EXPECT_EQ(
+      controller()->GetSheetData(),
+      AccessorySheetData::Builder(
+          AccessoryTabType::PASSWORDS,
+          /*user_info_title=*/trusted_vault_key_retrieval_required_title(),
+          /*plus_address_title=*/std::u16string())
+          .AppendFooterCommand(
+              retrieve_trusted_vault_key_str(),
+              autofill::AccessoryAction::RETRIEVE_TRUSTED_VAULT_KEY)
+          .AppendFooterCommand(manage_passwords_str(),
+                               autofill::AccessoryAction::MANAGE_PASSWORDS)
+          .Build());
+
+  EXPECT_CALL(*mock_error_message_helper_bridge_,
+              StartTrustedVaultKeyRetrievalFlow(
+                  _, syncer::TrustedVaultUserActionTriggerForUMA::
+                         kPasswordManagerKeyboardAccessory));
+
+  controller()->OnOptionSelected(
+      autofill::AccessoryAction::RETRIEVE_TRUSTED_VAULT_KEY);
+}
+
 class PasswordAccessoryControllerWithTestStoreTest
     : public PasswordAccessoryControllerTest,
       public testing::WithParamInterface<bool> {
diff --git a/chrome/browser/on_device_translation/language_pack_util.cc b/chrome/browser/on_device_translation/language_pack_util.cc
index 259fd5c..acbed61 100644
--- a/chrome/browser/on_device_translation/language_pack_util.cc
+++ b/chrome/browser/on_device_translation/language_pack_util.cc
@@ -102,24 +102,6 @@
 
 }  // namespace
 
-bool IsPopularLanguage(SupportedLanguage supported_language) {
-  return supported_language == SupportedLanguage::kEn ||
-         supported_language == SupportedLanguage::kZh ||
-         supported_language == SupportedLanguage::kZhHant ||
-         supported_language == SupportedLanguage::kJa ||
-         supported_language == SupportedLanguage::kPt ||
-         supported_language == SupportedLanguage::kRu ||
-         supported_language == SupportedLanguage::kEs ||
-         supported_language == SupportedLanguage::kTr ||
-         supported_language == SupportedLanguage::kHi ||
-         supported_language == SupportedLanguage::kVi ||
-         supported_language == SupportedLanguage::kBn ||
-         supported_language == SupportedLanguage::kKn ||
-         supported_language == SupportedLanguage::kTa ||
-         supported_language == SupportedLanguage::kTe ||
-         supported_language == SupportedLanguage::kMr;
-}
-
 // Converts a SupportedLanguage to a language code.
 std::string_view ToLanguageCode(SupportedLanguage supported_language) {
   return kSupportedLanguageCodeMap.at(supported_language);
diff --git a/chrome/browser/on_device_translation/language_pack_util.h b/chrome/browser/on_device_translation/language_pack_util.h
index e6c17768..8eef7ef 100644
--- a/chrome/browser/on_device_translation/language_pack_util.h
+++ b/chrome/browser/on_device_translation/language_pack_util.h
@@ -85,10 +85,6 @@
 std::optional<SupportedLanguage> ToSupportedLanguage(
     std::string_view language_code);
 
-// Returns whether the language is in the top 12 by number of native speakers.
-// https://en.wikipedia.org/wiki/List_of_languages_by_number_of_native_speakers#Top_languages_by_population
-bool IsPopularLanguage(SupportedLanguage supported_language);
-
 // The key for language pack components.
 enum class LanguagePackKey {
   kEn_Es = 0,
diff --git a/chrome/browser/on_device_translation/language_pack_util_unittest.cc b/chrome/browser/on_device_translation/language_pack_util_unittest.cc
index a1fc636d..43ab8c2 100644
--- a/chrome/browser/on_device_translation/language_pack_util_unittest.cc
+++ b/chrome/browser/on_device_translation/language_pack_util_unittest.cc
@@ -106,49 +106,6 @@
   EXPECT_EQ(ToSupportedLanguage(""), std::nullopt);
 }
 
-TEST(LanguagePackUtilTest, IsPopularLanguage) {
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kEn));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kEn));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kEs));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kJa));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kAr));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kBn));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kDe));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kFr));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kHi));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kIt));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kKo));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kNl));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kPl));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kPt));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kRu));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kTh));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kTr));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kVi));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kZh));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kZhHant));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kBg));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kCs));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kDa));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kEl));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kFi));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kHr));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kHu));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kId));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kIw));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kLt));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kNo));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kRo));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kSk));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kSl));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kSv));
-  EXPECT_FALSE(IsPopularLanguage(SupportedLanguage::kUk));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kKn));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kTa));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kTe));
-  EXPECT_TRUE(IsPopularLanguage(SupportedLanguage::kMr));
-}
-
 TEST(LanguagePackUtilTest, GetLanguagePackComponentConfig) {
   // En to Es.
   EXPECT_EQ(GetLanguagePackComponentConfig(LanguagePackKey::kEn_Es).language1,
diff --git a/chrome/browser/on_device_translation/on_device_translation_browsertest.cc b/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
index e07d1fe0..53af467 100644
--- a/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
+++ b/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
@@ -186,8 +186,6 @@
     case CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired:
       return "downloadable";
     case CanCreateTranslatorResult::kNoNotSupportedLanguage:
-    case CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
-    case CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation:
     case CanCreateTranslatorResult::kNoServiceCrashed:
     case CanCreateTranslatorResult::kNoDisallowedByPolicy:
     case CanCreateTranslatorResult::kNoExceedsServiceCountLimitation:
@@ -362,6 +360,36 @@
     EXPECT_FALSE(console_observer.messages().empty());
   }
 
+  void ClearSiteContentSettings() {
+    content::BrowsingDataRemover* remover =
+        browser()->profile()->GetBrowsingDataRemover();
+    content::BrowsingDataRemoverCompletionObserver observer(remover);
+    remover->RemoveAndReply(
+        base::Time(), base::Time::Max(),
+        chrome_browsing_data_remover::DATA_TYPE_CONTENT_SETTINGS,
+        chrome_browsing_data_remover::ALL_ORIGIN_TYPES, &observer);
+    observer.BlockUntilCompletion();
+  }
+
+  content::RenderFrameHost* CreateIframe(Browser* target_browser = nullptr) {
+    EXPECT_EQ(EvalJsCatchingError(R"(
+      window._iframe = document.createElement('iframe');
+      document.body.appendChild(window._iframe);
+      return "OK";
+  )",
+                                  target_browser),
+              "OK");
+
+    return ChildFrameAt((target_browser ? target_browser : browser())
+                            ->tab_strip_model()
+                            ->GetActiveWebContents(),
+                        0);
+  }
+
+  bool RemoveIframe(Browser* target_browser = nullptr) {
+    return ExecJs("document.body.removeChild(window._iframe);");
+  }
+
  private:
   base::ScopedTempDir tmp_dir_;
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -474,10 +502,13 @@
             "en to ja: hello");
 }
 
+// TODO(crbug.com/421947718): Disabled because there's a race between triggering
+// user activation and consuming it when calling `create` multiple times.
+//
 // Tests the behavior of multiple create() calls with different
 // source/target languages.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       CreateTranslatorInstallMultipleLanguagePacks) {
+                       DISABLED_CreateTranslatorInstallMultipleLanguagePacks) {
   MockComponentManager mock_component_manager(GetTempDir());
   NavigateToEmptyPage();
 
@@ -502,41 +533,45 @@
         run_loop_for_register_en_es_language_pack.Quit();
       }));
 
+  // Helper function to get the state of a promise at the moment the helper
+  // function is called.
+  EXPECT_TRUE(ExecJs(R"(
+    self.getPromiseState = async promise => {
+        const symbol = Symbol();
+        try {
+          const result = await Promise.race([promise, Promise.resolve(symbol)]);
+          return result == symbol ? "pending" : "fulfilled";
+        } catch (e) {
+          return "rejected";
+        }
+    }
+  )"));
+
   // Create create() multiple times.
   //   1. En => Ja.
   //   2. En => Es.
   //   3. En => Ja.
-  EXPECT_EQ(EvalJsCatchingError(R"(
-      window._testEnJaPromise1 = Translator.create({
+  EXPECT_TRUE(ExecJs(R"(
+      self.enJaPromise1 = Translator.create({
           sourceLanguage: 'en',
           targetLanguage: 'ja',
         });
-      window._testEnJaPromise1Resolved = false;
-      window._testEnJaPromise1.then(() => {
-        window._testEnJaPromise1Resolved = true;
-      });
-
-      window._testEnEsPromise = Translator.create({
+  )",
+                     browser(), content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
+  EXPECT_TRUE(ExecJs(R"(
+      self.enEsPromise = Translator.create({
           sourceLanguage: 'en',
           targetLanguage: 'es',
         });
-      window._testEnEsPromiseResolved = false;
-      window._testEnEsPromise.then(() => {
-        window._testEnEsPromiseResolved = true;
-      });
-
-      window._testEnJaPromise2 = Translator.create({
+  )",
+                     browser(), content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
+  EXPECT_TRUE(ExecJs(R"(
+      self.enJaPromise2 = Translator.create({
           sourceLanguage: 'en',
           targetLanguage: 'ja',
         });
-      window._testEnJaPromise2Resolved = false;
-      window._testEnJaPromise2.then(() => {
-        window._testEnJaPromise2Resolved = true;
-      });
-
-      return 'OK';
-  )"),
-            "OK");
+  )",
+                     browser(), content::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
 
   // Wait until RegisterTranslateKitComponentImpl() is called.
   run_loop_for_register_translate_kit.Run();
@@ -549,40 +584,40 @@
   mock_component_manager.InstallMockTranslateKitComponent();
 
   // All promises should not be resolved yet.
-  EXPECT_FALSE(EvalJs("window._testEnJaPromise1Resolved").ExtractBool());
-  EXPECT_FALSE(EvalJs("window._testEnJaPromise2Resolved").ExtractBool());
-  EXPECT_FALSE(EvalJs("window._testEnEsPromiseResolved").ExtractBool());
+  EXPECT_EQ(EvalJs("getPromiseState(enJaPromise1)").ExtractString(), "pending");
+  EXPECT_EQ(EvalJs("getPromiseState(enEsPromise)").ExtractString(), "pending");
+  EXPECT_EQ(EvalJs("getPromiseState(enJaPromise2)").ExtractString(), "pending");
 
   // Install the mock `en_ja` language pack.
   mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ja);
 
   // Translate to Japanese. Both `en_ja` promises should be resolved now.
+  EXPECT_EQ(EvalJsCatchingError(
+                "return await (await enJaPromise1).translate('hello');"),
+            "en to ja: hello");
   EXPECT_EQ(
-      EvalJsCatchingError(
-          "return await (await window._testEnJaPromise1).translate('hello');"),
-      "en to ja: hello");
-  EXPECT_EQ(
-      EvalJsCatchingError(
-          "return await (await window._testEnJaPromise2).translate('hi');"),
+      EvalJsCatchingError("return await (await enJaPromise2).translate('hi');"),
       "en to ja: hi");
 
   // The promise of `en_es` should not be resolved yet.
-  EXPECT_FALSE(EvalJs("window._testEnEsPromiseResolved").ExtractBool());
+  EXPECT_EQ(EvalJs("getPromiseState(enEsPromise)").ExtractString(), "pending");
 
   // Install the mock `en_es` language pack.
   mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Es);
 
   // Translate to Spanish. The `en_es` promise should be resolved now.
-  EXPECT_EQ(
-      EvalJsCatchingError(
-          "return await (await window._testEnEsPromise).translate('hello');"),
-      "en to es: hello");
+  EXPECT_EQ(EvalJsCatchingError(
+                "return await (await self.enEsPromise).translate('hello');"),
+            "en to es: hello");
 }
 
+// TODO(crbug.com/421947718): Disabled because there's a race between triggering
+// user activation and consuming it when calling `create` multiple times.
+//
 // Tests the behavior of create() when the number of pending tasks
 // exceeds the limit.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       ExceedMaxPendingTaskCount) {
+                       DISABLED_ExceedMaxPendingTaskCount) {
   MockComponentManager mock_component_manager(GetTempDir());
   NavigateToEmptyPage();
 
@@ -601,6 +636,12 @@
         run_loop_for_register_language_pack.Quit();
       }));
 
+  // TODO(crbug.com/421947718): Each `Translator.create` call should be in it's
+  // own `EvalJs` call like
+  // `CreateTranslator_Delay_ForTranslatorCreatedDuringInitialTranslatorCreationWithDelay`,
+  // but since we're blocked on the race issue from crbug.com/421947718, this
+  // hasn't been updated yet.
+  //
   // Call create() kMaxPendingTaskCount times.
   EXPECT_EQ(EvalJsCatchingError(base::StringPrintf(R"(
       window._testPromises = [];
@@ -670,49 +711,6 @@
             "OK");
 }
 
-// Tests the behavior of TranslationAPILimitLanguagePackCount
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       ExceedLanguagePackCount) {
-  MockComponentManager mock_component_manager(GetTempDir());
-  mock_component_manager.ExpectCallRegisterTranslateKitComponentAndInstall();
-  const base::span<const LanguagePackKey> language_packs =
-      base::span(kLanguagePackKeys);
-  NavigateToEmptyPage();
-
-  // Get the amount of packages we can install and assert that we have enough
-  // language packs for this test.
-  size_t installable_package_count =
-      on_device_translation::GetInstallablePackageCount(0);
-  ASSERT_GE(language_packs.size(), installable_package_count + 1);
-
-  // Add all the languages we're going to test to the selected languages so we
-  // don't fail PassAcceptLanguagesCheck.
-  SetSelectedLanguages(language_packs.first(installable_package_count + 1));
-
-  // Test that we can install all the language packs up to the language pack
-  // limitation.
-  mock_component_manager.ExpectCallRegisterLanguagePackComponentAndInstall(
-      language_packs.first(installable_package_count));
-  for (const auto& language_pack_key :
-       language_packs.first(installable_package_count)) {
-    TestSimpleTranslationWorks(browser(), language_pack_key);
-  }
-
-  // The language pack count is equal to the limitation. So no more language
-  // pack can be downloaded.
-  auto console_observer = CreateConsoleObserver(
-      "The Translator API language pack count exceeded the limitation. See "
-      "https://developer.chrome.com/docs/ai/"
-      "translator-api?#supported-languages for more details.");
-
-  TestCreateTranslator(browser(), language_packs.at(installable_package_count),
-                       "NotSupportedError: Unable to create translator for the "
-                       "given source and target language.");
-
-  // The console message should be logged.
-  WaitForConsoleObserver(*console_observer);
-}
-
 // Tests the behavior of the failure of translation.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest, TranslationFailure) {
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1091,71 +1089,10 @@
   ExpectUpdatesAre(expected_updates);
 }
 
-// Tests V1 behavior.
-class OnDeviceTranslationV1BrowserTest : public OnDeviceTranslationBrowserTest {
- public:
-  OnDeviceTranslationV1BrowserTest() {
-    scoped_feature_list_.InitAndEnableFeature(
-        blink::features::kTranslationAPIV1);
-  }
-  ~OnDeviceTranslationV1BrowserTest() override = default;
-
- protected:
-  void ClearSiteContentSettings() {
-    content::BrowsingDataRemover* remover =
-        browser()->profile()->GetBrowsingDataRemover();
-    content::BrowsingDataRemoverCompletionObserver observer(remover);
-    remover->RemoveAndReply(
-        base::Time(), base::Time::Max(),
-        chrome_browsing_data_remover::DATA_TYPE_CONTENT_SETTINGS,
-        chrome_browsing_data_remover::ALL_ORIGIN_TYPES, &observer);
-    observer.BlockUntilCompletion();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-// The language model limit is not triggered when the V1 flag is enabled.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
-                       NoLanguageModelLimitation) {
-  MockComponentManager mock_component_manager(GetTempDir());
-  mock_component_manager.ExpectCallRegisterTranslateKitComponentAndInstall();
-  const base::span<const LanguagePackKey> language_packs =
-      base::span(kLanguagePackKeys);
-  NavigateToEmptyPage();
-
-  // Expect that the number of available language packs is less than the
-  // installable language pack size, given there is no limitation in place.
-  size_t installable_package_count =
-      on_device_translation::GetInstallablePackageCount(0);
-  ASSERT_LE(language_packs.size() + 1, installable_package_count);
-
-  // Add all the languages we're going to test to the selected languages so we
-  // don't fail `PassAcceptLanguagesCheck`.
-  SetSelectedLanguages(language_packs);
-
-  // Test that we can install all of the possible language packs for
-  // translation.
-  mock_component_manager.ExpectCallRegisterLanguagePackComponentAndInstall(
-      language_packs);
-  for (const auto& language_pack_key : language_packs) {
-    TestSimpleTranslationWorks(browser(), language_pack_key);
-  }
-
-  // Get the last language pack key.
-  LanguagePackKey last_language_pack = *(language_packs.end() - 1);
-
-  // Confirm that the last language pack install succeeded.
-  TestTranslationAvailable(browser(), GetSourceLanguageCode(last_language_pack),
-                           GetTargetLanguageCode(last_language_pack),
-                           "available");
-}
-
 // Confirms that `Translator.availability()` is not masked for a translation
 // containing only English or the user's preferred languages.
 IN_PROC_BROWSER_TEST_F(
-    OnDeviceTranslationV1BrowserTest,
+    OnDeviceTranslationBrowserTest,
     TranslatorAvailabilityNotMasked_EnglishAndPreferredLanguages) {
   SetSelectedLanguages("fr");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1173,7 +1110,7 @@
 
 // Tests that `Translator.availability()` for a translation
 // containing a language outside of English + the user's preferred languages.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
                        TranslatorAvailabilityMasked_ForNonPreferredLanguages) {
   SetSelectedLanguages("fr");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1209,7 +1146,7 @@
 // A delay is triggered for a "downloadable" translation containing a language
 // outside of English + preferred languages.
 IN_PROC_BROWSER_TEST_F(
-    OnDeviceTranslationV1BrowserTest,
+    OnDeviceTranslationBrowserTest,
     CreateTranslator_Delay_ForMaskedDownloadableTranslation) {
   // Setup Translate Kit Component and select Spanish as the preferred language.
   SetSelectedLanguages("en,es");
@@ -1234,12 +1171,15 @@
   TestSimpleTranslationWorks(browser(), "en", "ja");
 }
 
+// TODO(crbug.com/421947718): Disabled because there's a race between triggering
+// user activation and consuming it when calling `create` multiple times.
+//
 // A delay is triggered when a second translator for a given translation is
 // created during the delay time window of an initial translator's creation
 // (which is also expected to trigger a delay).
 IN_PROC_BROWSER_TEST_F(
-    OnDeviceTranslationV1BrowserTest,
-    CreateTranslator_Delay_ForTranslatorCreatedDuringInitialTranslatorCreationWithDelay) {
+    OnDeviceTranslationBrowserTest,
+    DISABLED_CreateTranslator_Delay_ForTranslatorCreatedDuringInitialTranslatorCreationWithDelay) {
   SetSelectedLanguages("es");
   MockComponentManager mock_component_manager(GetTempDir());
   mock_component_manager.InstallMockTranslateKitComponent();
@@ -1278,7 +1218,7 @@
 // `Translator.create` should still require user activation if the language pair
 // is readily available but the site hasn't created a Translator for the
 // language pair yet.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
                        CreateRequiresUserActivationWhenDownloadedButMasked) {
   SetSelectedLanguages("es");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1301,7 +1241,7 @@
 
 // No delay is triggered for a "downloadable" translation between English +
 // preferred languages.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
                        CreateTranslator_NoDelay_DownloadableTranslation) {
   SetSelectedLanguages("en,es");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1322,7 +1262,7 @@
 
 // No delay is triggered in attempt to create a translator for an unsupported
 // language.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationV1BrowserTest,
+IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
                        CreateTranslator_NoDelay_UnsupportedLanguage) {
   SetSelectedLanguages("en,xx");
   MockComponentManager mock_component_manager(GetTempDir());
@@ -1481,16 +1421,17 @@
 
   NavigateToEmptyPage();
 
+  content::RenderFrameHost* iframe = CreateIframe();
+
   // Create a translator in an iframe.
-  EXPECT_EQ(EvalJsCatchingError(R"(
-      window._testIframe = document.createElement('iframe');
-      document.body.appendChild(window._testIframe);
-      window._testIframe.contentWindow.Translator.create({
+  EXPECT_EQ(content::EvalJs(iframe, R"(
+      Translator.create({
           sourceLanguage: 'en',
           targetLanguage: 'ja',
         });
-      return 'OK';
-    )"),
+      'OK';
+    )")
+                .ExtractString(),
             "OK");
   // Wait until RegisterTranslateKitComponentImpl() is called.
   run_loop_for_register_translate_kit.Run();
@@ -1498,7 +1439,7 @@
   run_loop_for_register_language_pack.Run();
 
   // Deletes the iframe after the browser process receives the request.
-  EXPECT_TRUE(ExecJs("document.body.removeChild(window._testIframe);"));
+  EXPECT_TRUE(RemoveIframe());
 
   // Install the mock TranslateKit component.
   mock_component_manager.InstallMockTranslateKitComponent();
@@ -1581,16 +1522,18 @@
   // Set the idle timeout to be 100 microseconds.
   service_controller->SetServiceIdleTimeoutForTesting(base::Microseconds(100));
 
+  content::RenderFrameHost* iframe = CreateIframe();
+
   // Test that Translator API on an iframe works.
-  EXPECT_EQ(EvalJsCatchingError(R"(
-      window._iframe = document.createElement('iframe');
-      document.body.appendChild(window._iframe);
-      const translator =
-          await window._iframe.contentWindow.Translator.create({
-            sourceLanguage: 'en',
-            targetLanguage: 'ja',
-          });
-      return await translator.translate('hello');
+  EXPECT_EQ(content::EvalJs(iframe, R"(
+     (async () => {
+        const translator =
+            await Translator.create({
+              sourceLanguage: 'en',
+              targetLanguage: 'ja',
+            });
+        return await translator.translate('hello');
+      })();
     )"),
             "en to ja: hello");
   // Check that the service is still running.
@@ -1624,7 +1567,15 @@
       .Times(0);
   mock_component_manager.InstallMockTranslateKitComponent();
   mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ja);
-  TestCanTranslateResult("en", "ja", CanCreateTranslatorResult::kReadily);
+
+  // Despite being ready, the availability will be masked since the site hasn't
+  // created a translator for this language pair yet.
+  // `kAfterDownloadTranslatorCreationRequired` is only ever returned in that
+  // situation, so receiving that value confirms that the package is readily
+  // available.
+  TestCanTranslateResult(
+      "en", "ja",
+      CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired);
 }
 
 // Test the behavior of availability() when the language pack is not ready.
@@ -1679,118 +1630,6 @@
                          CanCreateTranslatorResult::kNoNotSupportedLanguage);
 }
 
-// Test the behavior of availability() when the language pack is not ready, and
-// the language pack count will exceed the limitation.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       CanTranslateNoExceedsLanguagePackCountLimitation) {
-  // This test case uses English as the source language and French as the target
-  // language. To avoid the failure of PassAcceptLanguagesCheck(), we set the
-  // SelectedLanguages to be English and French.
-  SetSelectedLanguages("en,fr");
-  MockComponentManager mock_component_manager(GetTempDir());
-  EXPECT_CALL(mock_component_manager, RegisterTranslateKitComponentImpl())
-      .Times(0);
-  mock_component_manager.InstallMockTranslateKitComponent();
-
-  // No language packs are installed yet.
-  size_t installed_package_count = 0;
-
-  // Get the amount of packages we can install.
-  size_t installable_package_count =
-      on_device_translation::GetInstallablePackageCount(
-          installed_package_count);
-  ASSERT_NE(installable_package_count, std::numeric_limits<size_t>::max());
-
-  for (const auto& language_pack_key : kLanguagePackKeys) {
-    mock_component_manager.InstallMockLanguagePack(language_pack_key);
-    installed_package_count++;
-
-    if (installed_package_count < installable_package_count) {
-      // The language pack count is less than the limitation.
-      TestCanTranslateResult(
-          "en", "fr",
-          CanCreateTranslatorResult::kAfterDownloadLanguagePackNotReady);
-    } else {
-      // The language pack count is equal to the limitation. So no more language
-      // pack can be downloaded.
-      TestCanTranslateResult(
-          "en", "fr",
-          CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation);
-      break;
-    }
-  }
-
-  ASSERT_EQ(installed_package_count, installable_package_count);
-}
-
-// Test the behavior of availability() when the language pack is not ready, and
-// the language pack count exceed the limitation after downloading two language
-// packs.
-IN_PROC_BROWSER_TEST_F(
-    OnDeviceTranslationBrowserTest,
-    CanTranslateNoExceedsLanguagePackCountLimitationTwoPackagesRequired) {
-  // This test case use Hindi and French as the source and target languages.
-  // To translate from Hindi to French, two language packs are required one for
-  // hi->en and one for en->fr.
-  SetSelectedLanguages("hi,fr");
-  MockComponentManager mock_component_manager(GetTempDir());
-  EXPECT_CALL(mock_component_manager, RegisterTranslateKitComponentImpl())
-      .Times(0);
-  mock_component_manager.InstallMockTranslateKitComponent();
-
-  // No language packs are installed yet.
-  size_t installed_package_count = 0;
-
-  // Get the amount of packages we can install.
-  size_t installable_package_count =
-      on_device_translation::GetInstallablePackageCount(
-          installed_package_count);
-  ASSERT_NE(installable_package_count, std::numeric_limits<size_t>::max());
-
-  for (const auto& language_pack_key : kLanguagePackKeys) {
-    mock_component_manager.InstallMockLanguagePack(language_pack_key);
-    installed_package_count++;
-
-    if (installed_package_count < installable_package_count - 1) {
-      // The language pack count is less than the limitation.
-      TestCanTranslateResult(
-          "hi", "fr",
-          CanCreateTranslatorResult::kAfterDownloadLanguagePackNotReady);
-    } else if (installed_package_count < installable_package_count) {
-      // The language pack count is less than the limitation. But if
-      // we download the required language packs, the language pack count will
-      // exceed the limitation. So availability() returns `no`.
-      TestCanTranslateResult(
-          "hi", "fr",
-          CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation);
-    } else {
-      // The language pack count is 3, which is equal to the limitation. So no
-      // more language pack can be downloaded.
-      TestCanTranslateResult(
-          "hi", "fr",
-          CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation);
-      break;
-    }
-  }
-
-  ASSERT_EQ(installed_package_count, installable_package_count);
-}
-
-// Test the behavior of availability() when PassAcceptLanguagesCheck() checks
-// fails.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       CanTranslateNoAcceptLanguagesCheckFailed) {
-  MockComponentManager mock_component_manager(GetTempDir());
-  EXPECT_CALL(mock_component_manager, RegisterTranslateKitComponentImpl())
-      .Times(0);
-  mock_component_manager.InstallMockTranslateKitComponent();
-  mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ko);
-  // Korean is not treated as a popular language. So if `ko` is not in the
-  // accept languages, PassAcceptLanguagesCheck() will return false.
-  TestCanTranslateResult(
-      "en", "ko", CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed);
-}
-
 // Test the behavior of `availability()` when the execution context is not
 // valid.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
@@ -1828,20 +1667,6 @@
   TestTranslationAvailable(browser(), "en", "xx", "unavailable");
 }
 
-// Test the behavior of `availability()` when the `PassAcceptLanguagesCheck()`
-// check fails.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       Availability_Unavailable_AcceptLanguagesCheckFailed) {
-  MockComponentManager mock_component_manager(GetTempDir());
-  mock_component_manager.InstallMockTranslateKitComponent();
-  mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ko);
-  NavigateToEmptyPage();
-
-  // Korean is not treated as a popular language. So if `ko` is not in the
-  // accept languages, `PassAcceptLanguagesCheck()` will return false.
-  TestTranslationAvailable(browser(), "en", "ko", "unavailable");
-}
-
 // Test the behavior of `availability()` where the source language and the
 // target language are the same language.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
@@ -1854,92 +1679,6 @@
   TestTranslationAvailable(browser(), "en", "en", "unavailable");
 }
 
-// Test the behavior of `availability()` when the language pack is not ready,
-// and the language pack count will exceed the imitation.
-IN_PROC_BROWSER_TEST_F(OnDeviceTranslationBrowserTest,
-                       Availability_No_ExceedsLangPackCountLimitation) {
-  // This test case uses English as the source language and French as the target
-  // language. To avoid the failure of `PassAcceptLanguagesCheck()`, we set the
-  // preferred languages to English and French.
-  SetSelectedLanguages("en,fr");
-  MockComponentManager mock_component_manager(GetTempDir());
-  NavigateToEmptyPage();
-  mock_component_manager.InstallMockTranslateKitComponent();
-
-  // No language packs are installed yet.
-  size_t installed_package_count = 0;
-
-  // Get the amount of packages we can install.
-  size_t installable_package_count =
-      on_device_translation::GetInstallablePackageCount(
-          installed_package_count);
-  ASSERT_NE(installable_package_count, std::numeric_limits<size_t>::max());
-
-  for (const auto& language_pack_key : kLanguagePackKeys) {
-    mock_component_manager.InstallMockLanguagePack(language_pack_key);
-    installed_package_count++;
-
-    if (installed_package_count < installable_package_count) {
-      // The language pack count is less than the limitation.
-      TestTranslationAvailable(browser(), "en", "fr", "downloadable");
-    } else {
-      // The language pack count is equal to the limitation. So no more language
-      // pack can be downloaded.
-      TestTranslationAvailable(browser(), "en", "fr", "unavailable");
-      break;
-    }
-  }
-
-  ASSERT_EQ(installed_package_count, installable_package_count);
-}
-
-// Test the behavior of `availability()` when the language pack is not ready,
-// and the language pack count exceeds the limitation after downloading two
-// more language packs.
-IN_PROC_BROWSER_TEST_F(
-    OnDeviceTranslationBrowserTest,
-    Availability_Unavailable_ExceedsLangPackCountLimitationTwoPackagesRequired) {
-  // This test case uses Hindi and French as the source and target languages.
-  // To translate from Hindi to French, two language packs are required: one for
-  // the hi->en translation and one for the en->fr translation.
-  SetSelectedLanguages("hi,fr");
-  MockComponentManager mock_component_manager(GetTempDir());
-  NavigateToEmptyPage();
-  mock_component_manager.InstallMockTranslateKitComponent();
-
-  // No language packs are installed yet.
-  size_t installed_package_count = 0;
-
-  // Get the amount of packages we can install.
-  size_t installable_package_count =
-      on_device_translation::GetInstallablePackageCount(
-          installed_package_count);
-  ASSERT_NE(installable_package_count, std::numeric_limits<size_t>::max());
-
-  for (const auto& language_pack_key : kLanguagePackKeys) {
-    mock_component_manager.InstallMockLanguagePack(language_pack_key);
-    installed_package_count++;
-
-    if (installed_package_count < installable_package_count - 1) {
-      // The language pack count is less than the limitation.
-      TestTranslationAvailable(browser(), "hi", "fr", "downloadable");
-    } else if (installed_package_count < installable_package_count) {
-      // The language pack count is less than the limitation.
-      // If we download the required language packs, the language pack count
-      // will exceed the limitation. As a result, `availability()` is
-      // 'unavailable'.
-      TestTranslationAvailable(browser(), "hi", "fr", "unavailable");
-    } else {
-      // The language pack count is 3, which is equal to the limitation. As a
-      // result, no more language packs can be downloaded.
-      TestTranslationAvailable(browser(), "hi", "fr", "unavailable");
-      break;
-    }
-  }
-
-  ASSERT_EQ(installed_package_count, installable_package_count);
-}
-
 // Test the behavior of `availability()` when both the library and the language
 // packs are not ready.
 IN_PROC_BROWSER_TEST_F(
@@ -2085,7 +1824,14 @@
       .Times(0);
   mock_component_manager.InstallMockTranslateKitComponent();
   mock_component_manager.InstallMockLanguagePack(LanguagePackKey::kEn_Ko);
-  TestCanTranslateResult("en", "ko", CanCreateTranslatorResult::kReadily);
+  // Despite being ready, the availability will be masked since the site hasn't
+  // created a translator for this language pair yet.
+  // `kAfterDownloadTranslatorCreationRequired` is only ever returned in that
+  // situation, so receiving that value confirms that the package is readily
+  // available.
+  TestCanTranslateResult(
+      "en", "ko",
+      CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired);
 }
 
 // Test the behavior of Translator API in a cross origin iframe.
@@ -2127,14 +1873,19 @@
   }
 
   // Adds an iframe to the test page and optionally sets its permission policy.
-  void AddIframe(size_t index,
-                 Browser* target_browser,
-                 bool permission_policy_enabled) {
+  content::RenderFrameHost* AddIframe(size_t index,
+                                      Browser* target_browser,
+                                      bool permission_policy_enabled) {
     EXPECT_EQ(EvalJsCatchingError(JsReplace("return addIframe($1, $2);",
                                             CreateCrossOriginIframeUrl(index),
                                             permission_policy_enabled),
                                   target_browser),
               "loaded");
+
+    return ChildFrameAt((target_browser ? target_browser : browser())
+                            ->tab_strip_model()
+                            ->GetActiveWebContents(),
+                        index);
   }
 
   // Removes the iframe and waits for the service deletion.
@@ -2153,7 +1904,7 @@
 
   // Creates a translator and translates in the iframe. Returns successful
   // translation or the error message.
-  std::string CheckTranslateInIframe(size_t index, Browser* target_browser) {
+  std::string CheckTranslateInIframe(content::RenderFrameHost* iframe) {
     const std::string_view translateTestScript = R"(
         (async () => {
           try {
@@ -2169,14 +1920,11 @@
           }
         })()
       )";
-    return EvalJsCatchingError(
-        JsReplace("return evalInIframe($1, $2);",
-                  CreateCrossOriginIframeUrl(index), translateTestScript),
-        target_browser);
+    return content::EvalJs(iframe, translateTestScript).ExtractString();
   }
 
   // Checks the result of availability() in the iframe.
-  std::string TryCanTranslateInIframe(size_t index, Browser* target_browser) {
+  std::string TryCanTranslateInIframe(content::RenderFrameHost* iframe) {
     const std::string_view translateTestScript = R"(
       (async () => {
         try {
@@ -2189,10 +1937,7 @@
         }
       })()
     )";
-    return EvalJsCatchingError(
-        JsReplace("return evalInIframe($1, $2);",
-                  CreateCrossOriginIframeUrl(index), translateTestScript),
-        target_browser);
+    return content::EvalJs(iframe, translateTestScript).ExtractString();
   }
 
  private:
@@ -2268,11 +2013,12 @@
       .Times(0);
 
   NavigateToTestPage(browser());
-  AddIframe(0, browser(), /*enable_permission_policy=*/false);
+  content::RenderFrameHost* iframe =
+      AddIframe(0, browser(), /*enable_permission_policy=*/false);
 
   // Translation is not available in cross-origin iframes without permission
   // policy.
-  EXPECT_EQ(CheckTranslateInIframe(0, browser()), "NotAllowedError");
+  EXPECT_EQ(CheckTranslateInIframe(iframe), "NotAllowedError");
 }
 
 // Tests the behavior of the Translation API in a cross origin iframe when the
@@ -2289,25 +2035,27 @@
   // Until the service count exceeds the limit, the translator can be created,
   // and the translation is successful.
   for (; i < kTranslationAPIMaxServiceCount.Get(); i++) {
-    AddIframe(i, browser(), /*enable_permission_policy=*/true);
-    EXPECT_EQ(CheckTranslateInIframe(i, browser()), "en to ja: hello");
-    EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "available");
+    content::RenderFrameHost* iframe =
+        AddIframe(i, browser(), /*enable_permission_policy=*/true);
+    EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
+    EXPECT_EQ(TryCanTranslateInIframe(iframe), "available");
   }
 
   // When the service count exceeds the limit, the translator cannot be created,
   // even when the permission policy is still enabled.
-  AddIframe(i, browser(), /*enable_permission_policy=*/true);
+  content::RenderFrameHost* iframe =
+      AddIframe(i, browser(), /*enable_permission_policy=*/true);
   auto console_observer = CreateConsoleObserver(
       "The translation service count exceeded the limitation.");
-  EXPECT_EQ(CheckTranslateInIframe(i, browser()), "NotSupportedError");
+  EXPECT_EQ(CheckTranslateInIframe(iframe), "NotSupportedError");
   WaitForConsoleObserver(*console_observer);
-  EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "unavailable");
+  EXPECT_EQ(TryCanTranslateInIframe(iframe), "unavailable");
 
   // When the service count is back to under the limit, the translator can be
   // created again.
   RemoveIframeAndWaitForServiceDeletion(0, browser());
-  EXPECT_EQ(CheckTranslateInIframe(i, browser()), "en to ja: hello");
-  EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "available");
+  EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
+  EXPECT_EQ(TryCanTranslateInIframe(iframe), "available");
 }
 
 // Tests the behavior of the Translation API in a cross origin iframe using the
@@ -2322,11 +2070,13 @@
   Browser* incognito_browser = CreateIncognitoBrowser();
 
   NavigateToTestPage(incognito_browser);
-  AddIframe(0, incognito_browser, /*enable_permission_policy=*/true);
-  EXPECT_EQ(CheckTranslateInIframe(0, incognito_browser), "en to ja: hello");
+  content::RenderFrameHost* iframe0 =
+      AddIframe(0, incognito_browser, /*enable_permission_policy=*/true);
+  EXPECT_EQ(CheckTranslateInIframe(iframe0), "en to ja: hello");
 
-  AddIframe(1, incognito_browser, /*enable_permission_policy=*/false);
-  EXPECT_EQ(CheckTranslateInIframe(1, incognito_browser), "NotAllowedError");
+  content::RenderFrameHost* iframe1 =
+      AddIframe(1, incognito_browser, /*enable_permission_policy=*/false);
+  EXPECT_EQ(CheckTranslateInIframe(iframe1), "NotAllowedError");
 }
 
 // Tests the behavior of the Translation API in a cross origin iframe using the
@@ -2341,14 +2091,18 @@
   Browser* guest_browser = CreateGuestBrowser();
 
   NavigateToTestPage(guest_browser);
-  AddIframe(0, guest_browser, /*enable_permission_policy=*/true);
-  EXPECT_EQ(CheckTranslateInIframe(0, guest_browser), "en to ja: hello");
+  content::RenderFrameHost* iframe =
+      AddIframe(0, guest_browser, /*enable_permission_policy=*/true);
+  EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
 }
 
+// TODO(crbug.com/423029203): This is timing out on a CQ bot so it is disabled
+// for now until we can resolve that issue.
+//
 // Tests the behavior of the Translation API in a cross origin iframe using
 // multiple profiles.
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationCrossOriginBrowserTest,
-                       ServiceCountLimitIsolatedPerProfile) {
+                       DISABLED_ServiceCountLimitIsolatedPerProfile) {
   MockComponentManager mock_component_manager(GetTempDir());
   mock_component_manager.ExpectCallRegisterTranslateKitComponentAndInstall();
   mock_component_manager.ExpectCallRegisterLanguagePackComponentAndInstall(
@@ -2377,32 +2131,39 @@
   // translation is successful.
   for (size_t i = 0; i < kTranslationAPIMaxServiceCount.Get(); i++) {
     for (auto* target_browser : browsers) {
-      AddIframe(i, target_browser, /*enable_permission_policy=*/true);
-      EXPECT_EQ(CheckTranslateInIframe(i, target_browser), "en to ja: hello");
+      content::RenderFrameHost* iframe =
+          AddIframe(i, target_browser, /*enable_permission_policy=*/true);
+      EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
     }
   }
 
   const size_t limit_count = kTranslationAPIMaxServiceCount.Get();
 
+  std::vector<content::RenderFrameHost*> iframes;
+
   // When the service count per profile exceeds the limit, the translator
   // cannot be created.
   for (auto* target_browser : browsers) {
-    AddIframe(limit_count, target_browser, /*enable_permission_policy=*/true);
+    content::RenderFrameHost* iframe = AddIframe(
+        limit_count, target_browser, /*enable_permission_policy=*/true);
+    iframes.push_back(iframe);
     auto console_observer = CreateConsoleObserver(
         "The translation service count exceeded the limitation.",
         target_browser);
-    EXPECT_EQ(CheckTranslateInIframe(limit_count, target_browser),
-              "NotSupportedError");
+    EXPECT_EQ(CheckTranslateInIframe(iframe), "NotSupportedError");
     // The console message should be logged.
     WaitForConsoleObserver(*console_observer);
   }
 
+  ASSERT_EQ(iframes.size(), browsers.size());
+
   // When the service count per profile is back to under the limit, the
   // translator can be created again.
-  for (auto* target_browser : browsers) {
+  for (size_t i = 0; i < browsers.size(); i++) {
+    Browser* target_browser = browsers[i];
+    content::RenderFrameHost* iframe = iframes[i];
     RemoveIframeAndWaitForServiceDeletion(0, target_browser);
-    EXPECT_EQ(CheckTranslateInIframe(limit_count, target_browser),
-              "en to ja: hello");
+    EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
   }
 }
 
@@ -2437,21 +2198,23 @@
   // Until the service count exceeds the limit, the translator can be created,
   // and the translation is successful.
   for (; i < kTranslationAPIMaxServiceCount.Get(); i++) {
-    AddIframe(i, browser(), /*enable_permission_policy=*/true);
-    EXPECT_EQ(CheckTranslateInIframe(i, browser()), "en to ja: hello");
-    EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "available");
+    content::RenderFrameHost* iframe =
+        AddIframe(i, browser(), /*enable_permission_policy=*/true);
+    EXPECT_EQ(CheckTranslateInIframe(iframe), "en to ja: hello");
+    EXPECT_EQ(TryCanTranslateInIframe(iframe), "available");
   }
 
   // When the service count exceeds the limit, the translator cannot be created.
-  AddIframe(i, browser(), /*enable_permission_policy=*/true);
-  EXPECT_EQ(CheckTranslateInIframe(i, browser()), "NotSupportedError");
-  EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "unavailable");
+  content::RenderFrameHost* last_iframe =
+      AddIframe(i, browser(), /*enable_permission_policy=*/true);
+  EXPECT_EQ(CheckTranslateInIframe(last_iframe), "NotSupportedError");
+  EXPECT_EQ(TryCanTranslateInIframe(last_iframe), "unavailable");
 
   // When the service count is back to under the limit, the translator can be
   // created again.
   RemoveIframeAndWaitForServiceDeletion(0, browser());
-  EXPECT_EQ(CheckTranslateInIframe(i, browser()), "en to ja: hello");
-  EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "available");
+  EXPECT_EQ(CheckTranslateInIframe(last_iframe), "en to ja: hello");
+  EXPECT_EQ(TryCanTranslateInIframe(last_iframe), "available");
 }
 
 // Tests the behavior of the Origin Trial token for the Translation API.
@@ -2687,7 +2450,15 @@
   mock_component_manager.InstallMockTranslateKitComponent();
   mock_component_manager.DoNotExpectCallRegisterLanguagePackComponent();
   NavigateToEmptyPage();
-  TestCanTranslateResult("en", "ja", CanCreateTranslatorResult::kReadily);
+
+  // Despite being ready, the availability will be masked since the site hasn't
+  // created a translator for this language pair yet.
+  // `kAfterDownloadTranslatorCreationRequired` is only ever returned in that
+  // situation, so receiving that value confirms that the package is readily
+  // available.
+  TestCanTranslateResult(
+      "en", "ja",
+      CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired);
 }
 
 // Tests the behavior of availability() when the required language package
diff --git a/chrome/browser/on_device_translation/service_controller.cc b/chrome/browser/on_device_translation/service_controller.cc
index 5ffb9c0..bc48543 100644
--- a/chrome/browser/on_device_translation/service_controller.cc
+++ b/chrome/browser/on_device_translation/service_controller.cc
@@ -156,19 +156,6 @@
                                       to_be_registered_packs);
 
     if (!to_be_registered_packs.empty()) {
-      if (!base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) &&
-          (kTranslationAPILimitLanguagePackCount.Get() &&
-           to_be_registered_packs.size() >
-               GetInstallablePackageCount(
-                   ComponentManager::GetRegisteredLanguagePacks().size()))) {
-        RecordLanguagePairUma(
-            "Translate.OnDeviceTranslation.DownloadExceedLimit.LanguagePair",
-            source_lang, target_lang);
-        std::move(callback).Run(base::unexpected(
-            CreateTranslatorError::kExceedsLanguagePackCountLimitation));
-        return;
-      }
-
       for (const auto& language_pack : to_be_registered_packs) {
         RecordLanguagePairUma(
             "Translate.OnDeviceTranslation.Download.LanguagePair",
@@ -320,17 +307,6 @@
     return CanCreateTranslatorResult::kNoNotSupportedLanguage;
   }
 
-  if (!to_be_registered_packs.empty() &&
-      !base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) &&
-      kTranslationAPILimitLanguagePackCount.Get() &&
-      to_be_registered_packs.size() >
-          GetInstallablePackageCount(
-              ComponentManager::GetRegisteredLanguagePacks().size())) {
-    // The number of installed language packs will exceed the limitation if the
-    // new required language packs are installed.
-    return CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation;
-  }
-
   if (required_not_installed_packs.empty()) {
     // All required language packages are installed.
     if (ComponentManager::GetTranslateKitLibraryPath().empty()) {
diff --git a/chrome/browser/on_device_translation/translation_manager_impl.cc b/chrome/browser/on_device_translation/translation_manager_impl.cc
index 1c07542..e847883 100644
--- a/chrome/browser/on_device_translation/translation_manager_impl.cc
+++ b/chrome/browser/on_device_translation/translation_manager_impl.cc
@@ -293,15 +293,6 @@
     return;
   }
 
-  if (!PassAcceptLanguagesCheck(GetAcceptLanguages(browser_context()),
-                                source_language, target_language)) {
-    mojo::Remote(std::move(client))
-        ->OnResult(CreateTranslatorResult::NewError(
-                       CreateTranslatorError::kAcceptLanguagesCheckFailed),
-                   nullptr, nullptr);
-    return;
-  }
-
   if (options->observer_remote) {
     base::flat_set<std::string> component_ids = {
         component_updater::TranslateKitComponentInstallerPolicy::
@@ -325,8 +316,7 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(client),
                      source_language, target_language);
 
-  if (base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) &&
-      add_fake_download_delay) {
+  if (add_fake_download_delay) {
     base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
         FROM_HERE, std::move(create_translator), GetTranslatorDownloadDelay());
   } else {
@@ -374,14 +364,6 @@
   const std::vector<std::string_view> accept_languages =
       GetAcceptLanguages(browser_context());
 
-  // TODO(crbug.com/385173766): Remove once V1 is launched.
-  if (!PassAcceptLanguagesCheck(accept_languages, source_language,
-                                target_language)) {
-    std::move(callback).Run(
-        CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed);
-    return;
-  }
-
   bool are_source_and_target_accept_or_english =
       (IsInAcceptLanguage(accept_languages, source_language) ||
        l10n_util::GetLanguage(source_language) == "en") &&
@@ -389,7 +371,6 @@
        l10n_util::GetLanguage(target_language) == "en");
 
   bool mask_readily_result =
-      base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) &&
       !HasInitializedTranslator(source_language, target_language) &&
       !are_source_and_target_accept_or_english;
 
diff --git a/chrome/browser/on_device_translation/translation_manager_util.cc b/chrome/browser/on_device_translation/translation_manager_util.cc
index b5c6b6d..aa6018d 100644
--- a/chrome/browser/on_device_translation/translation_manager_util.cc
+++ b/chrome/browser/on_device_translation/translation_manager_util.cc
@@ -18,19 +18,6 @@
 
 namespace on_device_translation {
 
-namespace {
-
-bool IsSupportedPopularLanguage(const std::string& lang) {
-  const std::optional<SupportedLanguage> supported_lang =
-      ToSupportedLanguage(lang);
-  if (!supported_lang) {
-    return false;
-  }
-  return IsPopularLanguage(*supported_lang);
-}
-
-}  // namespace
-
 const std::vector<std::string_view> GetAcceptLanguages(
     content::BrowserContext* browser_context) {
   CHECK(browser_context);
@@ -61,34 +48,4 @@
       ->GetBoolean(prefs::kTranslatorAPIAllowed);
 }
 
-bool PassAcceptLanguagesCheck(
-    const std::vector<std::string_view>& accept_languages,
-    const std::string& source_lang,
-    const std::string& target_lang) {
-  if (base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) ||
-      !kTranslationAPIAcceptLanguagesCheck.Get()) {
-    return true;
-  }
-
-  // TODO(crbug.com/371899260): Implement better language code handling.
-
-  // One of the source or the destination language must be in the user's accept
-  // language.
-  const bool source_lang_is_in_accept_langs =
-      IsInAcceptLanguage(accept_languages, source_lang);
-  const bool target_lang_is_in_accept_langs =
-      IsInAcceptLanguage(accept_languages, target_lang);
-
-  // The other language must be a popular language.
-  if (!source_lang_is_in_accept_langs &&
-      !IsSupportedPopularLanguage(source_lang)) {
-    return false;
-  }
-  if (!target_lang_is_in_accept_langs &&
-      !IsSupportedPopularLanguage(target_lang)) {
-    return false;
-  }
-  return true;
-}
-
 }  // namespace on_device_translation
diff --git a/chrome/browser/on_device_translation/translation_manager_util.h b/chrome/browser/on_device_translation/translation_manager_util.h
index 67b24d2..09910fb 100644
--- a/chrome/browser/on_device_translation/translation_manager_util.h
+++ b/chrome/browser/on_device_translation/translation_manager_util.h
@@ -27,14 +27,6 @@
 // Determines if the Translator API is enabled.
 bool IsTranslatorAllowed(content::BrowserContext* browser_context);
 
-// When the `TranslationAPIAcceptLanguagesCheck` feature is enabled, the
-// Translation API will fail if neither the source nor destination language is
-// in Accept Languages. This is intended to mitigate privacy concerns.
-bool PassAcceptLanguagesCheck(
-    const std::vector<std::string_view>& accept_languages,
-    const std::string& source_lang,
-    const std::string& target_lang);
-
 // Implementation of LookupMatchingLocaleByBestFit
 // (https://tc39.es/ecma402/#sec-lookupmatchinglocalebybestfit) as
 // LookupMatchingLocaleByPrefix
diff --git a/chrome/browser/on_device_translation/translation_manager_util_unittest.cc b/chrome/browser/on_device_translation/translation_manager_util_unittest.cc
index 4033fc7..a5c767b 100644
--- a/chrome/browser/on_device_translation/translation_manager_util_unittest.cc
+++ b/chrome/browser/on_device_translation/translation_manager_util_unittest.cc
@@ -19,143 +19,6 @@
   ~TranslationManagerUtilTest() override = default;
 };
 
-TEST_F(TranslationManagerUtilTest, PassAcceptLanguagesCheck) {
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "es"}, "en", "es"));
-
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "fr"}, "en", "fr"));
-
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "es"}, "en", "zh"));
-
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target is not in accept-languages, and not popular.
-  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "en", "fr"));
-
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"de", "es"}, "de", "es"));
-
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"de", "fr"}, "de", "fr"));
-
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"de", "es"}, "de", "zh"));
-
-  // Source lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target is not in accept-languages, and not popular.
-  EXPECT_FALSE(PassAcceptLanguagesCheck({"de", "es"}, "de", "fr"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "es"}, "ja", "es"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "fr"}, "ja", "fr"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  EXPECT_TRUE(PassAcceptLanguagesCheck({"en", "es"}, "ja", "zh"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target is not in accept-languages, and not popular.
-  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "ja", "fr"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : true
-  // Source is not in accept-languages, and not popular.
-  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "de", "es"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : true
-  //   - Is popular lang        : false
-  // Source is not in accept-languages, and not popular.
-  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "fr"}, "de", "fr"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : true
-  // Source is not in accept-languages, and not popular.
-  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "de", "zh"));
-
-  // Source lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target lang:
-  //   - Is in accept-languages : false
-  //   - Is popular lang        : false
-  // Target and source are not in accept-languages, and not popular.
-  EXPECT_FALSE(PassAcceptLanguagesCheck({"en", "es"}, "de", "fr"));
-}
-
 TEST_F(TranslationManagerUtilTest,
        LookupMatchingLocaleByBestFitFindsSupportedLanguageTag) {
   std::vector<std::string> en_variations{
diff --git a/chrome/browser/optimization_guide/chrome_hints_manager_unittest.cc b/chrome/browser/optimization_guide/chrome_hints_manager_unittest.cc
index b748a24..442d52f 100644
--- a/chrome/browser/optimization_guide/chrome_hints_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/chrome_hints_manager_unittest.cc
@@ -99,7 +99,7 @@
 
     hint_store_ = std::make_unique<optimization_guide::OptimizationGuideStore>(
         db_provider_.get(), temp_dir(),
-        task_environment_.GetMainThreadTaskRunner(), /*pref_service=*/nullptr);
+        task_environment_.GetMainThreadTaskRunner());
 
     tab_url_provider_ = std::make_unique<FakeTabUrlProvider>();
 
diff --git a/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc b/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc
index dba7cc01..0a8476a 100644
--- a/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc
+++ b/chrome/browser/optimization_guide/hints_fetcher_browsertest.cc
@@ -1809,3 +1809,106 @@
       "OptimizationGuide.HintsFetcher.GetHintsRequest.RequestStatus.Journeys",
       optimization_guide::FetcherRequestStatus::kSuccess, 1);
 }
+
+class ProactivePersonalizationHintsFetcherBrowserTest
+    : public HintsFetcherBrowserTest {
+ public:
+  ProactivePersonalizationHintsFetcherBrowserTest() = default;
+
+  ProactivePersonalizationHintsFetcherBrowserTest(
+      const ProactivePersonalizationHintsFetcherBrowserTest&) = delete;
+  ProactivePersonalizationHintsFetcherBrowserTest& operator=(
+      const ProactivePersonalizationHintsFetcherBrowserTest&) = delete;
+
+  ~ProactivePersonalizationHintsFetcherBrowserTest() override = default;
+
+  void SetUpBrowserContextKeyedServices(
+      content::BrowserContext* context) override {
+    HintsFetcherBrowserTest::SetUpBrowserContextKeyedServices(context);
+    IdentityTestEnvironmentProfileAdaptor::
+        SetIdentityTestEnvironmentFactoriesOnBrowserContext(context);
+  }
+
+  void SetUpOnMainThread() override {
+    HintsFetcherBrowserTest::SetUpOnMainThread();
+    identity_test_env_adaptor_ =
+        std::make_unique<IdentityTestEnvironmentProfileAdaptor>(
+            browser()->profile());
+  }
+
+  void PopulateEnabledFeatures(
+      std::vector<base::test::FeatureRefAndParams>* enabled_features) override {
+    base::FieldTrialParams personalized_fetching_params = GetFieldTrialParams();
+    enabled_features->emplace_back(
+        optimization_guide::features::
+            kOptimizationGuideProactivePersonalizedHintsFetching,
+        personalized_fetching_params);
+  }
+
+  virtual base::FieldTrialParams GetFieldTrialParams() {
+    return {
+        {"allowed_optimization_types", "NOSCRIPT"},
+    };
+  }
+
+  void EnableSignin() {
+    identity_test_env_adaptor_->identity_test_env()
+        ->MakePrimaryAccountAvailable("user@gmail.com",
+                                      signin::ConsentLevel::kSignin);
+    identity_test_env_adaptor_->identity_test_env()
+        ->SetAutomaticIssueOfAccessTokens(true);
+  }
+
+ private:
+  // Identity test support.
+  std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
+      identity_test_env_adaptor_;
+};
+
+IN_PROC_BROWSER_TEST_F(ProactivePersonalizationHintsFetcherBrowserTest,
+                       HintsFetcherFetchesWithAccessToken) {
+  SetNetworkConnectionOnline();
+  SetResponseType(
+      optimization_guide::HintsFetcherRemoteResponseType::kSuccessful);
+
+  ResetCountHintsRequestsReceived();
+  EnableSignin();
+  SetExpectedBearerAccessToken("Bearer access_token");
+  ASSERT_TRUE(
+      ui_test_utils::NavigateToURL(browser(), search_results_page_url()));
+
+  base::flat_set<std::string> srp_request;
+  srp_request.insert(GURL(search_results_page_url()).host());
+  srp_request.insert(GURL(search_results_page_url()).spec());
+  SetExpectedHintsRequestForHostsAndUrls(srp_request);
+  EXPECT_EQ(1u, count_hints_requests_received());
+}
+
+class ProactivePersonalizationHintsWrongOptimizationTypeFetcherBrowserTest
+    : public ProactivePersonalizationHintsFetcherBrowserTest {
+  base::FieldTrialParams GetFieldTrialParams() override {
+    return {
+        {"allowed_optimization_types", "PERFORMANCE_HINTS"},
+    };
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(
+    ProactivePersonalizationHintsWrongOptimizationTypeFetcherBrowserTest,
+    HintsFetcherDoesNotFetchAccessToken) {
+  SetNetworkConnectionOnline();
+  SetResponseType(
+      optimization_guide::HintsFetcherRemoteResponseType::kSuccessful);
+
+  ResetCountHintsRequestsReceived();
+  EnableSignin();
+  SetExpectedBearerAccessToken(std::string());
+  ASSERT_TRUE(
+      ui_test_utils::NavigateToURL(browser(), search_results_page_url()));
+
+  base::flat_set<std::string> srp_request;
+  srp_request.insert(GURL(search_results_page_url()).host());
+  srp_request.insert(GURL(search_results_page_url()).spec());
+  SetExpectedHintsRequestForHostsAndUrls(srp_request);
+  EXPECT_EQ(1u, count_hints_requests_received());
+}
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index d3b1fbe8..22e2af3 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -331,8 +331,7 @@
                   profile_path.Append(
                       optimization_guide::kOptimizationGuideHintStore),
                   base::ThreadPool::CreateSequencedTaskRunner(
-                      {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
-                  profile->GetPrefs())
+                      {base::MayBlock(), base::TaskPriority::BEST_EFFORT}))
             : nullptr;
     hint_store = hint_store_ ? hint_store_->AsWeakPtr() : nullptr;
   }
diff --git a/chrome/browser/password_manager/android/mock_password_manager_error_message_helper_bridge.h b/chrome/browser/password_manager/android/mock_password_manager_error_message_helper_bridge.h
index 928c6be..a672059 100644
--- a/chrome/browser/password_manager/android/mock_password_manager_error_message_helper_bridge.h
+++ b/chrome/browser/password_manager/android/mock_password_manager_error_message_helper_bridge.h
@@ -22,7 +22,8 @@
               (override));
   MOCK_METHOD(void,
               StartTrustedVaultKeyRetrievalFlow,
-              (content::WebContents * web_contents),
+              (content::WebContents * web_contents,
+               syncer::TrustedVaultUserActionTriggerForUMA user_action_trigger),
               (override));
   MOCK_METHOD(bool,
               ShouldShowSignInErrorUI,
diff --git a/chrome/browser/password_manager/android/password_manager_error_message_delegate.cc b/chrome/browser/password_manager/android/password_manager_error_message_delegate.cc
index cc44de7..66b5bcb 100644
--- a/chrome/browser/password_manager/android/password_manager_error_message_delegate.cc
+++ b/chrome/browser/password_manager/android/password_manager_error_message_delegate.cc
@@ -13,6 +13,7 @@
 #include "components/password_manager/core/browser/password_manager_client.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "components/sync/service/sync_service_utils.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/android/window_android.h"
 #include "ui/aura/window.h"
@@ -229,7 +230,9 @@
     case PasswordStoreBackendErrorType::kKeyRetrievalRequired:
     case PasswordStoreBackendErrorType::kEmptySecurityDomain:
     case PasswordStoreBackendErrorType::kIrretrievableSecurityDomain:
-      helper_bridge_->StartTrustedVaultKeyRetrievalFlow(web_contents);
+      helper_bridge_->StartTrustedVaultKeyRetrievalFlow(
+          web_contents, syncer::TrustedVaultUserActionTriggerForUMA::
+                            kPasswordManagerErrorMessage);
       break;
     case PasswordStoreBackendErrorType::kGMSCoreOutdatedSavingPossible:
     case PasswordStoreBackendErrorType::kGMSCoreOutdatedSavingDisabled:
diff --git a/chrome/browser/password_manager/android/password_manager_error_message_delegate_unittest.cc b/chrome/browser/password_manager/android/password_manager_error_message_delegate_unittest.cc
index 0879adb..a24a730 100644
--- a/chrome/browser/password_manager/android/password_manager_error_message_delegate_unittest.cc
+++ b/chrome/browser/password_manager/android/password_manager_error_message_delegate_unittest.cc
@@ -18,6 +18,7 @@
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
+#include "components/sync/service/sync_service_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -416,7 +417,9 @@
   EXPECT_NE(nullptr, GetMessageWrapper());
 
   EXPECT_CALL(*helper_bridge(),
-              StartTrustedVaultKeyRetrievalFlow(web_contents()));
+              StartTrustedVaultKeyRetrievalFlow(
+                  web_contents(), syncer::TrustedVaultUserActionTriggerForUMA::
+                                      kPasswordManagerErrorMessage));
   GetMessageWrapper()->HandleActionClick(base::android::AttachCurrentThread());
 
   // The message needs to be dismissed manually in tests. In production code
@@ -440,7 +443,9 @@
   EXPECT_NE(nullptr, GetMessageWrapper());
 
   EXPECT_CALL(*helper_bridge(),
-              StartTrustedVaultKeyRetrievalFlow(web_contents()));
+              StartTrustedVaultKeyRetrievalFlow(
+                  web_contents(), syncer::TrustedVaultUserActionTriggerForUMA::
+                                      kPasswordManagerErrorMessage));
   GetMessageWrapper()->HandleActionClick(base::android::AttachCurrentThread());
 
   // The message needs to be dismissed manually in tests. In production code
@@ -465,7 +470,9 @@
   EXPECT_NE(nullptr, GetMessageWrapper());
 
   EXPECT_CALL(*helper_bridge(),
-              StartTrustedVaultKeyRetrievalFlow(web_contents()));
+              StartTrustedVaultKeyRetrievalFlow(
+                  web_contents(), syncer::TrustedVaultUserActionTriggerForUMA::
+                                      kPasswordManagerErrorMessage));
   GetMessageWrapper()->HandleActionClick(base::android::AttachCurrentThread());
 
   // The message needs to be dismissed manually in tests. In production code
diff --git a/chrome/browser/password_manager/android/password_manager_error_message_helper_bridge.h b/chrome/browser/password_manager/android/password_manager_error_message_helper_bridge.h
index c62adf37..990b4422 100644
--- a/chrome/browser/password_manager/android/password_manager_error_message_helper_bridge.h
+++ b/chrome/browser/password_manager/android/password_manager_error_message_helper_bridge.h
@@ -7,6 +7,7 @@
 
 #include <jni.h>
 
+#include "components/sync/service/sync_service_utils.h"
 #include "content/public/browser/web_contents.h"
 
 class PasswordManagerErrorMessageHelperBridge {
@@ -24,7 +25,8 @@
   // the Android process to retrieve key for on-device encryption. This method
   // will only work for users that are currently syncing.
   virtual void StartTrustedVaultKeyRetrievalFlow(
-      content::WebContents* web_contents) = 0;
+      content::WebContents* web_contents,
+      syncer::TrustedVaultUserActionTriggerForUMA user_action_trigger) = 0;
 
   // Checks if enough time has passed since the last error UI was shown.
   virtual bool ShouldShowSignInErrorUI(content::WebContents* web_contents) = 0;
diff --git a/chrome/browser/password_manager/android/password_manager_error_message_helper_bridge_impl.cc b/chrome/browser/password_manager/android/password_manager_error_message_helper_bridge_impl.cc
index e85c48b..f5a2353 100644
--- a/chrome/browser/password_manager/android/password_manager_error_message_helper_bridge_impl.cc
+++ b/chrome/browser/password_manager/android/password_manager_error_message_helper_bridge_impl.cc
@@ -30,7 +30,9 @@
 }
 
 void PasswordManagerErrorMessageHelperBridgeImpl::
-    StartTrustedVaultKeyRetrievalFlow(content::WebContents* web_contents) {
+    StartTrustedVaultKeyRetrievalFlow(
+        content::WebContents* web_contents,
+        syncer::TrustedVaultUserActionTriggerForUMA user_action_trigger) {
   ui::WindowAndroid* window_android =
       web_contents->GetNativeView()->GetWindowAndroid();
   if (window_android == nullptr) {
@@ -41,7 +43,7 @@
 
   Java_PasswordManagerErrorMessageHelperBridge_startTrustedVaultKeyRetrievalFlow(
       base::android::AttachCurrentThread(), window_android->GetJavaObject(),
-      profile->GetJavaObject());
+      profile->GetJavaObject(), static_cast<jint>(user_action_trigger));
 }
 
 bool PasswordManagerErrorMessageHelperBridgeImpl::ShouldShowSignInErrorUI(
diff --git a/chrome/browser/password_manager/android/password_manager_error_message_helper_bridge_impl.h b/chrome/browser/password_manager/android/password_manager_error_message_helper_bridge_impl.h
index 9dc0fca..e4ab0d4 100644
--- a/chrome/browser/password_manager/android/password_manager_error_message_helper_bridge_impl.h
+++ b/chrome/browser/password_manager/android/password_manager_error_message_helper_bridge_impl.h
@@ -16,7 +16,8 @@
   void StartUpdateAccountCredentialsFlow(
       content::WebContents* web_contents) override;
   void StartTrustedVaultKeyRetrievalFlow(
-      content::WebContents* web_contents) override;
+      content::WebContents* web_contents,
+      syncer::TrustedVaultUserActionTriggerForUMA user_action_trigger) override;
   bool ShouldShowSignInErrorUI(content::WebContents* web_contents) override;
   bool ShouldShowUpdateGMSCoreErrorUI(
       content::WebContents* web_contents) override;
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 1d7f622b..b5a7a8fd 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -89,6 +89,7 @@
 #include "components/password_manager/core/browser/password_manager_setting.h"
 #include "components/password_manager/core/browser/password_manager_settings_service.h"
 #include "components/password_manager/core/browser/password_requirements_service.h"
+#include "components/password_manager/core/browser/password_store/password_store_backend_error.h"
 #include "components/password_manager/core/browser/password_store/password_store_interface.h"
 #include "components/password_manager/core/browser/password_sync_util.h"
 #include "components/password_manager/core/common/password_manager_features.h"
@@ -207,6 +208,7 @@
 using password_manager::PasswordManagerMetricsRecorder;
 using password_manager::PasswordManagerSetting;
 using password_manager::PasswordManagerSettingsService;
+using password_manager::PasswordStoreBackendError;
 using password_manager::metrics_util::PasswordType;
 using sessions::SerializedNavigationEntry;
 
@@ -944,11 +946,12 @@
 void ChromePasswordManagerClient::UpdateCredentialCache(
     const url::Origin& origin,
     base::span<const PasswordForm> best_matches,
-    bool is_blocklisted) {
+    bool is_blocklisted,
+    std::optional<PasswordStoreBackendError> backend_error) {
 #if BUILDFLAG(IS_ANDROID)
   credential_cache_.SaveCredentialsAndBlocklistedForOrigin(
       best_matches, CredentialCache::IsOriginBlocklisted(is_blocklisted),
-      origin);
+      backend_error, origin);
 
 #endif
 }
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.h b/chrome/browser/password_manager/chrome_password_manager_client.h
index 0172b3d8..9ad644a0 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.h
+++ b/chrome/browser/password_manager/chrome_password_manager_client.h
@@ -203,7 +203,9 @@
   void UpdateCredentialCache(
       const url::Origin& origin,
       base::span<const password_manager::PasswordForm> best_matches,
-      bool is_blocklisted) override;
+      bool is_blocklisted,
+      std::optional<password_manager::PasswordStoreBackendError> backend_error)
+      override;
   void AutomaticPasswordSave(
       std::unique_ptr<password_manager::PasswordFormManagerForUI>
           saved_form_manager,
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index a74ba79..6525ba9 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -352,7 +352,8 @@
             password_client,
             driver_supplier,
             /*grouped_credential_sheet_controller=*/nullptr,
-            nullptr) {}
+            /*access_loss_warning_bridge=*/nullptr,
+            /*password_manager_error_message_helper_bridge=*/nullptr) {}
 
   MOCK_METHOD(void,
               RefreshSuggestionsForField,
@@ -1830,7 +1831,8 @@
   GetClient()
       ->GetCredentialCacheForTesting()
       ->SaveCredentialsAndBlocklistedForOrigin(
-          forms, CredentialCache::IsOriginBlocklisted(false), origin);
+          forms, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
+          origin);
 
   // Check that a navigation within the same document does not clear the cache.
   content::MockNavigationHandle handle(web_contents());
@@ -1991,7 +1993,8 @@
   GetClient()
       ->GetCredentialCacheForTesting()
       ->SaveCredentialsAndBlocklistedForOrigin(
-          forms, CredentialCache::IsOriginBlocklisted(false), origin);
+          forms, CredentialCache::IsOriginBlocklisted(false), std::nullopt,
+          origin);
 
   MockPasswordStoreInterface* profile_store =
       static_cast<MockPasswordStoreInterface*>(
diff --git a/chrome/browser/policy/BUILD.gn b/chrome/browser/policy/BUILD.gn
index 0c18560..00a7ce5b 100644
--- a/chrome/browser/policy/BUILD.gn
+++ b/chrome/browser/policy/BUILD.gn
@@ -236,7 +236,6 @@
     "test/policy_statistics_collector_browsertest.cc",
     "test/proxy_policies_browsertest.cc",
     "test/safe_browsing_policy_browsertest.cc",
-    "test/select_parser_relaxation_policy_browsertest.cc",
     "test/shared_clipboard_enabled_browsertest.cc",
     "test/ssl_error_overriding_allowed_policy_browsertest.cc",
     "test/standardized_browser_zoom_browsertest.cc",
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index b8296e6..8b05239 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -2268,9 +2268,6 @@
   { key::kAllowBackForwardCacheForCacheControlNoStorePageEnabled,
     policy_prefs::kAllowBackForwardCacheForCacheControlNoStorePageEnabled,
     base::Value::Type::BOOLEAN},
-  { key::kSelectParserRelaxationEnabled,
-    policy_prefs::kSelectParserRelaxationEnabled,
-    base::Value::Type::BOOLEAN},
   { key::kStandardizedBrowserZoomEnabled,
     policy_prefs::kStandardizedBrowserZoomEnabled,
     base::Value::Type::BOOLEAN},
diff --git a/chrome/browser/policy/test/select_parser_relaxation_policy_browsertest.cc b/chrome/browser/policy/test/select_parser_relaxation_policy_browsertest.cc
deleted file mode 100644
index 637fcff..0000000
--- a/chrome/browser/policy/test/select_parser_relaxation_policy_browsertest.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/policy/policy_test_utils.h"
-#include "components/policy/policy_constants.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/test_utils.h"
-#include "third_party/blink/public/common/features.h"
-
-namespace policy {
-
-namespace {
-
-enum class Policy {
-  kDefault,
-  kTrue,
-  kFalse,
-};
-
-}  // namespace
-
-class SelectParserRelaxationEnabledPolicyBrowserTest
-    : public PolicyTest,
-      public ::testing::WithParamInterface<Policy> {
- public:
-  static std::string DescribeParams(
-      const ::testing::TestParamInfo<ParamType>& info) {
-    switch (info.param) {
-      case Policy::kDefault:
-        return "Default";
-      case Policy::kTrue:
-        return "True";
-      case Policy::kFalse:
-        return "False";
-    }
-  }
-
- protected:
-  void SetUpInProcessBrowserTestFixture() override {
-    PolicyTest::SetUpInProcessBrowserTestFixture();
-
-    if (GetParam() == Policy::kDefault) {
-      return;
-    }
-    PolicyMap policies;
-    SetPolicy(&policies, key::kSelectParserRelaxationEnabled,
-              base::Value(GetParam() == Policy::kTrue));
-    UpdateProviderPolicy(policies);
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_P(SelectParserRelaxationEnabledPolicyBrowserTest,
-                       PolicyIsFollowed) {
-  // By default the new behavior should be enabled.
-  const bool expected_enabled = GetParam() != Policy::kFalse;
-
-  ASSERT_TRUE(embedded_test_server()->Start());
-  const GURL url(
-      embedded_test_server()->GetURL("/select_parser_relaxation.html"));
-  ASSERT_TRUE(NavigateToUrl(url, this));
-
-  const bool actual_enabled =
-      content::EvalJs(chrome_test_utils::GetActiveWebContents(this),
-                      "window.selectParserRelaxationEnabled")
-          .ExtractBool();
-  EXPECT_EQ(actual_enabled, expected_enabled);
-
-  // The dependency in RuntimeEnabledFeatures should disable CustomizableSelect
-  // when SelectParserRelaxation is disabled.
-  const bool customizable_select_enabled =
-      content::EvalJs(chrome_test_utils::GetActiveWebContents(this),
-                      "window.customizableSelectEnabled")
-          .ExtractBool();
-  EXPECT_EQ(customizable_select_enabled, expected_enabled);
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    /* no prefix */,
-    SelectParserRelaxationEnabledPolicyBrowserTest,
-    ::testing::Values(Policy::kDefault, Policy::kTrue, Policy::kFalse),
-    &SelectParserRelaxationEnabledPolicyBrowserTest::DescribeParams);
-
-}  // namespace policy
diff --git a/chrome/browser/privacy_sandbox/notice/BUILD.gn b/chrome/browser/privacy_sandbox/notice/BUILD.gn
index af67fd7..8af0a05 100644
--- a/chrome/browser/privacy_sandbox/notice/BUILD.gn
+++ b/chrome/browser/privacy_sandbox/notice/BUILD.gn
@@ -97,6 +97,7 @@
     public = [
       "desktop_entrypoint_handlers.h",
       "desktop_view_manager.h",
+      "desktop_view_manager_test_peer.h",
     ]
     deps = [
       ":core",
@@ -108,7 +109,7 @@
       "//chrome/browser/sync",
       "//chrome/browser/ui:ui",
       "//chrome/browser/ui/browser_window:browser_window",
-      "//chrome/browser/ui/privacy_sandbox:privacy_sandbox",
+      "//chrome/browser/ui/privacy_sandbox",
       "//chrome/common",
       "//components/privacy_sandbox:features",
       "//components/tabs:public",
@@ -217,10 +218,14 @@
   }
 
   if (!is_android) {
-    sources += [ "desktop_entrypoint_handlers_browsertest.cc" ]
+    sources += [
+      "desktop_entrypoint_handlers_browsertest.cc",
+      "desktop_view_manager_browsertest.cc",
+    ]
     deps += [
       ":desktop_view_manager",
       "//chrome/browser/sync",
+      "//chrome/browser/ui/privacy_sandbox",
       "//chrome/test:test_support_ui",
       "//components/privacy_sandbox:features",
     ]
diff --git a/chrome/browser/privacy_sandbox/notice/desktop_view_manager.cc b/chrome/browser/privacy_sandbox/notice/desktop_view_manager.cc
index 482d508..f33d6f1 100644
--- a/chrome/browser/privacy_sandbox/notice/desktop_view_manager.cc
+++ b/chrome/browser/privacy_sandbox/notice/desktop_view_manager.cc
@@ -112,12 +112,27 @@
   return navigation_handler_.get();
 }
 
+bool DesktopViewManager::IsPromptShowingOnBrowser(
+    BrowserWindowInterface* browser) {
+  for (auto& observer : observers_) {
+    // Return true if an observer is registered for the current browser.
+    if (observer.GetBrowser() == browser) {
+      return true;
+    }
+  }
+  return false;
+}
+
 void DesktopViewManager::HandleChromeOwnedPageNavigation(
     BrowserWindowInterface* browser_interface) {
   // TODO(crbug.com/408016824): Move this Feature flag check to the orchestrator
   // once implemented.
   if (base::FeatureList::IsEnabled(
           privacy_sandbox::kPrivacySandboxNoticeFramework)) {
+    if (IsPromptShowingOnBrowser(browser_interface)) {
+      return;
+    }
+
     // Create a view through the bound `Show` function.
     MaybeCreateView(browser_interface,
                     base::BindOnce(static_cast<void (*)(BrowserWindowInterface*,
diff --git a/chrome/browser/privacy_sandbox/notice/desktop_view_manager.h b/chrome/browser/privacy_sandbox/notice/desktop_view_manager.h
index 8e546a54..e50c9c7 100644
--- a/chrome/browser/privacy_sandbox/notice/desktop_view_manager.h
+++ b/chrome/browser/privacy_sandbox/notice/desktop_view_manager.h
@@ -54,6 +54,8 @@
   // Notifies open views to close.
   void CloseAllOpenViews();
 
+  bool IsPromptShowingOnBrowser(BrowserWindowInterface* browser);
+
   // If the event taken isn't a shown event, notifies open views to advance to
   // the next step. If the next step doesn't exist, all open views will be
   // notified to close.
diff --git a/chrome/browser/privacy_sandbox/notice/desktop_view_manager_browsertest.cc b/chrome/browser/privacy_sandbox/notice/desktop_view_manager_browsertest.cc
new file mode 100644
index 0000000..b3c0f88
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/notice/desktop_view_manager_browsertest.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 "chrome/browser/privacy_sandbox/notice/desktop_entrypoint_handlers.h"
+#include "chrome/browser/privacy_sandbox/notice/desktop_view_manager_test_peer.h"
+#include "chrome/browser/privacy_sandbox/notice/mocks/mock_desktop_view_manager.h"
+#include "chrome/browser/privacy_sandbox/notice/mocks/mock_notice_service.h"
+#include "chrome/browser/privacy_sandbox/notice/notice_service_factory.h"
+#include "chrome/browser/privacy_sandbox/notice/notice_service_interface.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt.h"
+#include "chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/platform_browser_test.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/privacy_sandbox/privacy_sandbox_features.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "ui/views/widget/any_widget_observer.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+
+namespace {
+using privacy_sandbox::notice::mojom::PrivacySandboxNotice;
+}
+
+namespace privacy_sandbox {
+namespace {
+
+using ::testing::_;
+using ::testing::Mock;
+using ::testing::Return;
+
+class PrivacySandboxNoticeViewManagerTest : public InProcessBrowserTest {
+ public:
+  PrivacySandboxNoticeViewManagerTest() {
+    feature_list_.InitWithFeaturesAndParameters(
+        /*enabled_features=*/{{privacy_sandbox::kPrivacySandboxNoticeFramework,
+                               {}}},
+        {});
+  }
+
+  void SetUpOnMainThread() override {
+    mock_notice_service_ = static_cast<MockPrivacySandboxNoticeService*>(
+        PrivacySandboxNoticeServiceFactory::GetInstance()
+            ->SetTestingFactoryAndUse(
+                browser()->profile(),
+                base::BindRepeating(&BuildMockPrivacySandboxNoticeService)));
+
+    desktop_view_manager_ =
+        std::make_unique<DesktopViewManager>(mock_notice_service_.get());
+
+    ON_CALL(*mock_notice_service_, GetDesktopViewManager())
+        .WillByDefault(Return(desktop_view_manager_.get()));
+
+    desktop_view_manager_test_peer_ =
+        std::make_unique<DesktopViewManagerTestPeer>(
+            desktop_view_manager_.get());
+  }
+
+  void TearDownOnMainThread() override {
+    desktop_view_manager_test_peer_.reset();
+    desktop_view_manager_.reset();
+    mock_notice_service_ = nullptr;
+    InProcessBrowserTest::TearDownOnMainThread();  // Call base class TearDown
+  }
+
+  DesktopViewManager* desktop_view_manager() {
+    return desktop_view_manager_.get();
+  }
+
+  void SetRequiredNotices(std::vector<PrivacySandboxNotice> required_notices) {
+    ON_CALL(*mock_notice_service_, GetRequiredNotices(_))
+        .WillByDefault(Return(required_notices));
+  }
+
+  bool IsPromptShowingOnBrowser(Browser* browser) {
+    return desktop_view_manager_test_peer_->IsPromptShowingOnBrowser(browser);
+  }
+
+  void HandleChromeOwnedPageNavigation() {
+    desktop_view_manager_test_peer_->HandleChromeOwnedPageNavigation(browser());
+  }
+
+ protected:
+  raw_ptr<MockPrivacySandboxNoticeService> mock_notice_service_;
+  std::unique_ptr<DesktopViewManager> desktop_view_manager_;
+  std::unique_ptr<DesktopViewManagerTestPeer> desktop_view_manager_test_peer_;
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Test that browsers are registered and unregistered correctly.
+IN_PROC_BROWSER_TEST_F(PrivacySandboxNoticeViewManagerTest,
+                       IsShowingPrompt_SingleWindow) {
+  SetRequiredNotices({PrivacySandboxNotice::kTopicsConsentNotice});
+
+  views::NamedWidgetShownWaiter waiter1(
+      views::test::AnyWidgetTestPasskey{},
+      PrivacySandboxDialogView::kViewClassName);
+  desktop_view_manager()->HandleChromeOwnedPageNavigation(browser());
+
+  auto* dialog1 = waiter1.WaitIfNeededAndGet();
+  auto* view1 = static_cast<PrivacySandboxDialogView*>(
+      dialog1->widget_delegate()->GetContentsView());
+
+  ASSERT_TRUE(IsPromptShowingOnBrowser(browser()));
+
+  view1->CloseNativeView();
+
+  // Must manually close the dialog for before test destruction begins.
+  dialog1->CloseNow();
+
+  ASSERT_FALSE(IsPromptShowingOnBrowser(browser()));
+}
+
+// Test that browsers on multiple windows are registered correctly.
+IN_PROC_BROWSER_TEST_F(PrivacySandboxNoticeViewManagerTest,
+                       IsShowingPrompt_MultiWindow) {
+  SetRequiredNotices({PrivacySandboxNotice::kTopicsConsentNotice});
+
+  views::NamedWidgetShownWaiter waiter1(
+      views::test::AnyWidgetTestPasskey{},
+      PrivacySandboxDialogView::kViewClassName);
+
+  desktop_view_manager()->HandleChromeOwnedPageNavigation(browser());
+
+  auto* dialog1 = waiter1.WaitIfNeededAndGet();
+  auto* view1 = static_cast<PrivacySandboxDialogView*>(
+      dialog1->widget_delegate()->GetContentsView());
+
+  ASSERT_TRUE(IsPromptShowingOnBrowser(browser()));
+
+  views::NamedWidgetShownWaiter waiter2(
+      views::test::AnyWidgetTestPasskey{},
+      PrivacySandboxDialogView::kViewClassName);
+
+  auto* new_rfh = ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL(chrome::kChromeUINewTabPageURL),
+      WindowOpenDisposition::NEW_WINDOW,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+  auto* new_browser = chrome::FindBrowserWithTab(
+      content::WebContents::FromRenderFrameHost(new_rfh));
+  PrivacySandboxDialog::Show(new_browser,
+                             PrivacySandboxService::PromptType::kM1Consent);
+
+  auto* dialog2 = waiter2.WaitIfNeededAndGet();
+  auto* view2 = static_cast<PrivacySandboxDialogView*>(
+      dialog2->widget_delegate()->GetContentsView());
+
+  ASSERT_TRUE(IsPromptShowingOnBrowser(new_browser));
+
+  view1->CloseNativeView();
+  dialog1->CloseNow();
+
+  ASSERT_FALSE(IsPromptShowingOnBrowser(browser()));
+
+  view2->CloseNativeView();
+  dialog2->CloseNow();
+
+  ASSERT_FALSE(IsPromptShowingOnBrowser(new_browser));
+}
+
+}  // namespace
+}  // namespace privacy_sandbox
diff --git a/chrome/browser/privacy_sandbox/notice/desktop_view_manager_interface.h b/chrome/browser/privacy_sandbox/notice/desktop_view_manager_interface.h
index c6675af0..7ff3af6 100644
--- a/chrome/browser/privacy_sandbox/notice/desktop_view_manager_interface.h
+++ b/chrome/browser/privacy_sandbox/notice/desktop_view_manager_interface.h
@@ -26,7 +26,10 @@
     // Fired whenever observers are required to proceed to the next step.
     virtual void MaybeNavigateToNextStep(
         std::optional<notice::mojom::PrivacySandboxNotice> next_id) = 0;
+
+    virtual BrowserWindowInterface* GetBrowser() = 0;
   };
+
   virtual ~DesktopViewManagerInterface();
 
   // Returns handler responsible for tracking navigations.
diff --git a/chrome/browser/privacy_sandbox/notice/desktop_view_manager_test_peer.h b/chrome/browser/privacy_sandbox/notice/desktop_view_manager_test_peer.h
new file mode 100644
index 0000000..9875420
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/notice/desktop_view_manager_test_peer.h
@@ -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.
+
+#ifndef CHROME_BROWSER_PRIVACY_SANDBOX_NOTICE_DESKTOP_VIEW_MANAGER_TEST_PEER_H_
+#define CHROME_BROWSER_PRIVACY_SANDBOX_NOTICE_DESKTOP_VIEW_MANAGER_TEST_PEER_H_
+
+#include "base/memory/raw_ptr.h"
+#include "chrome/browser/privacy_sandbox/notice/desktop_view_manager.h"
+
+namespace privacy_sandbox {
+
+// Allow tests to access private variables and functions from
+// `DesktopViewManager`.
+class DesktopViewManagerTestPeer {
+ public:
+  explicit DesktopViewManagerTestPeer(DesktopViewManager* desktop_view_manager)
+      : desktop_view_manager_(desktop_view_manager) {}
+  ~DesktopViewManagerTestPeer() = default;
+
+  bool IsPromptShowingOnBrowser(BrowserWindowInterface* browser) {
+    return desktop_view_manager_->IsPromptShowingOnBrowser(browser);
+  }
+
+  void HandleChromeOwnedPageNavigation(BrowserWindowInterface* browser) {
+    desktop_view_manager_->HandleChromeOwnedPageNavigation(browser);
+  }
+
+  void SetPendingNotices(
+      std::vector<notice::mojom::PrivacySandboxNotice> pending_notices) {
+    desktop_view_manager_->SetPendingNoticesToShow(pending_notices);
+  }
+
+  void CreateView(BrowserWindowInterface* browser,
+                  DesktopViewManager::ShowViewCallback show) {
+    desktop_view_manager_->MaybeCreateView(browser, std::move(show));
+  }
+
+ private:
+  raw_ptr<DesktopViewManager> desktop_view_manager_;
+};
+}  // namespace privacy_sandbox
+#endif  // CHROME_BROWSER_PRIVACY_SANDBOX_NOTICE_DESKTOP_VIEW_MANAGER_TEST_PEER_H_
diff --git a/chrome/browser/privacy_sandbox/notice/desktop_view_manager_unittest.cc b/chrome/browser/privacy_sandbox/notice/desktop_view_manager_unittest.cc
index 36ce97e..a3955a6 100644
--- a/chrome/browser/privacy_sandbox/notice/desktop_view_manager_unittest.cc
+++ b/chrome/browser/privacy_sandbox/notice/desktop_view_manager_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
+#include "chrome/browser/privacy_sandbox/notice/desktop_view_manager_test_peer.h"
 #include "chrome/browser/privacy_sandbox/notice/mocks/mock_desktop_view_manager_observer.h"
 #include "chrome/browser/privacy_sandbox/notice/mocks/mock_notice_service.h"
 #include "chrome/browser/privacy_sandbox/notice/notice.mojom.h"
@@ -17,28 +18,6 @@
 
 namespace privacy_sandbox {
 
-// Allow tests to access private variables and functions from
-// `DesktopViewManager`.
-class DesktopViewManagerTestPeer {
- public:
-  explicit DesktopViewManagerTestPeer(DesktopViewManager* desktop_view_manager)
-      : desktop_view_manager_(desktop_view_manager) {}
-  ~DesktopViewManagerTestPeer() = default;
-
-  void SetPendingNotices(
-      std::vector<notice::mojom::PrivacySandboxNotice> pending_notices) {
-    desktop_view_manager_->SetPendingNoticesToShow(pending_notices);
-  }
-
-  void CreateView(BrowserWindowInterface* browser,
-                  DesktopViewManager::ShowViewCallback show) {
-    desktop_view_manager_->MaybeCreateView(browser, std::move(show));
-  }
-
- private:
-  raw_ptr<DesktopViewManager> desktop_view_manager_;
-};
-
 namespace {
 using base::MockCallback;
 using notice::mojom::PrivacySandboxNotice;
diff --git a/chrome/browser/privacy_sandbox/notice/mocks/mock_desktop_view_manager_observer.h b/chrome/browser/privacy_sandbox/notice/mocks/mock_desktop_view_manager_observer.h
index 341c621..a1dbf84 100644
--- a/chrome/browser/privacy_sandbox/notice/mocks/mock_desktop_view_manager_observer.h
+++ b/chrome/browser/privacy_sandbox/notice/mocks/mock_desktop_view_manager_observer.h
@@ -22,6 +22,7 @@
               MaybeNavigateToNextStep,
               (std::optional<notice::mojom::PrivacySandboxNotice>),
               (override));
+  MOCK_METHOD(BrowserWindowInterface*, GetBrowser, (), (override));
 };
 
 }  // namespace privacy_sandbox
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
index bf1a6d8..b0d2e9e 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -130,6 +130,7 @@
 #include "net/test/embedded_test_server/http_response.h"
 #include "pdf/buildflags.h"
 #include "pdf/pdf_features.h"
+#include "services/network/public/cpp/network_switches.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -348,7 +349,7 @@
   }
 
   void OpenImagePageAndContextMenu(std::string image_path) {
-    ASSERT_TRUE(embedded_test_server()->Start());
+    ASSERT_TRUE(embedded_test_server()->Started());
     GURL image_url(embedded_test_server()->GetURL(image_path));
     GURL page("data:text/html,<img src='" + image_url.spec() + "'>");
     ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), page));
@@ -475,6 +476,57 @@
 #endif
   }
 
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    ContextMenuBrowserTestBase::SetUpCommandLine(command_line);
+    ASSERT_TRUE(embedded_test_server()->Start());
+    // Treat the test server as public to bypass Local Network Access checks.
+    command_line->AppendSwitchASCII(
+        network::switches::kIpAddressSpaceOverrides,
+        base::StringPrintf(
+            "%s=public",
+            embedded_test_server()->host_port_pair().ToString().c_str()));
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Like ContextMenuBrowserTest, but doesn't set up
+// embedded_test_server()-Start() because a few tests need to do special setup.
+class ContextMenuBrowserNoEmbeddedServerStartTest
+    : public ContextMenuBrowserTestBase,
+      public ::testing::WithParamInterface</*is_preview_enabled*/ bool> {
+ protected:
+  ContextMenuBrowserNoEmbeddedServerStartTest() {
+    if (IsPreviewEnabled()) {
+      scoped_feature_list_.InitWithFeatures(
+          {blink::features::kLinkPreview,
+#if BUILDFLAG(ENABLE_GLIC)
+           features::kGlic,
+#endif  // BUILDFLAG(ENABLE_GLIC)
+           media::kContextMenuSaveVideoFrameAs,
+           media::kContextMenuSearchForVideoFrame},
+          {});
+    } else {
+      scoped_feature_list_.InitWithFeatures(
+          {
+#if BUILDFLAG(ENABLE_GLIC)
+              features::kGlic,
+#endif  // BUILDFLAG(ENABLE_GLIC)
+              media::kContextMenuSaveVideoFrameAs,
+              media::kContextMenuSearchForVideoFrame},
+          {blink::features::kLinkPreview});
+    }
+  }
+
+  bool IsPreviewEnabled() {
+#if BUILDFLAG(IS_ANDROID)
+    return false;
+#else
+    return GetParam();
+#endif
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -487,6 +539,14 @@
                                              : "LinkPreviewDisabled";
                          });
 
+INSTANTIATE_TEST_SUITE_P(All,
+                         ContextMenuBrowserNoEmbeddedServerStartTest,
+                         testing::Bool(),
+                         [](const testing::TestParamInfo<bool>& info) {
+                           return info.param ? "LinkPreviewEnabled"
+                                             : "LinkPreviewDisabled";
+                         });
+
 class PdfPluginContextMenuBrowserTest : public PDFExtensionTestBase {
  public:
   PdfPluginContextMenuBrowserTest() = default;
@@ -743,8 +803,6 @@
 // Verifies "Save as" is not enabled for links blocked via policy.
 IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest,
                        SaveAsEntryIsDisabledForBlockedUrls) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
   auto initial_url = embedded_test_server()->GetURL("/empty.html");
   browser()->profile()->GetPrefs()->SetList(
       policy::policy_prefs::kUrlBlocklist,
@@ -764,8 +822,6 @@
 // Verifies "Save as" is enabled for links that are not blocked via policy.
 IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest,
                        SaveAsEntryIsNotDisabledForNonBlockedUrls) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
   auto initial_url = embedded_test_server()->GetURL("/empty.html");
   browser()->profile()->GetPrefs()->SetList(
       policy::policy_prefs::kUrlBlocklist,
@@ -1590,7 +1646,6 @@
 
 IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest,
                        OpenNewTabInChromeFromWebAppWithAnOpenBrowser) {
-  ASSERT_TRUE(embedded_test_server()->Start());
   GURL title1(embedded_test_server()->GetURL("/title1.html"));
   GURL title2(embedded_test_server()->GetURL("/title2.html"));
 
@@ -1634,7 +1689,6 @@
 
 IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest,
                        OpenNewTabInChromeFromWebAppWithoutAnOpenBrowser) {
-  ASSERT_TRUE(embedded_test_server()->Start());
   GURL title1(embedded_test_server()->GetURL("/title1.html"));
 
   const AppId app_id = InstallTestWebApp(
@@ -1683,7 +1737,6 @@
 IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest, OpenAboutBlankInNewTab) {
   ui_test_utils::AllBrowserTabAddedWaiter add_tab;
 
-  ASSERT_TRUE(embedded_test_server()->Start());
   GURL page(embedded_test_server()->GetURL("/title1.html"));
 
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), page));
@@ -1709,7 +1762,6 @@
 IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest, OpenDataURLInNewTab) {
   ui_test_utils::AllBrowserTabAddedWaiter add_tab;
 
-  ASSERT_TRUE(embedded_test_server()->Start());
   GURL page(embedded_test_server()->GetURL("/title1.html"));
 
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), page));
@@ -1734,7 +1786,6 @@
 IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest, OpenInNewTabReferrer) {
   ui_test_utils::AllBrowserTabAddedWaiter add_tab;
 
-  ASSERT_TRUE(embedded_test_server()->Start());
   GURL echoheader(embedded_test_server()->GetURL("/echoheader?Referer"));
 
   // Go to a |page| with a link to echoheader URL.
@@ -1773,7 +1824,6 @@
 IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest, OpenIncognitoNoneReferrer) {
   ui_test_utils::AllBrowserTabAddedWaiter add_tab;
 
-  ASSERT_TRUE(embedded_test_server()->Start());
   GURL echoheader(embedded_test_server()->GetURL("/echoheader?Referer"));
 
   // Go to a |page| with a link to echoheader URL.
@@ -1809,7 +1859,6 @@
   // Register observer.
   ContextMenuWaiter menu_observer;
 
-  ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/download-anchor-same-origin.html"));
 
   // Go to a page with a link having download attribute.
@@ -1847,7 +1896,6 @@
 // frame.  This is a regression test for https://crbug.com/1085040.
 IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest,
                        MenuContentsVerification_MainFrame) {
-  ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/iframe.html"));
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
 
@@ -1895,7 +1943,6 @@
 // subframe.
 IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest,
                        MenuContentsVerification_Subframe) {
-  ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/iframe.html"));
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
   content::WebContents* tab =
@@ -1954,7 +2001,6 @@
 // with height more than visual viewport bounds.
 IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest,
                        NonLocatedContextMenuOnLargeImageElement) {
-  ASSERT_TRUE(embedded_test_server()->Start());
   GURL image_url(
       "data:text/html,<html><img src=\"http://example.test/cat.jpg\" "
       "width=\"200\" height=\"10000\" tabindex=\"-1\" /></html>");
@@ -2039,7 +2085,6 @@
   // Register observer.
   ContextMenuWaiter menu_observer;
 
-  ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(
       embedded_test_server()->GetURL("/download-anchor-cross-origin.html"));
 
@@ -2272,7 +2317,6 @@
     }
   }
 
-  ASSERT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/"));
 
   std::unique_ptr<TestRenderViewContextMenu> menu(
@@ -2318,7 +2362,6 @@
       profile, chrome::startup::IsProcessStartup::kNo,
       chrome::startup::IsFirstRun::kNo, false);
 
-  ASSERT_TRUE(embedded_test_server()->Start());
 
   GURL echoheader(embedded_test_server()->GetURL("/echoheader?Referer"));
   // Go to a |page| with a link to echoheader URL.
@@ -3322,7 +3365,6 @@
 // Ensure that the context menu can tolerate changes to session history that
 // happen between menu initialization and command execution.
 IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest, BackAfterBackEntryRemoved) {
-  ASSERT_TRUE(embedded_test_server()->Start());
 
   WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
@@ -3410,7 +3452,8 @@
                                          nullptr);
 }
 
-IN_PROC_BROWSER_TEST_P(ContextMenuBrowserTest, SubframeNewTabInitiator) {
+IN_PROC_BROWSER_TEST_P(ContextMenuBrowserNoEmbeddedServerStartTest,
+                       SubframeNewTabInitiator) {
   // If a frame on example.com opens a subframe with a different opaque origin,
   // the subframe origin should be passed through to a context menu on that
   // initiator, so:
diff --git a/chrome/browser/resources/chromeos/nearby_share/shared/nearby_onboarding_one_page.ts b/chrome/browser/resources/chromeos/nearby_share/shared/nearby_onboarding_one_page.ts
index b6a63a3..e7ef1e2 100644
--- a/chrome/browser/resources/chromeos/nearby_share/shared/nearby_onboarding_one_page.ts
+++ b/chrome/browser/resources/chromeos/nearby_share/shared/nearby_onboarding_one_page.ts
@@ -18,6 +18,7 @@
 
 import type {CrInputElement} from 'chrome://resources/ash/common/cr_elements/cr_input/cr_input.js';
 import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {DeviceNameValidationResult, Visibility} from 'chrome://resources/mojo/chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom-webui.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
@@ -63,12 +64,20 @@
         value: NearbyShareOnboardingEntryPoint.MAX,
       },
 
+      /**
+       * Determines whether the QuickShareV2 flag is enabled.
+       */
+      isQuickShareV2Enabled_: {
+        type: Boolean,
+        value: () => loadTimeData.getBoolean('isQuickShareV2Enabled'),
+      },
     };
   }
 
   errorMessage: string;
   settings: NearbySettings|null;
   private entryPoint_: NearbyShareOnboardingEntryPoint;
+  private isQuickShareV2Enabled_: boolean;
 
   override ready(): void {
     super.ready();
@@ -200,25 +209,61 @@
 
   private getVisibilitySelectionButtonText_(): string {
     const visibility = this.getDefaultVisibility_();
+
+    if (this.isQuickShareV2Enabled_) {
+      switch (visibility) {
+        case Visibility.kAllContacts:
+          return this.i18n('nearbyShareContactVisiblityContactsButton');
+        case Visibility
+            .kSelectedContacts:  // Selected Contacts does not exist in Quick
+        // Share v2. Your devices set instead.
+        case Visibility.kYourDevices:
+          return this.i18n('nearbyShareContactVisibilityYourDevices');
+        case Visibility.kNoOne:
+          return this.i18n('nearbyShareContactVisibilityNone');
+        default:
+          return this.i18n('nearbyShareContactVisiblityContactsButton');
+      }
+    }
+
     switch (visibility) {
       case Visibility.kAllContacts:
-        return this.i18n('nearbyShareContactVisibilityAll');
+        return this.i18n('nearbyShareContactVisiblityContactsButton');
       case Visibility.kSelectedContacts:
         return this.i18n('nearbyShareContactVisibilitySome');
+      case Visibility.kYourDevices:
+        return this.i18n('nearbyShareContactVisibilityYourDevices');
       case Visibility.kNoOne:
         return this.i18n('nearbyShareContactVisibilityNone');
       default:
-        return this.i18n('nearbyShareContactVisibilityAll');
+        return this.i18n('nearbyShareContactVisiblityContactsButton');
     }
   }
 
   private getVisibilitySelectionButtonIcon_(): string {
     const visibility = this.getDefaultVisibility_();
+    if (this.isQuickShareV2Enabled_) {
+      switch (visibility) {
+        case Visibility.kAllContacts:
+          return 'contact-all';
+        case Visibility
+            .kSelectedContacts:  // Selected Contacts does not exist in Quick
+                                 // Share v2. Your devices set instead.
+        case Visibility.kYourDevices:
+          return 'your-devices';
+        case Visibility.kNoOne:
+          return 'visibility-off';
+        default:
+          return 'contact-all';
+      }
+    }
     switch (visibility) {
       case Visibility.kAllContacts:
         return 'contact-all';
       case Visibility.kSelectedContacts:
         return 'contact-group';
+      case Visibility.kYourDevices:
+        return 'your-devices';
       case Visibility.kNoOne:
         return 'visibility-off';
       default:
diff --git a/chrome/browser/resources/data_sharing/data_sharing_app.ts b/chrome/browser/resources/data_sharing/data_sharing_app.ts
index 7226488..d25a39c6 100644
--- a/chrome/browser/resources/data_sharing/data_sharing_app.ts
+++ b/chrome/browser/resources/data_sharing/data_sharing_app.ts
@@ -537,6 +537,10 @@
               onJoinSuccessful: () => {
                 this.successfullyJoined_ = true;
                 this.browserProxy_.handler!.openTabGroup(groupId!);
+                // No need to return this promise since we want the bubble to
+                // keep spinning until closed by the browser when new shared tab
+                // group is received.
+                return new Promise<void>(() => {});
               },
               fetchPreviewData: () => {
                 return this.browserProxy_.getTabGroupPreview(
@@ -545,14 +549,19 @@
               logger: this,
             })
             .then((res) => {
-              let code: Code = res.status;
-              if (!this.successfullyJoined_ && !this.abandonJoin_) {
-                // If user neither succesfully joined nor abandon join, there
-                // must be an error.
-                code = Code.UNKNOWN;
+              if (this.successfullyJoined_) {
+                // If user successfully joined, do nothing and keep the dialog
+                // around until the tab group is delivered by sync server.
+                return;
               }
 
-              this.browserProxy_.closeUi(code);
+              if (this.abandonJoin_) {
+                this.browserProxy_.closeUi(res.status);
+              } else {
+                // If user neither successfully joined nor abandon join, there
+                // must be an error.
+                this.browserProxy_.closeUi(Code.UNKNOWN);
+              }
             });
         break;
       case FlowValues.MANAGE:
diff --git a/chrome/browser/resources/data_sharing/data_sharing_sdk_types.ts b/chrome/browser/resources/data_sharing/data_sharing_sdk_types.ts
index 5427bd8..3bcf32dd 100644
--- a/chrome/browser/resources/data_sharing/data_sharing_sdk_types.ts
+++ b/chrome/browser/resources/data_sharing/data_sharing_sdk_types.ts
@@ -271,7 +271,7 @@
   parent: HTMLElement;
   translatedMessages: TranslationMap;
   learnMoreUrlMap: {[type in LearnMoreUrlType]?: () => string};
-  onJoinSuccessful: () => void;
+  onJoinSuccessful: () => void | Promise<void>;
   fetchPreviewData: () => Promise<DataSharingSdkSitePreview[]>;
   logger?: Logger;
 }
diff --git a/chrome/browser/resources/glic/glic_api/glic_api.ts b/chrome/browser/resources/glic/glic_api/glic_api.ts
index 2a0eac8e..2b28a69a 100644
--- a/chrome/browser/resources/glic/glic_api/glic_api.ts
+++ b/chrome/browser/resources/glic/glic_api/glic_api.ts
@@ -970,6 +970,8 @@
   INVALID_ACTION_PROTO = 2,
   /** Action target is not found. */
   TARGET_NOT_FOUND = 3,
+  /** Failed to start a new task. */
+  FAILED_TO_START_TASK = 4,
 }
 
 /**
diff --git a/chrome/browser/resources/glic/glic_app_controller.ts b/chrome/browser/resources/glic/glic_app_controller.ts
index 5e86f62..ca81e99 100644
--- a/chrome/browser/resources/glic/glic_app_controller.ts
+++ b/chrome/browser/resources/glic/glic_app_controller.ts
@@ -60,6 +60,21 @@
   reloadOnOpen?: boolean;
 }
 
+// Web client unresponsiveness state tracking values for metrics reporting.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+//
+// LINT.IfChange(WebClientUnresponsiveState)
+export enum WebClientUnresponsiveState {
+  ENTERED_FROM_WEBVIEW_EVENT = 0,
+  ENTERED_FROM_CUSTOM_HEARTBEAT = 1,
+  ALREADY_ON_FROM_WEBVIEW_EVENT = 2,
+  ALREADY_ON_FROM_CUSTOM_HEARTBEAT = 3,
+  EXITED = 4,
+  MAX_VALUE = EXITED,
+}
+// LINT.ThenChange(//tools/metrics/histograms/metadata/glic/enums.xml:WebClientUnresponsiveState)
+
 export class GlicAppController implements PageInterface, WebviewDelegate,
                                           ApiHostEmbedder {
   loadingTimer: number|undefined;
@@ -84,6 +99,8 @@
   private profileReadyState: ProfileReadyState|undefined = undefined;
   private profileReadyInitialState = Promise.withResolvers<void>();
 
+  private enteredUnresponsiveTimestampMs?: number;
+
   state: WebUiState|undefined;
 
   // When entering loading state, this represents the earliest timestamp at
@@ -132,9 +149,38 @@
   // WebviewDelegate implementation.
   webviewUnresponsive(): void {
     console.warn('webview unresponsive');
+    this.trackUnresponsiveState(
+        this.state === WebUiState.kUnresponsive ?
+            WebClientUnresponsiveState.ALREADY_ON_FROM_WEBVIEW_EVENT :
+            WebClientUnresponsiveState.ENTERED_FROM_WEBVIEW_EVENT);
     this.setState(WebUiState.kUnresponsive);
   }
 
+  trackUnresponsiveState(newState: WebClientUnresponsiveState): void {
+    // Track and record unresponsive state duration.
+    if (newState === WebClientUnresponsiveState.ENTERED_FROM_WEBVIEW_EVENT ||
+        newState === WebClientUnresponsiveState.ENTERED_FROM_CUSTOM_HEARTBEAT) {
+      // Entering an unresponsive state.
+      this.enteredUnresponsiveTimestampMs = Date.now();
+    } else if (newState === WebClientUnresponsiveState.EXITED) {
+      // Existing an unresponsive state.
+      if (this.enteredUnresponsiveTimestampMs !== undefined) {
+        const unresponsiveDuration =
+            Date.now() - this.enteredUnresponsiveTimestampMs;
+        chrome.metricsPrivate.recordMediumTime(
+            'Glic.Host.WebClientUnresponsiveState.Duration',
+            unresponsiveDuration);
+        this.enteredUnresponsiveTimestampMs = undefined;
+      } else {
+        console.error('Unresponsive state exited without an entering timestamp');
+      }
+    }
+    // Record unresponsive state detections and transitions.
+    chrome.metricsPrivate.recordEnumerationValue(
+        'Glic.Host.WebClientUnresponsiveState', newState,
+        WebClientUnresponsiveState.MAX_VALUE + 1);
+  }
+
   webviewError(reason: string): void {
     console.warn(`webview exit. reason: ${reason}`);
     this.setState(WebUiState.kError);
@@ -247,6 +293,7 @@
             },
         onExit:
             () => {
+              this.trackUnresponsiveState(WebClientUnresponsiveState.EXITED);
               $.unresponsiveOverlay.classList.toggle('hidden', true);
             },
       },
@@ -475,6 +522,10 @@
         this.setState(WebUiState.kReady);
         break;
       case WebClientState.UNRESPONSIVE:
+        this.trackUnresponsiveState(
+            this.state === WebUiState.kUnresponsive ?
+                WebClientUnresponsiveState.ALREADY_ON_FROM_CUSTOM_HEARTBEAT :
+                WebClientUnresponsiveState.ENTERED_FROM_CUSTOM_HEARTBEAT);
         this.setState(WebUiState.kUnresponsive);
         break;
       case WebClientState.ERROR:
diff --git a/chrome/browser/resources/lens/overlay/lens_overlay_app.html b/chrome/browser/resources/lens/overlay/lens_overlay_app.html
index f5f63aa8..06283921 100644
--- a/chrome/browser/resources/lens/overlay/lens_overlay_app.html
+++ b/chrome/browser/resources/lens/overlay/lens_overlay_app.html
@@ -385,16 +385,6 @@
   }
 
   /**
-   * Fade out the ghost loader bars when the searchbox dropdown is showing
-   * (results available).
-   */
-  :host([enable-csb-motion-tweaks][show-ghost-loader])
-    #searchboxContainer:has(cr-searchbox[dropdown-is-visible])
-    cr-searchbox-ghost-loader .ghost-loader-item {
-    animation: 400ms ghost-loader-exit-animation cubic-bezier(0.4, 0, 0.2, 1) forwards;
-  }
-
-  /**
    * Slide in the ghost loader when the searchbox is not already manually
    * focused (e.g., on initial invocation). Once it has been manually focused,
    * subsequent focus events will not trigger the slide in animation.
@@ -412,16 +402,6 @@
     }
   }
 
-  @keyframes ghost-loader-exit-animation {
-    0% {
-      opacity: 1;
-    }
-
-    100% {
-      opacity: 0;
-    }
-  }
-
   /* Default values for the searchbox dropdown that enable the fade in animation. */
   :host([enable-csb-motion-tweaks][show-ghost-loader]) cr-searchbox::part(searchbox-dropdown) {
     opacity: 0;
@@ -495,7 +475,8 @@
       id="searchboxGhostLoader"
       tabindex="-1"
       show-error-state="{{showErrorState}}"
-      page-content-type="[[pageContentType]]">
+      page-content-type="[[pageContentType]]"
+      suggestion-count="[[searchboxSuggestionCount]]"
     </cr-searchbox-ghost-loader>
   </div>
   <div id="translateButtonContainer" class="button-container">
diff --git a/chrome/browser/resources/lens/overlay/lens_overlay_app.ts b/chrome/browser/resources/lens/overlay/lens_overlay_app.ts
index bd35361d..2749baf3 100644
--- a/chrome/browser/resources/lens/overlay/lens_overlay_app.ts
+++ b/chrome/browser/resources/lens/overlay/lens_overlay_app.ts
@@ -213,6 +213,10 @@
         value: () => loadTimeData.getBoolean('enableCloseButtonTweaks'),
         reflectToAttribute: true,
       },
+      searchboxSuggestionCount: {
+        type: Number,
+        value: 0,
+      },
     };
   }
 
@@ -268,6 +272,8 @@
       loadTimeData.getValue('autoFocusSearchbox');
   declare private toastMessage: string;
   declare private enableCloseButtonTweaks: boolean;
+  // The number of suggestions currently being shown to the user.
+  declare private searchboxSuggestionCount: number;
   // What the current page content type is.
   declare private pageContentType: PageContentType;
   // Whether the ghost loader is enabled via feature flag.
@@ -295,7 +301,7 @@
   private invocationTime: number = loadTimeData.getValue('invocationTime');
 
   private searchboxBoundingClientRectObserver: ResizeObserver =
-      new ResizeObserver(this.focusShimmerOnSearchbox.bind(this));
+      new ResizeObserver(this.onSearchboxBoundsChanged.bind(this));
 
   // The ID returned by requestAnimationFrame for the updateCursorPosition
   // function.
@@ -433,6 +439,13 @@
     this.showErrorState = false;
   }
 
+  private onSearchboxBoundsChanged() {
+    this.focusShimmerOnSearchbox();
+
+    this.searchboxSuggestionCount =
+        this.$.searchbox.getSuggestionsElement().selectableMatchElements.length;
+  }
+
   private focusShimmerOnSearchbox() {
     const suggestionsContainer = this.$.searchbox.getSuggestionsElement();
     const areSuggestionsShowing =
@@ -650,7 +663,8 @@
     // The searchbox is not focusable until the animation has ended.
     // Only called here if not already called in onScreenshotRendered
     if (this.autoFocusSearchbox &&
-        this.isLensOverlayContextualSearchboxVisible && !this.enableCsbMotionTweaks) {
+        this.isLensOverlayContextualSearchboxVisible &&
+        !this.enableCsbMotionTweaks) {
       this.focusSearchbox();
     }
   }
diff --git a/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.html b/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.html
index d461458..78fbec77 100644
--- a/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.html
+++ b/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.html
@@ -185,16 +185,6 @@
   }
 
   /**
-   * Fade out the ghost loader bars when the searchbox dropdown is showing
-   * (results available).
-   */
-  :host([enable-csb-motion-tweaks][show-ghost-loader])
-    #searchboxContainer:has(cr-searchbox[dropdown-is-visible])
-    cr-searchbox-ghost-loader .ghost-loader-item {
-    animation: 400ms ghost-loader-exit-animation cubic-bezier(0.4, 0, 0.2, 1) forwards;
-  }
-
-  /**
    * Slide in the ghost loader when the searchbox is not already manually
    * focused (e.g., on initial invocation). Once it has been manually focused,
    * subsequent focus events will not trigger the slide in animation.
@@ -212,16 +202,6 @@
     }
   }
 
-  @keyframes ghost-loader-exit-animation {
-    0% {
-      opacity: 1;
-    }
-
-    100% {
-      opacity: 0;
-    }
-  }
-
   /* Default values for the searchbox dropdown that enable the fade in animation. */
   :host([enable-csb-motion-tweaks][show-ghost-loader]) cr-searchbox::part(searchbox-dropdown) {
     opacity: 0;
@@ -254,6 +234,7 @@
       id="searchboxGhostLoader"
       show-error-state="{{showErrorState}}"
       page-content-type="[[pageContentType]]"
+      suggestion-count="[[searchboxSuggestionCount]]"
       tabindex="-1">
   </cr-searchbox-ghost-loader>
 </div>
diff --git a/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.ts b/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.ts
index 23249fb..32fa89e 100644
--- a/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.ts
+++ b/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.ts
@@ -172,6 +172,10 @@
         type: String,
         value: '',
       },
+      searchboxSuggestionCount: {
+        type: Number,
+        value: 0,
+      },
     };
   }
 
@@ -227,8 +231,13 @@
   private pageHandler: LensSidePanelPageHandlerInterface;
   declare private wasBackArrowAvailable: boolean;
   declare private toastMessage: string;
+  // The number of suggestions currently being shown to the user.
+  declare private searchboxSuggestionCount: number;
   private eventTracker_: EventTracker = new EventTracker();
 
+  private searchboxBoundingClientRectObserver: ResizeObserver =
+      new ResizeObserver(this.onSearchboxBoundsChanged.bind(this));
+
   constructor() {
     super();
     // Need to get the page classification through a mojom call since the
@@ -420,6 +429,11 @@
     this.notifyHelpBubbleAnchorCustomEvent(
         'kLensSidePanelSearchBoxElementId',
         'kLensSidePanelSearchBoxFocusedEventId');
+
+    // Setup a listener on the suggestions container to adjust the ghost loader
+    // number of suggestions.
+    this.searchboxBoundingClientRectObserver.observe(
+        this.$.searchbox.getSuggestionsElement());
   }
 
   private onSearchboxFocusOut_(event: FocusEvent) {
@@ -436,6 +450,14 @@
     this.isSearchboxFocused = false;
     this.autocompleteRequestStarted = false;
     this.showErrorState = false;
+
+    // Disconnect the ResizeObserver.
+    this.searchboxBoundingClientRectObserver.disconnect();
+  }
+
+  private onSearchboxBoundsChanged() {
+    this.searchboxSuggestionCount =
+        this.$.searchbox.getSuggestionsElement().selectableMatchElements.length;
   }
 
   private computeShowGhostLoader(): boolean {
diff --git a/chrome/browser/resources/lens/shared/searchbox_ghost_loader.html b/chrome/browser/resources/lens/shared/searchbox_ghost_loader.html
index 759d7dd..cedeaddf 100644
--- a/chrome/browser/resources/lens/shared/searchbox_ghost_loader.html
+++ b/chrome/browser/resources/lens/shared/searchbox_ghost_loader.html
@@ -102,6 +102,13 @@
   height: 48px;
 }
 
+
+/* Fade out animation for the ghost loader items when real suggestions are shown. */
+:host([enable-csb-motion-tweaks][should-fade-out])
+  .ghost-loader-item {
+  animation: 400ms ghost-loader-exit-animation cubic-bezier(0.4, 0, 0.2, 1) forwards;
+}
+
 .searchbox-icon {
   --cr-searchbox-icon-border-radius: 12px;
   margin-right: 10px;
@@ -215,7 +222,8 @@
       </span>
     </div>
     <div class="suggestion-loader-container">
-      <template is="dom-repeat" items="[0, 1, 2, 3, 4]" as="item">
+      <template is="dom-repeat"
+          items="[[getSuggestionItems(suggestionCount)]]" as="item">
         <div class="ghost-loader-item">
           <cr-searchbox-icon
             class="searchbox-icon"
diff --git a/chrome/browser/resources/lens/shared/searchbox_ghost_loader.ts b/chrome/browser/resources/lens/shared/searchbox_ghost_loader.ts
index b11c4fbb..bf48dad8 100644
--- a/chrome/browser/resources/lens/shared/searchbox_ghost_loader.ts
+++ b/chrome/browser/resources/lens/shared/searchbox_ghost_loader.ts
@@ -63,6 +63,12 @@
         value: () => loadTimeData.getBoolean('enableSummarizeSuggestionHint'),
         reflectToAttribute: true,
       },
+      suggestionCount: {
+        type: Number,
+        value: 0,
+      },
+      shouldFadeOut:
+          {type: Boolean, computed: 'computeShouldFadeOut(suggestionCount)'},
     };
   }
 
@@ -75,6 +81,11 @@
   // What the current page content type is.
   declare private pageContentType: PageContentType;
   declare private enableSummarizeSuggestionHint: boolean;
+  // The number of suggestions to show in the ghost loader.
+  declare private suggestionCount: number;
+  // Whether the ghost loader suggestions should fade out now that suggestions
+  // came in.
+  declare private shouldFadeOut: boolean;
   private browserProxy: BrowserProxy = BrowserProxyImpl.getInstance();
   private listenerIds: number[];
   declare private ghostLoaderPrimaryMessage: string;
@@ -120,6 +131,20 @@
         this.i18n('searchboxGhostLoaderHintTextPrimaryPdf') :
         this.i18n('searchboxGhostLoaderHintTextPrimaryDefault');
   }
+
+  private computeShouldFadeOut() {
+    // Once the suggestionCount is no longer 0, fade out the ghost loader
+    // suggestions.
+    return this.suggestionCount !== 0;
+  }
+
+  private getSuggestionItems(): number[] {
+    if (this.suggestionCount === 0) {
+      return Array(5).fill(0);
+    }
+    // The content of the array is unused.
+    return Array(this.suggestionCount).fill(0);
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/new_tab_footer/app.css b/chrome/browser/resources/new_tab_footer/app.css
index 555d043..b4eb27e 100644
--- a/chrome/browser/resources/new_tab_footer/app.css
+++ b/chrome/browser/resources/new_tab_footer/app.css
@@ -75,9 +75,22 @@
   display: flex;
 }
 
-#managementNoticeLogo {
-  padding-right: 16px;
+#managementNoticeLogoContainer {
+  align-items: center;
+  display: flex;
+  flex-shrink: 0;
+  height: 32px;
+  justify-content: center;
+  width: 32px;
+  margin-right: 8px;
+}
+
+.custom_logo {
+  background-color: var(--color-new-tab-footer-logo-background);
+  border-radius: 8px;
+}
+
+#managementNoticeLogoContainer > img {
   width: 20px;
   height: 20px;
-  transform: translateY(+25%);
-}
+}
\ No newline at end of file
diff --git a/chrome/browser/resources/new_tab_footer/app.html.ts b/chrome/browser/resources/new_tab_footer/app.html.ts
index 574dc010..8c4e549d 100644
--- a/chrome/browser/resources/new_tab_footer/app.html.ts
+++ b/chrome/browser/resources/new_tab_footer/app.html.ts
@@ -15,10 +15,14 @@
 -->
 <div id="container">
   <div id="infoContainer">
-    ${this.managementNotice_ ? html`
-      <div id="managementNoticeContainer" class="notice-item">
-        <img id="managementNoticeLogo" alt=""
-            src="${this.managementNotice_.bitmapDataUrl.url}">
+  ${this.managementNotice_ ?
+      html`<div id="managementNoticeContainer" class="notice-item">
+        <div id="managementNoticeLogoContainer"
+             class=${this.managementNotice_.isCustomLogo ?
+             'custom_logo' : ''}>
+          <img id="managementNoticeLogo" alt=""
+               src="${this.managementNotice_.bitmapDataUrl.url}">
+        </div>
         <p title="${this.managementNotice_.text}">
           ${this.managementNotice_.text}
         </p>
diff --git a/chrome/browser/resources/print_preview/data/model.ts b/chrome/browser/resources/print_preview/data/model.ts
index 8caffcb..dac753d 100644
--- a/chrome/browser/resources/print_preview/data/model.ts
+++ b/chrome/browser/resources/print_preview/data/model.ts
@@ -867,16 +867,7 @@
     if (this.settings_.mediaSize.available) {
       const defaultOption = caps.media_size!.option.find(o => !!o.is_default) ||
           caps.media_size!.option[0];
-      let matchingOption = null;
-      // If the setting does not have a valid value, the UI has just started so
-      // do not try to get a matching value; just set the printer default in
-      // case the user doesn't have sticky settings.
-      if (this.settings_.mediaSize.setFromUi) {
-        const currentMediaSize = this.getSettingValue('mediaSize');
-        matchingOption = this.destination.getMediaSize(
-            currentMediaSize.width_microns, currentMediaSize.height_microns);
-      }
-      this.setSetting('mediaSize', matchingOption || defaultOption, true);
+      this.setSetting('mediaSize', defaultOption, true);
     }
 
     if (this.settings_.dpi.available) {
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
index 7fc45cf..379d7bb081 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -13,8 +13,10 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/ref_counted.h"
+#include "base/metrics/statistics_recorder.h"
 #include "base/run_loop.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/gmock_move_support.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_command_line.h"
@@ -72,6 +74,7 @@
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "url/gurl.h"
 
+using base::test::RunOnceClosure;
 using content::BrowserThread;
 using content::RenderFrameHostTester;
 using content::WebContents;
@@ -285,7 +288,6 @@
     request.set_client_score(0.8);
     std::move(callback).Run(mojom::PhishingDetectorResult::SUCCESS,
                             mojo_base::ProtoWrapper(request));
-
     return;
   }
 
@@ -394,17 +396,10 @@
     raw_token_fetcher_ = nullptr;
     raw_delegate_ = nullptr;
 
-    // Delete the host object on the UI thread and release the
-    // SafeBrowsingService.
-    content::GetUIThreadTaskRunner({})->DeleteSoon(FROM_HERE,
-                                                   csd_host_.release());
-
-    // RenderProcessHostCreationObserver expects to be torn down on UI.
-    content::GetUIThreadTaskRunner({})->DeleteSoon(FROM_HERE,
-                                                   csd_service_.release());
+    csd_host_.reset();
+    csd_service_.reset();
     database_manager_.reset();
     ui_manager_.reset();
-    base::RunLoop().RunUntilIdle();
 
     ChromeRenderViewHostTestHarness::TearDown();
   }
@@ -464,14 +459,25 @@
       EXPECT_CALL(*csd_service_, IsLocalResource(_))
           .WillOnce(Return(*is_local));
     }
+
+    pre_classification_run_loop_ = std::make_unique<base::RunLoop>();
+    pre_classification_histogram_observer_ = std::make_unique<
+        base::StatisticsRecorder::ScopedHistogramSampleObserver>(
+        "SBClientPhishing.PreClassificationCheckResult",
+        base::IgnoreArgs<std::string_view, uint64_t,
+                         base::HistogramBase::Sample32>(
+            pre_classification_run_loop_->QuitClosure()));
   }
 
+  // Wait for the preclassification check histogram to be logged, then
+  // flush the generated IPC (if it exists).
   void WaitAndCheckPreClassificationChecks() {
-    // Wait for CheckCsdAllowlist and CheckCache() to be called if at all.
-    base::RunLoop().RunUntilIdle();
-    EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
-    EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
-    EXPECT_TRUE(Mock::VerifyAndClear(database_manager_.get()));
+    if (!pre_classification_run_loop_->AnyQuitCalled()) {
+      pre_classification_run_loop_->Run();
+    }
+    if (csd_host_->phishing_detector_) {
+      csd_host_->phishing_detector_.FlushForTesting();
+    }
   }
 
   void NavigateAndCommit(const GURL& safe_url) {
@@ -527,6 +533,11 @@
   signin::IdentityTestEnvironment identity_test_env_;
   base::test::ScopedFeatureList feature_list_;
   std::unique_ptr<WebContentsObserver> observer_;
+
+  // Used to synchronize waiting for pre-classification to complete.
+  std::unique_ptr<base::RunLoop> pre_classification_run_loop_;
+  std::unique_ptr<base::StatisticsRecorder::ScopedHistogramSampleObserver>
+      pre_classification_histogram_observer_;
 };
 
 class ClientSideDetectionHostTest : public ClientSideDetectionHostTestBase {
@@ -575,7 +586,6 @@
   // Make sure DisplayBlockingPage is not going to be called.
   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_)).Times(0);
   std::move(cb).Run(GURL(verdict.url()), false, net::HTTP_OK, std::nullopt);
-  base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
 }
 
@@ -602,12 +612,14 @@
   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
   ASSERT_FALSE(cb.is_null());
 
+  base::RunLoop run_loop;
   UnsafeResource resource;
   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
-      .WillOnce(SaveArg<0>(&resource));
+      .WillOnce(
+          DoAll(SaveArg<0>(&resource), RunOnceClosure(run_loop.QuitClosure())));
   std::move(cb).Run(phishing_url, true, net::HTTP_OK, std::nullopt);
+  run_loop.Run();
 
-  base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
   EXPECT_EQ(phishing_url, resource.url);
   EXPECT_EQ(phishing_url, resource.original_url);
@@ -659,11 +671,17 @@
   ClientSideDetectionService::ClientReportPhishingRequestCallback cb_other;
   verdict.set_url(other_phishing_url.spec());
   verdict.set_client_score(0.8f);
-  EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(
-                                 PartiallyEqualVerdict(verdict), _, _))
-      .WillOnce(MoveArg<1>(&cb_other));
-  PhishingDetectionDone(mojo_base::ProtoWrapper(verdict));
-  base::RunLoop().RunUntilIdle();
+
+  {
+    base::RunLoop run_loop;
+    EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(
+                                   PartiallyEqualVerdict(verdict), _, _))
+        .WillOnce(DoAll(RunOnceClosure(run_loop.QuitClosure()),
+                        MoveArg<1>(&cb_other)));
+    PhishingDetectionDone(mojo_base::ProtoWrapper(verdict));
+    run_loop.Run();
+  }
+
   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
   ASSERT_FALSE(cb_other.is_null());
@@ -671,15 +689,20 @@
   // We expect that the interstitial is shown for the second phishing URL and
   // not for the first phishing URL.
   UnsafeResource resource;
-  EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
-      .WillOnce(SaveArg<0>(&resource));
 
-  std::move(cb).Run(phishing_url, true, net::HTTP_OK,
-                    std::nullopt);  // Should have no effect.
-  std::move(cb_other).Run(other_phishing_url, true, net::HTTP_OK,
-                          std::nullopt);  // Should show interstitial.
+  {
+    base::RunLoop run_loop;
+    EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
+        .WillOnce(DoAll(SaveArg<0>(&resource),
+                        RunOnceClosure(run_loop.QuitClosure())));
 
-  base::RunLoop().RunUntilIdle();
+    std::move(cb).Run(phishing_url, true, net::HTTP_OK,
+                      std::nullopt);  // Should have no effect.
+    std::move(cb_other).Run(other_phishing_url, true, net::HTTP_OK,
+                            std::nullopt);  // Should show interstitial.
+    run_loop.Run();
+  }
+
   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
   EXPECT_EQ(other_phishing_url, resource.url);
   EXPECT_EQ(other_phishing_url, resource.original_url);
@@ -1072,32 +1095,6 @@
       PreClassificationCheckResult::NO_CLASSIFY_MATCH_HC_ALLOWLIST, 0);
 }
 
-TEST_F(ClientSideDetectionHostTest,
-       TestPreClassificationCheckSameDocumentNavigation) {
-  if (base::FeatureList::IsEnabled(kClientSideDetectionKillswitch))
-    GTEST_SKIP();
-
-  GURL url("http://host.com/");
-  database_manager_->SetAllowlistLookupDetailsForUrl(url, false);
-  ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
-                                &kFalse);
-  NavigateAndKeepLoading(web_contents(), url);
-  WaitAndCheckPreClassificationChecks();
-
-  fake_phishing_detector_.CheckMessage(&url);
-  fake_phishing_detector_.Reset();
-
-  // Now try an same-document navigation.  This should not trigger an IPC.
-  EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_)).Times(0);
-  GURL inpage("http://host.com/#foo");
-  ExpectPreClassificationChecks(inpage, nullptr, nullptr, nullptr, nullptr,
-                                nullptr);
-  NavigateAndKeepLoading(web_contents(), inpage);
-  WaitAndCheckPreClassificationChecks();
-
-  fake_phishing_detector_.CheckMessage(nullptr);
-}
-
 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckXHTML) {
   if (base::FeatureList::IsEnabled(kClientSideDetectionKillswitch))
     GTEST_SKIP();
@@ -1974,9 +1971,14 @@
 
   void SetUp() override {
     ClientSideDetectionHostTest::SetUp();
-
     SetEnhancedProtectionPrefForTests(profile()->GetPrefs(), true);
     SetFeatures({kClientSideDetectionAcceptHCAllowlist}, {});
+    database_manager_->SetAllowlistLookupDetailsForUrl(example_url_, false);
+    ON_CALL(*raw_token_fetcher_, Start(_))
+        .WillByDefault(
+            testing::Invoke([&](SafeBrowsingTokenFetcher::Callback callback) {
+              std::move(callback).Run("fake_access_token");
+            }));
 
     AsyncCheckTracker::CreateForWebContents(
         web_contents(),
@@ -2037,6 +2039,9 @@
         /*all_checks_completed=*/true);
     tracker->PendingCheckerCompleted(/*navigation_id=*/0, result);
   }
+
+  base::HistogramTester histogram_tester_;
+  GURL example_url_{"http://suspiciousurl.com/"};
 };
 
 TEST_F(ClientSideDetectionRTLookupResponseForceRequestTest,
@@ -2045,67 +2050,33 @@
     GTEST_SKIP();
   }
 
-  base::HistogramTester histogram_tester;
-
-  GURL example_url("http://suspiciousurl.com/");
-
-  database_manager_->SetAllowlistLookupDetailsForUrl(example_url, false);
-
-  // First navigate to a page, which should trigger preclassification check.
-  ExpectPreClassificationChecks(
-      /*url=*/example_url, /*is_private=*/&kFalse,
-      /*match_csd_allowlist=*/&kFalse, /*get_valid_cached_result=*/&kFalse,
-      /*over_phishing_report_limit=*/&kFalse, /*is_local=*/&kFalse);
-  NavigateAndCommit(example_url);
-  WaitAndCheckPreClassificationChecks();
-
+  NavigateAndCommit(example_url_);
   // Force request should not be triggered, because RTLookupResponse hasn't
   // been cached.
-  histogram_tester.ExpectBucketCount(
+  histogram_tester_.ExpectBucketCount(
       "SBClientPhishing.ClientSideDetectionTypeRequest",
       ClientSideDetectionType::FORCE_REQUEST, 0);
 
   SetRTResponseInCacheManager(/*is_enforced=*/true);
 
-  // get_valid_cached_result is set to nullptr, because the request type is not
-  // TRIGGER_MODELS. Force request triggers also bypass the CSD allowlist.
-  ExpectPreClassificationChecks(
-      example_url, &kFalse, /*match_csd_allowlist=*/nullptr,
-      /*get_valid_cached_result=*/nullptr, &kFalse, &kFalse);
+  // We will send a ping because the async check has completed and forced
+  // request is set.
+  base::RunLoop run_loop;
+  EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(_, _, _))
+      .Times(1)
+      .WillOnce(testing::Invoke(
+          [&](std::unique_ptr<ClientPhishingRequest>,
+              ClientSideDetectionService::ClientReportPhishingRequestCallback,
+              const std::string&) { run_loop.Quit(); }));
   // This call should trigger preclassification check again.
   CompleteAsyncCheck();
+  run_loop.Run();
 
-  // Set up mock call to token fetcher.
-  SafeBrowsingTokenFetcher::Callback token_cb;
-  EXPECT_CALL(*raw_token_fetcher_, Start(_))
-      .Times(1)
-      .WillRepeatedly(MoveArg<0>(&token_cb));
-  task_environment()->RunUntilIdle();
-
-  ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
-  // The verdict's is_phishing is false, but we will still send a ping!
-  ClientPhishingRequest verdict;
-  verdict.set_url(example_url.spec());
-  verdict.set_client_score(0.8f);
-  verdict.set_is_phishing(false);
-  EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(
-                                 _, _, "fake_access_token_for_force_request"))
-      .WillOnce(MoveArg<1>(&cb));
-
-  ASSERT_FALSE(token_cb.is_null());
-  std::move(token_cb).Run("fake_access_token_for_force_request");
-  task_environment()->RunUntilIdle();
-
-  // Token is now fetched, so we will now callback on
-  // ClientReportPhishingRequest.
   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
-
-  task_environment()->RunUntilIdle();
-
-  histogram_tester.ExpectBucketCount(
+  histogram_tester_.ExpectBucketCount(
       "SBClientPhishing.ClientSideDetectionTypeRequest",
       ClientSideDetectionType::FORCE_REQUEST, 1);
-  histogram_tester.ExpectUniqueSample(
+  histogram_tester_.ExpectUniqueSample(
       "SBClientPhishing.ClientSideDetection."
       "AsyncCheckTriggerForceRequestResult",
       ClientSideDetectionHost::AsyncCheckTriggerForceRequestResult::kTriggered,
@@ -2118,23 +2089,10 @@
     GTEST_SKIP();
   }
 
-  base::HistogramTester histogram_tester;
-
-  GURL example_url("http://suspiciousurl.com/");
-
-  database_manager_->SetAllowlistLookupDetailsForUrl(example_url, false);
-
-  // First navigate to a page, which should trigger preclassification check.
-  ExpectPreClassificationChecks(
-      /*url=*/example_url, /*is_private=*/&kFalse,
-      /*match_csd_allowlist=*/&kFalse, /*get_valid_cached_result=*/&kFalse,
-      /*over_phishing_report_limit=*/&kFalse, /*is_local=*/&kFalse);
-  NavigateAndCommit(example_url);
-  WaitAndCheckPreClassificationChecks();
-
+  NavigateAndCommit(example_url_);
   // Force request should not be triggered, because RTLookupResponse hasn't
   // been cached.
-  histogram_tester.ExpectBucketCount(
+  histogram_tester_.ExpectBucketCount(
       "SBClientPhishing.ClientSideDetectionTypeRequest",
       ClientSideDetectionType::FORCE_REQUEST, 0);
 
@@ -2144,54 +2102,33 @@
   // the suspicious URL. For the purpose of this test, we will set that there's
   // a match.
   csd_host_->set_high_confidence_allowlist_acceptance_rate_for_testing(1.0f);
-  database_manager_->SetAllowlistLookupDetailsForUrl(example_url, true);
+  database_manager_->SetAllowlistLookupDetailsForUrl(example_url_, true);
 
-  // get_valid_cached_result is set to nullptr, because the request type is not
-  // TRIGGER_MODELS. Force request bypasses CSD allowlist check.
-  ExpectPreClassificationChecks(
-      example_url, &kFalse, /*match_csd_allowlist=*/nullptr,
-      /*get_valid_cached_result=*/nullptr, &kFalse, &kFalse);
+  // We will send a ping because the async check has completed and forced
+  // request is set.
+  base::RunLoop run_loop;
+  EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(_, _, _))
+      .Times(1)
+      .WillOnce(testing::Invoke(
+          [&](std::unique_ptr<ClientPhishingRequest>,
+              ClientSideDetectionService::ClientReportPhishingRequestCallback,
+              const std::string&) { run_loop.Quit(); }));
   // This call should trigger preclassification check again.
   CompleteAsyncCheck();
+  run_loop.Run();
 
-  // Set up mock call to token fetcher.
-  SafeBrowsingTokenFetcher::Callback token_cb;
-  EXPECT_CALL(*raw_token_fetcher_, Start(_))
-      .Times(1)
-      .WillRepeatedly(MoveArg<0>(&token_cb));
-  task_environment()->RunUntilIdle();
-
-  ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
-  // The verdict's is_phishing is false, but we will still send a ping!
-  ClientPhishingRequest verdict;
-  verdict.set_url(example_url.spec());
-  verdict.set_client_score(0.8f);
-  verdict.set_is_phishing(false);
-  EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(
-                                 _, _, "fake_access_token_for_force_request"))
-      .WillOnce(MoveArg<1>(&cb));
-
-  ASSERT_FALSE(token_cb.is_null());
-  std::move(token_cb).Run("fake_access_token_for_force_request");
-  task_environment()->RunUntilIdle();
-
-  // Token is now fetched, so we will now callback on
-  // ClientReportPhishingRequest.
   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
-
-  task_environment()->RunUntilIdle();
-
-  histogram_tester.ExpectBucketCount(
+  histogram_tester_.ExpectBucketCount(
       "SBClientPhishing.ClientSideDetectionTypeRequest",
       ClientSideDetectionType::FORCE_REQUEST, 1);
-  histogram_tester.ExpectUniqueSample(
+  histogram_tester_.ExpectUniqueSample(
       "SBClientPhishing.ClientSideDetection."
       "AsyncCheckTriggerForceRequestResult",
       ClientSideDetectionHost::AsyncCheckTriggerForceRequestResult::kTriggered,
       1);
-  histogram_tester.ExpectTotalCount(
+  histogram_tester_.ExpectTotalCount(
       "SBClientPhishing.MatchHighConfidenceAllowlist.ForceRequest", 1);
-  histogram_tester.ExpectBucketCount(
+  histogram_tester_.ExpectBucketCount(
       "SBClientPhishing.PreClassificationCheckResult",
       PreClassificationCheckResult::NO_CLASSIFY_MATCH_HC_ALLOWLIST, 0);
 }
@@ -2202,29 +2139,17 @@
     GTEST_SKIP();
   }
 
-  base::HistogramTester histogram_tester;
-
-  GURL example_url("http://suspiciousurl.com/");
-
-  database_manager_->SetAllowlistLookupDetailsForUrl(example_url, false);
-
-  // First navigate to a page, which should trigger preclassification check.
-  ExpectPreClassificationChecks(
-      /*url=*/example_url, /*is_private=*/&kFalse,
-      /*match_csd_allowlist=*/&kFalse, /*get_valid_cached_result=*/&kFalse,
-      /*over_phishing_report_limit=*/&kFalse, /*is_local=*/&kFalse);
-  NavigateAndCommit(example_url);
-  WaitAndCheckPreClassificationChecks();
+  NavigateAndCommit(example_url_);
 
   SetRTResponseInCacheManager(/*is_enforced=*/false);
 
   CompleteAsyncCheck();
   task_environment()->RunUntilIdle();
 
-  histogram_tester.ExpectBucketCount(
+  histogram_tester_.ExpectBucketCount(
       "SBClientPhishing.ClientSideDetectionTypeRequest",
       ClientSideDetectionType::FORCE_REQUEST, 0);
-  histogram_tester.ExpectUniqueSample(
+  histogram_tester_.ExpectUniqueSample(
       "SBClientPhishing.ClientSideDetection."
       "AsyncCheckTriggerForceRequestResult",
       ClientSideDetectionHost::AsyncCheckTriggerForceRequestResult::
@@ -2238,80 +2163,47 @@
     GTEST_SKIP();
   }
 
-  base::HistogramTester histogram_tester;
+  NavigateAndCommit(example_url_);
 
-  GURL example_url("http://suspiciousurl.com/");
-  database_manager_->SetAllowlistLookupDetailsForUrl(example_url, false);
-  ExpectPreClassificationChecks(
-      /*url=*/example_url, /*is_private=*/&kFalse,
-      /*match_csd_allowlist=*/&kFalse, /*get_valid_cached_result=*/&kFalse,
-      /*over_phishing_report_limit=*/&kFalse, /*is_local=*/&kFalse);
-  NavigateAndCommit(example_url);
-  WaitAndCheckPreClassificationChecks();
-
-  SafeBrowsingTokenFetcher::Callback token_cb;
-  EXPECT_CALL(*raw_token_fetcher_, Start(_))
-      .Times(1)
-      .WillRepeatedly(MoveArg<0>(&token_cb));
-  ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
   ClientPhishingRequest verdict;
-  verdict.set_url(example_url.spec());
+  verdict.set_url(example_url_.spec());
   verdict.set_client_score(1.0f);
   verdict.set_is_phishing(true);
   PhishingDetectionDone(mojo_base::ProtoWrapper(verdict));
-  task_environment()->RunUntilIdle();
 
   // Check that the page is phishing and triggers a ping.
-  histogram_tester.ExpectBucketCount(
+  histogram_tester_.ExpectBucketCount(
       "SBClientPhishing.LocalModelDetectsPhishing", true, 1);
-  histogram_tester.ExpectBucketCount(
+  histogram_tester_.ExpectBucketCount(
       "SBClientPhishing.LocalModelDetectsPhishing.TriggerModel", true, 1);
 
   SetRTResponseInCacheManager(/*is_enforced=*/true);
 
-  // get_valid_cached_result is set to nullptr, because the request type is not
-  // TRIGGER_MODELS. Force request triggers also bypass the CSD allowlist.
-  ExpectPreClassificationChecks(
-      example_url, &kFalse, /*match_csd_allowlist=*/nullptr,
-      /*get_valid_cached_result=*/nullptr, &kFalse, &kFalse);
   // Calling this should trigger preclassification again, because local model
   // phishy verdict is not a condition to skip the async check force request
   // because the force request ping will contain information that the page load
   // request may have missed.
-  CompleteAsyncCheck();
-  // Set up mock call to token fetcher.
-  SafeBrowsingTokenFetcher::Callback token_cb2;
-  EXPECT_CALL(*raw_token_fetcher_, Start(_))
+  base::RunLoop run_loop;
+  EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(_, _, _))
       .Times(1)
-      .WillRepeatedly(MoveArg<0>(&token_cb2));
-  task_environment()->RunUntilIdle();
-
-  ClientSideDetectionService::ClientReportPhishingRequestCallback cb2;
-  // The verdict's is_phishing is false, but we will still send a ping!
-  ClientPhishingRequest verdict2;
-  verdict2.set_url(example_url.spec());
-  verdict2.set_client_score(0.8f);
-  verdict2.set_is_phishing(false);
-  EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(
-                                 _, _, "fake_access_token_for_force_request"))
-      .WillOnce(MoveArg<1>(&cb2));
-
-  ASSERT_FALSE(token_cb2.is_null());
-  std::move(token_cb2).Run("fake_access_token_for_force_request");
+      .WillOnce(testing::Invoke(
+          [&](std::unique_ptr<ClientPhishingRequest>,
+              ClientSideDetectionService::ClientReportPhishingRequestCallback,
+              const std::string&) { run_loop.Quit(); }));
+  CompleteAsyncCheck();
+  run_loop.Run();
 
   // Token is now fetched, so we will now callback on
   // ClientReportPhishingRequest.
   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
 
-  task_environment()->RunUntilIdle();
-
   // Enforce request should be triggered again, because the local model will
   // think it's phishy, but force request pings can contain other information,
   // such as the LLM information.
-  histogram_tester.ExpectBucketCount(
+  histogram_tester_.ExpectBucketCount(
       "SBClientPhishing.ClientSideDetectionTypeRequest",
       ClientSideDetectionType::FORCE_REQUEST, 1);
-  histogram_tester.ExpectUniqueSample(
+  histogram_tester_.ExpectUniqueSample(
       "SBClientPhishing.ClientSideDetection."
       "AsyncCheckTriggerForceRequestResult",
       ClientSideDetectionHost::AsyncCheckTriggerForceRequestResult::kTriggered,
@@ -2325,60 +2217,34 @@
     GTEST_SKIP();
   }
 
-  base::HistogramTester histogram_tester;
+  NavigateAndCommit(example_url_);
 
-  GURL example_url("http://suspiciousurl.com/");
-  database_manager_->SetAllowlistLookupDetailsForUrl(example_url, false);
-  ExpectPreClassificationChecks(
-      /*url=*/example_url, /*is_private=*/&kFalse,
-      /*match_csd_allowlist=*/&kFalse, /*get_valid_cached_result=*/&kFalse,
-      /*over_phishing_report_limit=*/&kFalse, /*is_local=*/&kFalse);
-  NavigateAndCommit(example_url);
-  WaitAndCheckPreClassificationChecks();
-
-  // Set up mock call to token fetcher.
-  SafeBrowsingTokenFetcher::Callback token_cb;
-  EXPECT_CALL(*raw_token_fetcher_, Start(_))
-      .Times(1)
-      .WillRepeatedly(MoveArg<0>(&token_cb));
-  ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
-  ClientPhishingRequest verdict;
-  verdict.set_url(example_url.spec());
-  verdict.set_client_score(0.5f);
-  verdict.set_is_phishing(false);
-
-  EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(
-                                 PartiallyEqualVerdict(verdict), _,
-                                 "fake_access_token_for_force_request"))
-      .WillOnce(MoveArg<1>(&cb));
   // Setup RTResponse in cache prior to calling PhishingDetectionDone.
   SetRTResponseInCacheManager(/*is_enforced=*/true);
 
+  ClientPhishingRequest verdict;
+  verdict.set_url(example_url_.spec());
+  verdict.set_client_score(0.5f);
+  verdict.set_is_phishing(false);
   PhishingDetectionDone(mojo_base::ProtoWrapper(verdict));
   task_environment()->RunUntilIdle();
 
-  // Wait for token fetcher to be called.
-  EXPECT_TRUE(Mock::VerifyAndClear(raw_token_fetcher_));
-
-  ASSERT_FALSE(token_cb.is_null());
-  std::move(token_cb).Run("fake_access_token_for_force_request");
-
   // Check that the page is phishing and triggers a ping.
-  histogram_tester.ExpectBucketCount(
+  histogram_tester_.ExpectBucketCount(
       "SBClientPhishing.ClientSideDetectionTypeRequest",
       ClientSideDetectionType::FORCE_REQUEST, 1);
-  histogram_tester.ExpectBucketCount("SBClientPhishing.RTLookupForceRequest",
-                                     true, 1);
+  histogram_tester_.ExpectBucketCount("SBClientPhishing.RTLookupForceRequest",
+                                      true, 1);
 
   CompleteAsyncCheck();
   task_environment()->RunUntilIdle();
 
   // Enforce request should NOT be triggered again, because the page load ping
   // has been converted to a force request ping.
-  histogram_tester.ExpectBucketCount(
+  histogram_tester_.ExpectBucketCount(
       "SBClientPhishing.ClientSideDetectionTypeRequest",
       ClientSideDetectionType::FORCE_REQUEST, 1);
-  histogram_tester.ExpectUniqueSample(
+  histogram_tester_.ExpectUniqueSample(
       "SBClientPhishing.ClientSideDetection."
       "AsyncCheckTriggerForceRequestResult",
       ClientSideDetectionHost::AsyncCheckTriggerForceRequestResult::
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
index 17a2eed..ab92908 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
@@ -122,8 +122,7 @@
       : profile_manager_(TestingBrowserProcess::GetGlobal()) {
     EXPECT_TRUE(profile_manager_.SetUp());
     profile_ = profile_manager_.CreateTestingProfile("test-user");
-    std::vector<base::test::FeatureRefAndParams> enabled_features = {
-        {kSafeBrowsingRemoveCookiesInAuthRequests, {}}};
+    std::vector<base::test::FeatureRefAndParams> enabled_features = {};
     if (ShouldEnableESBDailyPhishingLimit()) {
       base::FieldTrialParams params;
       params["kMaxReportsPerIntervalESB"] = "10";
@@ -482,9 +481,9 @@
         EXPECT_THAT(
             request.headers.GetHeader(net::HttpRequestHeaders::kAuthorization),
             testing::Optional("Bearer " + access_token));
-        // Cookies should be removed when token is set.
+        // Cookies should still be included when token is set.
         EXPECT_EQ(request.credentials_mode,
-                  network::mojom::CredentialsMode::kOmit);
+                  network::mojom::CredentialsMode::kInclude);
       }));
   SetClientReportPhishingResponse(response.SerializeAsString(), net::OK);
   EXPECT_TRUE(SendClientReportPhishingRequest(url, score, access_token));
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
index d03c7f49..05ac725 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
@@ -282,6 +282,12 @@
       ->set_content_area_account_email(email);
 }
 
+void BinaryUploadService::Request::set_source_content_area_account_email(
+    const std::string& email) {
+  content_analysis_request_.mutable_request_data()
+      ->set_source_content_area_account_email(email);
+}
+
 void BinaryUploadService::Request::set_frame_url_chain(
     const google::protobuf::RepeatedPtrField<std::string> frame_url_chain) {
   *content_analysis_request_.mutable_request_data()->mutable_frame_url_chain() =
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
index 18aa5d37..080bcdd 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
@@ -198,6 +198,7 @@
     void set_referrer_chain(const google::protobuf::RepeatedPtrField<
                             safe_browsing::ReferrerChainEntry> referrer_chain);
     void set_content_area_account_email(const std::string& email);
+    void set_source_content_area_account_email(const std::string& email);
     void set_frame_url_chain(
         const google::protobuf::RepeatedPtrField<std::string> frame_url_chain);
 
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader.cc b/chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader.cc
index c525007..d0c5dbb1 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader.cc
@@ -177,7 +177,7 @@
   if (!access_token_.empty()) {
     LogAuthenticatedCookieResets(
         *request, SafeBrowsingAuthenticatedEndpoint::kDeepScanning);
-    SetAccessTokenAndClearCookieInResourceRequest(request, access_token_);
+    SetAccessToken(request, access_token_);
   }
   request->credentials_mode = network::mojom::CredentialsMode::kOmit;
 }
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/resumable_uploader.cc b/chrome/browser/safe_browsing/cloud_content_scanning/resumable_uploader.cc
index 314d4b3..d988dea 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/resumable_uploader.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/resumable_uploader.cc
@@ -140,7 +140,7 @@
   if (!access_token_.empty()) {
     LogAuthenticatedCookieResets(
         *request, SafeBrowsingAuthenticatedEndpoint::kDeepScanning);
-    SetAccessTokenAndClearCookieInResourceRequest(request, access_token_);
+    SetAccessToken(request, access_token_);
   }
   request->credentials_mode = network::mojom::CredentialsMode::kOmit;
 }
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
index 72d5629..9a5cae6 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
@@ -475,8 +475,7 @@
     LogAuthenticatedCookieResets(
         *resource_request,
         SafeBrowsingAuthenticatedEndpoint::kDownloadProtection);
-    SetAccessTokenAndClearCookieInResourceRequest(resource_request.get(),
-                                                  access_token_);
+    SetAccessToken(resource_request.get(), access_token_);
   }
 #endif
 
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
index 601b0dd..0ef0e916 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
@@ -919,12 +919,9 @@
 class EnhancedProtectionDownloadTest : public DownloadProtectionServiceTest {
  public:
   EnhancedProtectionDownloadTest() {
-    EnableFeatures({
-        kSafeBrowsingRemoveCookiesInAuthRequests,
 #if BUILDFLAG(IS_ANDROID)
-        kMaliciousApkDownloadCheck,
+    EnableFeatures({kMaliciousApkDownloadCheck});
 #endif
-    });
   }
 };
 
@@ -4425,9 +4422,9 @@
               EXPECT_EQ(*request.headers.GetHeader(
                             net::HttpRequestHeaders::kAuthorization),
                         "Bearer access_token");
-              // Cookies should be removed when token is set.
+              // Cookies should be included even when token is set.
               EXPECT_EQ(request.credentials_mode,
-                        network::mojom::CredentialsMode::kOmit);
+                        network::mojom::CredentialsMode::kInclude);
             }));
 
     RunLoop run_loop;
@@ -4586,9 +4583,9 @@
               EXPECT_EQ(*request.headers.GetHeader(
                             net::HttpRequestHeaders::kAuthorization),
                         "Bearer access_token");
-              // Cookies should be removed when token is set.
+              // Cookies should be included even when token is set.
               EXPECT_EQ(request.credentials_mode,
-                        network::mojom::CredentialsMode::kOmit);
+                        network::mojom::CredentialsMode::kInclude);
             }));
 
     RunLoop run_loop;
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.cc b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.cc
index 488f27bd..4d4800c 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.cc
@@ -149,8 +149,7 @@
     LogAuthenticatedCookieResets(
         *resource_request,
         SafeBrowsingAuthenticatedEndpoint::kExtensionTelemetry);
-    SetAccessTokenAndClearCookieInResourceRequest(resource_request.get(),
-                                                  access_token);
+    SetAccessToken(resource_request.get(), access_token);
   } else {
     resource_request->credentials_mode =
         network::mojom::CredentialsMode::kInclude;
diff --git a/chrome/browser/safe_browsing/url_lookup_service_browsertest.cc b/chrome/browser/safe_browsing/url_lookup_service_browsertest.cc
index 6df6cfe7..6ba7497 100644
--- a/chrome/browser/safe_browsing/url_lookup_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/url_lookup_service_browsertest.cc
@@ -26,10 +26,7 @@
 
 class SafeBrowsingUrlLookupServiceTest : public InProcessBrowserTest {
  public:
-  SafeBrowsingUrlLookupServiceTest() {
-    scoped_feature_list_.InitAndDisableFeature(
-        kSafeBrowsingRemoveCookiesInAuthRequests);
-  }
+  SafeBrowsingUrlLookupServiceTest() = default;
   SafeBrowsingUrlLookupServiceTest(const SafeBrowsingUrlLookupServiceTest&) =
       delete;
   SafeBrowsingUrlLookupServiceTest& operator=(
@@ -92,7 +89,6 @@
         SetIdentityTestEnvironmentFactoriesOnBrowserContext(context);
   }
 
-  base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<net::EmbeddedTestServer> secure_embedded_test_server_;
   base::CallbackListSubscription create_services_subscription_;
   std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
diff --git a/chrome/browser/search_engine_choice/search_engine_choice_dialog_service.cc b/chrome/browser/search_engine_choice/search_engine_choice_dialog_service.cc
index 8ea59ac..ba87c4a9 100644
--- a/chrome/browser/search_engine_choice/search_engine_choice_dialog_service.cc
+++ b/chrome/browser/search_engine_choice/search_engine_choice_dialog_service.cc
@@ -82,7 +82,7 @@
     // are a cause of multi-prompts.
     SCOPED_CRASH_KEY_BOOL("ChoiceService", "browser_has_open_dialog",
                           HasOpenDialog(browser));
-    NOTREACHED(base::NotFatalUntil::M138);
+    NOTREACHED(base::NotFatalUntil::M141);
     return false;
   }
 
@@ -220,7 +220,7 @@
     // browser.
     // TODO(crbug.com/400119363): Investigate whether we can more formally
     // handle this.
-    NOTREACHED(base::NotFatalUntil::M138);
+    NOTREACHED(base::NotFatalUntil::M141);
   } else {
     if (search_engine_choice_service_->IsDsePropagationAllowedForGuest()) {
       base::UmaHistogramBoolean("Search.SaveGuestModeSelection",
diff --git a/chrome/browser/ssl/https_upgrades_browsertest.cc b/chrome/browser/ssl/https_upgrades_browsertest.cc
index 563e06e..14f7c34 100644
--- a/chrome/browser/ssl/https_upgrades_browsertest.cc
+++ b/chrome/browser/ssl/https_upgrades_browsertest.cc
@@ -325,8 +325,8 @@
 
     http_server_.AddDefaultHandlers(GetChromeTestDataDir());
     https_server_.AddDefaultHandlers(GetChromeTestDataDir());
-    ASSERT_TRUE(http_server_.Start());
-    ASSERT_TRUE(https_server_.Start());
+    http_server_.StartAcceptingConnections();
+    https_server_.StartAcceptingConnections();
 
     HttpsUpgradesInterceptor::SetHttpsPortForTesting(https_server()->port());
     HttpsUpgradesInterceptor::SetHttpPortForTesting(http_server()->port());
@@ -367,6 +367,19 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     mock_cert_verifier_.SetUpCommandLine(command_line);
+
+    ASSERT_TRUE(http_server_.InitializeAndListen());
+    ASSERT_TRUE(https_server_.InitializeAndListen());
+
+    // Treat the test server as public to bypass Local Network Access checks.
+    //
+    // TODO(crbug.com/422956041): figure out why we need to do this, there may
+    // be a bug where IP address spaces are not being set correctly.
+    command_line->AppendSwitchASCII(
+        network::switches::kIpAddressSpaceOverrides,
+        base::StringPrintf("%s=public,%s=public",
+                           http_server_.host_port_pair().ToString().c_str(),
+                           https_server_.host_port_pair().ToString().c_str()));
   }
 
   void SetUpInProcessBrowserTestFixture() override {
diff --git a/chrome/browser/tab_ui/android/java/res/values/colors.xml b/chrome/browser/tab_ui/android/java/res/values/colors.xml
index 1508f18f..9089c99 100644
--- a/chrome/browser/tab_ui/android/java/res/values/colors.xml
+++ b/chrome/browser/tab_ui/android/java/res/values/colors.xml
@@ -13,4 +13,6 @@
   <color name="incognito_tab_thumbnail_placeholder_selected_color">@color/baseline_primary_20_alpha_38</color>
 
   <color name="incognito_tab_bg_selected_color">@color/baseline_primary_80</color>
+
+  <color name="incognito_tab_tile_number_color">@color/baseline_neutral_90</color>
 </resources>
\ No newline at end of file
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabThumbnailView.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabThumbnailView.java
index f57b773..18d16c7d 100644
--- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabThumbnailView.java
+++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabThumbnailView.java
@@ -233,7 +233,7 @@
         // Step 1: Background color.
         mBackgroundDrawable.setColor(
                 TabUiThemeUtils.getMiniThumbnailPlaceholderColor(
-                        getContext(), isIncognito, isSelected));
+                        getContext(), isIncognito, isSelected, colorId));
         final int oldColor = mPaint.getColor();
         final int newColor =
                 TabUiThemeUtils.getCardViewBackgroundColor(
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabUiThemeUtils.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabUiThemeUtils.java
index 34d00f0..e4d0796 100644
--- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabUiThemeUtils.java
+++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabUiThemeUtils.java
@@ -7,7 +7,6 @@
 import android.content.Context;
 
 import androidx.annotation.ColorInt;
-import androidx.annotation.ColorRes;
 import androidx.core.content.ContextCompat;
 
 import com.google.android.material.color.MaterialColors;
@@ -44,9 +43,10 @@
     /**
      * Returns the title text appearance for the tab grid card based on the incognito mode.
      *
+     * @param context {@link Context} used to retrieve color.
      * @param isIncognito Whether the text appearance is used for incognito mode.
      * @param isSelected Whether the tab is currently selected.
-     * @param colorId colorId Color chosen by user for the TabGroup, Null if not a tab group.
+     * @param colorId Color chosen by user for the TabGroup, null if not a tab group.
      * @return The text appearance for the tab grid card title.
      */
     public static @ColorInt int getTitleTextColor(
@@ -70,28 +70,26 @@
      * @param context {@link Context} used to retrieve color.
      * @param isIncognito Whether the color is used for incognito mode.
      * @param isSelected Whether the tab is currently selected.
+     * @param colorId Color chosen by user for the TabGroup, null if not a tab group.
      * @return The mini-thumbnail placeholder color.
      */
     public static @ColorInt int getMiniThumbnailPlaceholderColor(
-            Context context, boolean isIncognito, boolean isSelected) {
-        if (isIncognito) {
-            @ColorRes
-            int colorRes =
-                    isSelected
-                            ? R.color.incognito_tab_thumbnail_placeholder_selected_color
-                            : R.color.incognito_tab_thumbnail_placeholder_color;
-            return context.getColor(colorRes);
-        }
-
+            Context context,
+            boolean isIncognito,
+            boolean isSelected,
+            @Nullable @TabGroupColorId Integer colorId) {
         if (isSelected) {
+            if (isIncognito) {
+                return context.getColor(R.color.incognito_tab_thumbnail_placeholder_selected_color);
+            }
             int alpha =
                     context.getResources()
                             .getInteger(R.integer.tab_thumbnail_placeholder_selected_color_alpha);
             @ColorInt int baseColor = SemanticColorUtils.getColorOnPrimary(context);
             return MaterialColors.compositeARGBWithAlpha(baseColor, alpha);
         }
-
-        return SemanticColorUtils.getColorSurfaceContainerLow(context);
+        return SurfaceColorUpdateUtils.getCardViewMiniThumbnailPlaceholderColor(
+                context, isIncognito, colorId);
     }
 
     /**
@@ -100,7 +98,7 @@
      * @param context {@link Context} used to retrieve color.
      * @param isIncognito Whether the color is used for incognito mode.
      * @param isSelected Whether the tab is currently selected.
-     * @param colorId colorId Color chosen by user for the TabGroup, Null if not a tab group.
+     * @param colorId Color chosen by user for the TabGroup, null if not a tab group.
      * @return The {@link ColorInt} for tab grid card view background.
      */
     public static @ColorInt int getCardViewBackgroundColor(
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java
index 7e9fadf..66c9d50 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java
@@ -27,14 +27,6 @@
     int determineInsertionIndex(@TabLaunchType int type, int position, Tab newTab);
 
     /**
-     * Determine the insertion index of the next tab.
-     *
-     * @param type The launch type of the new tab.
-     * @return Where to insert the tab.
-     */
-    int determineInsertionIndex(@TabLaunchType int type, Tab newTab);
-
-    /**
      * Determine if a launch type will result in the tab being opened in the foreground.
      *
      * @param type The type of opening event.
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java
index a969364..67121d6 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java
@@ -34,7 +34,7 @@
             return -1;
         }
         if (mightBeAdjacent(type)) {
-            position = determineInsertionIndex(type, newTab);
+            position = determineInsertionIndexIfMaybeAdjacent(type, newTab);
         }
 
         if (willOpenInForeground(type, newTab.isIncognitoBranded())) {
@@ -54,8 +54,7 @@
         return position;
     }
 
-    @Override
-    public int determineInsertionIndex(@TabLaunchType int type, Tab newTab) {
+    private int determineInsertionIndexIfMaybeAdjacent(@TabLaunchType int type, Tab newTab) {
         TabModel currentModel = mTabModelSelector.getCurrentModel();
 
         if (sameModelType(currentModel, newTab)) {
diff --git a/chrome/browser/tabwindow/android/java/src/org/chromium/chrome/browser/tabwindow/TabWindowManager.java b/chrome/browser/tabwindow/android/java/src/org/chromium/chrome/browser/tabwindow/TabWindowManager.java
index 09a1dc5f..95d9f169 100644
--- a/chrome/browser/tabwindow/android/java/src/org/chromium/chrome/browser/tabwindow/TabWindowManager.java
+++ b/chrome/browser/tabwindow/android/java/src/org/chromium/chrome/browser/tabwindow/TabWindowManager.java
@@ -72,11 +72,6 @@
     void removeObserver(Observer observer);
 
     /**
-     * Returns the maximum number of simultaneous TabModelSelector instances in this Application.
-     */
-    int getMaxSimultaneousSelectors();
-
-    /**
      * Called to request a {@link TabModelSelector} based on {@code index}. Note that the {@link
      * TabModelSelector} returned might not actually be the one related to {@code index} and {@link
      * #getIdForWindow(Activity)} should be called to grab the actual index if required.
diff --git a/chrome/browser/tabwindow/internal/android/java/src/org/chromium/chrome/browser/tabwindow/TabWindowManagerImpl.java b/chrome/browser/tabwindow/internal/android/java/src/org/chromium/chrome/browser/tabwindow/TabWindowManagerImpl.java
index 565d261..8ca138d 100644
--- a/chrome/browser/tabwindow/internal/android/java/src/org/chromium/chrome/browser/tabwindow/TabWindowManagerImpl.java
+++ b/chrome/browser/tabwindow/internal/android/java/src/org/chromium/chrome/browser/tabwindow/TabWindowManagerImpl.java
@@ -136,11 +136,6 @@
     }
 
     @Override
-    public int getMaxSimultaneousSelectors() {
-        return mMaxSelectors;
-    }
-
-    @Override
     public @Nullable Pair<@WindowId Integer, TabModelSelector> requestSelector(
             Activity activity,
             ModalDialogManager modalDialogManager,
@@ -149,12 +144,12 @@
             NextTabPolicySupplier nextTabPolicySupplier,
             MismatchedIndicesHandler mismatchedIndicesHandler,
             @WindowId int windowId) {
-        if (windowId < 0 || windowId >= mMaxSelectors) return null;
+        if (windowId == INVALID_WINDOW_ID) return null;
 
         // Return the already existing selector if found.
         if (mActivityAssignments.get(activity) != null) {
             TabModelSelector assignedSelector = mActivityAssignments.get(activity);
-            for (int i = 0; i < mMaxSelectors; i++) {
+            for (Integer i : mSelectorsToWindowId.values()) {
                 if (mWindowIdToSelectors.get(i) == assignedSelector) {
                     @WindowId
                     int existingWindowId =
@@ -228,7 +223,7 @@
     @Override
     public @Nullable TabModelSelector requestSelectorWithoutActivity(
             @WindowId int windowId, Profile profile) {
-        if (windowId < 0 || windowId >= mMaxSelectors) return null;
+        if (windowId == INVALID_WINDOW_ID) return null;
 
         if (mWindowIdToSelectors.containsKey(windowId)) {
             return mWindowIdToSelectors.get(windowId);
diff --git a/chrome/browser/tabwindow/internal/android/java/src/org/chromium/chrome/browser/tabwindow/TabWindowManagerImplUnitTest.java b/chrome/browser/tabwindow/internal/android/java/src/org/chromium/chrome/browser/tabwindow/TabWindowManagerImplUnitTest.java
index 683de98..dbdf7a3 100644
--- a/chrome/browser/tabwindow/internal/android/java/src/org/chromium/chrome/browser/tabwindow/TabWindowManagerImplUnitTest.java
+++ b/chrome/browser/tabwindow/internal/android/java/src/org/chromium/chrome/browser/tabwindow/TabWindowManagerImplUnitTest.java
@@ -115,6 +115,7 @@
     private OneshotSupplierImpl<ProfileProvider> mProfileProviderSupplier;
     private AsyncTabParamsManager mAsyncTabParamsManager;
     private TabWindowManager mSubject;
+    private int mMaxSelectors;
 
     @Before
     public void setUp() {
@@ -172,12 +173,12 @@
                 () -> {
                     mAsyncTabParamsManager =
                             AsyncTabParamsManagerFactory.createAsyncTabParamsManager();
-                    int maxInstances =
+                    mMaxSelectors =
                             (Build.VERSION.SDK_INT >= VERSION_CODES.S
                                     ? TabWindowManager.MAX_SELECTORS_S
                                     : TabWindowManager.MAX_SELECTORS_LEGACY);
                     return TabWindowManagerFactory.createInstance(
-                            tabModelSelectorFactory, mAsyncTabParamsManager, maxInstances);
+                            tabModelSelectorFactory, mAsyncTabParamsManager, mMaxSelectors);
                 });
     }
 
@@ -209,7 +210,7 @@
     @Test
     @Feature({"Multiwindow"})
     public void testMultipleActivities() {
-        assertTrue("Not enough selectors", mSubject.getMaxSimultaneousSelectors() >= 2);
+        assertTrue("Not enough selectors", mMaxSelectors >= 2);
 
         ActivityController<Activity> activityController0 = createActivity();
         Activity activity0 = activityController0.get();
@@ -253,7 +254,7 @@
     @Feature({"Multiwindow"})
     public void testTooManyActivities() {
         List<ActivityController<Activity>> activityControllerList = new ArrayList<>();
-        for (int i = 0; i < mSubject.getMaxSimultaneousSelectors(); i++) {
+        for (int i = 0; i < mMaxSelectors; i++) {
             ActivityController<Activity> c = createActivity();
             activityControllerList.add(c);
             assertNotNull(
@@ -293,7 +294,7 @@
     @Test
     @Feature({"Multiwindow"})
     public void testWindowIdFallback() {
-        assertTrue("Not enough selectors", mSubject.getMaxSimultaneousSelectors() >= 2);
+        assertTrue("Not enough selectors", mMaxSelectors >= 2);
 
         ActivityController<Activity> activityController0 = createActivity();
         Activity activity0 = activityController0.get();
@@ -337,7 +338,7 @@
     @Test
     @Feature({"Multiwindow"})
     public void testWindowIdFallback2() {
-        assertTrue("Not enough selectors", mSubject.getMaxSimultaneousSelectors() >= 3);
+        assertTrue("Not enough selectors", mMaxSelectors >= 3);
 
         ActivityController<Activity> activityController0 = createActivity();
         Activity activity0 = activityController0.get();
@@ -456,7 +457,7 @@
     @Test
     @Feature({"Multiwindow"})
     public void testActivityDeathWithMultipleActivities() {
-        assertTrue("Not enough selectors", mSubject.getMaxSimultaneousSelectors() >= 2);
+        assertTrue("Not enough selectors", mMaxSelectors >= 2);
 
         ActivityController<Activity> activityController0 = createActivity();
         Activity activity0 = activityController0.get();
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 17b6fdbc..2be1a0c 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -5281,7 +5281,10 @@
       "webui/plural_string_handler.cc",
       "webui/plural_string_handler.h",
     ]
-    deps += [ "//chrome/browser/extensions" ]
+    deps += [
+      "//chrome/browser/extensions",
+      "//chrome/browser/ui/extensions",
+    ]
     allow_circular_includes_from += [ "//chrome/browser/extensions" ]
 
     if (is_win || is_mac || is_linux || is_chromeos) {
diff --git a/chrome/browser/ui/android/autofill/manual_filling_view_android.cc b/chrome/browser/ui/android/autofill/manual_filling_view_android.cc
index 8f40721..decf85e 100644
--- a/chrome/browser/ui/android/autofill/manual_filling_view_android.cc
+++ b/chrome/browser/ui/android/autofill/manual_filling_view_android.cc
@@ -8,6 +8,7 @@
 
 #include <map>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -354,7 +355,7 @@
       ->SaveCredentialsAndBlocklistedForOrigin(
           credentials,
           password_manager::CredentialCache::IsOriginBlocklisted(j_blocklisted),
-          origin);
+          std::nullopt, origin);
 }
 
 // static
diff --git a/chrome/browser/ui/android/edge_to_edge/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeUtils.java b/chrome/browser/ui/android/edge_to_edge/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeUtils.java
index 623d722..2b3064b 100644
--- a/chrome/browser/ui/android/edge_to_edge/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeUtils.java
+++ b/chrome/browser/ui/android/edge_to_edge/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeUtils.java
@@ -499,6 +499,9 @@
 
             String windowMetricsInsetsState = "";
             String windowMetricsInsetsStateTappable = "";
+            String windowMetricsInsetsStateMandatoryGestures = "";
+            String windowMetricsInsetsStateSystemGestures = "";
+            String windowMetricsInsetsStateSystemOverlays = "";
             if (Build.VERSION.SDK_INT >= VERSION_CODES.R) {
                 if (window != null
                         && window.getWindowManager() != null
@@ -521,6 +524,36 @@
                                             .toString();
                     windowMetricsInsetsStateTappable =
                             " \nwindowMetricsInsetsTappable: " + insetsStringTappable;
+
+                    var insetsStringMandatoryGestures =
+                            windowInsets == null
+                                    ? "null"
+                                    : windowInsets
+                                            .getInsets(
+                                                    WindowInsetsCompat.Type
+                                                            .mandatorySystemGestures())
+                                            .toString();
+                    windowMetricsInsetsStateMandatoryGestures =
+                            " \nwindowMetricsInsetsMandatoryGestures: "
+                                    + insetsStringMandatoryGestures;
+
+                    var insetsStringSystemGestures =
+                            windowInsets == null
+                                    ? "null"
+                                    : windowInsets
+                                            .getInsets(WindowInsetsCompat.Type.systemGestures())
+                                            .toString();
+                    windowMetricsInsetsStateSystemGestures =
+                            " \nwindowMetricsInsetsSystemGestures: " + insetsStringSystemGestures;
+
+                    var insetsStringSystemOverlays =
+                            windowInsets == null
+                                    ? "null"
+                                    : windowInsets
+                                            .getInsets(WindowInsetsCompat.Type.systemOverlays())
+                                            .toString();
+                    windowMetricsInsetsStateSystemOverlays =
+                            " \nwindowMetricsInsetsSystemOverlays: " + insetsStringSystemOverlays;
                 }
             }
 
@@ -535,7 +568,10 @@
                             + rawWindowInsetsIgnoringVisibilityState
                             + rawWindowInsetsTappableState
                             + windowMetricsInsetsState
-                            + windowMetricsInsetsStateTappable);
+                            + windowMetricsInsetsStateTappable
+                            + windowMetricsInsetsStateMandatoryGestures
+                            + windowMetricsInsetsStateSystemGestures
+                            + windowMetricsInsetsStateSystemOverlays);
         }
 
         /** Returns whether the the instance has uploaded any report. */
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtils.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtils.java
index 0039875..b2f8b965 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtils.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtils.java
@@ -239,7 +239,8 @@
         mSearchEngineIconObservers.removeObserver(observer);
     }
 
-    private void setSearchEngineIcon(@Nullable StatusIconResource newIcon) {
+    @VisibleForTesting
+    public void setSearchEngineIcon(@Nullable StatusIconResource newIcon) {
         if (Objects.equals(mFavicon, newIcon)) return;
         mFavicon = newIcon;
         for (var observer : mSearchEngineIconObservers) {
diff --git a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java
index d051ea94..0654f38f 100644
--- a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java
+++ b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java
@@ -160,7 +160,7 @@
      *
      * @param context {@link Context} used to retrieve colors.
      * @param isIncognito Whether the color is used for incognito mode.
-     * @param colorId User chosen color ID.
+     * @param colorId Color chosen by user for the TabGroup, null if not a tab group.
      * @return The background color.
      */
     public static @ColorInt int getCardViewBackgroundColor(
@@ -187,12 +187,13 @@
     }
 
     /**
-     * Returns the text color for the card view in grid tab switcher on the enabled flag
+     * Returns the text color for the card view in grid tab switcher on the enabled flag and
+     * incognito.
      *
      * @param context {@link Context} used to retrieve colors.
      * @param isIncognito Whether the color is used for incognito mode.
-     * @param colorId USer chosen color ID.
-     * @return The text color.
+     * @param colorId Color chosen by user for the TabGroup, null if not a tab group.
+     * @return The text appearance for the tab grid card title.
      */
     public static @ColorInt int getCardViewTextColor(
             Context context, boolean isIncognito, @Nullable @TabGroupColorId Integer colorId) {
@@ -205,6 +206,46 @@
     }
 
     /**
+     * Returns the placeholder color for the card view in grid tab switcher on the enabled flag and
+     * incognito.
+     *
+     * @param context {@link Context} used to retrieve colors.
+     * @param isIncognito Whether the color is used for incognito mode.
+     * @param colorId Color chosen by user for the TabGroup, null if not a tab group.
+     * @return The mini-thumbnail placeholder color.
+     */
+    public static @ColorInt int getCardViewMiniThumbnailPlaceholderColor(
+            Context context, boolean isIncognito, @Nullable @TabGroupColorId Integer colorId) {
+        if (useNewGm3GtsTabGroupColors() && colorId != null) {
+            return TabGroupColorPickerUtils.getTabGroupCardMiniThumbnailPlaceholderColor(
+                    context, colorId, isIncognito);
+        }
+        if (isIncognito) {
+            return context.getColor(R.color.incognito_tab_thumbnail_placeholder_color);
+        }
+        return SemanticColorUtils.getColorSurfaceContainerLow(context);
+    }
+
+    /**
+     * Returns the text color used for the card view in grid tab switcher on the enabled flag and
+     * incognito.
+     *
+     * @param context {@link Context} used to retrieve colors.
+     * @param isIncognito Whether the color is used for incognito mode.
+     * @param colorId Color chosen by user for the TabGroup, null if not a tab group.
+     * @return The text color for the number used on the tab group cards.
+     */
+    public static @ColorInt int getCardViewGroupNumberTextColor(
+            Context context, boolean isIncognito, @Nullable @TabGroupColorId Integer colorId) {
+        if (useNewGm3GtsTabGroupColors() && colorId != null) {
+            return TabGroupColorPickerUtils.getTabGroupCardTextColor(context, colorId, isIncognito);
+        }
+        return isIncognito
+                ? context.getColor(R.color.incognito_tab_tile_number_color)
+                : SemanticColorUtils.getDefaultTextColor(context);
+    }
+
+    /**
      * Returns the background color for the grid tab switcher message card based on the enabled flag
      * and incognito.
      *
diff --git a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java
index 2c0b5d6..092b0d23 100644
--- a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java
+++ b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java
@@ -403,7 +403,7 @@
     @Test
     @DisableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
     public void testGetCardViewTextColor_NewGm3FlagDisabled_withColorId() {
-        // If GM3 flag is disabled, colorId should be ignored, and it should use fallback logic.
+
         @TabGroupColorId int testColorId = TabGroupColorId.RED;
 
         // Test non-incognito.
@@ -433,7 +433,6 @@
     @Test
     @DisableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
     public void testGetCardViewTextColor_NewGm3FlagDisabled_colorIdNull() {
-        // If GM3 flag is disabled and colorId is null, it should use fallback logic.
 
         // Test non-incognito.
         @ColorInt int expectedNonIncognito = SemanticColorUtils.getDefaultTextColor(mContext);
@@ -458,4 +457,163 @@
                 expectedIncognito,
                 actualIncognito);
     }
+
+    @Test
+    @EnableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
+    public void testGetCardViewGroupNumberTextColor_gm3FlagEnabled_withColorId() {
+        @TabGroupColorId int testColorId = TabGroupColorId.CYAN;
+
+        // Test non-incognito.
+        @ColorInt
+        int expectedNonIncognito =
+                TabGroupColorPickerUtils.getTabGroupCardTextColor(
+                        mContext, testColorId, /* isIncognito= */ false);
+        @ColorInt
+        int actualNonIncognito =
+                SurfaceColorUpdateUtils.getCardViewGroupNumberTextColor(
+                        mContext, /* isIncognito= */ false, testColorId);
+        assertEquals(
+                "Group number text color mismatch for non-incognito with GM3 flag and colorId.",
+                expectedNonIncognito,
+                actualNonIncognito);
+
+        // Test incognito.
+        @ColorInt
+        int expectedIncognito =
+                TabGroupColorPickerUtils.getTabGroupCardTextColor(
+                        mContext, testColorId, /* isIncognito= */ true);
+        @ColorInt
+        int actualIncognito =
+                SurfaceColorUpdateUtils.getCardViewGroupNumberTextColor(
+                        mContext, /* isIncognito= */ true, testColorId);
+        assertEquals(
+                "Group number text color mismatch for incognito with GM3 flag and colorId.",
+                expectedIncognito,
+                actualIncognito);
+    }
+
+    @Test
+    @DisableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
+    public void testGetCardViewGroupNumberTextColor_gm3FlagDisabled() {
+
+        @TabGroupColorId int testColorId = TabGroupColorId.PINK;
+
+        // Test non-incognito (fallback path).
+        @ColorInt int expectedNonIncognito = SemanticColorUtils.getDefaultTextColor(mContext);
+        @ColorInt
+        int actualNonIncognito =
+                SurfaceColorUpdateUtils.getCardViewGroupNumberTextColor(
+                        mContext, /* isIncognito= */ false, testColorId);
+        assertEquals(
+                "Group number text color mismatch for non-incognito with GM3 flag disabled (colorId"
+                        + " ignored).",
+                expectedNonIncognito,
+                actualNonIncognito);
+
+        // Test incognito (fallback path).
+        @ColorInt
+        int expectedIncognito =
+                ContextCompat.getColor(mContext, R.color.incognito_tab_tile_number_color);
+        @ColorInt
+        int actualIncognito =
+                SurfaceColorUpdateUtils.getCardViewGroupNumberTextColor(
+                        mContext, /* isIncognito= */ true, testColorId);
+        assertEquals(
+                "Group number text color mismatch for incognito with GM3 flag disabled (colorId"
+                        + " ignored).",
+                expectedIncognito,
+                actualIncognito);
+
+        // Test with a null colorId to ensure it behaves the same as with a non-null colorId when
+        // the flag is off.
+        @ColorInt
+        int actualNonIncognitoNullId =
+                SurfaceColorUpdateUtils.getCardViewGroupNumberTextColor(
+                        mContext, /* isIncognito= */ false, /* colorId= */ null);
+        assertEquals(
+                "Group number text color should be the same for null and non-null colorId when GM3"
+                        + " flag is disabled.",
+                expectedNonIncognito,
+                actualNonIncognitoNullId);
+    }
+
+    @Test
+    @EnableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
+    public void testGetCardViewMiniThumbnailPlaceholderColor_gm3FlagEnabled_withColorId() {
+        @TabGroupColorId int testColorId = TabGroupColorId.PURPLE;
+
+        // Test non-incognito.
+        @ColorInt
+        int expectedNonIncognito =
+                TabGroupColorPickerUtils.getTabGroupCardMiniThumbnailPlaceholderColor(
+                        mContext, testColorId, /* isIncognito= */ false);
+        @ColorInt
+        int actualNonIncognito =
+                SurfaceColorUpdateUtils.getCardViewMiniThumbnailPlaceholderColor(
+                        mContext, /* isIncognito= */ false, testColorId);
+        assertEquals(
+                "Placeholder color mismatch for non-incognito with GM3 flag and colorId.",
+                expectedNonIncognito,
+                actualNonIncognito);
+
+        // Test incognito.
+        @ColorInt
+        int expectedIncognito =
+                TabGroupColorPickerUtils.getTabGroupCardMiniThumbnailPlaceholderColor(
+                        mContext, testColorId, /* isIncognito= */ true);
+        @ColorInt
+        int actualIncognito =
+                SurfaceColorUpdateUtils.getCardViewMiniThumbnailPlaceholderColor(
+                        mContext, /* isIncognito= */ true, testColorId);
+        assertEquals(
+                "Placeholder color mismatch for incognito with GM3 flag and colorId.",
+                expectedIncognito,
+                actualIncognito);
+    }
+
+    @Test
+    @DisableFeatures({ChromeFeatureList.ANDROID_TAB_GROUPS_COLOR_UPDATE_GM3})
+    public void testGetCardViewMiniThumbnailPlaceholderColor_gm3FlagDisabled() {
+
+        @TabGroupColorId int testColorId = TabGroupColorId.ORANGE;
+
+        // Test non-incognito (fallback path).
+        @ColorInt
+        int expectedNonIncognito = SemanticColorUtils.getColorSurfaceContainerLow(mContext);
+        @ColorInt
+        int actualNonIncognito =
+                SurfaceColorUpdateUtils.getCardViewMiniThumbnailPlaceholderColor(
+                        mContext, /* isIncognito= */ false, testColorId);
+        assertEquals(
+                "Placeholder color mismatch for non-incognito with GM3 flag disabled (colorId"
+                        + " ignored).",
+                expectedNonIncognito,
+                actualNonIncognito);
+
+        // Test incognito (fallback path).
+        @ColorInt
+        int expectedIncognito =
+                ContextCompat.getColor(mContext, R.color.incognito_tab_thumbnail_placeholder_color);
+        @ColorInt
+        int actualIncognito =
+                SurfaceColorUpdateUtils.getCardViewMiniThumbnailPlaceholderColor(
+                        mContext, /* isIncognito= */ true, testColorId);
+        assertEquals(
+                "Placeholder color mismatch for incognito with GM3 flag disabled (colorId"
+                        + " ignored).",
+                expectedIncognito,
+                actualIncognito);
+
+        // Test with a null colorId to ensure it behaves the same as with a non-null colorId when
+        // the flag is off.
+        @ColorInt
+        int actualNonIncognitoNullId =
+                SurfaceColorUpdateUtils.getCardViewMiniThumbnailPlaceholderColor(
+                        mContext, /* isIncognito= */ false, /* colorId= */ null);
+        assertEquals(
+                "Placeholder color should be the same for null and non-null colorId when GM3 flag"
+                        + " is disabled.",
+                expectedNonIncognito,
+                actualNonIncognitoNullId);
+    }
 }
diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn
index 24836900..39841120 100644
--- a/chrome/browser/ui/android/toolbar/BUILD.gn
+++ b/chrome/browser/ui/android/toolbar/BUILD.gn
@@ -337,7 +337,6 @@
     "java/res/drawable/ic_switch_to_incognito.xml",
     "java/res/drawable/ic_widgets.xml",
     "java/res/drawable/menu_bg_bottom_tinted.xml",
-    "java/res/drawable/modern_toolbar_text_box_background_with_primary_color.xml",
     "java/res/drawable/modern_toolbar_text_box_background_with_primary_color_square.xml",
     "java/res/drawable/new_tab_icon.xml",
     "java/res/drawable/optional_button_background.xml",
diff --git a/chrome/browser/ui/android/toolbar/java/res/drawable/modern_toolbar_text_box_background_with_primary_color.xml b/chrome/browser/ui/android/toolbar/java/res/drawable/modern_toolbar_text_box_background_with_primary_color.xml
deleted file mode 100644
index 09cf77c..0000000
--- a/chrome/browser/ui/android/toolbar/java/res/drawable/modern_toolbar_text_box_background_with_primary_color.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2018 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<shape
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle" >
-    <solid android:color="@color/toolbar_text_box_bg_color" />
-    <size
-        android:width="@dimen/modern_toolbar_background_size"
-        android:height="@dimen/modern_toolbar_background_size" />
-    <corners android:radius="@dimen/modern_toolbar_background_corner_radius" />
-</shape>
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java
index 94b3835e..ece5f28e5 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/optional_button/OptionalButtonView.java
@@ -282,7 +282,7 @@
                     isCpaCheckedState
                             ? R.drawable
                                     .modern_toolbar_text_box_background_with_primary_color_square
-                            : R.drawable.modern_toolbar_text_box_background_with_primary_color;
+                            : R.drawable.modern_toolbar_text_box_background;
 
             mBackground.setImageDrawable(AppCompatResources.getDrawable(getContext(), resId));
             setBackgroundResourceHelper(isCpaCheckedState);
@@ -469,8 +469,7 @@
         // If isCpaSpecUpdateEnabled, overriding the background in #updateButtonWithAnimation.
         mBackground.setImageDrawable(
                 AppCompatResources.getDrawable(
-                        getContext(),
-                        R.drawable.modern_toolbar_text_box_background_with_primary_color));
+                        getContext(), R.drawable.modern_toolbar_text_box_background));
     }
 
     /**
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 7dced63..e1a04dd 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -417,8 +417,7 @@
     public static GradientDrawable createModernLocationBarBackground(Context context) {
         GradientDrawable drawable =
                 (GradientDrawable)
-                        context.getDrawable(
-                                R.drawable.modern_toolbar_text_box_background_with_primary_color);
+                        context.getDrawable(R.drawable.modern_toolbar_text_box_background);
         assumeNonNull(drawable);
         drawable.mutate();
         drawable.setColor(
diff --git a/chrome/browser/ui/color/chrome_color_id.h b/chrome/browser/ui/color/chrome_color_id.h
index a9441e3..8e44a23 100644
--- a/chrome/browser/ui/color/chrome_color_id.h
+++ b/chrome/browser/ui/color/chrome_color_id.h
@@ -328,6 +328,7 @@
   /* New Tab Footer colors. */ \
   E_CPONLY(kColorNewTabFooterBackground) \
   E_CPONLY(kColorNewTabFooterText) \
+  E_CPONLY(kColorNewTabFooterLogoBackground) \
   /* Omnibox colors. */ \
   E_CPONLY(kColorOmniboxActionIcon) \
   E_CPONLY(kColorOmniboxActionIconHover) \
diff --git a/chrome/browser/ui/color/material_new_tab_page_color_mixer.cc b/chrome/browser/ui/color/material_new_tab_page_color_mixer.cc
index 7ed12eb..c8a432af 100644
--- a/chrome/browser/ui/color/material_new_tab_page_color_mixer.cc
+++ b/chrome/browser/ui/color/material_new_tab_page_color_mixer.cc
@@ -142,5 +142,6 @@
   /* NewTabFooter */
   mixer[kColorNewTabFooterBackground] = {ui::kColorSysSurface2};
   mixer[kColorNewTabFooterText] = {ui::kColorSysOnSurface};
-  // LINT.ThenChange(chrome/browser/ui/color/new_tab_page_color_mixer.cc)
+  mixer[kColorNewTabFooterLogoBackground] = {ui::kColorSysSurface};
+  // LINT.ThenChange(//chrome/browser/ui/color/new_tab_page_color_mixer.cc)
 }
diff --git a/chrome/browser/ui/color/new_tab_page_color_mixer.cc b/chrome/browser/ui/color/new_tab_page_color_mixer.cc
index 0fc1506..fa44bdd8d 100644
--- a/chrome/browser/ui/color/new_tab_page_color_mixer.cc
+++ b/chrome/browser/ui/color/new_tab_page_color_mixer.cc
@@ -387,6 +387,7 @@
   mixer[kColorNewTabFooterBackground] = {kColorToolbar};
   mixer[kColorNewTabFooterText] =
       ui::GetColorWithMaxContrast({kColorNewTabFooterBackground});
+  mixer[kColorNewTabFooterLogoBackground] = {SK_ColorWHITE};
 }
 
 void AddWebThemeNewTabPageColors(ui::ColorMixer& mixer, bool dark_mode) {
diff --git a/chrome/browser/ui/extensions/BUILD.gn b/chrome/browser/ui/extensions/BUILD.gn
index 255e102..f6c4725d 100644
--- a/chrome/browser/ui/extensions/BUILD.gn
+++ b/chrome/browser/ui/extensions/BUILD.gn
@@ -4,8 +4,6 @@
 
 import("//extensions/buildflags/buildflags.gni")
 
-assert(enable_extensions)
-
 source_set("extension_enable_flow_delegate") {
   sources = [ "extension_enable_flow_delegate.h" ]
 }
@@ -16,28 +14,8 @@
 }
 
 source_set("extensions") {
-  sources = [
-    "accelerator_priority.h",
-    "app_launch_params.h",
-    "application_launch.h",
-    "controlled_home_bubble_delegate.h",
-    "extension_action_platform_delegate.h",
-    "extension_action_view_controller.h",
-    "extension_enable_flow.h",
-    "extension_installed_bubble_model.h",
-    "extension_installed_waiter.h",
-    "extension_settings_overridden_dialog.h",
-    "extension_side_panel_utils.h",
-    "extensions_container.h",
-    "extensions_dialogs.h",
-    "hosted_app_browser_controller.h",
-    "icon_with_badge_image_source.h",
-    "installation_error_infobar_delegate.h",
-    "mv2_disabled_dialog_controller.h",
-    "settings_api_bubble_helpers.h",
-    "settings_overridden_dialog_controller.h",
-    "settings_overridden_params_providers.h",
-  ]
+  sources = []
+  public_deps = []
 
   if (enable_extensions_core) {
     sources += [ "extension_install_ui.h" ]
@@ -49,29 +27,55 @@
     if (is_win || is_mac || is_linux || is_chromeos) {
       sources += [ "extension_install_ui_desktop.h" ]
     }
+    public_deps += [ "//base" ]
   }
 
-  public_deps = [
-    ":extension_popup_types",
-    "//base",
-    "//chrome/browser/apps/app_service:constants",
-    "//chrome/browser/ui:browser_list",
-    "//chrome/browser/ui/tabs:tab_strip_model_observer",
-    "//chrome/browser/ui/toolbar",
-    "//chrome/browser/ui/web_applications",
-    "//components/infobars/core",
-    "//components/omnibox/browser",
-    "//components/services/app_service/public/cpp:app_types",
-    "//extensions/browser",
-    "//extensions/browser/install",
-    "//extensions/buildflags",
-    "//extensions/common",
-    "//skia",
-    "//ui/base",
-    "//ui/base:types",
-    "//ui/base/mojom:ui_base_types",
-    "//ui/gfx",
-    "//ui/gfx/geometry",
-    "//url",
-  ]
+  if (enable_extensions) {
+    sources += [
+      "accelerator_priority.h",
+      "app_launch_params.h",
+      "application_launch.h",
+      "controlled_home_bubble_delegate.h",
+      "extension_action_platform_delegate.h",
+      "extension_action_view_controller.h",
+      "extension_enable_flow.h",
+      "extension_installed_bubble_model.h",
+      "extension_installed_waiter.h",
+      "extension_settings_overridden_dialog.h",
+      "extension_side_panel_utils.h",
+      "extensions_container.h",
+      "extensions_dialogs.h",
+      "hosted_app_browser_controller.h",
+      "icon_with_badge_image_source.h",
+      "installation_error_infobar_delegate.h",
+      "mv2_disabled_dialog_controller.h",
+      "settings_api_bubble_helpers.h",
+      "settings_overridden_dialog_controller.h",
+      "settings_overridden_params_providers.h",
+    ]
+
+    public_deps += [
+      ":extension_popup_types",
+      "//base",
+      "//chrome/browser/apps/app_service:constants",
+      "//chrome/browser/ui:browser_list",
+      "//chrome/browser/ui/tabs:tab_strip_model_observer",
+      "//chrome/browser/ui/toolbar",
+      "//chrome/browser/ui/web_applications",
+      "//components/infobars/core",
+      "//components/omnibox/browser",
+      "//components/services/app_service/public/cpp:app_types",
+      "//extensions/browser",
+      "//extensions/browser/install",
+      "//extensions/buildflags",
+      "//extensions/common",
+      "//skia",
+      "//ui/base",
+      "//ui/base:types",
+      "//ui/base/mojom:ui_base_types",
+      "//ui/gfx",
+      "//ui/gfx/geometry",
+      "//url",
+    ]
+  }
 }
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
index 3eead22..c3771509 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.cc
@@ -800,7 +800,8 @@
     auto* const helper =
         OmniboxTabHelper::FromWebContents(location_bar_->GetWebContents());
     CHECK(helper);
-    helper->OnPopupVisibilityChanged(popup_is_open);
+    helper->OnPopupVisibilityChanged(
+        popup_is_open, GetPageClassification(/*is_prefetch=*/false));
   }
 }
 
diff --git a/chrome/browser/ui/omnibox/omnibox_tab_helper.cc b/chrome/browser/ui/omnibox/omnibox_tab_helper.cc
index 4ec9086..6c3fcf0ba 100644
--- a/chrome/browser/ui/omnibox/omnibox_tab_helper.cc
+++ b/chrome/browser/ui/omnibox/omnibox_tab_helper.cc
@@ -4,7 +4,15 @@
 
 #include "chrome/browser/ui/omnibox/omnibox_tab_helper.h"
 
+#include <optional>
+#include <string>
+#include <string_view>
+
+#include "base/metrics/histogram_functions.h"
 #include "base/observer_list.h"
+#include "base/strings/strcat.h"
+#include "base/time/time.h"
+#include "base/timer/elapsed_timer.h"
 #include "chrome/browser/page_content_annotations/page_content_extraction_service.h"
 #include "chrome/browser/page_content_annotations/page_content_extraction_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -13,9 +21,36 @@
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "components/omnibox/browser/omnibox_view.h"
 #include "components/omnibox/common/omnibox_feature_configs.h"
+#include "content/public/browser/render_frame_host.h"
+#include "third_party/metrics_proto/omnibox_event.pb.h"
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(OmniboxTabHelper);
 
+namespace {
+
+constexpr char kNavigationToPopupShownHistogramPrefix[] =
+    "Omnibox.NavigationToPopupShown";
+constexpr char kMainDocumentElementAvailableHistogramSuffix[] =
+    "MainDocumentElementAvailable";
+constexpr char kPrimaryPageChangedHistogramSuffix[] = "PrimaryPageChanged";
+constexpr char kDomContentLoadedHistogramSuffix[] = "DomContentLoaded";
+constexpr char kByPageContextHistogramPrefix[] = "ByPageContext";
+
+void LogNavigationToPopupUma(std::string_view event_name,
+                             std::string_view page_context,
+                             base::TimeDelta time_to_log) {
+  // Custom buckets from 1 millisecond to 1 minute, with 60 buckets in total.
+  // Meaning each bucket is 1 second wide.
+  base::UmaHistogramCustomTimes(
+      base::StrCat({kNavigationToPopupShownHistogramPrefix, ".", event_name}),
+      time_to_log, base::Milliseconds(0), base::Seconds(60), 60);
+  base::UmaHistogramCustomTimes(
+      base::StrCat({kNavigationToPopupShownHistogramPrefix, ".", event_name,
+                    ".", kByPageContextHistogramPrefix, ".", page_context}),
+      time_to_log, base::Milliseconds(0), base::Seconds(60), 60);
+}
+}  // namespace
+
 OmniboxTabHelper::~OmniboxTabHelper() = default;
 OmniboxTabHelper::OmniboxTabHelper(content::WebContents* contents,
                                    Profile* profile)
@@ -57,10 +92,16 @@
   }
 }
 
-void OmniboxTabHelper::OnPopupVisibilityChanged(bool popup_is_open) {
+void OmniboxTabHelper::OnPopupVisibilityChanged(
+    bool popup_is_open,
+    metrics::OmniboxEventProto::PageClassification page_classification) {
   for (auto& observer : observers_) {
     observer.OnOmniboxPopupVisibilityChanged(popup_is_open);
   }
+
+  if (popup_is_open) {
+    MaybeLogNavigationToPopupShownTimings(page_classification);
+  }
 }
 
 std::optional<bool> OmniboxTabHelper::IsPagePaywalled() {
@@ -85,4 +126,55 @@
 
 void OmniboxTabHelper::PrimaryPageChanged(content::Page& page) {
   page_has_apc_paywall_signal_.reset();
+
+  // Reset old times to avoid logging them incorrectly.
+  primary_main_document_element_available_time_.reset();
+  dom_content_loaded_time_.reset();
+  logged_current_navigation_timings_ = false;
+
+  primary_page_changed_time_ = base::ElapsedTimer();
+}
+
+void OmniboxTabHelper::PrimaryMainDocumentElementAvailable() {
+  primary_main_document_element_available_time_ = base::ElapsedTimer();
+}
+
+void OmniboxTabHelper::DOMContentLoaded(
+    content::RenderFrameHost* render_frame_host) {
+  // Ignore events from subframes.
+  if (render_frame_host->GetParent()) {
+    return;
+  }
+  dom_content_loaded_time_ = base::ElapsedTimer();
+}
+
+void OmniboxTabHelper::MaybeLogNavigationToPopupShownTimings(
+    metrics::OmniboxEventProto::PageClassification page_classification) {
+  if (logged_current_navigation_timings_) {
+    return;
+  }
+  logged_current_navigation_timings_ = true;
+
+  // If the primary page hasn't changed, then there is nothing to log, and this
+  // tab is probably on the NTP, so exit early to avoid skewing metrics.
+  if (!primary_page_changed_time_.has_value()) {
+    return;
+  }
+
+  const std::string page_context =
+      metrics::OmniboxEventProto::PageClassification_Name(page_classification);
+
+  LogNavigationToPopupUma(kPrimaryPageChangedHistogramSuffix, page_context,
+                          primary_page_changed_time_->Elapsed());
+
+  if (primary_main_document_element_available_time_.has_value()) {
+    LogNavigationToPopupUma(
+        kMainDocumentElementAvailableHistogramSuffix, page_context,
+        primary_main_document_element_available_time_->Elapsed());
+  }
+
+  if (dom_content_loaded_time_.has_value()) {
+    LogNavigationToPopupUma(kDomContentLoadedHistogramSuffix, page_context,
+                            dom_content_loaded_time_->Elapsed());
+  }
 }
diff --git a/chrome/browser/ui/omnibox/omnibox_tab_helper.h b/chrome/browser/ui/omnibox/omnibox_tab_helper.h
index 8ac6291d1..52e405a 100644
--- a/chrome/browser/ui/omnibox/omnibox_tab_helper.h
+++ b/chrome/browser/ui/omnibox/omnibox_tab_helper.h
@@ -5,17 +5,22 @@
 #ifndef CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_TAB_HELPER_H_
 #define CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_TAB_HELPER_H_
 
+#include <optional>
+
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 #include "base/scoped_observation.h"
+#include "base/timer/elapsed_timer.h"
 #include "chrome/browser/page_content_annotations/page_content_extraction_service.h"
 #include "components/omnibox/common/omnibox_focus_state.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
+#include "third_party/metrics_proto/omnibox_event.pb.h"
 
 namespace content {
 class WebContents;
-}
+class RenderFrameHost;
+}  // namespace content
 
 class Profile;
 
@@ -54,7 +59,9 @@
   void OnInputStateChanged();
   void OnInputInProgress(bool in_progress);
   void OnFocusChanged(OmniboxFocusState state, OmniboxFocusChangeReason reason);
-  void OnPopupVisibilityChanged(bool popup_is_open);
+  void OnPopupVisibilityChanged(
+      bool popup_is_open,
+      metrics::OmniboxEventProto::PageClassification page_classification);
 
   // Returns true if the current page has the paywall signal in the Annotated
   // Page Content. Returns false if the page does not have the paywall signal.
@@ -75,12 +82,33 @@
 
   // content::WebContentsObserver
   void PrimaryPageChanged(content::Page& page) override;
+  void PrimaryMainDocumentElementAvailable() override;
+  void DOMContentLoaded(content::RenderFrameHost* render_frame_host) override;
+
+  // Logs the timings from a navigation to the omnibox being focused, IFF they
+  // have not already been logged for this navigation.
+  void MaybeLogNavigationToPopupShownTimings(
+      metrics::OmniboxEventProto::PageClassification page_classification);
 
   // Whether the current page has a paywall signal in the Annotated Page
   // Content. std::nullopt if the page content wasn't yet extracted and
   // therefore the signal could not be calculated.
   std::optional<bool> page_has_apc_paywall_signal_;
 
+  // The time when the primary page changed.
+  std::optional<base::ElapsedTimer> primary_page_changed_time_;
+
+  // The time when the primary main document element was available.
+  std::optional<base::ElapsedTimer>
+      primary_main_document_element_available_time_;
+
+  // The time when the DOMContentLoaded event was fired.
+  std::optional<base::ElapsedTimer> dom_content_loaded_time_;
+
+  // Whether the timings from a navigation to the omnibox being focused have
+  // been logged for this navigation.
+  bool logged_current_navigation_timings_ = false;
+
   // Observer to observer Annotated Page Content updates. Updates are fire on
   // every page, not only the current tab. The page content is generated a few
   // seconds after page load, once the page has stabilized.
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 7ba2245..63c4b64b 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
@@ -132,6 +132,8 @@
     return;
   }
 
+  DataSharingBubbleController::GetOrCreateForBrowser(browser_)->Close();
+
   ShowErrorDialog(error);
   error_ui_callback_ = std::move(result);
 }
@@ -164,9 +166,7 @@
   }
   auto* controller =
       DataSharingBubbleController::GetOrCreateForBrowser(browser_);
-  controller->SetOnCloseCallback(base::BindOnce(
-      &CollaborationControllerDelegateDesktop::OnJoinDialogClosing,
-      weak_ptr_factory_.GetWeakPtr(), std::move(result)));
+  controller->SetJoinCallback(std::move(result));
   controller->SetShowErrorDialogCallback(base::BindOnce(
       &CollaborationControllerDelegateDesktop::ShowErrorDialog,
       weak_ptr_factory_.GetWeakPtr(), ErrorInfo(ErrorInfo::Type::kUnknown)));
@@ -267,6 +267,9 @@
   if (!browser_) {
     return;
   }
+
+  DataSharingBubbleController::GetOrCreateForBrowser(browser_)->Close();
+
   // Open tab group by group id.
   tab_groups::TabGroupSyncService* tab_group_sync_service =
       tab_groups::TabGroupSyncServiceFactory::GetForProfile(
@@ -326,22 +329,6 @@
   }
 }
 
-void CollaborationControllerDelegateDesktop::OnJoinDialogClosing(
-    ResultCallback result,
-    std::optional<data_sharing::mojom::GroupAction> action,
-    std::optional<data_sharing::mojom::GroupActionProgress> progress) {
-  // Joins flow should end when the shared tab group is open after join
-  // or cancel without joining.
-  CollaborationControllerDelegate::Outcome outcome =
-      CollaborationControllerDelegate::Outcome::kCancel;
-  if (action == data_sharing::mojom::GroupAction::kJoinGroup &&
-      progress == data_sharing::mojom::GroupActionProgress::kSuccess) {
-    outcome = CollaborationControllerDelegate::Outcome::kSuccess;
-  }
-
-  std::move(result).Run(outcome);
-}
-
 void CollaborationControllerDelegateDesktop::OnManageDialogClosing(
     ResultCallback result,
     std::optional<data_sharing::mojom::GroupAction> action,
diff --git a/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.h b/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.h
index d92df32..b9d77d9 100644
--- a/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.h
+++ b/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.h
@@ -74,11 +74,6 @@
   // BrowserListObserver:
   void OnBrowserClosing(Browser* browser) override;
 
-  void OnJoinDialogClosing(
-      ResultCallback result,
-      std::optional<data_sharing::mojom::GroupAction> action,
-      std::optional<data_sharing::mojom::GroupActionProgress> progress);
-
   void OnManageDialogClosing(
       ResultCallback result,
       std::optional<data_sharing::mojom::GroupAction> action,
diff --git a/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop_interactive_uitest.cc b/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop_interactive_uitest.cc
index a366c4a..1cd9320 100644
--- a/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop_interactive_uitest.cc
+++ b/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop_interactive_uitest.cc
@@ -125,11 +125,13 @@
   base::MockCallback<
       collaboration::CollaborationControllerDelegate::ResultCallback>
       callback;
-  RunTestSequence(Do([&]() {
-                    delegate.ShowJoinDialog(token, preview_data,
-                                            callback.Get());
-                  }),
-                  WaitForShow(kDataSharingBubbleElementId));
+  RunTestSequence(
+      Do([&]() {
+        delegate.ShowJoinDialog(token, preview_data, callback.Get());
+      }),
+      WaitForShow(kDataSharingBubbleElementId), Do([&]() {
+        DataSharingBubbleController::GetOrCreateForBrowser(browser())->Close();
+      }));
 }
 
 IN_PROC_BROWSER_TEST_F(CollaborationControllerDelegateDesktopInteractiveUITest,
diff --git a/chrome/browser/ui/views/data_sharing/data_sharing_bubble_controller.cc b/chrome/browser/ui/views/data_sharing/data_sharing_bubble_controller.cc
index 1d564fad..2f3d88e0 100644
--- a/chrome/browser/ui/views/data_sharing/data_sharing_bubble_controller.cc
+++ b/chrome/browser/ui/views/data_sharing/data_sharing_bubble_controller.cc
@@ -176,6 +176,11 @@
   on_share_link_requested_callback_ = std::move(callback);
 }
 
+void DataSharingBubbleController::SetJoinCallback(
+    collaboration::CollaborationControllerDelegate::ResultCallback callback) {
+  join_callback_ = std::move(callback);
+}
+
 void DataSharingBubbleController::OnUrlReadyToShare(GURL url) {
   if (share_link_callback_) {
     std::move(share_link_callback_).Run(url);
@@ -190,6 +195,8 @@
              std::nullopt);
   }
 
+  MaybeRunJoinCallback(/*on_close=*/true);
+
   if (on_close_callback_) {
     std::move(on_close_callback_).Run(group_action_, group_action_progress_);
   }
@@ -233,6 +240,27 @@
     data_sharing::mojom::GroupActionProgress progress) {
   group_action_ = action;
   group_action_progress_ = progress;
+
+  MaybeRunJoinCallback(/*on_close=*/false);
+}
+
+void DataSharingBubbleController::MaybeRunJoinCallback(bool on_close) {
+  // Joins flow should end when the shared tab group is open after join
+  // or cancel without joining.
+  if (join_callback_) {
+    if (group_action_ == data_sharing::mojom::GroupAction::kJoinGroup &&
+        group_action_progress_ ==
+            data_sharing::mojom::GroupActionProgress::kSuccess) {
+      std::move(join_callback_)
+          .Run(collaboration::CollaborationControllerDelegate::Outcome::
+                   kSuccess);
+    } else if (on_close) {
+      // Only run cancel on close if not success.
+      std::move(join_callback_)
+          .Run(
+              collaboration::CollaborationControllerDelegate::Outcome::kCancel);
+    }
+  }
 }
 
 DataSharingBubbleController::DataSharingBubbleController(Browser* browser)
diff --git a/chrome/browser/ui/views/data_sharing/data_sharing_bubble_controller.h b/chrome/browser/ui/views/data_sharing/data_sharing_bubble_controller.h
index f6bc944..95e9554 100644
--- a/chrome/browser/ui/views/data_sharing/data_sharing_bubble_controller.h
+++ b/chrome/browser/ui/views/data_sharing/data_sharing_bubble_controller.h
@@ -47,6 +47,9 @@
       collaboration::CollaborationControllerDelegate::
           ResultWithGroupTokenCallback callback);
 
+  void SetJoinCallback(
+      collaboration::CollaborationControllerDelegate::ResultCallback callback);
+
   void OnUrlReadyToShare(GURL url);
 
   // views::WidgetObserver
@@ -78,6 +81,8 @@
   }
 
  private:
+  void MaybeRunJoinCallback(bool on_close);
+
   friend class BrowserUserData<DataSharingBubbleController>;
 
   explicit DataSharingBubbleController(Browser* browser);
@@ -100,6 +105,10 @@
   // failed to share.
   base::OnceCallback<void(const std::optional<GURL>&)> share_link_callback_;
 
+  // Callback passed from CollaborationService to invoke after successfully join
+  // a group or cancel.
+  collaboration::CollaborationControllerDelegate::ResultCallback join_callback_;
+
   // The latest group action received from Data Sharing SDK.
   std::optional<data_sharing::mojom::GroupAction> group_action_;
 
diff --git a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.cc b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.cc
index 9ea7f66f..82f8fce 100644
--- a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.cc
+++ b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.cc
@@ -242,6 +242,10 @@
   GetWidget()->Close();
 }
 
+BrowserWindowInterface* PrivacySandboxDialogView::GetBrowser() {
+  return browser_;
+}
+
 void PrivacySandboxDialogView::ResizeNativeView(int height) {
   const int max_height = browser_->GetWebContentsModalDialogHostForWindow()
                              ->GetMaximumDialogSize()
diff --git a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.h b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.h
index 4682f1a..d356f98 100644
--- a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.h
+++ b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.h
@@ -44,6 +44,7 @@
   void ResizeNativeView(int height) override;
   void ShowNativeView(base::OnceCallback<void()> view_shown_callback =
                           base::DoNothing()) override;
+  BrowserWindowInterface* GetBrowser() override;
   privacy_sandbox::notice::mojom::PrivacySandboxNotice GetPrivacySandboxNotice()
       override;
   void SetPrivacySandboxNotice(
diff --git a/chrome/browser/ui/views/webauthn/combined_selector_sheet_view.cc b/chrome/browser/ui/views/webauthn/combined_selector_sheet_view.cc
index e9687f0..0d27027 100644
--- a/chrome/browser/ui/views/webauthn/combined_selector_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/combined_selector_sheet_view.cc
@@ -29,46 +29,12 @@
 #include "ui/views/view.h"
 #include "ui/views/view_utils.h"
 
-namespace {
-
-std::unique_ptr<views::Label> BuildTitleView(std::u16string title) {
-  auto title_label =
-      std::make_unique<views::Label>(title, views::style::CONTEXT_DIALOG_TITLE,
-                                     views::style::STYLE_HEADLINE_4);
-  title_label->SetMultiLine(true);
-  title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  title_label->GetViewAccessibility().SetRole(ax::mojom::Role::kHeading);
-  title_label->SetAllowCharacterBreak(true);
-  return title_label;
-}
-
-}  // namespace
-
 CombinedSelectorSheetView::CombinedSelectorSheetView(
     std::unique_ptr<CombinedSelectorSheetModel> model)
     : AuthenticatorRequestSheetView(std::move(model)) {}
 
 CombinedSelectorSheetView::~CombinedSelectorSheetView() = default;
 
-std::unique_ptr<views::View>
-CombinedSelectorSheetView::BuildStepSpecificHeader() {
-  std::u16string title = l10n_util::GetStringFUTF16(
-      IDS_WEBAUTHN_SIGN_IN_TO_WEBSITE_DIALOG_TITLE,
-      base::UTF8ToUTF16(static_cast<CombinedSelectorSheetModel*>(model())
-                            ->dialog_model()
-                            ->relying_party_id));
-
-  auto view = std::make_unique<views::TableLayoutView>();
-  view->AddPaddingRow(0, kTopPadding);
-  view->AddColumn(views::LayoutAlignment::kStart,
-                  views::LayoutAlignment::kCenter, 1.0f,
-                  views::TableLayout::ColumnSize::kUsePreferred, 0, 0);
-  view->AddRows(1, 0);
-  view->AddChildView(BuildTitleView(title));
-
-  return view;
-}
-
 std::pair<std::unique_ptr<views::View>,
           AuthenticatorRequestSheetView::AutoFocus>
 CombinedSelectorSheetView::BuildStepSpecificContent() {
diff --git a/chrome/browser/ui/views/webauthn/combined_selector_sheet_view.h b/chrome/browser/ui/views/webauthn/combined_selector_sheet_view.h
index fdac29c7..abda5e9f 100644
--- a/chrome/browser/ui/views/webauthn/combined_selector_sheet_view.h
+++ b/chrome/browser/ui/views/webauthn/combined_selector_sheet_view.h
@@ -21,8 +21,6 @@
   METADATA_HEADER(CombinedSelectorSheetView, AuthenticatorRequestSheetView)
 
  public:
-  static constexpr int kTopPadding = 8;
-
   explicit CombinedSelectorSheetView(
       std::unique_ptr<CombinedSelectorSheetModel> model);
 
@@ -34,7 +32,6 @@
 
  private:
   // AuthenticatorRequestSheetView:
-  std::unique_ptr<views::View> BuildStepSpecificHeader() override;
   std::pair<std::unique_ptr<views::View>, AutoFocus> BuildStepSpecificContent()
       override;
 
diff --git a/chrome/browser/ui/webauthn/sheet_models.cc b/chrome/browser/ui/webauthn/sheet_models.cc
index 3c21c5e..27d6cccb 100644
--- a/chrome/browser/ui/webauthn/sheet_models.cc
+++ b/chrome/browser/ui/webauthn/sheet_models.cc
@@ -876,6 +876,13 @@
   }
 }
 
+bool AuthenticatorClientPinEntrySheetModel::IsOtherMechanismButtonVisible()
+    const {
+  // Always allow restarting the request to select a different security key or
+  // hybrid authenticator.
+  return true;
+}
+
 // AuthenticatorClientPinTapAgainSheetModel ----------------------
 
 AuthenticatorClientPinTapAgainSheetModel::
@@ -2176,7 +2183,9 @@
 }
 
 std::u16string CombinedSelectorSheetModel::GetStepTitle() const {
-  return u"";
+  return l10n_util::GetStringFUTF16(
+      IDS_WEBAUTHN_SIGN_IN_TO_WEBSITE_DIALOG_TITLE,
+      base::UTF8ToUTF16(dialog_model()->relying_party_id));
 }
 
 std::u16string CombinedSelectorSheetModel::GetStepDescription() const {
diff --git a/chrome/browser/ui/webauthn/sheet_models.h b/chrome/browser/ui/webauthn/sheet_models.h
index de35622..1d3c1f17 100644
--- a/chrome/browser/ui/webauthn/sheet_models.h
+++ b/chrome/browser/ui/webauthn/sheet_models.h
@@ -336,6 +336,7 @@
   AcceptButtonState GetAcceptButtonState() const override;
   std::u16string GetAcceptButtonLabel() const override;
   void OnAccept() override;
+  bool IsOtherMechanismButtonVisible() const override;
 
   std::u16string pin_code_;
   std::u16string pin_confirmation_;
diff --git a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer.mojom b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer.mojom
index 912fee7c..4894a7fe 100644
--- a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer.mojom
+++ b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer.mojom
@@ -15,6 +15,7 @@
   // Url to the management icon. This will be a default business icon or custom
   // icon set by policy.
   url.mojom.Url bitmap_data_url;
+  bool is_custom_logo;
 };
 
 // Used by the WebUI document to bootstrap bidirectional communication.
diff --git a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler.cc b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler.cc
index 73671da8..d994a9d 100644
--- a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler.cc
+++ b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/extensions/settings_api_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/managed_ui.h"
 #include "chrome/browser/ui/webui/new_tab_footer/new_tab_footer.mojom.h"
 #include "chrome/browser/ui/webui/webui_embedding_context.h"
@@ -100,6 +101,9 @@
   notice->text = GetManagementNoticeText();
   notice->bitmap_data_url =
       GURL(webui::GetBitmapDataUrl(GetManagementNoticeIconBitmap()));
+  notice->is_custom_logo =
+      policy::ManagementServiceFactory::GetForProfile(profile_)
+          ->GetManagementIconForBrowser() != nullptr;
   document_->SetManagementNotice(std::move(notice));
 }
 
@@ -149,7 +153,7 @@
   const gfx::ImageSkia default_management_icon =
       gfx::CreateVectorIcon(gfx::IconDescription(
           vector_icons::kBusinessIcon, 20,
-          web_contents_->GetColorProvider().GetColor(ui::kColorIcon)));
+          web_contents_->GetColorProvider().GetColor(kColorNewTabFooterText)));
   return default_management_icon.GetRepresentation(1.0f).GetBitmap();
 }
 
diff --git a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler_unittest.cc b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler_unittest.cc
index da88c67..d116e46 100644
--- a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler_unittest.cc
+++ b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler_unittest.cc
@@ -26,6 +26,7 @@
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/enterprise/browser_management/management_service_factory.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/managed_ui.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/test/base/testing_browser_process.h"
@@ -344,6 +345,7 @@
         // We only test the base URL as the data is very long and not readable.
         EXPECT_TRUE(base::StartsWith(notice->bitmap_data_url.spec(),
                                      "data:image/png;base64,"));
+        EXPECT_TRUE(notice->is_custom_logo);
       });
   handler().UpdateManagementNotice();
 
@@ -360,7 +362,7 @@
   const gfx::ImageSkia default_logo =
       gfx::CreateVectorIcon(gfx::IconDescription(
           vector_icons::kBusinessIcon, 20,
-          web_contents_->GetColorProvider().GetColor(ui::kColorIcon)));
+          web_contents_->GetColorProvider().GetColor(kColorNewTabFooterText)));
   EXPECT_TRUE(gfx::test::AreBitmapsEqual(
       default_logo.GetRepresentation(1.0f).GetBitmap(),
       handler_->GetManagementNoticeIconBitmap()));
@@ -370,6 +372,7 @@
         // We only test the base URL as the data is very long and not readable.
         EXPECT_TRUE(base::StartsWith(notice->bitmap_data_url.spec(),
                                      "data:image/png;base64,"));
+        EXPECT_FALSE(notice->is_custom_logo);
       });
   handler().UpdateManagementNotice();
 
diff --git a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.cc b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.cc
index a240b4f..fcf7564 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.cc
+++ b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.cc
@@ -122,4 +122,8 @@
   return native_dialog_shown_;
 }
 
+BrowserWindowInterface* BaseDialogHandler::GetBrowser() {
+  return delegate_->GetBrowser();
+}
+
 }  // namespace privacy_sandbox
diff --git a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.h b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.h
index 34d2e27..c4830d2 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.h
+++ b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler.h
@@ -37,6 +37,9 @@
       std::optional<privacy_sandbox::notice::mojom::PrivacySandboxNotice>
           next_id) override;
 
+  // DesktopViewManagerInterface::Observer:
+  BrowserWindowInterface* GetBrowser() override;
+
   // privacy_sandbox::dialog::mojom::BaseDialogPageHandler
   void ResizeDialog(uint32_t height) override;
   void ShowDialog() override;
diff --git a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler_unittest.cc b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler_unittest.cc
index 41730488..79ce7d9 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler_unittest.cc
+++ b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_handler_unittest.cc
@@ -41,6 +41,7 @@
               (override));
   MOCK_METHOD(void, OpenPrivacySandboxSettings, (), (override));
   MOCK_METHOD(void, OpenPrivacySandboxAdMeasurementSettings, (), (override));
+  MOCK_METHOD(BrowserWindowInterface*, GetBrowser, (), (override));
 };
 
 class MockBaseDialogPage : public dialog::mojom::BaseDialogPage {
diff --git a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_ui.h b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_ui.h
index 603c32a..d369025 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/base_dialog_ui.h
+++ b/chrome/browser/ui/webui/privacy_sandbox/base_dialog_ui.h
@@ -23,6 +23,7 @@
   virtual ~BaseDialogUIDelegate() = default;
 
   virtual void ResizeNativeView(int height) = 0;
+  virtual BrowserWindowInterface* GetBrowser() = 0;
   virtual void ShowNativeView(base::OnceCallback<void()> callback) = 0;
   virtual void CloseNativeView() = 0;
   virtual notice::mojom::PrivacySandboxNotice GetPrivacySandboxNotice() = 0;
diff --git a/chrome/browser/ui/webui/top_chrome/preload_context.h b/chrome/browser/ui/webui/top_chrome/preload_context.h
index 49a519b..6cc98c06 100644
--- a/chrome/browser/ui/webui/top_chrome/preload_context.h
+++ b/chrome/browser/ui/webui/top_chrome/preload_context.h
@@ -7,8 +7,6 @@
 
 #include <variant>
 
-#include "base/memory/raw_ptr.h"
-
 class Browser;
 class Profile;
 
@@ -42,4 +40,4 @@
 
 }  // namespace webui
 
-#endif  /// CHROME_BROWSER_UI_WEBUI_TOP_CHROME_PRELOAD_CONTEXT_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_TOP_CHROME_PRELOAD_CONTEXT_H_
diff --git a/chrome/browser/vr/webxr_vr_frame_pose_browser_test.cc b/chrome/browser/vr/webxr_vr_frame_pose_browser_test.cc
index 1ba89d5..410e49628 100644
--- a/chrome/browser/vr/webxr_vr_frame_pose_browser_test.cc
+++ b/chrome/browser/vr/webxr_vr_frame_pose_browser_test.cc
@@ -13,10 +13,6 @@
 #include "chrome/browser/vr/test/ui_utils.h"
 #include "chrome/browser/vr/test/webxr_vr_browser_test.h"
 
-// TODO(https://crbug.com/381000093): Fix tests on Android.
-// Note: This test needs frame pixels to be sent to the runtime to properly
-// be enabled.
-#if !BUILDFLAG(IS_ANDROID)
 namespace vr {
 namespace {
 
@@ -232,4 +228,3 @@
 }
 
 }  // namespace vr
-#endif  // if !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/vr/webxr_vr_pixel_browser_test.cc b/chrome/browser/vr/webxr_vr_pixel_browser_test.cc
index 09ef00c..20dd947 100644
--- a/chrome/browser/vr/webxr_vr_pixel_browser_test.cc
+++ b/chrome/browser/vr/webxr_vr_pixel_browser_test.cc
@@ -15,8 +15,6 @@
 #include "chrome/browser/vr/test/ui_utils.h"
 #include "chrome/browser/vr/test/webxr_vr_browser_test.h"
 
-// TODO(https://crbug.com/381000093): Fix tests on Android
-#if !BUILDFLAG(IS_ANDROID)
 namespace vr {
 
 class MyXRMock : public MockXRDeviceHookBase {
@@ -25,8 +23,7 @@
       std::vector<device_test::mojom::ViewDataPtr> views) final;
 
   base::Lock color_lock;
-  device_test::mojom::ColorPtr last_submitted_color_
-      GUARDED_BY(color_lock) = {};
+  device_test::mojom::ColorPtr last_submitted_color_ GUARDED_BY(color_lock);
 };
 
 void MyXRMock::ProcessSubmittedFrameUnlocked(
@@ -78,4 +75,3 @@
 }
 
 }  // namespace vr
-#endif  // if !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.cc b/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.cc
index dd7fa1a..3f432278 100644
--- a/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.cc
+++ b/chrome/browser/win/installer_downloader/installer_downloader_infobar_delegate.cc
@@ -84,7 +84,7 @@
 }
 
 std::u16string InstallerDownloaderInfoBarDelegate::GetLinkText() const {
-  return l10n_util::GetStringUTF16(IDS_LEARN_MORE);
+  return l10n_util::GetStringUTF16(IDS_INSTALLER_DOWNLOADER_LINK);
 }
 
 int InstallerDownloaderInfoBarDelegate::GetButtons() const {
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 0b6a168..647a0ac 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1749217180-4759f404a8e111801ef06d2f7a444ca2dc01409b-c093fd335b7744d3c9d6ceadf4e8bb0a412196f9.profdata
+chrome-android64-main-1749239254-7a1fa12000b6855d8dbe79b16e4c5b7541c2d510-a7f9dff219b0307fc2cd4281cdf8915f6ef01e25.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 72b59007..cd746ce 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1749211034-83b239b0dc7abac49e3517b1fbf02b438d3164b4-d2038f87e77355d9568c6446169e2e3f07197ee8.profdata
+chrome-linux-main-1749232678-3376b8bfe668093e81c16d8c5ff9c878ac790f69-e51b0fe6641a99269aec9e9ca6a54b786d3a2685.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 9c46aa87..e6127cf 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1749225124-207afed711ff64b382d495a8108d2f75e6cf1ae2-46f84921ae0ff82839898770bbcaa8078b083f27.profdata
+chrome-mac-arm-main-1749239942-927368156257ef76e49a6e2f87c841e7363a70a6-b15355c7009892fee8ab66bc594e9650452e108a.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 10a7110d..8c5dfa25 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1749211034-3674a5b2f90fb2527ee2b2e3d1cc70aec4cad8b4-d2038f87e77355d9568c6446169e2e3f07197ee8.profdata
+chrome-mac-main-1749232678-e76acfb2a86247c13643d697eb1826d477842edb-e51b0fe6641a99269aec9e9ca6a54b786d3a2685.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index b7704fec..163024a 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1749146264-e6093957aa37f69346ff93b935b89d1421d6a113-46225ed4889da320c0d607a3a8e9ce0400bd3e88.profdata
+chrome-win-arm64-main-1749232678-c8871d8c830f75e0f0f6a5bf556332caa6788bec-e51b0fe6641a99269aec9e9ca6a54b786d3a2685.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 5b5baf6..faca330 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1749189211-31f2701b076d24e669f77c5e9c8d8097c861a8cb-e074fdf9bf476ecb65885b2fc4b04be6d99b5ab3.profdata
+chrome-win32-main-1749200370-43b0e7ce07f31184a9850042b9cfe1fb9a2463b6-aaf17e79b3ee1b9365e27ec6b26da97ee8d40d83.profdata
diff --git a/chrome/common/extensions/api/api_sources.gni b/chrome/common/extensions/api/api_sources.gni
index 7c96374..5b8326f 100644
--- a/chrome/common/extensions/api/api_sources.gni
+++ b/chrome/common/extensions/api/api_sources.gni
@@ -21,6 +21,7 @@
   "identity.idl",
   "notifications.idl",
   "permissions.json",
+  "processes.idl",
   "scripting.idl",
   "tabs.json",
   "webstore_private.json",
@@ -86,10 +87,6 @@
     "webrtc_logging_private.idl",
   ]
 
-  if (!is_android) {
-    schema_sources_ += [ "processes.idl" ]
-  }
-
   if (is_chromeos) {
     schema_sources_ += [
       "accessibility_private.json",
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index e022ae83..3d67ccaa 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -1752,7 +1752,7 @@
 #endif
 }
 
-void ChromeContentRendererClient::WillEvaluateServiceWorkerOnWorkerThread(
+void ChromeContentRendererClient::WillPrepareForEvaluationOnWorkerThread(
     blink::WebServiceWorkerContextProxy* context_proxy,
     v8::Local<v8::Context> v8_context,
     int64_t service_worker_version_id,
@@ -1762,12 +1762,20 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
   extensions::ExtensionsRendererClient::Get()
       ->dispatcher()
-      ->WillEvaluateServiceWorkerOnWorkerThread(
+      ->WillPrepareForEvaluationOnWorkerThread(
           context_proxy, v8_context, service_worker_version_id,
           service_worker_scope, script_url, service_worker_token);
 #endif
 }
 
+void ChromeContentRendererClient::WillEvaluateServiceWorkerOnWorkerThread() {
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
+  extensions::ExtensionsRendererClient::Get()
+      ->dispatcher()
+      ->WillEvaluateServiceWorkerOnWorkerThread();
+#endif
+}
+
 void ChromeContentRendererClient::DidStartServiceWorkerContextOnWorkerThread(
     int64_t service_worker_version_id,
     const GURL& service_worker_scope,
diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h
index 8c08514..5b4a0f2 100644
--- a/chrome/renderer/chrome_content_renderer_client.h
+++ b/chrome/renderer/chrome_content_renderer_client.h
@@ -207,13 +207,14 @@
       blink::WebServiceWorkerContextProxy* context_proxy,
       const GURL& service_worker_scope,
       const GURL& script_url) override;
-  void WillEvaluateServiceWorkerOnWorkerThread(
+  void WillPrepareForEvaluationOnWorkerThread(
       blink::WebServiceWorkerContextProxy* context_proxy,
       v8::Local<v8::Context> v8_context,
       int64_t service_worker_version_id,
       const GURL& service_worker_scope,
       const GURL& script_url,
       const blink::ServiceWorkerToken& service_worker_token) override;
+  void WillEvaluateServiceWorkerOnWorkerThread() override;
   void DidStartServiceWorkerContextOnWorkerThread(
       int64_t service_worker_version_id,
       const GURL& service_worker_scope,
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 2fb9aaa..4d6541f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1540,7 +1540,9 @@
       "../browser/extensions/api/offscreen/audio_lifetime_enforcer_browsertest.cc",
       "../browser/extensions/api/offscreen/offscreen_apitest.cc",
       "../browser/extensions/api/offscreen/offscreen_document_manager_browsertest.cc",
+      "../browser/extensions/api/permissions/permissions_apitest.cc",
       "../browser/extensions/api/power/power_apitest.cc",
+      "../browser/extensions/api/processes/processes_apitest.cc",
       "../browser/extensions/api/runtime/runtime_apitest.cc",
       "../browser/extensions/api/scripting/scripting_apitest.cc",
       "../browser/extensions/api/settings_overrides/settings_overrides_browsertest.cc",
@@ -1594,6 +1596,7 @@
       "//chrome/browser/extensions:test_support",
       "//chrome/browser/extensions/api/bookmarks/test:test_support",
       "//chrome/browser/sync",
+      "//chrome/browser/ui/extensions",
       "//components/bookmarks/managed",
       "//components/embedder_support",
       "//components/privacy_sandbox:privacy_sandbox_settings_headers",
@@ -4455,10 +4458,8 @@
         "../browser/extensions/api/metrics_private/metrics_apitest.cc",
         "../browser/extensions/api/page_capture/page_capture_apitest.cc",
         "../browser/extensions/api/passwords_private/passwords_private_apitest.cc",
-        "../browser/extensions/api/permissions/permissions_apitest.cc",
         "../browser/extensions/api/preference/preference_apitest.cc",
         "../browser/extensions/api/printer_provider/printer_provider_apitest.cc",
-        "../browser/extensions/api/processes/processes_apitest.cc",
         "../browser/extensions/api/reading_list/reading_list_apitest.cc",
         "../browser/extensions/api/resources_private/resources_private_apitest.cc",
         "../browser/extensions/api/search/search_api_apitest.cc",
@@ -4629,6 +4630,7 @@
 
         sources += [
           "../browser/extensions/api/identity/launch_web_auth_flow_delegate_ash_browsertest.cc",
+          "../browser/extensions/component_extension_browsertest.cc",
           "../browser/ui/views/extensions/web_file_handlers/web_file_handlers_file_launch_browsertest.cc",
         ]
       }
diff --git a/chrome/test/data/extensions/api_test/permissions/add_host_access_request/worker.js b/chrome/test/data/extensions/api_test/permissions/add_host_access_request/worker.js
index 0a540da..8865ed6 100644
--- a/chrome/test/data/extensions/api_test/permissions/add_host_access_request/worker.js
+++ b/chrome/test/data/extensions/api_test/permissions/add_host_access_request/worker.js
@@ -60,6 +60,12 @@
   // Tests that an error is returned when the extension adds a request for a
   // tabId that it can already access its current web contents.
   async function accessAlreadyGrantedForTabId() {
+    // TODO(crbug.com/371432155): Port to desktop Android when chrome.tabs API
+    // is available.
+    if (/Android/.test(navigator.userAgent)) {
+      chrome.test.succeed();
+      return;
+    }
     let tab = await navigateTo('requested.com');
 
     const request = {tabId: tab.id};
@@ -74,6 +80,12 @@
   // Tests that an error is returned when the extension adds a request for a
   // documentId that it can already access its current web contents.
   async function accessAlreadyGrantedForDocumentId() {
+    // TODO(crbug.com/371432155): Port to desktop Android when chrome.tabs API
+    // is available.
+    if (/Android/.test(navigator.userAgent)) {
+      chrome.test.succeed();
+      return;
+    }
     let tab = await navigateTo('requested.com');
     let frame = await chrome.webNavigation.getFrame({frameId: 0, tabId: tab.id})
 
@@ -89,6 +101,12 @@
   // Tests that an error is returned when the extension adds a request with an
   // invalid pattern.
   async function invalidPattern() {
+    // TODO(crbug.com/371432155): Port to desktop Android when chrome.tabs API
+    // is available.
+    if (/Android/.test(navigator.userAgent)) {
+      chrome.test.succeed();
+      return;
+    }
     let tab = await navigateTo('requested.com');
 
     const request = {tabId: tab.id, pattern: 'invalid pattern'};
diff --git a/chrome/test/data/extensions/api_test/permissions/disabled/background.js b/chrome/test/data/extensions/api_test/permissions/disabled/background.js
index f7e5b0eb..69d9b6d 100644
--- a/chrome/test/data/extensions/api_test/permissions/disabled/background.js
+++ b/chrome/test/data/extensions/api_test/permissions/disabled/background.js
@@ -19,7 +19,9 @@
 
   function bookmarks() {
     try {
-      chrome.bookmarks.get("1", function(results) {
+      // Use getRecent() instead of get("1") because desktop Android doesn't
+      // create the bookmark bar (id "1") by default.
+      chrome.bookmarks.getRecent(1, function (results) {
         chrome.test.fail();
       });
     } catch (e) {
@@ -30,6 +32,12 @@
   // Tabs functionality should be enabled even if the tabs permissions are not
   // present.
   function tabs() {
+    // TODO(crbug.com/371432155): Port to desktop Android when chrome.tabs API
+    // is available.
+    if (/Android/.test(navigator.userAgent)) {
+      chrome.test.succeed();
+      return;
+    }
     try {
       chrome.tabs.create({'url': '1'}, function(tab) {
         // Tabs strip sensitive data without permissions.
diff --git a/chrome/test/data/extensions/api_test/permissions/enabled/background.js b/chrome/test/data/extensions/api_test/permissions/enabled/background.js
index 196ba73..34aa3f3 100644
--- a/chrome/test/data/extensions/api_test/permissions/enabled/background.js
+++ b/chrome/test/data/extensions/api_test/permissions/enabled/background.js
@@ -19,7 +19,9 @@
 
   function bookmarks() {
     try {
-      chrome.bookmarks.get("1", pass(function(results) {}));
+      // Use getRecent() instead of get("1") because desktop Android doesn't
+      // create the bookmark bar (id "1") by default.
+      chrome.bookmarks.getRecent(1, pass(function (results) { }));
     } catch (e) {
       chrome.test.fail();
     }
diff --git a/chrome/test/data/extensions/api_test/permissions/remove_host_access_request/worker.js b/chrome/test/data/extensions/api_test/permissions/remove_host_access_request/worker.js
index 990c7989..3d1d463 100644
--- a/chrome/test/data/extensions/api_test/permissions/remove_host_access_request/worker.js
+++ b/chrome/test/data/extensions/api_test/permissions/remove_host_access_request/worker.js
@@ -60,6 +60,12 @@
   // Tests that an error is returned when the request cannot be removed since
   // the request doesn't exist.
   async function nonExistentRequest() {
+    // TODO(crbug.com/371432155): Port to desktop Android when chrome.tabs API
+    // is available.
+    if (/Android/.test(navigator.userAgent)) {
+      chrome.test.succeed();
+      return;
+    }
     const tab = await navigateTo('example.com');
     const request = {tabId: tab.id};
     await chrome.test.assertPromiseRejects(
@@ -73,6 +79,12 @@
   // Tests that an error is returned when the request to remove has an invalid
   // pattern.
   async function invalidPattern() {
+    // TODO(crbug.com/371432155): Port to desktop Android when chrome.tabs API
+    // is available.
+    if (/Android/.test(navigator.userAgent)) {
+      chrome.test.succeed();
+      return;
+    }
     let tab = await navigateTo('requested.com');
 
     const request = {tabId: tab.id, pattern: 'invalid pattern'};
diff --git "a/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/\043script.js" "b/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/\043script.js"
new file mode 100644
index 0000000..495a803
--- /dev/null
+++ "b/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/\043script.js"
@@ -0,0 +1,7 @@
+// 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.
+
+var div = document.createElement('div');
+div.id = 'hashtag_script';
+document.body.appendChild(div);
diff --git "a/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/\043script_2.js" "b/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/\043script_2.js"
new file mode 100644
index 0000000..565b185
--- /dev/null
+++ "b/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/\043script_2.js"
@@ -0,0 +1,7 @@
+// 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.
+
+var div = document.createElement('div');
+div.id = 'hashtag_script_2';
+document.body.appendChild(div);
diff --git a/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/%23script.js b/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/%23script.js
new file mode 100644
index 0000000..0fb311d4a
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/%23script.js
@@ -0,0 +1,7 @@
+// 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.
+
+var div = document.createElement('div');
+div.id = 'hashtag_script_escaped';
+document.body.appendChild(div);
diff --git a/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/manifest.json b/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/manifest.json
new file mode 100644
index 0000000..da220bd
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/manifest.json
@@ -0,0 +1,12 @@
+{
+  "name": "executeScript scripts with special characters",
+  "version": "1.0",
+  "manifest_version": 3,
+  "description": "ExecuteScript scripts that contain special characters are loaded correctly.",
+  "background": {
+    "service_worker": "worker.js",
+    "type": "module"
+  },
+  "permissions": ["scripting", "tabs"],
+  "host_permissions": ["*://example.com/*"]
+}
diff --git a/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/worker.js b/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/worker.js
new file mode 100644
index 0000000..3ec01fb
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/scripting/execute_script_special_characters/worker.js
@@ -0,0 +1,58 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {getInjectedElementIds, openTab} from '/_test_resources/test_util/tabs_util.js';
+
+chrome.test.runTests([
+  // Check that a script with special characters in its file name is loaded
+  // correctly. Specifically, make sure that the input file name doesn't go
+  // through a transformation such as URL encoding.
+  async function scriptWithSpecialCharacters() {
+    const config = await chrome.test.getConfig();
+    const url = `http://example.com:${config.testServer.port}/simple.html`;
+    let tab = await openTab(url);
+
+    await chrome.scripting.executeScript(
+        {target: {tabId: tab.id}, files: ['#script.js']});
+
+    chrome.test.assertEq(['hashtag_script'],
+        await getInjectedElementIds(tab.id));
+
+    chrome.test.succeed();
+  },
+
+  // Make sure that there's no special treatment for names that look like a
+  // URL-encoded string.
+  async function scriptWithEscapedSpecialCharacters() {
+    const config = await chrome.test.getConfig();
+    const url = `http://example.com:${config.testServer.port}/simple.html`;
+    let tab = await openTab(url);
+
+    await chrome.scripting.executeScript(
+        {target: {tabId: tab.id}, files: ['%23script.js']});
+
+    chrome.test.assertEq(['hashtag_script_escaped'],
+        await getInjectedElementIds(tab.id));
+
+    chrome.test.succeed();
+  },
+
+  // Check that given an escaped path for a script that does not exist,
+  // executeScript() returns an error, even if a file with the non-escaped name
+  // exists.
+  async function nonExistentScriptWithEscapedSpecialCharacters() {
+    const config = await chrome.test.getConfig();
+    const url = `http://example.com:${config.testServer.port}/simple.html`;
+    let tab = await openTab(url);
+
+    await chrome.test.assertPromiseRejects(
+        chrome.scripting.executeScript(
+            {target: {tabId: tab.id}, files: ['%23script_2.js']}),
+        `Error: Could not load file: '%23script_2.js'.`);
+
+    chrome.test.assertEq([], await getInjectedElementIds(tab.id));
+
+    chrome.test.succeed();
+  },
+]);
diff --git a/chrome/test/data/extensions/service_worker/mojo/manifest.json b/chrome/test/data/extensions/service_worker/mojo/manifest.json
new file mode 100644
index 0000000..51c919b
--- /dev/null
+++ b/chrome/test/data/extensions/service_worker/mojo/manifest.json
@@ -0,0 +1,12 @@
+{
+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA22ThlvdzIV2u5FrtEP9ab+EjrI/nMcrY9NPoBqIannUPKpB4RjcKWNwsjyiHC/n5x1FQ6xNQ2LT9PSgOqk6hn1tIYkdrKYKGzpWHDaIsRqtIDPj0htcV1QvXB1Ct3xzR54Dn1OtZceoxPxtTOOD4po8I8zfos59TSyZk3XKSBlP8/laxo33Lze9gadH2wF6u1crBtbNq4O1Wj6DB5o7YKwu/KjY8MUcL4Eks8cAfvR+GXEgCqS5l4x2nnfAYdu/YhzeVCYKbwqUB+trCeYdtc20zLExOm9ruwJs9moD6wmAOBtgiTBqFb7iN4EpbvoupD0FFB/FWU1MzlkZXm/x+eQIDAQAB",
+  "name": "Test component extension",
+  "version": "1.0",
+  "manifest_version": 3,
+  "background": {
+    "service_worker": "sw.js"
+  },
+  "permissions": [
+    "mojoPrivate"
+  ]
+}
diff --git a/chrome/test/data/extensions/service_worker/mojo/sw.js b/chrome/test/data/extensions/service_worker/mojo/sw.js
new file mode 100644
index 0000000..d7697b2
--- /dev/null
+++ b/chrome/test/data/extensions/service_worker/mojo/sw.js
@@ -0,0 +1,12 @@
+// 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.
+
+chrome.test.runTests([
+  function createMojoMessagePipe() {
+    let {handle0, handle1} = Mojo.createMessagePipe();
+    handle0.close();
+    handle1.close();
+    chrome.test.succeed();
+  }
+]);
diff --git a/chrome/test/data/select_parser_relaxation.html b/chrome/test/data/select_parser_relaxation.html
deleted file mode 100644
index 1ae8744..0000000
--- a/chrome/test/data/select_parser_relaxation.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-<select>
-  <div>
-    <option>option</option>
-  </div>
-</select>
-
-<style>
-select {
-  appearance: base-select;
-}
-</style>
-
-<script>
-  window.selectParserRelaxationEnabled = !!document.querySelector('div');
-  const style = getComputedStyle(document.querySelector('select'));
-  window.customizableSelectEnabled = style.appearance == 'base-select';
-</script>
diff --git a/chrome/test/data/webui/chromeos/nearby_share/shared/nearby_onboarding_one_page_test.js b/chrome/test/data/webui/chromeos/nearby_share/shared/nearby_onboarding_one_page_test.js
index 049737d..8fb9f57 100644
--- a/chrome/test/data/webui/chromeos/nearby_share/shared/nearby_onboarding_one_page_test.js
+++ b/chrome/test/data/webui/chromeos/nearby_share/shared/nearby_onboarding_one_page_test.js
@@ -60,7 +60,7 @@
     const buttonContent =
         element.shadowRoot.querySelector('#visibilityModeLabel')
             .textContent.trim();
-    assertEquals('All contacts', buttonContent);
+    assertEquals('Contacts', buttonContent);
   });
 
   test('Device name is focused', async () => {
diff --git a/chrome/test/data/webui/data_sharing/data_sharing_test.ts b/chrome/test/data/webui/data_sharing/data_sharing_test.ts
index 63c37a7..dae3613 100644
--- a/chrome/test/data/webui/data_sharing/data_sharing_test.ts
+++ b/chrome/test/data/webui/data_sharing/data_sharing_test.ts
@@ -225,8 +225,9 @@
     const arg = testDataSharingSdk.getArgs('runJoinFlow')[0];
     assertEquals('fake_group_id', arg.groupId);
     assertEquals('fake_token', arg.tokenSecret);
-    assertEquals(1, testBrowserProxy.getCallCount('closeUi'));
-    assertEquals(Code.OK, testBrowserProxy.getArgs('closeUi')[0]);
+    // Do not close UI if successfully joined because it's supposed to wait
+    // until the flow is complete.
+    assertEquals(0, testBrowserProxy.getCallCount('closeUi'));
   });
 
   test('Join flow error case', async () => {
diff --git a/chrome/test/data/webui/new_tab_footer/app_test.ts b/chrome/test/data/webui/new_tab_footer/app_test.ts
index 027f9572..fc3b8b8d 100644
--- a/chrome/test/data/webui/new_tab_footer/app_test.ts
+++ b/chrome/test/data/webui/new_tab_footer/app_test.ts
@@ -123,6 +123,7 @@
       const managementNotice: ManagementNotice = {
         text: 'Managed by your organization',
         bitmapDataUrl: {url: 'chrome://resources/images/chrome_logo_dark.svg'},
+        isCustomLogo: false,
       };
 
       // Act.
@@ -133,12 +134,12 @@
       const managementNoticeContainer =
           element.shadowRoot.querySelector('#managementNoticeContainer');
       assertTrue(!!managementNoticeContainer);
-      let managementNoticeText = managementNoticeContainer.querySelector('p');
+      let managementNoticeText = $$(element, '#managementNoticeContainer p');
       assertTrue(!!managementNoticeText);
       assertEquals(
           managementNoticeText.innerText, 'Managed by your organization');
       let managementNoticeLogo =
-          managementNoticeContainer.querySelector<HTMLImageElement>('img');
+          $$<HTMLImageElement>(element, '#managementNoticeLogo');
       assertTrue(!!managementNoticeLogo);
       assertEquals(
           managementNoticeLogo.src,
@@ -154,6 +155,39 @@
       assertFalse(!!managementNoticeText);
       assertFalse(!!managementNoticeLogo);
     });
+
+    test('Management notice logo style', async () => {
+      // Arrange.
+      const managementNoticeWithCustomLogo: ManagementNotice = {
+        text: 'Managed by your organization',
+        bitmapDataUrl: {url: 'chrome://resources/images/chrome_logo_dark.svg'},
+        isCustomLogo: true,
+      };
+
+      // Act.
+      callbackRouter.setManagementNotice(managementNoticeWithCustomLogo);
+      await callbackRouter.$.flushForTesting();
+
+      // Assert.
+      let logoContainter = $$(element, '#managementNoticeLogoContainer');
+      assertTrue(!!logoContainter);
+      assertTrue(logoContainter.classList.contains('custom_logo'));
+
+      const managementNoticeWithDefaultLogo: ManagementNotice = {
+        text: 'Managed by your organization',
+        bitmapDataUrl: {url: 'chrome://resources/images/chrome_logo_dark.svg'},
+        isCustomLogo: false,
+      };
+
+      // Act.
+      callbackRouter.setManagementNotice(managementNoticeWithDefaultLogo);
+      await callbackRouter.$.flushForTesting();
+
+      logoContainter = $$(element, '#managementNoticeLogoContainer');
+      assertTrue(!!logoContainter);
+      assertEquals(logoContainter.classList.length, 0);
+    });
+
   });
 
   suite('CustomizeChromeButton', () => {
diff --git a/chrome/test/data/webui/print_preview/model_test.ts b/chrome/test/data/webui/print_preview/model_test.ts
index 55c76b6..4b6034a 100644
--- a/chrome/test/data/webui/print_preview/model_test.ts
+++ b/chrome/test/data/webui/print_preview/model_test.ts
@@ -573,14 +573,21 @@
     assertEquals(true, model.getSettingValue('duplex'));
 
     // Set to a new destination with the same capabilities. Confirm that
-    // everything stays the same.
-    const oldSettings = JSON.stringify(model.observable.getTarget());
+    // everything stays the same, except for 'mediaSize' which is reverted back
+    // to the printer's default value.
+    const oldSettings: Record<string, any> =
+        structuredClone(model.observable.getTarget());
     model.destination = testDestination2;
     await microtasksFinished();
-    const newSettings = JSON.stringify(model.observable.getTarget());
+    const newSettings: Record<string, any> =
+        structuredClone(model.observable.getTarget());
 
-    // Should be the same (same printer capabilities).
-    assertEquals(oldSettings, newSettings);
+    // Should be the same (same printer capabilities), except for 'mediaSize'
+    // which is reset to the printer's default value.
+    assertEquals('NA_LETTER', model.getSettingValue('mediaSize').name);
+    delete oldSettings['mediaSize'];
+    delete newSettings['mediaSize'];
+    assertEquals(JSON.stringify(oldSettings), JSON.stringify(newSettings));
 
     // Create a printer with different capabilities.
     const testDestination3 =
diff --git a/chrome/test/data/webui/print_preview/restore_state_test.ts b/chrome/test/data/webui/print_preview/restore_state_test.ts
index 66020f3..0a76a1e 100644
--- a/chrome/test/data/webui/print_preview/restore_state_test.ts
+++ b/chrome/test/data/webui/print_preview/restore_state_test.ts
@@ -4,7 +4,7 @@
 
 import type {NativeInitialSettings, PrintPreviewAppElement, SerializedSettings, Settings, SettingsMixinInterface} from 'chrome://print/print_preview.js';
 import {getInstance, MarginsType, NativeLayerImpl, PluginProxyImpl, ScalingType} from 'chrome://print/print_preview.js';
-import {assertEquals} from 'chrome://webui-test/chai_assert.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import {NativeLayerStub} from './native_layer_stub.js';
@@ -293,6 +293,10 @@
       nativeLayer.whenCalled('getInitialSettings'),
       nativeLayer.whenCalled('getPrinterCapabilities'),
     ]);
+
+    await microtasksFinished();
+    assertTrue(getInstance().initialized());
+
     // Set all the settings sections.
     testData.forEach((testValue: TestCase, index: number) => {
       if (index === testData.length - 1) {
diff --git a/chromecast/app/android/cast_crash_reporter_client_android.cc b/chromecast/app/android/cast_crash_reporter_client_android.cc
index 14a3fcd..46e6f11 100644
--- a/chromecast/app/android/cast_crash_reporter_client_android.cc
+++ b/chromecast/app/android/cast_crash_reporter_client_android.cc
@@ -69,7 +69,7 @@
     return false;
   }
 
-  *crash_dir = app_data.Append("Crashpad");
+  *crash_dir = app_data.Append("CrashpadBrowser");
   return true;
 }
 
diff --git a/clank b/clank
index c96cd8b3..b59fa7e 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit c96cd8b39560ced5458b17bd824fac671f8622f3
+Subproject commit b59fa7eada24f104539a738e2e81ff70154b14e0
diff --git a/components/autofill/core/browser/data_model/autofill_ai/entity_instance.cc b/components/autofill/core/browser/data_model/autofill_ai/entity_instance.cc
index 04a1432..c48bf366 100644
--- a/components/autofill/core/browser/data_model/autofill_ai/entity_instance.cc
+++ b/components/autofill/core/browser/data_model/autofill_ai/entity_instance.cc
@@ -24,25 +24,21 @@
 
 namespace autofill {
 
-AttributeInstance::AttributeInstance(AttributeType type) : type_(type) {
-  switch (type.data_type()) {
-    case AttributeType::DataType::kName:
-      info_ = NameInfo();
-      break;
-    case AttributeType::DataType::kCountry:
-      info_ = CountryInfo();
-      break;
-    case AttributeType::DataType::kDate:
-      info_ = DateInfo();
-      break;
-    case AttributeType::DataType::kState:
-      info_ = StateInfo();
-      break;
-    case AttributeType::DataType::kString:
-      info_ = u"";
-      break;
-  }
-}
+AttributeInstance::AttributeInstance(AttributeType type)
+    : type_(type), info_([&]() -> InfoStructure {
+        switch (type.data_type()) {
+          case AttributeType::DataType::kName:
+            return NameInfo();
+          case AttributeType::DataType::kCountry:
+            return CountryInfo();
+          case AttributeType::DataType::kDate:
+            return DateInfo();
+          case AttributeType::DataType::kState:
+            return StateInfo();
+          case AttributeType::DataType::kString:
+            return u"";
+        }
+      }()) {}
 
 AttributeInstance::AttributeInstance(const AttributeInstance&) = default;
 AttributeInstance& AttributeInstance::operator=(const AttributeInstance&) =
@@ -142,14 +138,13 @@
   std::visit(
       absl::Overload{
           [&](CountryInfo& country) {
-            // We assume that the given `value` is either a valid
-            // country code or a valid country name localized to the
-            // provided `app_locale`.
+            // We assume that the given `value` is either a valid country code
+            // or a valid country name localized to the provided `app_locale`.
             if (!country.SetCountryFromCountryCode(value) &&
                 !country.SetCountryFromCountryName(value, app_locale)) {
-              // In case `value` turns out to be neither of the two
-              // options mentioned above, we reset the country value to
-              // indicate failure.
+              // In case `value` turns out to be neither of the two options
+              // mentioned above, we reset the country value to indicate
+              // failure.
               country = CountryInfo();
             }
           },
@@ -177,9 +172,8 @@
   std::visit(absl::Overload{
                  [&](CountryInfo& country) {
                    if (!country.SetCountryFromCountryCode(value)) {
-                     // In case `value` isn't a valid country
-                     // code, we reset the country value to
-                     // indicate failure.
+                     // In case `value` isn't a valid country code, we reset the
+                     // country value to indicate failure.
                      country = CountryInfo();
                    }
                  },
diff --git a/components/autofill/core/browser/data_model/autofill_ai/entity_instance.h b/components/autofill/core/browser/data_model/autofill_ai/entity_instance.h
index d8195ceb..9558912 100644
--- a/components/autofill/core/browser/data_model/autofill_ai/entity_instance.h
+++ b/components/autofill/core/browser/data_model/autofill_ai/entity_instance.h
@@ -92,15 +92,6 @@
 
   const AttributeType& type() const { return type_; }
 
-  // In the functions below, `type` refers to the type of data we want to fetch
-  // from the attribute, and not the type of the attribute itself. The two might
-  // coincide for unstructured types but they are different for structured
-  // types. See `GetNormalizedType()` below for more information about the
-  // correlation between the needed data type and the type of the attribute.
-  // Also note that `type` below is mostly interesting for structured attributes
-  // and is assumed to be just the attribute-type-equivalent field type for
-  // unstructured ones.
-
   // Returns a string that contains all information stored in this attribute
   // instance, formatted according to the given `app_locale`.
   //
@@ -109,8 +100,11 @@
     return GetInfo(type_.field_type(), app_locale, std::nullopt);
   }
 
-  // Returns the value stored in this attribute instance for a specific `type`,
-  // formatted according to a given `app_locale` and `format_string`.
+  // Returns the value stored in this attribute instance.
+  //
+  // The `field_type` may be any of `type().field_subtypes()`; otherwise we fall
+  // back to `type().field_type()`. That is, the `field_type` only matters for
+  // name attributes.
   //
   // Currently, the `format_string` only matters for dates. If it is empty, it
   // defaults to u"YYYY-MM-DD". See AutofillField::format_string() for the
@@ -129,16 +123,22 @@
 
   // Same as `GetInfo` but returns the value as stored with no formatting
   // whatsoever.
+  //
+  // See GetInfo() for the meaning of `field_type`.
   std::u16string GetRawInfo(GetRawInfoPassKey pass_key,
                             FieldType field_type) const;
 
   // Returns the verification status of a value stored in this attribute
   // instance for a specific `type`.
+  //
+  // See GetInfo() for the meaning of `field_type`.
   VerificationStatus GetVerificationStatus(FieldType field_type) const;
 
   // Populates the attribute with a value for a specific `type`, according to a
   // given `app_locale`.
   //
+  // See GetInfo() for the meaning of `field_type`.
+  //
   // Currently, the `format_string` only matters for dates. Dates are updated
   // incrementally, e.g., SetInfo(..., u"16", ..., u"DD", ...) only changes the
   // day and does not reset the month or year. If `value` doesn't fully match
@@ -151,10 +151,9 @@
                std::u16string_view format_string,
                VerificationStatus status);
 
-  // Same as `SetInfoWithVerificationStatus`, but for structured types this
-  // function does nothing but modify the information in `type`, while the other
-  // function might perform additional steps (e.g., name formatting). This
-  // function should only be used by database logic and settings page logic.
+  // Similar to SetInfo() but without canonicalization: It does not accept
+  // country names and does not format names. This function should only be used
+  // by database logic and settings page logic.
   // TODO(crbug.com/389625753): Investigate merging SetInfo* and SetRawInfo*.
   void SetRawInfo(FieldType field_type,
                   const std::u16string& value,
@@ -171,9 +170,6 @@
                          const AttributeInstance& rhs) = default;
 
  private:
-  // This function checks that `info_type` is supported by the attribute and
-  // otherwise tries to convert it into one that is. Returns the supported type
-  // if found and UNKNOWN_TYPE otherwise.
   FieldType GetNormalizedFieldType(FieldType field_type) const;
 
   AttributeType type_;
diff --git a/components/autofill/core/browser/form_import/form_data_importer.cc b/components/autofill/core/browser/form_import/form_data_importer.cc
index 72a27d6..44b06df 100644
--- a/components/autofill/core/browser/form_import/form_data_importer.cc
+++ b/components/autofill/core/browser/form_import/form_data_importer.cc
@@ -181,32 +181,6 @@
   return false;
 }
 
-// `extracted_credit_card` refers to the credit card that was most recently
-// submitted and |fetched_card_instrument_id| refers to the instrument id of the
-// most recently downstreamed (fetched from the server) credit card.
-// These need to match to offer virtual card enrollment for the
-// `extracted_credit_card`.
-bool ShouldOfferVirtualCardEnrollment(
-    const std::optional<CreditCard>& extracted_credit_card,
-    std::optional<int64_t> fetched_card_instrument_id) {
-  if (!extracted_credit_card) {
-    return false;
-  }
-
-  if (extracted_credit_card->virtual_card_enrollment_state() !=
-      CreditCard::VirtualCardEnrollmentState::kUnenrolledAndEligible) {
-    return false;
-  }
-
-  if (!fetched_card_instrument_id.has_value() ||
-      extracted_credit_card->instrument_id() !=
-          fetched_card_instrument_id.value()) {
-    return false;
-  }
-
-  return true;
-}
-
 bool HasSynthesizedTypes(
     const base::flat_map<FieldType, std::u16string>& observed_field_values,
     AddressCountryCode country_code) {
@@ -309,6 +283,7 @@
         credit_card_upload_enabled, ukm_source_id);
   }
   fetched_card_instrument_id_.reset();
+  card_was_fetched_from_cache_.reset();
 
   bool iban_prompt_potentially_shown = false;
   if (extracted_data.extracted_iban.has_value() &&
@@ -851,6 +826,11 @@
     return true;
   }
 
+  // All of following processing requires the extracted credit card to exist.
+  if (!extracted_credit_card.has_value()) {
+    return false;
+  }
+
   // If a virtual card was extracted from the form, return as we do not do
   // anything with virtual cards beyond this point.
   if (credit_card_import_type_ == CreditCardImportType::kVirtualCard) {
@@ -863,21 +843,21 @@
     return false;
   }
 
-  if (client_->GetPaymentsAutofillClient()->GetVirtualCardEnrollmentManager() &&
-      ShouldOfferVirtualCardEnrollment(extracted_credit_card,
-                                       fetched_card_instrument_id_)) {
-    client_->GetPaymentsAutofillClient()
-        ->GetVirtualCardEnrollmentManager()
-        ->InitVirtualCardEnroll(*extracted_credit_card,
-                                VirtualCardEnrollmentSource::kDownstream);
+  auto* virtual_card_enrollment_manager =
+      client_->GetPaymentsAutofillClient()->GetVirtualCardEnrollmentManager();
+  if (virtual_card_enrollment_manager &&
+      virtual_card_enrollment_manager->ShouldOfferVirtualCardEnrollment(
+          *extracted_credit_card, fetched_card_instrument_id_,
+          card_was_fetched_from_cache_)) {
+    virtual_card_enrollment_manager->InitVirtualCardEnroll(
+        *extracted_credit_card, VirtualCardEnrollmentSource::kDownstream);
     return true;
   }
 
   // Proceed with card or CVC saving if applicable.
-  return extracted_credit_card &&
-         credit_card_save_manager_->ProceedWithSavingIfApplicable(
-             submitted_form, *extracted_credit_card, credit_card_import_type_,
-             is_credit_card_upstream_enabled, ukm_source_id);
+  return credit_card_save_manager_->ProceedWithSavingIfApplicable(
+      submitted_form, *extracted_credit_card, credit_card_import_type_,
+      is_credit_card_upstream_enabled, ukm_source_id);
 }
 
 bool FormDataImporter::ProcessIbanImportCandidate(Iban& extracted_iban) {
diff --git a/components/autofill/core/browser/form_import/form_data_importer.h b/components/autofill/core/browser/form_import/form_data_importer.h
index 366fe8c2..5e32ea7 100644
--- a/components/autofill/core/browser/form_import/form_data_importer.h
+++ b/components/autofill/core/browser/form_import/form_data_importer.h
@@ -133,6 +133,10 @@
       std::optional<NonInteractivePaymentMethodType>
           payment_method_type_if_non_interactive_authentication_flow_completed);
 
+  void set_card_was_fetched_from_cache(bool card_was_fetched_from_cache) {
+    card_was_fetched_from_cache_ = card_was_fetched_from_cache;
+  }
+
  private:
   // Defines an extracted address profile, which is a candidate for address
   // profile import.
@@ -347,6 +351,13 @@
   // optional and is set when an Autofill Downstream has happened.
   std::optional<int64_t> fetched_card_instrument_id_;
 
+  // TODO(crbug.com/403617982): Combine all last fetched card related
+  // information into a struct.
+  // Whether the last unmasked card (note: it may or may not be the extracted
+  // card) is fetched from the local cache (instead of going through a server
+  // retrieval process).
+  std::optional<bool> card_was_fetched_from_cache_;
+
   friend class FormDataImporterTestApi;
 };
 
diff --git a/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.cc b/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.cc
index 78a912d..bdf9b4af 100644
--- a/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.cc
+++ b/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.cc
@@ -148,6 +148,11 @@
       "Autofill.VirtualCardEnrollBubble.LatencySinceUpstream", latency);
 }
 
+void LogVirtualCardEnrollBubbleLatencySinceDownstream(base::TimeDelta latency) {
+  base::UmaHistogramTimes(
+      "Autofill.VirtualCardEnrollBubble.LatencySinceDownstream", latency);
+}
+
 void LogVirtualCardEnrollmentNotOfferedDueToMaxStrikes(
     VirtualCardEnrollmentSource source) {
   base::UmaHistogramEnumeration(
diff --git a/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h b/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h
index d7856e95..60b94e7 100644
--- a/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h
+++ b/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h
@@ -138,10 +138,12 @@
     bool card_art_available,
     VirtualCardEnrollmentSource source);
 
-// Latency Since Upstream metrics. Used to determine the time that it takes for
-// the server calls that need to be made between Save Card Bubble accept and
-// when the Virtual Card Enroll Bubble is shown.
+// Latency Since Upstream / Downstream metrics. Used to determine the time that
+// it takes for the server calls that need to be made between Save Card Bubble
+// accept / card extraction from form and when the Virtual Card Enroll
+// Bubble is shown.
 void LogVirtualCardEnrollBubbleLatencySinceUpstream(base::TimeDelta latency);
+void LogVirtualCardEnrollBubbleLatencySinceDownstream(base::TimeDelta latency);
 
 // Logs the reason from strikedatabase perspective why virtual card enrollment
 // is not offered.
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc
index 8cba3ee3..b325bf0 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc
@@ -273,6 +273,8 @@
     autofill_metrics::LogCardInfoRetrievalEnrolledUnmaskResult(
         CardInfoRetrievalEnrolledUnmaskResult::kAuthenticationUnmasked);
   }
+  autofill_client().GetFormDataImporter()->set_card_was_fetched_from_cache(
+      false);
   std::move(on_credit_card_fetched_callback_).Run(*card_);
 }
 
@@ -362,6 +364,11 @@
         ServerCardUnmaskResult::kLocalCacheHit, record_type,
         ServerCardUnmaskFlowType::kUnspecified);
 
+    // If the card is fetched from the in-memory cache, notify the
+    // FormDataImporter.
+    autofill_client().GetFormDataImporter()->set_card_was_fetched_from_cache(
+        true);
+
     Reset();
     return;
   }
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
index cc382f16..8e1c4d7 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
@@ -22,7 +22,6 @@
 #include "components/autofill/core/browser/strike_databases/payments/virtual_card_enrollment_strike_database.h"
 #include "components/autofill/core/browser/strike_databases/strike_database.h"
 #include "components/autofill/core/browser/strike_databases/strike_database_base.h"
-#include "components/autofill/core/common/autofill_clock.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image.h"
@@ -71,6 +70,31 @@
 
 VirtualCardEnrollmentManager::~VirtualCardEnrollmentManager() = default;
 
+bool VirtualCardEnrollmentManager::ShouldOfferVirtualCardEnrollment(
+    const CreditCard& credit_card,
+    std::optional<int64_t> fetched_card_instrument_id,
+    std::optional<bool> card_unmasked_from_cache) {
+  if (credit_card.virtual_card_enrollment_state() !=
+      CreditCard::VirtualCardEnrollmentState::kUnenrolledAndEligible) {
+    return false;
+  }
+
+  if (credit_card.instrument_id() != fetched_card_instrument_id.value()) {
+    return false;
+  }
+
+  // If card is eligible for virtual card enrollment and card is retrieved from
+  // the server, set the timestamp for card extraction of the eligible unmasked
+  // card. This will be used to measure the user perceived latency for virtual
+  // card downstream enrollment.
+  if (card_unmasked_from_cache.has_value() &&
+      !card_unmasked_from_cache.value()) {
+    server_retrieved_eligible_card_extraction_timestamp_ = base::Time::Now();
+  }
+
+  return true;
+}
+
 void VirtualCardEnrollmentManager::InitVirtualCardEnroll(
     const CreditCard& credit_card,
     VirtualCardEnrollmentSource virtual_card_enrollment_source,
@@ -135,6 +159,10 @@
   LogUpdateVirtualCardEnrollmentRequestAttempt(
       state_.virtual_card_enrollment_fields.virtual_card_enrollment_source,
       VirtualCardEnrollmentRequestType::kEnroll);
+
+  RemoveAllStrikesToBlockOfferingVirtualCardEnrollment(base::NumberToString(
+      state_.virtual_card_enrollment_fields.credit_card.instrument_id()));
+
   payments::UpdateVirtualCardEnrollmentRequestDetails request_details;
   request_details.virtual_card_enrollment_source =
       state_.virtual_card_enrollment_fields.virtual_card_enrollment_source;
@@ -169,9 +197,6 @@
                        weak_ptr_factory_.GetWeakPtr(),
                        VirtualCardEnrollmentRequestType::kEnroll));
   }
-
-  RemoveAllStrikesToBlockOfferingVirtualCardEnrollment(base::NumberToString(
-      state_.virtual_card_enrollment_fields.credit_card.instrument_id()));
 }
 
 void VirtualCardEnrollmentManager::Unenroll(
@@ -340,6 +365,7 @@
   state_ = VirtualCardEnrollmentProcessState();
   enroll_response_details_received_ = false;
   virtual_card_enrollment_update_response_callback_.reset();
+  server_retrieved_eligible_card_extraction_timestamp_.reset();
 }
 
 VirtualCardEnrollmentStrikeDatabase*
@@ -369,8 +395,16 @@
           VirtualCardEnrollmentSource::kUpstream &&
       save_card_bubble_accepted_timestamp_.has_value()) {
     LogVirtualCardEnrollBubbleLatencySinceUpstream(
-        AutofillClock::Now() - save_card_bubble_accepted_timestamp_.value());
+        base::Time::Now() - save_card_bubble_accepted_timestamp_.value());
     save_card_bubble_accepted_timestamp_.reset();
+  } else if (state_.virtual_card_enrollment_fields
+                     .virtual_card_enrollment_source ==
+                 VirtualCardEnrollmentSource::kDownstream &&
+             server_retrieved_eligible_card_extraction_timestamp_.has_value()) {
+    LogVirtualCardEnrollBubbleLatencySinceDownstream(
+        base::Time::Now() -
+        server_retrieved_eligible_card_extraction_timestamp_.value());
+    server_retrieved_eligible_card_extraction_timestamp_.reset();
   }
 
   // Check in StrikeDatabase whether enrollment has been offered for this card
@@ -447,7 +481,7 @@
   request_details.source =
       state_.virtual_card_enrollment_fields.virtual_card_enrollment_source;
 
-  get_details_for_enrollment_request_sent_timestamp_ = AutofillClock::Now();
+  get_details_for_enrollment_request_sent_timestamp_ = base::Time::Now();
 
   if (base::FeatureList::IsEnabled(
           features::
@@ -477,7 +511,7 @@
     LogGetDetailsForEnrollmentRequestLatency(
         state_.virtual_card_enrollment_fields.virtual_card_enrollment_source,
         result,
-        AutofillClock::Now() -
+        base::Time::Now() -
             get_details_for_enrollment_request_sent_timestamp_.value());
     get_details_for_enrollment_request_sent_timestamp_.reset();
   }
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
index 8ea3c89..3cd6440 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
@@ -119,6 +119,16 @@
   using VirtualCardEnrollmentUpdateResponseCallback = base::OnceCallback<void(
       payments::PaymentsAutofillClient::PaymentsRpcResult result)>;
 
+  // `fetched_card_instrument_id` refers to the instrument id of the
+  // most recently unmasked credit card. It should match `credit_card` to offer
+  // virtual card enrollment. `card_unmasked_from_cache` indicates whether the
+  // most recently unmasked card is retrieved from in-memory cache (or from the
+  // payments server).
+  bool ShouldOfferVirtualCardEnrollment(
+      const CreditCard& credit_card,
+      std::optional<int64_t> fetched_card_instrument_id,
+      std::optional<bool> card_unmasked_from_cache);
+
   // Starting point for the VCN enroll flow. The fields in |credit_card| will
   // be used throughout the flow, such as for request fields as well as credit
   // card specific fields for the bubble to display.
@@ -373,6 +383,14 @@
   // metric. |save_card_bubble_accepted_timestamp_| will then be reset.
   std::optional<base::Time> save_card_bubble_accepted_timestamp_;
 
+  // Used to track the latency metrics between credit card extraction from form
+  // and VirtualCardEnrollBubble show. Applicable only for masked server cards
+  // retrieved from the Payments server, and not for those retrieved from the
+  // local in-memory cache. Only set if the card is eligible to be enrolled in
+  // virtual card feature.
+  std::optional<base::Time>
+      server_retrieved_eligible_card_extraction_timestamp_;
+
   // The timestamp when a GetDetailsForEnrollment request is sent.
   std::optional<base::Time> get_details_for_enrollment_request_sent_timestamp_;
 
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
index e8b3a5d..347b04b 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
@@ -5,6 +5,7 @@
 #include <string>
 
 #include "base/functional/callback.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
@@ -910,4 +911,32 @@
       1);
 }
 
+class DownstreamLatencyMetricsTest
+    : public VirtualCardEnrollmentManagerTest,
+      public ::testing::WithParamInterface<bool> {
+ public:
+  bool card_unmasked_from_cache() const { return GetParam(); }
+};
+
+INSTANTIATE_TEST_SUITE_P(VirtualCardEnrollmentManagerTest,
+                         DownstreamLatencyMetricsTest,
+                         ::testing::Bool());
+
+TEST_P(DownstreamLatencyMetricsTest, LatencySinceDownstream) {
+  base::HistogramTester histogram_tester;
+  CreditCard card = test::GetMaskedServerCard();
+  card.set_virtual_card_enrollment_state(
+      CreditCard::VirtualCardEnrollmentState::kUnenrolledAndEligible);
+  virtual_card_enrollment_manager_->ShouldOfferVirtualCardEnrollment(
+      card, card.instrument_id(), card_unmasked_from_cache());
+  virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState()
+      ->virtual_card_enrollment_fields.virtual_card_enrollment_source =
+      VirtualCardEnrollmentSource::kDownstream;
+  task_environment_.FastForwardBy(base::Minutes(1));
+  virtual_card_enrollment_manager_->ShowVirtualCardEnrollBubble();
+  histogram_tester.ExpectTimeBucketCount(
+      "Autofill.VirtualCardEnrollBubble.LatencySinceDownstream",
+      base::Minutes(1), card_unmasked_from_cache() ? 0 : 1);
+}
+
 }  // namespace autofill
diff --git a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsTest.java b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsTest.java
index 84c9e9e9..1f82a43 100644
--- a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsTest.java
+++ b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsTest.java
@@ -484,7 +484,7 @@
     // Helper methods.
 
     private static final BaseMatcher<View> sDisabled =
-            new BaseMatcher<View>() {
+            new BaseMatcher<>() {
                 @Override
                 public boolean matches(Object o) {
                     return !((ChromeImageButton) o).isEnabled();
diff --git a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomProperties.java b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomProperties.java
index 086a50b..c5435e0f 100644
--- a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomProperties.java
+++ b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomProperties.java
@@ -35,7 +35,7 @@
     static final WritableIntPropertyKey CURRENT_SEEK_VALUE = new WritableIntPropertyKey();
 
     static final WritableObjectPropertyKey<Double> DEFAULT_ZOOM_FACTOR =
-            new WritableObjectPropertyKey<Double>();
+            new WritableObjectPropertyKey<>();
 
     static final PropertyKey[] ALL_KEYS = {
         DECREASE_ZOOM_CALLBACK,
diff --git a/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactDetails.java b/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactDetails.java
index 111b7c6..ac1b2f5 100644
--- a/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactDetails.java
+++ b/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactDetails.java
@@ -77,9 +77,9 @@
             @Nullable List<String> phoneNumbers,
             @Nullable List<PaymentAddress> addresses) {
         mDisplayName = displayName != null ? displayName : "";
-        mEmails = emails != null ? emails : new ArrayList<String>();
-        mPhoneNumbers = phoneNumbers != null ? phoneNumbers : new ArrayList<String>();
-        mAddresses = addresses != null ? addresses : new ArrayList<PaymentAddress>();
+        mEmails = emails != null ? emails : new ArrayList<>();
+        mPhoneNumbers = phoneNumbers != null ? phoneNumbers : new ArrayList<>();
+        mAddresses = addresses != null ? addresses : new ArrayList<>();
         mIcons = new ArrayList<>();
 
         mId = id;
diff --git a/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactsFetcherWorkerTask.java b/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactsFetcherWorkerTask.java
index ddd90c4..5bfd52f 100644
--- a/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactsFetcherWorkerTask.java
+++ b/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactsFetcherWorkerTask.java
@@ -114,7 +114,7 @@
         Map<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();
 
         Cursor cursor = mContentResolver.query(source, null, null, null, sortOrder);
-        ArrayList<String> list = new ArrayList<String>();
+        ArrayList<String> list = new ArrayList<>();
         String key = "";
         String value;
         assumeNonNull(cursor);
@@ -130,7 +130,7 @@
                     list.add(value);
                 } else {
                     map.put(key, list);
-                    list = new ArrayList<String>();
+                    list = new ArrayList<>();
                     list.add(value);
                     key = id;
                 }
@@ -277,10 +277,10 @@
         assumeNonNull(cursor);
         if (!cursor.moveToFirst()) {
             cursor.close();
-            return new ArrayList<ContactDetails>();
+            return new ArrayList<>();
         }
 
-        ArrayList<ContactDetails> contacts = new ArrayList<ContactDetails>(cursor.getCount());
+        ArrayList<ContactDetails> contacts = new ArrayList<>(cursor.getCount());
         do {
             String id =
                     cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID));
diff --git a/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactsPickerDialogTest.java b/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactsPickerDialogTest.java
index 57179b1..5dc76009 100644
--- a/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactsPickerDialogTest.java
+++ b/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactsPickerDialogTest.java
@@ -56,7 +56,6 @@
 import org.chromium.ui.test.util.BlankUiTestActivity;
 import org.chromium.ui.test.util.RenderTestRule;
 
-import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -423,7 +422,7 @@
      * @param ownerEmail If not null, includes a few contact entries representing owners.
      */
     private void setTestContacts(String ownerEmail) {
-        mTestContacts = new ArrayList<ContactDetails>();
+        mTestContacts = new ArrayList<>();
         PaymentAddress address = new PaymentAddress();
         address.city = "city";
         address.country = "country";
@@ -699,7 +698,7 @@
 
         Assert.assertEquals(ContactsPickerAction.CONTACTS_SELECTED, mLastActionRecorded);
         Assert.assertEquals(1, mLastSelectedContacts.size());
-        Assert.assertEquals(new ArrayList<String>(), mLastSelectedContacts.get(0).names);
+        Assert.assertEquals(new ArrayList<>(), mLastSelectedContacts.get(0).names);
         Assert.assertEquals(
                 mTestContacts.get(0).getEmails().get(0),
                 mLastSelectedContacts.get(0).emails.get(0));
@@ -738,7 +737,7 @@
         Assert.assertEquals(1, mLastSelectedContacts.size());
         Assert.assertEquals(
                 mTestContacts.get(0).getDisplayName(), mLastSelectedContacts.get(0).names.get(0));
-        Assert.assertEquals(new ArrayList<String>(), mLastSelectedContacts.get(0).emails);
+        Assert.assertEquals(new ArrayList<>(), mLastSelectedContacts.get(0).emails);
         Assert.assertEquals(16, mLastPercentageShared);
         Assert.assertEquals(31, mLastPropertiesRequested);
         Assert.assertEquals(ContactsPickerProperties.PROPERTIES_EMAILS, mLastPropertiesRejected);
@@ -761,7 +760,7 @@
         Assert.assertEquals(1, mLastSelectedContacts.size());
         Assert.assertEquals(
                 mTestContacts.get(0).getDisplayName(), mLastSelectedContacts.get(0).names.get(0));
-        Assert.assertEquals(new ArrayList<String>(), mLastSelectedContacts.get(0).tel);
+        Assert.assertEquals(new ArrayList<>(), mLastSelectedContacts.get(0).tel);
         Assert.assertEquals(16, mLastPercentageShared);
         Assert.assertEquals(31, mLastPropertiesRequested);
         Assert.assertEquals(ContactsPickerProperties.PROPERTIES_TELS, mLastPropertiesRejected);
@@ -784,8 +783,7 @@
         Assert.assertEquals(1, mLastSelectedContacts.size());
         Assert.assertEquals(
                 mTestContacts.get(0).getDisplayName(), mLastSelectedContacts.get(0).names.get(0));
-        Assert.assertEquals(
-                new ArrayList<ByteBuffer>(), mLastSelectedContacts.get(0).serializedAddresses);
+        Assert.assertEquals(new ArrayList<>(), mLastSelectedContacts.get(0).serializedAddresses);
         Assert.assertEquals(16, mLastPercentageShared);
         Assert.assertEquals(31, mLastPropertiesRequested);
         Assert.assertEquals(ContactsPickerProperties.PROPERTIES_ADDRESSES, mLastPropertiesRejected);
@@ -808,8 +806,7 @@
         Assert.assertEquals(1, mLastSelectedContacts.size());
         Assert.assertEquals(
                 mTestContacts.get(0).getDisplayName(), mLastSelectedContacts.get(0).names.get(0));
-        Assert.assertEquals(
-                new ArrayList<ByteBuffer>(), mLastSelectedContacts.get(0).serializedIcons);
+        Assert.assertEquals(new ArrayList<>(), mLastSelectedContacts.get(0).serializedIcons);
         Assert.assertEquals(16, mLastPercentageShared);
         Assert.assertEquals(31, mLastPropertiesRequested);
         Assert.assertEquals(ContactsPickerProperties.PROPERTIES_ICONS, mLastPropertiesRejected);
@@ -971,7 +968,7 @@
     @Test
     @LargeTest
     public void testEmptyContactListCrash() throws Throwable {
-        PickerAdapter.setTestContactsAndOwner(new ArrayList<ContactDetails>(), null);
+        PickerAdapter.setTestContactsAndOwner(new ArrayList<>(), null);
 
         createDialog(/* multiselect= */ true);
         Assert.assertTrue(mDialog.isShowing());
diff --git a/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/PickerAdapter.java b/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/PickerAdapter.java
index 15206112..8c48da0a 100644
--- a/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/PickerAdapter.java
+++ b/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/PickerAdapter.java
@@ -195,7 +195,7 @@
             mSearchResults.clear();
             mSearchResults = null;
         } else {
-            mSearchResults = new ArrayList<Integer>();
+            mSearchResults = new ArrayList<>();
             Integer count = 0;
             String query_lower = query.toLowerCase(Locale.getDefault());
             assumeNonNull(mContactDetails);
@@ -425,7 +425,7 @@
             return true;
         }
 
-        ArrayList<Integer> matches = new ArrayList<Integer>();
+        ArrayList<Integer> matches = new ArrayList<>();
         for (int i = 0; i < contacts.size(); ++i) {
             List<String> emails = contacts.get(i).getEmails();
             for (int y = 0; y < emails.size(); ++y) {
diff --git a/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/PickerCategoryView.java b/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/PickerCategoryView.java
index c50f93a..143da62a6 100644
--- a/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/PickerCategoryView.java
+++ b/components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/PickerCategoryView.java
@@ -168,7 +168,7 @@
         mSiteWantsAddresses = shouldIncludeAddresses;
         mSiteWantsIcons = shouldIncludeIcons;
 
-        mSelectionDelegate = new SelectionDelegate<ContactDetails>();
+        mSelectionDelegate = new SelectionDelegate<>();
         if (!multiSelectionAllowed) mSelectionDelegate.setSingleSelectionMode();
         mSelectionDelegate.addObserver(this);
 
@@ -275,7 +275,7 @@
 
         // Showing the search clears current selection. Save it, so we can restore it after the
         // search has completed.
-        mPreviousSelection = new HashSet<ContactDetails>(mSelectionDelegate.getSelectedItems());
+        mPreviousSelection = new HashSet<>(mSelectionDelegate.getSelectedItems());
         mSearchButton.setVisibility(GONE);
         mPickerAdapter.setSearchMode(true);
         mToolbar.showSearchView(true);
@@ -343,8 +343,7 @@
     public void onSelectAllToggled(boolean allSelected) {
         if (allSelected) {
             mPreviousSelection = mSelectionDelegate.getSelectedItems();
-            mSelectionDelegate.setSelectedItems(
-                    new HashSet<ContactDetails>(mPickerAdapter.getAllContacts()));
+            mSelectionDelegate.setSelectedItems(new HashSet<>(mPickerAdapter.getAllContacts()));
             mListener.onContactsPickerUserAction(
                     ContactsPickerListener.ContactsPickerAction.SELECT_ALL,
                     /* contacts= */ null,
@@ -352,7 +351,7 @@
                     /* propertiesSiteRequested= */ 0,
                     /* propertiesUserRejected= */ 0);
         } else {
-            mSelectionDelegate.setSelectedItems(new HashSet<ContactDetails>());
+            mSelectionDelegate.setSelectedItems(new HashSet<>());
             mPreviousSelection = null;
             mListener.onContactsPickerUserAction(
                     ContactsPickerListener.ContactsPickerAction.UNDO_SELECT_ALL,
@@ -445,7 +444,7 @@
 
         if (!isEnabled) {
             // The user doesn't want to share this property, so return an empty array.
-            return new ArrayList<T>();
+            return new ArrayList<>();
         }
 
         // Share whatever was selected.
diff --git a/components/browser_ui/device_lock/android/java/src/org/chromium/components/browser_ui/device_lock/DeviceLockActivityLauncherSupplier.java b/components/browser_ui/device_lock/android/java/src/org/chromium/components/browser_ui/device_lock/DeviceLockActivityLauncherSupplier.java
index f39294e..95f68d83 100644
--- a/components/browser_ui/device_lock/android/java/src/org/chromium/components/browser_ui/device_lock/DeviceLockActivityLauncherSupplier.java
+++ b/components/browser_ui/device_lock/android/java/src/org/chromium/components/browser_ui/device_lock/DeviceLockActivityLauncherSupplier.java
@@ -19,8 +19,7 @@
 public class DeviceLockActivityLauncherSupplier
         extends UnownedUserDataSupplier<DeviceLockActivityLauncher> {
     private static final UnownedUserDataKey<DeviceLockActivityLauncherSupplier> KEY =
-            new UnownedUserDataKey<DeviceLockActivityLauncherSupplier>(
-                    DeviceLockActivityLauncherSupplier.class);
+            new UnownedUserDataKey<>(DeviceLockActivityLauncherSupplier.class);
 
     /**
      * Return {@link DeviceLockActivityLauncher} supplier associated with the given {@link
diff --git a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageManagerTest.java b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageManagerTest.java
index d6bca5a..0c87831 100644
--- a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageManagerTest.java
+++ b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaImageManagerTest.java
@@ -66,15 +66,15 @@
         mMediaImageManager = new MediaImageManager(MIN_IMAGE_SIZE_PX, IDEAL_IMAGE_SIZE_PX);
         mMediaImageManager.setWebContents(mWebContents);
 
-        mImages = new ArrayList<MediaImage>();
-        mImages.add(new MediaImage(IMAGE_URL_1, "", new ArrayList<Rect>()));
+        mImages = new ArrayList<>();
+        mImages.add(new MediaImage(IMAGE_URL_1, "", new ArrayList<>()));
 
-        mBitmaps = new ArrayList<Bitmap>();
+        mBitmaps = new ArrayList<>();
         mBitmaps.add(
                 Bitmap.createBitmap(
                         IDEAL_IMAGE_SIZE_PX, IDEAL_IMAGE_SIZE_PX, Bitmap.Config.ARGB_8888));
 
-        mOriginalImageSizes = new ArrayList<Rect>();
+        mOriginalImageSizes = new ArrayList<>();
         mOriginalImageSizes.add(new Rect(0, 0, IDEAL_IMAGE_SIZE_PX, IDEAL_IMAGE_SIZE_PX));
     }
 
@@ -162,7 +162,7 @@
                 .downloadImage(
                         any(), anyBoolean(), anyInt(), anyBoolean(), any(MediaImageManager.class));
         mImages.clear();
-        mImages.add(new MediaImage(IMAGE_URL_2, "", new ArrayList<Rect>()));
+        mImages.add(new MediaImage(IMAGE_URL_2, "", new ArrayList<>()));
 
         mMediaImageManager.downloadImage(mImages, mCallback);
         mMediaImageManager.onFinishDownloadImage(
@@ -197,7 +197,7 @@
                 .downloadImage(
                         any(), anyBoolean(), anyInt(), anyBoolean(), any(MediaImageManager.class));
         mImages.clear();
-        mImages.add(new MediaImage(IMAGE_URL_2, "", new ArrayList<Rect>()));
+        mImages.add(new MediaImage(IMAGE_URL_2, "", new ArrayList<>()));
 
         mMediaImageManager.downloadImage(mImages, mCallback);
 
@@ -252,7 +252,7 @@
     @Test
     public void testTinyImagesRemovedBeforeDownloading() {
         mImages.clear();
-        ArrayList<Rect> sizes = new ArrayList<Rect>();
+        ArrayList<Rect> sizes = new ArrayList<>();
         sizes.add(new Rect(0, 0, TINY_IMAGE_SIZE_PX, TINY_IMAGE_SIZE_PX));
         mImages.add(new MediaImage(IMAGE_URL_1, "", sizes));
         mMediaImageManager.downloadImage(mImages, mCallback);
@@ -287,7 +287,7 @@
     public void testDownloadImageFails() {
         mMediaImageManager.downloadImage(mImages, mCallback);
         mMediaImageManager.onFinishDownloadImage(
-                REQUEST_ID_1, 404, IMAGE_URL_1, new ArrayList<Bitmap>(), new ArrayList<Rect>());
+                REQUEST_ID_1, 404, IMAGE_URL_1, new ArrayList<>(), new ArrayList<Rect>());
 
         verify(mCallback).onImageDownloaded((Bitmap) isNull());
         verify(mCallback, times(0)).onImageDownloaded((Bitmap) isNotNull());
diff --git a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationManager.java b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationManager.java
index fc2cbd9..c77fb037 100644
--- a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationManager.java
+++ b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaNotificationManager.java
@@ -20,7 +20,7 @@
     private static final SparseArray<MediaNotificationController> sControllers;
 
     static {
-        sControllers = new SparseArray<MediaNotificationController>();
+        sControllers = new SparseArray<>();
     }
 
     private MediaNotificationManager() {}
diff --git a/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java b/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java
index b21438d..e613bfb5 100644
--- a/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java
+++ b/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java
@@ -940,7 +940,7 @@
     }
 
     private static Matcher<View> touchFilterEnabled() {
-        return new TypeSafeMatcher<View>() {
+        return new TypeSafeMatcher<>() {
             @Override
             public void describeTo(Description description) {
                 description.appendText("Touch filtering enabled");
diff --git a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/MockNotificationManagerProxy.java b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/MockNotificationManagerProxy.java
index 33e26bb..ee550d1e 100644
--- a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/MockNotificationManagerProxy.java
+++ b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/MockNotificationManagerProxy.java
@@ -147,7 +147,7 @@
 
     @Override
     public List<NotificationChannel> getNotificationChannels() {
-        return new ArrayList<NotificationChannel>(mChannels.values());
+        return new ArrayList<>(mChannels.values());
     }
 
     @Override
diff --git a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ThrottlingNotificationScheduler.java b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ThrottlingNotificationScheduler.java
index 25afc305..f801b9d 100644
--- a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ThrottlingNotificationScheduler.java
+++ b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/ThrottlingNotificationScheduler.java
@@ -33,7 +33,7 @@
 
     // Priority queue hold all the pending notifications.
     private final PriorityQueue<PendingNotificationTask> mPendingNotificationTasks =
-            new PriorityQueue<PendingNotificationTask>(5, PendingNotificationTask::compare);
+            new PriorityQueue<>(5, PendingNotificationTask::compare);
 
     private final Handler mHandler;
     private boolean mScheduled;
diff --git a/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/BitmapUtils.java b/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/BitmapUtils.java
index 5ccb41e..d3f7719 100644
--- a/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/BitmapUtils.java
+++ b/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/BitmapUtils.java
@@ -115,7 +115,7 @@
             int frames,
             boolean fullWidth,
             long intervalMs) {
-        List<Bitmap> bitmaps = new ArrayList<Bitmap>();
+        List<Bitmap> bitmaps = new ArrayList<>();
         Bitmap bitmap = null;
         Float ratio = null;
         for (int frame = 0; frame < frames; ++frame) {
diff --git a/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/DecoderServiceHost.java b/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/DecoderServiceHost.java
index f9949303..b7a36d1d 100644
--- a/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/DecoderServiceHost.java
+++ b/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/DecoderServiceHost.java
@@ -85,7 +85,7 @@
     private @Nullable DecoderServiceParams mProcessingRequest;
 
     // The callbacks used to notify the clients when the service is ready.
-    private final List<DecoderStatusCallback> mCallbacks = new ArrayList<DecoderStatusCallback>();
+    private final List<DecoderStatusCallback> mCallbacks = new ArrayList<>();
 
     // Keeps track of the last decoding ordinal issued.
     static int sLastDecodingOrdinal = 0;
diff --git a/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/FileEnumWorkerTaskTest.java b/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/FileEnumWorkerTaskTest.java
index 6e3b699..a62d2d2 100644
--- a/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/FileEnumWorkerTaskTest.java
+++ b/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/FileEnumWorkerTaskTest.java
@@ -82,7 +82,7 @@
                 String whereClause,
                 String[] whereArgs,
                 String orderBy) {
-            ArrayList<TestData> list = new ArrayList<TestData>();
+            ArrayList<TestData> list = new ArrayList<>();
             list.add(new TestData("file0", "text/html", 0));
             list.add(new TestData("file1", "image/jpeg", 1));
             list.add(new TestData("file2", "image/jpeg", 2));
diff --git a/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/PickerCategoryView.java b/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/PickerCategoryView.java
index e31fc366..98eff2b 100644
--- a/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/PickerCategoryView.java
+++ b/components/browser_ui/photo_picker/android/java/src/org/chromium/components/browser_ui/photo_picker/PickerCategoryView.java
@@ -219,7 +219,7 @@
         mDecoderServiceHost = new DecoderServiceHost(this, context);
         mDecoderServiceHost.bind();
 
-        mSelectionDelegate = new SelectionDelegate<PickerBitmap>();
+        mSelectionDelegate = new SelectionDelegate<>();
         mSelectionDelegate.addObserver(this);
         if (!multiSelectionAllowed) mSelectionDelegate.setSingleSelectionMode();
 
diff --git a/components/browser_ui/settings/android/widget/test_support_java/src/org/chromium/components/browser_ui/settings/Matchers.java b/components/browser_ui/settings/android/widget/test_support_java/src/org/chromium/components/browser_ui/settings/Matchers.java
index 61bb9e8..f293aa92 100644
--- a/components/browser_ui/settings/android/widget/test_support_java/src/org/chromium/components/browser_ui/settings/Matchers.java
+++ b/components/browser_ui/settings/android/widget/test_support_java/src/org/chromium/components/browser_ui/settings/Matchers.java
@@ -18,7 +18,7 @@
      * android:drawableStart}.
      */
     public static Matcher<View> hasDrawableStart() {
-        return new TypeSafeMatcher<View>() {
+        return new TypeSafeMatcher<>() {
             @Override
             protected boolean matchesSafely(View view) {
                 return ((TextView) view).getCompoundDrawablesRelative()[0] != null;
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ChosenObjectSettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ChosenObjectSettings.java
index bf7a8db..decfae81b 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ChosenObjectSettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ChosenObjectSettings.java
@@ -219,7 +219,7 @@
             String exampleObject = mObjectInfos.get(0).getObject();
 
             mObjectInfos.clear();
-            mSites = new ArrayList<Website>();
+            mSites = new ArrayList<>();
             for (Website site : sites) {
                 for (ChosenObjectInfo info : site.getChosenObjectInfo()) {
                     if (info.getObject().equals(exampleObject)) {
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FileEditingInfo.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FileEditingInfo.java
index 6fa504b..5d338ad 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FileEditingInfo.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FileEditingInfo.java
@@ -41,7 +41,7 @@
     }
 
     private void fetchGrants(SiteSettingsDelegate delegate) {
-        mGrants = new ArrayList<Grant>();
+        mGrants = new ArrayList<>();
         String[][] pathsAndDisplayNames = delegate.getFileSystemAccessGrants(mOrigin);
         assert pathsAndDisplayNames != null;
         assert pathsAndDisplayNames.length == 2;
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
index 48eb5dad..9e43f82 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
@@ -1117,10 +1117,7 @@
                     Pair<ArrayList<ChosenObjectInfo>, ArrayList<Website>> entry =
                             objects.get(info.getObject());
                     if (entry == null) {
-                        entry =
-                                Pair.create(
-                                        new ArrayList<ChosenObjectInfo>(),
-                                        new ArrayList<Website>());
+                        entry = Pair.create(new ArrayList<>(), new ArrayList<>());
                         objects.put(info.getObject(), entry);
                     }
                     entry.first.add(info);
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/Website.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/Website.java
index 097ba38..4a5b0f0 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/Website.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/Website.java
@@ -56,7 +56,7 @@
     // The collection of chooser-based permissions (e.g. USB device access) granted to this site.
     // Each entry declares its own ContentSettingsType and so depending on how this object was
     // built this list could contain multiple types of objects.
-    private final List<ChosenObjectInfo> mObjectInfo = new ArrayList<ChosenObjectInfo>();
+    private final List<ChosenObjectInfo> mObjectInfo = new ArrayList<>();
 
     private boolean mIsDomainImportant;
 
@@ -410,7 +410,7 @@
     }
 
     public List<StorageInfo> getStorageInfo() {
-        return new ArrayList<StorageInfo>(mStorageInfo);
+        return new ArrayList<>(mStorageInfo);
     }
 
     public void addSharedDictionaryInfo(SharedDictionaryInfo info) {
@@ -418,7 +418,7 @@
     }
 
     public List<SharedDictionaryInfo> getSharedDictionaryInfo() {
-        return new ArrayList<SharedDictionaryInfo>(mSharedDictionaryInfo);
+        return new ArrayList<>(mSharedDictionaryInfo);
     }
 
     public void setCookiesInfo(CookiesInfo info) {
@@ -493,7 +493,7 @@
 
     /** Returns the set of objects this website has been granted permission to access. */
     public List<ChosenObjectInfo> getChosenObjectInfo() {
-        return new ArrayList<ChosenObjectInfo>(mObjectInfo);
+        return new ArrayList<>(mObjectInfo);
     }
 
     public String getTitleForEmbeddedPreferenceRow() {
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java
index 7f6cf4d..6e15b6b 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java
@@ -545,7 +545,7 @@
             public void runAsync(final TaskQueue queue) {
                 mWebsitePreferenceBridge.fetchLocalStorageInfo(
                         mBrowserContextHandle,
-                        new Callback<HashMap>() {
+                        new Callback<>() {
                             @Override
                             public void onResult(HashMap result) {
                                 for (Object o : result.entrySet()) {
@@ -599,7 +599,7 @@
             public void runAsync(final TaskQueue queue) {
                 mWebsitePreferenceBridge.fetchStorageInfo(
                         mBrowserContextHandle,
-                        new Callback<ArrayList>() {
+                        new Callback<>() {
                             @Override
                             public void onResult(ArrayList result) {
                                 @SuppressWarnings("unchecked")
@@ -628,7 +628,7 @@
             public void runAsync(final TaskQueue queue) {
                 mWebsitePreferenceBridge.fetchSharedDictionaryInfo(
                         mBrowserContextHandle,
-                        new Callback<ArrayList>() {
+                        new Callback<>() {
                             @Override
                             public void onResult(ArrayList result) {
                                 @SuppressWarnings("unchecked")
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java
index 167e6ea..7b78811b7 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java
@@ -85,7 +85,7 @@
 
     @CalledByNative
     private static Object createStorageInfoList() {
-        return new ArrayList<StorageInfo>();
+        return new ArrayList<>();
     }
 
     @CalledByNative
@@ -103,7 +103,7 @@
 
     @CalledByNative
     private static Object createSharedDictionaryInfoList() {
-        return new ArrayList<SharedDictionaryInfo>();
+        return new ArrayList<>();
     }
 
     @SuppressWarnings("unchecked")
@@ -139,7 +139,7 @@
             return exceptions;
         }
 
-        List<ContentSettingException> managedExceptions = new ArrayList<ContentSettingException>();
+        List<ContentSettingException> managedExceptions = new ArrayList<>();
         for (ContentSettingException exception : exceptions) {
             if (exception.getSource() == ProviderType.POLICY_PROVIDER) {
                 managedExceptions.add(exception);
@@ -185,7 +185,7 @@
     public List<ChosenObjectInfo> getChosenObjectInfo(
             BrowserContextHandle browserContextHandle,
             @ContentSettingsType.EnumType int contentSettingsType) {
-        ArrayList<ChosenObjectInfo> list = new ArrayList<ChosenObjectInfo>();
+        ArrayList<ChosenObjectInfo> list = new ArrayList<>();
         org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridgeJni.get()
                 .getChosenObjects(browserContextHandle, contentSettingsType, list);
         return list;
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ChromeTransitionDrawable.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ChromeTransitionDrawable.java
index 64f7d91..4e5963b 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ChromeTransitionDrawable.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ChromeTransitionDrawable.java
@@ -66,7 +66,7 @@
     }
 
     private final IntProperty<ChromeTransitionDrawable> mTransitionProgressProperty =
-            new IntProperty<ChromeTransitionDrawable>("ChromeTransitionDrawableProgress") {
+            new IntProperty<>("ChromeTransitionDrawableProgress") {
                 @Override
                 public Integer get(ChromeTransitionDrawable target) {
                     return target.mProgress;
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/DateDividedAdapter.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/DateDividedAdapter.java
index 2830a0c5..dfe0d2e 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/DateDividedAdapter.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/DateDividedAdapter.java
@@ -337,7 +337,7 @@
 
             Collections.sort(
                     mItems,
-                    new Comparator<TimedItem>() {
+                    new Comparator<>() {
                         @Override
                         public int compare(TimedItem lhs, TimedItem rhs) {
                             return compareItem(lhs, rhs);
@@ -445,7 +445,7 @@
 
     private final SortedSet<ItemGroup> mGroups =
             new TreeSet<>(
-                    new Comparator<ItemGroup>() {
+                    new Comparator<>() {
                         @Override
                         public int compare(ItemGroup lhs, ItemGroup rhs) {
                             if (lhs == rhs) return 0;
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/NumberRollView.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/NumberRollView.java
index a8719c3..31caa8c 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/NumberRollView.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/NumberRollView.java
@@ -37,7 +37,7 @@
      * NumberRollView#setNumberRoll(float)} and {@link NumberRollView#getNumberRoll()} methods.
      */
     public static final FloatProperty<NumberRollView> NUMBER_PROPERTY =
-            new FloatProperty<NumberRollView>("") {
+            new FloatProperty<>("") {
                 @Override
                 public void setValue(NumberRollView view, float value) {
                     view.setNumberRoll(value);
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogTest.java
index 1bc9fbae..e367ab9 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/PromoDialogTest.java
@@ -62,7 +62,7 @@
             mDialogParams = dialogParams;
             dialog =
                     ThreadUtils.runOnUiThreadBlocking(
-                            new Callable<PromoDialog>() {
+                            new Callable<>() {
                                 @Override
                                 public PromoDialog call() {
                                     PromoDialog dialog =
@@ -91,7 +91,7 @@
                             });
             dialogLayout =
                     ThreadUtils.runOnUiThreadBlocking(
-                            new Callable<PromoDialogLayout>() {
+                            new Callable<>() {
                                 @Override
                                 public PromoDialogLayout call() {
                                     PromoDialogLayout promoDialogLayout =
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonLayoutTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonLayoutTest.java
index d826de2..9830609e5 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonLayoutTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonLayoutTest.java
@@ -43,7 +43,7 @@
         RadioButtonLayout layout = new RadioButtonLayout(mContext);
 
         // Add one set of options.
-        List<CharSequence> messages = new ArrayList<CharSequence>();
+        List<CharSequence> messages = new ArrayList<>();
         messages.add("a");
         messages.add("b");
         messages.add("c");
@@ -57,7 +57,7 @@
         }
 
         // Add more options.
-        List<CharSequence> moreMessages = new ArrayList<CharSequence>();
+        List<CharSequence> moreMessages = new ArrayList<>();
         moreMessages.add("d");
         moreMessages.add("e");
         moreMessages.add("f");
@@ -78,7 +78,7 @@
         RadioButtonLayout layout = new RadioButtonLayout(mContext);
 
         // Add one set of options.
-        List<CharSequence> messages = new ArrayList<CharSequence>();
+        List<CharSequence> messages = new ArrayList<>();
         messages.add("a");
         messages.add("b");
         messages.add("c");
@@ -91,7 +91,7 @@
         }
 
         // Add even more options, but without tags.
-        List<CharSequence> moreMessages = new ArrayList<CharSequence>();
+        List<CharSequence> moreMessages = new ArrayList<>();
         moreMessages.add("d");
         moreMessages.add("e");
         moreMessages.add("f");
@@ -115,7 +115,7 @@
         final RadioButtonLayout layout = new RadioButtonLayout(mContext);
 
         // Add one set of options.
-        List<CharSequence> messages = new ArrayList<CharSequence>();
+        List<CharSequence> messages = new ArrayList<>();
         messages.add("a");
         messages.add("b");
         messages.add("c");
@@ -136,7 +136,7 @@
         }
 
         // Add even more options.
-        List<CharSequence> moreMessages = new ArrayList<CharSequence>();
+        List<CharSequence> moreMessages = new ArrayList<>();
         moreMessages.add("d");
         moreMessages.add("e");
         moreMessages.add("f");
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/WrappingLayout.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/WrappingLayout.java
index c92dfc1f..b506271 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/WrappingLayout.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/WrappingLayout.java
@@ -42,7 +42,7 @@
 
     // The indices of visible child views of this layout. Allocated as a member class to avoid
     // allocations while drawing.
-    private final ArrayList<Integer> mVisibleChildren = new ArrayList<Integer>();
+    private final ArrayList<Integer> mVisibleChildren = new ArrayList<>();
 
     public WrappingLayout(Context context) {
         this(context, null);
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/animation/FocusAnimator.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/animation/FocusAnimator.java
index 5a32a44..e769177 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/animation/FocusAnimator.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/animation/FocusAnimator.java
@@ -95,7 +95,7 @@
         }
 
         // Animate each child moving and changing size to match their final locations.
-        ArrayList<Animator> animators = new ArrayList<Animator>();
+        ArrayList<Animator> animators = new ArrayList<>();
         ValueAnimator childAnimator = ValueAnimator.ofFloat(0f, 1f);
         animators.add(childAnimator);
         for (int i = 0; i < mLayout.getChildCount(); i++) {
@@ -196,12 +196,12 @@
     /**
      * Calculates where the top of each child view should be.
      *
-     * @return Array containing the values of {@link View#getTop} for each child of the layout.
-     *         An additional value at the end indicates the total height of the layout and points at
-     *         the bottom of the last child.
+     * @return Array containing the values of {@link View#getTop} for each child of the layout. An
+     *     additional value at the end indicates the total height of the layout and points at the
+     *     bottom of the last child.
      */
     private ArrayList<Integer> calculateChildTops() {
-        ArrayList<Integer> tops = new ArrayList<Integer>();
+        ArrayList<Integer> tops = new ArrayList<>();
 
         int runningTotal = 0;
         for (int i = 0; i < mLayout.getChildCount(); i++) {
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemViewBaseTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemViewBaseTest.java
index 89cc040b2..2f803b4 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemViewBaseTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableItemViewBaseTest.java
@@ -52,7 +52,7 @@
         Activity activity = Robolectric.buildActivity(Activity.class).setup().get();
         mHandleNonSelectionClickHelper = new CallbackHelper();
         mSelectableItemViewBase =
-                new SelectableItemViewBaseTestImpl<Integer>(
+                new SelectableItemViewBaseTestImpl<>(
                         activity, /* attrs= */ null, mHandleNonSelectionClickHelper);
     }
 
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectionDelegate.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectionDelegate.java
index a65fa0f..596d4335 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectionDelegate.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectionDelegate.java
@@ -116,9 +116,11 @@
         return mSelectedItems;
     }
 
-    /** @return The list of selected items. */
+    /**
+     * @return The list of selected items.
+     */
     public List<E> getSelectedItemsAsList() {
-        return new ArrayList<E>(mSelectedItems);
+        return new ArrayList<>(mSelectedItems);
     }
 
     /**
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectionDelegateTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectionDelegateTest.java
index d294e1ea..282121b 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectionDelegateTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectionDelegateTest.java
@@ -22,7 +22,7 @@
 
     @Test
     public void testSelectionDelegateSingle() {
-        SelectionDelegate<Object> delegate = new SelectionDelegate<Object>();
+        SelectionDelegate<Object> delegate = new SelectionDelegate<>();
         delegate.setSingleSelectionMode();
 
         // Starting state, nothing is selected.
@@ -54,7 +54,7 @@
 
     @Test
     public void testSelectionDelegateMulti() {
-        SelectionDelegate<Object> delegate = new SelectionDelegate<Object>();
+        SelectionDelegate<Object> delegate = new SelectionDelegate<>();
 
         // Starting state, nothing is selected.
         assertFalse(delegate.isItemSelected(mData1));
diff --git a/components/enterprise/common/proto/connectors.proto b/components/enterprise/common/proto/connectors.proto
index e67ca2ce..001f189 100644
--- a/components/enterprise/common/proto/connectors.proto
+++ b/components/enterprise/common/proto/connectors.proto
@@ -98,6 +98,10 @@
   // for Workspace sites.
   optional string content_area_account_email = 15;
 
+  // The email of the active user in the content area of the copy source. Only
+  // populated for paste events when the copy source is Workspace sites.
+  optional string source_content_area_account_email = 17;
+
   // The parent URL chain of the frame from which the action was triggered,
   // ordered from current frame URL to tab URL, inclusive.
   repeated string frame_url_chain = 16;
diff --git a/components/feature_engagement/internal/testing_clock_time_provider.h b/components/feature_engagement/internal/testing_clock_time_provider.h
index ee9ae400..ee9c4bc 100644
--- a/components/feature_engagement/internal/testing_clock_time_provider.h
+++ b/components/feature_engagement/internal/testing_clock_time_provider.h
@@ -6,7 +6,6 @@
 #define COMPONENTS_FEATURE_ENGAGEMENT_INTERNAL_TESTING_CLOCK_TIME_PROVIDER_H_
 
 #include "base/compiler_specific.h"
-#include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
 #include "components/feature_engagement/internal/time_provider.h"
 
diff --git a/components/metrics/content/content_stability_metrics_provider.cc b/components/metrics/content/content_stability_metrics_provider.cc
index 9be8ba4..db70eb4 100644
--- a/components/metrics/content/content_stability_metrics_provider.cc
+++ b/components/metrics/content/content_stability_metrics_provider.cc
@@ -81,6 +81,9 @@
 #if BUILDFLAG(IS_WIN)
            || data.sandbox_type == sandbox::mojom::Sandbox::kMediaFoundationCdm
 #endif
+#if BUILDFLAG(IS_ANDROID)
+           || data.metrics_name == "media.mojom.MediaDrmSupport"
+#endif
            ));
 }
 
diff --git a/components/metrics/content/content_stability_metrics_provider.h b/components/metrics/content/content_stability_metrics_provider.h
index 0d0baa43..7de4f5649 100644
--- a/components/metrics/content/content_stability_metrics_provider.h
+++ b/components/metrics/content/content_stability_metrics_provider.h
@@ -76,6 +76,10 @@
   FRIEND_TEST_ALL_PREFIXES(ContentStabilityMetricsProviderTest,
                            MediaFoundationServiceProcessObserverUtility);
 #endif  // BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_ANDROID)
+  FRIEND_TEST_ALL_PREFIXES(ContentStabilityMetricsProviderTest,
+                           MediaDrmSupportProcessObserverUtility);
+#endif  // BUILDFLAG(IS_ANDROID)
   FRIEND_TEST_ALL_PREFIXES(ContentStabilityMetricsProviderTest,
                            UnknownCdmServiceProcessObserverUtility);
   FRIEND_TEST_ALL_PREFIXES(ContentStabilityMetricsProviderTest,
diff --git a/components/metrics/content/content_stability_metrics_provider_unittest.cc b/components/metrics/content/content_stability_metrics_provider_unittest.cc
index 67b09db4..2cd91a10 100644
--- a/components/metrics/content/content_stability_metrics_provider_unittest.cc
+++ b/components/metrics/content/content_stability_metrics_provider_unittest.cc
@@ -34,7 +34,11 @@
 #if BUILDFLAG(IS_WIN)
 const char kTestMediaFoundationServiceUtilityProcessName[] =
     "media.mojom.MediaFoundationServiceBroker";
-#endif
+#endif  // BUILDFLAG(IS_WIN)
+#if BUILDFLAG(IS_ANDROID)
+const char kTestMediaDrmSupportUtlityProcessName[] =
+    "media.mojom.MediaDrmSupport";
+#endif  // BUILDFLAG(IS_ANDROID)
 
 class MockExtensionsHelper : public ExtensionsHelper {
  public:
@@ -198,6 +202,34 @@
 }
 #endif  // BUILDFLAG(IS_WIN)
 
+#if BUILDFLAG(IS_ANDROID)
+TEST_F(ContentStabilityMetricsProviderTest,
+       MediaDrmSupportProcessObserverUtility) {
+  base::HistogramTester histogram_tester;
+  metrics::ContentStabilityMetricsProvider provider(prefs(), nullptr);
+
+  content::ChildProcessData child_process_data(content::PROCESS_TYPE_UTILITY,
+                                               content::ChildProcessId());
+  child_process_data.metrics_name = kTestMediaDrmSupportUtlityProcessName;
+  child_process_data.sandbox_type = sandbox::mojom::Sandbox::kNoSandbox;
+
+  provider.BrowserChildProcessLaunchedAndConnected(child_process_data);
+  const int kExitCode = 555;
+  content::ChildProcessTerminationInfo abnormal_termination_info;
+  abnormal_termination_info.status =
+      base::TERMINATION_STATUS_ABNORMAL_TERMINATION;
+  abnormal_termination_info.exit_code = kExitCode;
+  provider.BrowserChildProcessCrashed(child_process_data,
+                                      abnormal_termination_info);
+  provider.BrowserChildProcessCrashed(child_process_data,
+                                      abnormal_termination_info);
+
+  // Verify metrics.
+  histogram_tester.ExpectUniqueSample(
+      "Stability.Media.MediaDrmSupport.Crash.ExitCode", kExitCode, 2);
+}
+#endif  // BUILDFLAG(IS_ANDROID)
+
 #if !BUILDFLAG(IS_ANDROID)
 TEST_F(ContentStabilityMetricsProviderTest, RenderProcessObserver) {
   metrics::ContentStabilityMetricsProvider provider(prefs(), nullptr);
diff --git a/components/metrics/metrics_data_validation.cc b/components/metrics/metrics_data_validation.cc
index 7bdcf19..ab73a942f 100644
--- a/components/metrics/metrics_data_validation.cc
+++ b/components/metrics/metrics_data_validation.cc
@@ -31,17 +31,6 @@
 
 }  // namespace internal
 
-BASE_FEATURE(kNonUniformityValidationFeature,
-             "UMANonUniformityLogNormal",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
-const base::FeatureParam<double> kLogNormalMean{
-    &kNonUniformityValidationFeature, "mean", 4.605};
-const base::FeatureParam<double> kLogNormalDelta{
-    &kNonUniformityValidationFeature, "delta", 0};
-const base::FeatureParam<double> kLogNormalStdDev{
-    &kNonUniformityValidationFeature, "stdDev", 1.238};
-
 double GetPseudoMetricsSample(double sample) {
   return sample * internal::kMultiplicativeFactor.Get() +
          internal::kAdditiveFactor.Get();
diff --git a/components/metrics/metrics_data_validation.h b/components/metrics/metrics_data_validation.h
index da67bbb..74a13a6 100644
--- a/components/metrics/metrics_data_validation.h
+++ b/components/metrics/metrics_data_validation.h
@@ -6,7 +6,6 @@
 #define COMPONENTS_METRICS_METRICS_DATA_VALIDATION_H_
 
 #include "base/feature_list.h"
-#include "base/metrics/field_trial_params.h"
 #include "base/time/time.h"
 
 // Features and functions in this file are necessary to set up artificial A / B
@@ -20,36 +19,6 @@
 BASE_DECLARE_FEATURE(kPseudoMetricsEffectFeature);
 }  // namespace internal
 
-// Used to assess the reliability of field trial data by sending artificial
-// non-uniform data drawn from a log normal distribution.
-BASE_DECLARE_FEATURE(kNonUniformityValidationFeature);
-
-// The parameters for the log normal distribution. They refer to the default
-// mean, the delta that would be applied to the default mean (the actual mean
-// equals mean + log(1 + delta)) and the standard deviation of the distribution
-// that's being generated. These parameters are carefully calculated so that
-// ~0.01% of data drawn from the distribution would fall in the underflow bucket
-// and ~0.01% of data in the overflow bucket. And they also leave us enough
-// wiggle room to shift mean using delta in experiments without losing precision
-// badly because of data in the overflow bucket.
-//
-// The way we get these numbers are based on the following calculation:
-// u := the lower threshold for the overflow bucket (in this case, 10000).
-// l := the upper threshold for the smallest bucket (in this case, 1).
-// p := the probability that an observation will fall in the highest bucket (in
-//   this case, 0.01%) and also the probability that an observation will fall in
-//   the lowest bucket.
-//
-// mean = (log(u) + log(l)) / 2
-// sd = (log(u) - log(l)) / (2 * qnorm(1-p))
-//
-// At this point, experiments should only control the delta but not mean and
-// stdDev. Putting them in feature params so that we can configure them from the
-// server side if we want.
-extern const base::FeatureParam<double> kLogNormalMean;
-extern const base::FeatureParam<double> kLogNormalDelta;
-extern const base::FeatureParam<double> kLogNormalStdDev;
-
 // In order to assess if we're able to accurately detect a statistically
 // significant difference in our field trial data, we set up pseudo metrics for
 // some of our key metrics. Values of these pseudo metrics are the linear
diff --git a/components/metrics/metrics_state_manager.cc b/components/metrics/metrics_state_manager.cc
index 156f47f9..a5308cec 100644
--- a/components/metrics/metrics_state_manager.cc
+++ b/components/metrics/metrics_state_manager.cc
@@ -13,7 +13,6 @@
 #include <cstdint>
 #include <limits>
 #include <memory>
-#include <random>
 #include <string>
 #include <string_view>
 #include <tuple>
@@ -40,7 +39,6 @@
 #include "components/metrics/cloned_install_detector.h"
 #include "components/metrics/enabled_state_provider.h"
 #include "components/metrics/entropy_state.h"
-#include "components/metrics/metrics_data_validation.h"
 #include "components/metrics/metrics_log.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/metrics_provider.h"
@@ -55,6 +53,7 @@
 #include "third_party/metrics_proto/system_profile.pb.h"
 
 namespace metrics {
+
 namespace {
 
 int64_t ReadEnabledDate(PrefService* local_state) {
@@ -94,37 +93,6 @@
   exit(1);
 }
 
-// Returns a log normal distribution based on the feature params of
-// |kNonUniformityValidationFeature|.
-std::lognormal_distribution<double> GetLogNormalDist() {
-  double mean = kLogNormalMean.Get();
-  double delta = kLogNormalDelta.Get();
-  double std_dev = kLogNormalStdDev.Get();
-  return std::lognormal_distribution<double>(mean + std::log(1.0 + delta),
-                                             std_dev);
-}
-
-// Used to draw a data point from a log normal distribution.
-struct LogNormalMetricState {
-  LogNormalMetricState()
-      : dist(GetLogNormalDist()), gen(std::mt19937(base::RandUint64())) {}
-
-  // Records the artificial non-uniformity histogram for data validation.
-  void LogArtificialNonUniformity() {
-    double rand = dist(gen);
-    // We pick 10k as the upper bound for this histogram so as to avoid losing
-    // precision. See comments for |kLogNormalMean|.
-    base::UmaHistogramCounts10000("UMA.DataValidation.LogNormal",
-                                  base::saturated_cast<int>(rand));
-  }
-
-  // A log normal distribution generator generated by the `GetLogNormalDist()`
-  // function.
-  std::lognormal_distribution<double> dist;
-  // The pseudo-random generator used to generate a data point from |dist|.
-  std::mt19937 gen;
-};
-
 class MetricsStateMetricsProvider : public MetricsProvider {
  public:
   MetricsStateMetricsProvider(
@@ -202,12 +170,6 @@
     if (cloned_install_detector_->ClonedInstallDetectedInCurrentSession()) {
       LogClonedInstall();
     }
-    log_normal_metric_state_.LogArtificialNonUniformity();
-  }
-
-  // Set a random seed for the random number generator.
-  void SetRandomSeedForTesting(int64_t seed) {
-    log_normal_metric_state_.gen = std::mt19937(seed);
   }
 
  private:
@@ -220,7 +182,6 @@
   // the low entropy source was used to do randomization.
   const std::string initial_client_id_;
   const raw_ref<const ClonedInstallDetector> cloned_install_detector_;
-  LogNormalMetricState log_normal_metric_state_;
 };
 
 bool ShouldEnableBenchmarking(bool force_benchmarking_mode) {
@@ -326,15 +287,6 @@
       initial_client_id_, cloned_install_detector_);
 }
 
-std::unique_ptr<MetricsProvider>
-MetricsStateManager::GetProviderAndSetRandomSeedForTesting(int64_t seed) {
-  auto provider = std::make_unique<MetricsStateMetricsProvider>(
-      local_state_, metrics_ids_were_reset_, previous_client_id_,
-      initial_client_id_, cloned_install_detector_);
-  provider->SetRandomSeedForTesting(seed);  // IN-TEST
-  return provider;
-}
-
 bool MetricsStateManager::IsMetricsReportingEnabled() {
   return enabled_state_provider_->IsReportingEnabled();
 }
diff --git a/components/metrics/metrics_state_manager.h b/components/metrics/metrics_state_manager.h
index 2bec07a3..6e503f0 100644
--- a/components/metrics/metrics_state_manager.h
+++ b/components/metrics/metrics_state_manager.h
@@ -289,11 +289,6 @@
   // team to be able to control and monitor if/how this function gets called.
   const ClonedInstallDetector& GetClonedInstallDetector() const;
 
-  // Returns a MetricsStateManagerProvider instance and sets its
-  // |log_normal_metric_state_.gen| with the provided random seed.
-  std::unique_ptr<MetricsProvider> GetProviderAndSetRandomSeedForTesting(
-      int64_t seed);
-
   // Backs up the current client info via |store_client_info_|.
   void BackUpCurrentClientInfo();
 
diff --git a/components/metrics/metrics_state_manager_unittest.cc b/components/metrics/metrics_state_manager_unittest.cc
index a0b4372a..12d81a77 100644
--- a/components/metrics/metrics_state_manager_unittest.cc
+++ b/components/metrics/metrics_state_manager_unittest.cc
@@ -572,34 +572,6 @@
   histogram_tester.ExpectTotalCount("UMA.IsClonedInstall", 0);
 }
 
-TEST_F(MetricsStateManagerTest, CheckProviderLogNormal) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  std::unique_ptr<MetricsStateManager> state_manager(CreateStateManager());
-  // Set the random seed to have a deterministic test.
-  std::unique_ptr<MetricsProvider> provider =
-      state_manager->GetProviderAndSetRandomSeedForTesting(42);
-
-  base::HistogramTester histogram_tester;
-  ChromeUserMetricsExtension uma_proto;
-  provider->ProvideCurrentSessionData(&uma_proto);
-  histogram_tester.ExpectUniqueSample("UMA.DataValidation.LogNormal", 189, 1);
-}
-
-TEST_F(MetricsStateManagerTest, CheckProviderLogNormalWithParams) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      kNonUniformityValidationFeature, {{"delta", "10.0"}});
-  std::unique_ptr<MetricsStateManager> state_manager(CreateStateManager());
-  // Set the random seed to have a deterministic test.
-  std::unique_ptr<MetricsProvider> provider =
-      state_manager->GetProviderAndSetRandomSeedForTesting(42);
-
-  base::HistogramTester histogram_tester;
-  ChromeUserMetricsExtension uma_proto;
-  provider->ProvideCurrentSessionData(&uma_proto);
-  histogram_tester.ExpectUniqueSample("UMA.DataValidation.LogNormal", 2081, 1);
-}
-
 TEST_F(MetricsStateManagerTest, CheckClientIdWasNotUsedToAssignFieldTrial) {
   EnableMetricsReporting();
   ClientInfo client_info;
diff --git a/components/metrics/stability_metrics_helper.cc b/components/metrics/stability_metrics_helper.cc
index 33c46e6..27597a1 100644
--- a/components/metrics/stability_metrics_helper.cc
+++ b/components/metrics/stability_metrics_helper.cc
@@ -94,6 +94,8 @@
     return uma_prefix + "CdmServiceBroker.";
   } else if (metrics_name == "media.mojom.MediaFoundationServiceBroker") {
     return uma_prefix + "MediaFoundationServiceBroker.";
+  } else if (metrics_name == "media.mojom.MediaDrmSupport") {
+    return uma_prefix + "MediaDrmSupport.";
   }
 
   NOTREACHED();
diff --git a/components/optimization_guide/core/BUILD.gn b/components/optimization_guide/core/BUILD.gn
index be0091e58..8393d18 100644
--- a/components/optimization_guide/core/BUILD.gn
+++ b/components/optimization_guide/core/BUILD.gn
@@ -224,7 +224,6 @@
     ":features",
     ":filters",
     ":interfaces",
-    ":model_executor",
     "//components/leveldb_proto",
     "//components/optimization_guide/proto:optimization_guide_proto",
     "//components/prefs",
@@ -232,6 +231,7 @@
     "//google_apis",
     "//services/metrics/public/cpp:metrics_cpp",
     "//services/metrics/public/cpp:ukm_builders",
+    "//third_party/re2",
   ]
 
   # TODO: crbug.com/421262905 - Some utilities should be extracted from
diff --git a/components/optimization_guide/core/hints/hint_cache_unittest.cc b/components/optimization_guide/core/hints/hint_cache_unittest.cc
index 5f5d1346..ad312570 100644
--- a/components/optimization_guide/core/hints/hint_cache_unittest.cc
+++ b/components/optimization_guide/core/hints/hint_cache_unittest.cc
@@ -57,8 +57,7 @@
     optimization_guide_store_ =
         IsBackedByPersistentStore()
             ? std::make_unique<OptimizationGuideStore>(
-                  db_provider_.get(), database_path, database_task_runner,
-                  /*pref_service_=*/nullptr)
+                  db_provider_.get(), database_path, database_task_runner)
             : nullptr;
     hint_cache_ = std::make_unique<HintCache>(
         optimization_guide_store_ ? optimization_guide_store_->AsWeakPtr()
diff --git a/components/optimization_guide/core/hints/hints_manager.cc b/components/optimization_guide/core/hints/hints_manager.cc
index 64251ca..c229643 100644
--- a/components/optimization_guide/core/hints/hints_manager.cc
+++ b/components/optimization_guide/core/hints/hints_manager.cc
@@ -374,7 +374,9 @@
       background_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT})),
       allowed_contexts_for_personalized_metadata_(
-          features::GetAllowedContextsForPersonalizedMetadata()) {
+          features::GetAllowedContextsForPersonalizedMetadata()),
+      allowed_optimization_types_for_proactive_personalization_(
+          features::GetAllowedOptimizationTypesForProactivePersonalization()) {
   if (push_notification_manager_) {
     push_notification_manager_->SetDelegate(this);
   }
@@ -675,7 +677,7 @@
                           hints_updated);
 
   // Initiate the hints fetch scheduling if deferred startup handling is not
-  // enabled. Otherwise OnDeferredStartup() will iniitate it.
+  // enabled. Otherwise OnDeferredStartup() will initiate it.
   if (!features::ShouldDeferStartupActiveTabsHintsFetch()) {
     InitiateHintsFetchScheduling();
   }
@@ -1167,7 +1169,7 @@
     }
   }
   if (allowed_contexts_for_personalized_metadata_.Has(request_context)) {
-    // Request the token before fetching the hints.
+    // Request access token before fetching hints.
     RequestAccessToken(
         identity_manager_,
         {GaiaConstants::kOptimizationGuideServiceGetHintsOAuth2Scope},
@@ -1695,17 +1697,35 @@
     return;
   }
 
-  MaybeFetchHintsForNavigation(navigation_data);
+  if (std::any_of(
+          registered_optimization_types_.begin(),
+          registered_optimization_types_.end(), [&](auto opt_type) {
+            return allowed_optimization_types_for_proactive_personalization_
+                .Has(opt_type);
+          })) {
+    // Request access token before fetching hints.
+    RequestAccessToken(
+        identity_manager_,
+        {GaiaConstants::kOptimizationGuideServiceGetHintsOAuth2Scope},
+        base::BindOnce(&HintsManager::MaybeFetchHintsForNavigation,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       navigation_data->GetWeakPtr()));
+  } else {
+    MaybeFetchHintsForNavigation(navigation_data->GetWeakPtr(),
+                                 /*access_token=*/std::string());
+  }
 }
 
 void HintsManager::MaybeFetchHintsForNavigation(
-    OptimizationGuideNavigationData* navigation_data) {
+    base::WeakPtr<OptimizationGuideNavigationData> navigation_data_weak_ptr,
+    const std::string& access_token) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (registered_optimization_types_.empty()) {
+  if (registered_optimization_types_.empty() || !navigation_data_weak_ptr) {
     return;
   }
-
+  OptimizationGuideNavigationData* navigation_data =
+      navigation_data_weak_ptr.get();
   const GURL url = navigation_data->navigation_url();
   if (!IsAllowedToFetchNavigationHints(url)) {
     return;
@@ -1760,8 +1780,7 @@
                              optimization_guide_logger_);
   bool fetch_attempted = it->second->FetchOptimizationGuideServiceHints(
       hosts, urls, registered_optimization_types_,
-      proto::CONTEXT_PAGE_NAVIGATION, application_locale_,
-      /*access_token=*/std::string(),
+      proto::CONTEXT_PAGE_NAVIGATION, application_locale_, access_token,
       /*skip_cache=*/false,
       base::BindOnce(&HintsManager::OnPageNavigationHintsFetched,
                      weak_ptr_factory_.GetWeakPtr(),
diff --git a/components/optimization_guide/core/hints/hints_manager.h b/components/optimization_guide/core/hints/hints_manager.h
index 3bea556..d59bc9a 100644
--- a/components/optimization_guide/core/hints/hints_manager.h
+++ b/components/optimization_guide/core/hints/hints_manager.h
@@ -430,7 +430,8 @@
   // |navigation_data| if the associated hints for each are not already in the
   // cache.
   void MaybeFetchHintsForNavigation(
-      OptimizationGuideNavigationData* navigation_data);
+      base::WeakPtr<OptimizationGuideNavigationData> navigation_data_weak_ptr,
+      const std::string& access_token);
 
   // If an entry for |navigation_url| is contained in |registered_callbacks_|,
   // it will load the hint for |navigation_url|'s host and upon completion, will
@@ -583,6 +584,10 @@
   // Requests contexts for which personalized metadata should be enabled.
   const features::RequestContextSet allowed_contexts_for_personalized_metadata_;
 
+  // Optimization types for which proactive personalization is enabled.
+  const features::OptimizationTypeSet
+      allowed_optimization_types_for_proactive_personalization_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   // Used to get |weak_ptr_| to self.
diff --git a/components/optimization_guide/core/hints/hints_manager_unittest.cc b/components/optimization_guide/core/hints/hints_manager_unittest.cc
index 980bf24e..6d495943 100644
--- a/components/optimization_guide/core/hints/hints_manager_unittest.cc
+++ b/components/optimization_guide/core/hints/hints_manager_unittest.cc
@@ -350,7 +350,7 @@
 
     hint_store_ = std::make_unique<OptimizationGuideStore>(
         db_provider_.get(), temp_dir(),
-        task_environment_.GetMainThreadTaskRunner(), pref_service_.get());
+        task_environment_.GetMainThreadTaskRunner());
 
     tab_url_provider_ = std::make_unique<FakeTabUrlProvider>();
 
@@ -3759,4 +3759,115 @@
       OptimizationGuideAccessTokenResult::kUserNotSignedIn, 1);
 }
 
+class HintsManagerProactivePersonalizedFetchingTest : public HintsManagerTest {
+ public:
+  HintsManagerProactivePersonalizedFetchingTest() {
+    scoped_list_.InitWithFeaturesAndParameters(
+        {
+            {
+                features::kRemoteOptimizationGuideFetching,
+                {{"batch_update_hints_for_top_hosts", "true"},
+                 {"max_concurrent_page_navigation_fetches", "2"},
+                 {"max_concurrent_batch_update_fetches",
+                  base::NumberToString(batch_concurrency_limit_)}},
+            },
+            {
+                features::kOptimizationGuideProactivePersonalizedHintsFetching,
+                {{"allowed_optimization_types", "SHOPPING_DISCOUNTS"}},
+            },
+        },
+        {features::kRemoteOptimizationGuideFetchingAnonymousDataConsent});
+  }
+
+  void SetUp() override {
+    HintsManagerTest::SetUp();
+    CreateHintsManager(/*top_host_provider=*/nullptr,
+                       identity_test_env()->identity_manager());
+  }
+
+  size_t batch_concurrency_limit() const { return batch_concurrency_limit_; }
+
+  signin::IdentityTestEnvironment* identity_test_env() {
+    return &identity_test_env_;
+  }
+
+ private:
+  size_t batch_concurrency_limit_ = 2;
+  variations::ScopedVariationsIdsProvider scoped_variations_ids_provider_{
+      variations::VariationsIdsProvider::Mode::kUseSignedInState};
+  signin::IdentityTestEnvironment identity_test_env_;
+  base::test::ScopedFeatureList scoped_list_;
+};
+
+TEST_F(HintsManagerProactivePersonalizedFetchingTest,
+       NotAnAllowedOptimizationTypeHintsFetchedAtNavigationTime) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kDisableCheckingUserPermissionsForTesting);
+  hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
+  InitializeWithDefaultConfig("1.0.0.0");
+
+  auto navigation_data =
+      CreateTestNavigationData(url_without_hints(), {proto::DEFER_ALL_SCRIPT});
+  base::HistogramTester histogram_tester;
+  CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
+  RunUntilIdle();
+
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 1, 1);
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.UrlCount", 1, 1);
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
+      RaceNavigationFetchAttemptStatus::kRaceNavigationFetchHostAndURL, 1);
+}
+
+TEST_F(HintsManagerProactivePersonalizedFetchingTest,
+       AllowedOptimizationTypeHintsFetchedAtNavigationTime) {
+  ASSERT_TRUE(identity_test_env()->identity_manager());
+  AccountInfo account_info = identity_test_env()->MakePrimaryAccountAvailable(
+      "test_email", signin::ConsentLevel::kSignin);
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kDisableCheckingUserPermissionsForTesting);
+  hints_manager()->RegisterOptimizationTypes({proto::SHOPPING_DISCOUNTS});
+  InitializeWithDefaultConfig("1.0.0.0");
+
+  auto navigation_data = CreateTestNavigationData(url_without_hints(),
+                                                  {proto::SHOPPING_DISCOUNTS});
+  base::HistogramTester histogram_tester;
+  CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
+  identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+      "access_token", base::Time::Max());
+  RunUntilIdle();
+
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 1, 1);
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.HintsFetcher.GetHintsRequest.UrlCount", 1, 1);
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
+      RaceNavigationFetchAttemptStatus::kRaceNavigationFetchHostAndURL, 1);
+}
+
+TEST_F(HintsManagerProactivePersonalizedFetchingTest, TokenFailure) {
+  ASSERT_TRUE(identity_test_env()->identity_manager());
+  AccountInfo account_info = identity_test_env()->MakePrimaryAccountAvailable(
+      "test_email", signin::ConsentLevel::kSignin);
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kDisableCheckingUserPermissionsForTesting);
+  hints_manager()->RegisterOptimizationTypes({proto::SHOPPING_DISCOUNTS});
+  InitializeWithDefaultConfig("1.0.0.0");
+
+  auto navigation_data = CreateTestNavigationData(url_without_hints(),
+                                                  {proto::SHOPPING_DISCOUNTS});
+  base::HistogramTester histogram_tester;
+  CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
+  identity_test_env()->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
+      GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
+  RunUntilIdle();
+
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.AccessTokenHelper.Result",
+      OptimizationGuideAccessTokenResult::kTransientError, 1);
+}
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/hints/optimization_guide_store.cc b/components/optimization_guide/core/hints/optimization_guide_store.cc
index 3012720a..6048e64 100644
--- a/components/optimization_guide/core/hints/optimization_guide_store.cc
+++ b/components/optimization_guide/core/hints/optimization_guide_store.cc
@@ -8,7 +8,6 @@
 #include <optional>
 #include <string>
 
-#include "base/files/file_util.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/logging.h"
@@ -24,13 +23,9 @@
 #include "components/leveldb_proto/public/proto_database_provider.h"
 #include "components/leveldb_proto/public/shared_proto_database_client_list.h"
 #include "components/optimization_guide/core/hints/memory_hint.h"
-#include "components/optimization_guide/core/model_util.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/optimization_guide/core/optimization_guide_prefs.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
 #include "components/optimization_guide/proto/hint_cache.pb.h"
-#include "components/prefs/pref_service.h"
-#include "components/prefs/scoped_user_pref_update.h"
 
 namespace optimization_guide {
 
@@ -111,30 +106,19 @@
 OptimizationGuideStore::OptimizationGuideStore(
     leveldb_proto::ProtoDatabaseProvider* database_provider,
     const base::FilePath& database_dir,
-    scoped_refptr<base::SequencedTaskRunner> store_task_runner,
-    PrefService* pref_service)
-    : store_task_runner_(store_task_runner), pref_service_(pref_service) {
+    scoped_refptr<base::SequencedTaskRunner> store_task_runner)
+    : store_task_runner_(store_task_runner) {
   database_ = database_provider->GetDB<proto::StoreEntry>(
       leveldb_proto::ProtoDbType::HINT_CACHE_STORE, database_dir,
       store_task_runner_);
-
   RecordStatusChange(status_);
-
-  // Clean up any file paths that were slated for deletion in previous sessions.
-  CleanUpFilePaths();
 }
 
 OptimizationGuideStore::OptimizationGuideStore(
     std::unique_ptr<leveldb_proto::ProtoDatabase<proto::StoreEntry>> database,
-    scoped_refptr<base::SequencedTaskRunner> store_task_runner,
-    PrefService* pref_service)
-    : database_(std::move(database)),
-      store_task_runner_(store_task_runner),
-      pref_service_(pref_service) {
+    scoped_refptr<base::SequencedTaskRunner> store_task_runner)
+    : database_(std::move(database)), store_task_runner_(store_task_runner) {
   RecordStatusChange(status_);
-
-  // Clean up any file paths that were slated for deletion in previous sessions.
-  CleanUpFilePaths();
 }
 
 OptimizationGuideStore::~OptimizationGuideStore() {
@@ -828,50 +812,4 @@
           expiry_time, std::unique_ptr<proto::Hint>(entry->release_hint())));
 }
 
-void OptimizationGuideStore::CleanUpFilePaths() {
-  if (!pref_service_) {
-    return;
-  }
-
-  ScopedDictPrefUpdate file_paths_to_delete_pref(
-      pref_service_, prefs::kStoreFilePathsToDelete);
-  for (const auto entry : *file_paths_to_delete_pref) {
-    std::optional<base::FilePath> path_to_delete =
-        StringToFilePath(entry.first);
-    if (!path_to_delete) {
-      // This is probably not a real file path so delete it from the pref, so we
-      // don't go through this sequence again.
-      OnFilePathDeleted(entry.first, /*success=*/true);
-      continue;
-    }
-    // Note that the delete function doesn't care whether the target is a
-    // directory or file. But in the case of a directory, it is recursively
-    // deleted.
-    //
-    // We post it to the generic thread pool since we don't really care what
-    // thread deletes it at this point as long as it gets deleted.
-    base::ThreadPool::PostTaskAndReplyWithResult(
-        FROM_HERE,
-        {base::TaskPriority::BEST_EFFORT, base::MayBlock(),
-         base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-        base::BindOnce(&base::DeletePathRecursively, *path_to_delete),
-        base::BindOnce(&OptimizationGuideStore::OnFilePathDeleted,
-                       weak_ptr_factory_.GetWeakPtr(), entry.first));
-  }
-}
-
-void OptimizationGuideStore::OnFilePathDeleted(
-    const std::string& file_path_to_clean_up,
-    bool success) {
-  if (!success) {
-    // Try to delete again later.
-    return;
-  }
-
-  // If we get here, we should have a pref service.
-  DCHECK(pref_service_);
-  ScopedDictPrefUpdate update(pref_service_, prefs::kStoreFilePathsToDelete);
-  update->Remove(file_path_to_clean_up);
-}
-
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/hints/optimization_guide_store.h b/components/optimization_guide/core/hints/optimization_guide_store.h
index 7e1b16a..2442d3d 100644
--- a/components/optimization_guide/core/hints/optimization_guide_store.h
+++ b/components/optimization_guide/core/hints/optimization_guide_store.h
@@ -22,8 +22,6 @@
 #include "components/optimization_guide/core/hints/store_update_data.h"
 #include "components/optimization_guide/proto/models.pb.h"
 
-class PrefService;
-
 namespace base {
 class SequencedTaskRunner;
 }  // namespace base
@@ -91,14 +89,12 @@
   OptimizationGuideStore(
       leveldb_proto::ProtoDatabaseProvider* database_provider,
       const base::FilePath& database_dir,
-      scoped_refptr<base::SequencedTaskRunner> store_task_runner,
-      PrefService* pref_service);
+      scoped_refptr<base::SequencedTaskRunner> store_task_runner);
 
   // Creates a model store. For tests only.
   OptimizationGuideStore(
       std::unique_ptr<StoreEntryProtoDatabase> database,
-      scoped_refptr<base::SequencedTaskRunner> store_task_runner,
-      PrefService* pref_service);
+      scoped_refptr<base::SequencedTaskRunner> store_task_runner);
 
   OptimizationGuideStore(const OptimizationGuideStore&) = delete;
   OptimizationGuideStore& operator=(const OptimizationGuideStore&) = delete;
@@ -337,13 +333,6 @@
                   bool success,
                   std::unique_ptr<proto::StoreEntry> entry);
 
-  // Clean up file paths that were slated for deletion in previous sessions.
-  void CleanUpFilePaths();
-
-  // Callback invoked when |deleted_file_path| completed its attempt to be
-  // deleted. Will clean up the path if |success| is true.
-  void OnFilePathDeleted(const std::string& deleted_file_path, bool success);
-
   // Proto database used by the store.
   std::unique_ptr<StoreEntryProtoDatabase> database_;
 
@@ -377,9 +366,6 @@
   // The background task runner used to perform operations on the store.
   scoped_refptr<base::SequencedTaskRunner> store_task_runner_;
 
-  // Pref service. Not owned. Guaranteed to outlive |this|.
-  raw_ptr<PrefService> pref_service_;
-
   SEQUENCE_CHECKER(sequence_checker_);
 
   base::WeakPtrFactory<OptimizationGuideStore> weak_ptr_factory_{this};
diff --git a/components/optimization_guide/core/hints/optimization_guide_store_unittest.cc b/components/optimization_guide/core/hints/optimization_guide_store_unittest.cc
index 98032aa..6f2f598d 100644
--- a/components/optimization_guide/core/hints/optimization_guide_store_unittest.cc
+++ b/components/optimization_guide/core/hints/optimization_guide_store_unittest.cc
@@ -19,12 +19,9 @@
 #include "components/optimization_guide/core/hints/store_update_data.h"
 #include "components/optimization_guide/core/model_util.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/optimization_guide/core/optimization_guide_prefs.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
 #include "components/optimization_guide/proto/hint_cache.pb.h"
 #include "components/optimization_guide/proto/models.pb.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -68,11 +65,6 @@
   OptimizationGuideStoreTest& operator=(const OptimizationGuideStoreTest&) =
       delete;
 
-  void SetUp() override {
-    pref_service_ = std::make_unique<TestingPrefServiceSimple>();
-    prefs::RegisterProfilePrefs(pref_service_->registry());
-  }
-
   void TearDown() override { last_loaded_hint_.reset(); }
 
   // Initializes the entries contained within the database on startup.
@@ -164,8 +156,7 @@
     db_ = db.get();
 
     guide_store_ = std::make_unique<OptimizationGuideStore>(
-        std::move(db), task_environment_.GetMainThreadTaskRunner(),
-        pref_service_.get());
+        std::move(db), task_environment_.GetMainThreadTaskRunner());
   }
 
   void InitializeDatabase(bool success, bool purge_existing_data = false) {
@@ -327,11 +318,6 @@
     last_loaded_entry_key_ = hint_entry_key;
     last_loaded_hint_ = std::move(loaded_hint);
   }
-  bool IsStoreFilesToDeletePrefEmpty() {
-    const base::Value::Dict& pref_dict =
-        pref_service_->GetDict(prefs::kStoreFilePathsToDelete);
-    return pref_dict.empty();
-  }
 
   void RunUntilIdle() { task_environment_.RunUntilIdle(); }
 
@@ -340,7 +326,6 @@
 
  private:
   base::test::TaskEnvironment task_environment_;
-  std::unique_ptr<TestingPrefServiceSimple> pref_service_;
   StoreEntryMap db_store_;
   std::unique_ptr<OptimizationGuideStore> guide_store_;
   raw_ptr<FakeDB<proto::StoreEntry>> db_;
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index 667312c..a062984 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit 667312c22e24f5b3ad4bd09f229d837cd880a5db
+Subproject commit a06298498a2ca3f88c5c894a54076e970152c035
diff --git a/components/password_manager/core/browser/credential_cache.cc b/components/password_manager/core/browser/credential_cache.cc
index 3a77c29f..9287687b 100644
--- a/components/password_manager/core/browser/credential_cache.cc
+++ b/components/password_manager/core/browser/credential_cache.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <iterator>
 #include <map>
+#include <optional>
 #include <utility>
 #include <vector>
 
@@ -15,6 +16,7 @@
 #include "components/password_manager/core/browser/features/password_features.h"
 #include "components/password_manager/core/browser/origin_credential_store.h"
 #include "components/password_manager/core/browser/password_form.h"
+#include "components/password_manager/core/browser/password_store/password_store_backend_error.h"
 #include "url/origin.h"
 
 namespace password_manager {
@@ -47,6 +49,7 @@
 void CredentialCache::SaveCredentialsAndBlocklistedForOrigin(
     base::span<const PasswordForm> best_matches,
     IsOriginBlocklisted is_blocklisted,
+    std::optional<PasswordStoreBackendError> backend_error,
     const url::Origin& origin) {
   std::vector<UiCredential> credentials;
   credentials.reserve(best_matches.size());
@@ -91,11 +94,13 @@
       // The cache is only useful when the sharing notification UI is displayed
       // since it is used to mark those credentials as notified after the user
       // interacts with the UI.
-        unnotified_shared_credentials.push_back(form);
+      unnotified_shared_credentials.push_back(form);
     }
   }
   GetOrCreateCredentialStore(origin).SaveUnnotifiedSharedCredentials(
       std::move(unnotified_shared_credentials));
+
+  backend_error_ = backend_error;
 }
 
 const OriginCredentialStore& CredentialCache::GetCredentialStore(
@@ -103,6 +108,11 @@
   return GetOrCreateCredentialStore(origin);
 }
 
+const std::optional<PasswordStoreBackendError> CredentialCache::backend_error()
+    const {
+  return backend_error_;
+}
+
 void CredentialCache::ClearCredentials() {
   origin_credentials_.clear();
 }
diff --git a/components/password_manager/core/browser/credential_cache.h b/components/password_manager/core/browser/credential_cache.h
index 9272544..2435bb2 100644
--- a/components/password_manager/core/browser/credential_cache.h
+++ b/components/password_manager/core/browser/credential_cache.h
@@ -10,6 +10,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/types/strong_alias.h"
+#include "components/password_manager/core/browser/password_store/password_store_backend_error.h"
 #include "url/origin.h"
 
 namespace password_manager {
@@ -34,12 +35,17 @@
   void SaveCredentialsAndBlocklistedForOrigin(
       base::span<const PasswordForm> matches,
       IsOriginBlocklisted is_blocklisted,
+      std::optional<PasswordStoreBackendError> backend_error,
       const url::Origin& origin);
 
   // Returns the credential store for a given origin. If it does not exist, an
   // empty store will be created.
   const OriginCredentialStore& GetCredentialStore(const url::Origin& origin);
 
+  // Returns the backend error if there was one encountered during the most
+  // recent `SaveCredentialsAndBlocklistedForOrigin()` call.
+  const std::optional<PasswordStoreBackendError> backend_error() const;
+
   // Removes all credentials for all origins.
   void ClearCredentials();
 
@@ -48,6 +54,9 @@
 
   // Contains the store for credential of each requested origin.
   std::map<url::Origin, OriginCredentialStore> origin_credentials_;
+
+  // Contains an error reported by the backend, if any.
+  std::optional<PasswordStoreBackendError> backend_error_;
 };
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/credential_cache_unittest.cc b/components/password_manager/core/browser/credential_cache_unittest.cc
index 7c701fa..eaf6fa3 100644
--- a/components/password_manager/core/browser/credential_cache_unittest.cc
+++ b/components/password_manager/core/browser/credential_cache_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/password_manager/core/browser/credential_cache.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <string_view>
 
@@ -15,6 +16,7 @@
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/browser/password_manager_test_utils.h"
 #include "components/password_manager/core/browser/password_manager_util.h"
+#include "components/password_manager/core/browser/password_store/password_store_backend_error.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/origin.h"
@@ -23,6 +25,7 @@
 
 namespace {
 
+using testing::IsEmpty;
 using testing::Property;
 using url::Origin;
 using IsOriginBlocklisted = CredentialCache::IsOriginBlocklisted;
@@ -100,7 +103,7 @@
       CreateEntry("Alf", "R4nd50m", GURL(kExampleSiteMobile),
                   PasswordForm::MatchType::kPSL)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, IsOriginBlocklisted(false), origin);
+      matches, IsOriginBlocklisted(false), std::nullopt, origin);
 
   EXPECT_THAT(
       cache()->GetCredentialStore(origin).GetCredentials(),
@@ -164,7 +167,7 @@
                   PasswordForm::MatchType::kPSL)};
 
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, IsOriginBlocklisted(false), origin);
+      matches, IsOriginBlocklisted(false), std::nullopt, origin);
 
   EXPECT_THAT(
       cache()->GetCredentialStore(origin).GetCredentials(),
@@ -234,7 +237,7 @@
                                        shared_notified_credentials,
                                        shared_unnotified_credentials};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, IsOriginBlocklisted(false), origin);
+      matches, IsOriginBlocklisted(false), std::nullopt, origin);
 
   EXPECT_THAT(
       cache()->GetCredentialStore(origin).GetUnnotifiedSharedCredentials(),
@@ -258,11 +261,11 @@
   std::vector<PasswordForm> matches1 = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches1, IsOriginBlocklisted(false), origin);
+      matches1, IsOriginBlocklisted(false), std::nullopt, origin);
   std::vector<PasswordForm> matches2 = {CreateEntry(
       "Abe", "B4dPW", GURL(kExampleSite2), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches2, IsOriginBlocklisted(false), origin2);
+      matches2, IsOriginBlocklisted(false), std::nullopt, origin2);
 
   EXPECT_THAT(cache()->GetCredentialStore(origin).GetCredentials(),
               testing::ElementsAre(MakeUiCredential("Ben", "S3cur3")));
@@ -276,7 +279,8 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, IsOriginBlocklisted(false), Origin::Create(GURL(kExampleSite)));
+      matches, IsOriginBlocklisted(false), std::nullopt,
+      Origin::Create(GURL(kExampleSite)));
   ASSERT_THAT(cache()->GetCredentialStore(origin).GetCredentials(),
               testing::ElementsAre(MakeUiCredential("Ben", "S3cur3")));
 
@@ -289,11 +293,45 @@
   std::vector<PasswordForm> matches = {CreateEntry(
       "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, IsOriginBlocklisted(true), Origin::Create(GURL(kExampleSite)));
+      matches, IsOriginBlocklisted(true), std::nullopt,
+      Origin::Create(GURL(kExampleSite)));
   EXPECT_EQ(OriginCredentialStore::BlocklistedStatus::kIsBlocklisted,
             cache()->GetCredentialStore(origin).GetBlocklistedStatus());
 }
 
+TEST_F(CredentialCacheTest, StoresBackendErrorAndCredentials) {
+  Origin origin = Origin::Create(GURL(kExampleSite));
+
+  // Only backend error.
+  cache()->SaveCredentialsAndBlocklistedForOrigin(
+      {}, IsOriginBlocklisted(false),
+      PasswordStoreBackendErrorType::kAuthErrorResolvable,
+      Origin::Create(GURL(kExampleSite)));
+  EXPECT_EQ(PasswordStoreBackendErrorType::kAuthErrorResolvable,
+            cache()->backend_error());
+  EXPECT_THAT(cache()->GetCredentialStore(origin).GetCredentials(), IsEmpty());
+
+  // Backend error and matched passwords.
+  std::vector<PasswordForm> matches = {CreateEntry(
+      "Ben", "S3cur3", GURL(kExampleSite), PasswordForm::MatchType::kExact)};
+  cache()->SaveCredentialsAndBlocklistedForOrigin(
+      matches, IsOriginBlocklisted(false),
+      PasswordStoreBackendErrorType::kAuthErrorResolvable,
+      Origin::Create(GURL(kExampleSite)));
+  EXPECT_EQ(PasswordStoreBackendErrorType::kAuthErrorResolvable,
+            cache()->backend_error());
+  EXPECT_THAT(cache()->GetCredentialStore(origin).GetCredentials(),
+              testing::ElementsAre(MakeUiCredential("Ben", "S3cur3")));
+
+  // Backend error is cleared.
+  cache()->SaveCredentialsAndBlocklistedForOrigin(
+      matches, IsOriginBlocklisted(false), std::nullopt,
+      Origin::Create(GURL(kExampleSite)));
+  EXPECT_EQ(std::nullopt, cache()->backend_error());
+  EXPECT_THAT(cache()->GetCredentialStore(origin).GetCredentials(),
+              testing::ElementsAre(MakeUiCredential("Ben", "S3cur3")));
+}
+
 #if BUILDFLAG(IS_ANDROID)
 TEST_F(CredentialCacheTest, SplitsCredentialsInMainAndBackupFlagEnabled) {
   base::test::ScopedFeatureList feature_list_;
@@ -305,7 +343,7 @@
   match_with_backup.SetPasswordBackupNote(u"backuppassword");
   std::vector<PasswordForm> matches = {std::move(match_with_backup)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, IsOriginBlocklisted(false), origin);
+      matches, IsOriginBlocklisted(false), std::nullopt, origin);
 
   EXPECT_THAT(
       cache()->GetCredentialStore(origin).GetCredentials(),
@@ -326,7 +364,7 @@
   match_with_backup.SetPasswordBackupNote(u"backuppassword");
   std::vector<PasswordForm> matches = {std::move(match_with_backup)};
   cache()->SaveCredentialsAndBlocklistedForOrigin(
-      matches, IsOriginBlocklisted(false), origin);
+      matches, IsOriginBlocklisted(false), std::nullopt, origin);
 
   EXPECT_THAT(
       cache()->GetCredentialStore(origin).GetCredentials(),
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index b08591e..8d2e9fc 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -893,10 +893,11 @@
   newly_blocklisted_ = false;
   autofills_left_ = kMaxTimesAutofill;
 
+  std::optional<PasswordStoreBackendError> error = std::nullopt;
 #if BUILDFLAG(IS_ANDROID)
-  std::optional<PasswordStoreBackendError> error = GetErrorForErrorMessage(
-      form_fetcher_->GetProfileStoreBackendError(),
-      form_fetcher_->GetAccountStoreBackendError(), client_);
+  error = GetErrorForErrorMessage(form_fetcher_->GetProfileStoreBackendError(),
+                                  form_fetcher_->GetAccountStoreBackendError(),
+                                  client_);
 
   // If there is no FormData, this is an http authentication form. We don't
   // show the message for it because it would be hidden behind a sign in
@@ -935,7 +936,7 @@
 
   client_->UpdateCredentialCache(url::Origin::Create(GetURL()),
                                  form_fetcher_->GetBestMatches(),
-                                 form_fetcher_->IsBlocklisted());
+                                 form_fetcher_->IsBlocklisted(), error);
 
   if (is_submitted_) {
     CreatePendingCredentials();
diff --git a/components/password_manager/core/browser/password_manager_client.cc b/components/password_manager/core/browser/password_manager_client.cc
index 83d0fc0..d49aac8e 100644
--- a/components/password_manager/core/browser/password_manager_client.cc
+++ b/components/password_manager/core/browser/password_manager_client.cc
@@ -57,7 +57,8 @@
 void PasswordManagerClient::UpdateCredentialCache(
     const url::Origin& origin,
     base::span<const PasswordForm> best_matches,
-    bool is_blocklisted) {}
+    bool is_blocklisted,
+    std::optional<PasswordStoreBackendError> backend_error) {}
 
 void PasswordManagerClient::PasswordWasAutofilled(
     base::span<const PasswordForm> best_matches,
diff --git a/components/password_manager/core/browser/password_manager_client.h b/components/password_manager/core/browser/password_manager_client.h
index 6a025a1..40489f4 100644
--- a/components/password_manager/core/browser/password_manager_client.h
+++ b/components/password_manager/core/browser/password_manager_client.h
@@ -287,7 +287,8 @@
   virtual void UpdateCredentialCache(
       const url::Origin& origin,
       base::span<const PasswordForm> best_matches,
-      bool is_blocklisted);
+      bool is_blocklisted,
+      std::optional<PasswordStoreBackendError> backend_error);
 
   // Called when a password is saved in an automated fashion. Embedder may
   // inform the user that this save has occurred.
diff --git a/components/payments/content/android/BUILD.gn b/components/payments/content/android/BUILD.gn
index 0482310..6f2a877 100644
--- a/components/payments/content/android/BUILD.gn
+++ b/components/payments/content/android/BUILD.gn
@@ -390,6 +390,7 @@
     ":java",
     ":service_java",
     "//base:base_java_test_support",
+    "//components/payments/content/android/spc:unit_device_javatests",
     "//components/payments/mojom:mojom_java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/androidx:androidx_test_rules_java",
diff --git a/components/payments/content/secure_payment_confirmation_app_factory.cc b/components/payments/content/secure_payment_confirmation_app_factory.cc
index 587905b..a40c8fc 100644
--- a/components/payments/content/secure_payment_confirmation_app_factory.cc
+++ b/components/payments/content/secure_payment_confirmation_app_factory.cc
@@ -186,8 +186,6 @@
                                                         caller_origin);
 }
 
-enum class IconType { PAYMENT_INSTRUMENT, NETWORK, ISSUER };
-
 struct IconInfo {
   GURL url;
   std::optional<int> request_id;
@@ -246,7 +244,8 @@
   scoped_refptr<payments::PaymentManifestWebDataService> web_data_service;
   mojom::SecurePaymentConfirmationRequestPtr mojo_request;
   std::unique_ptr<webauthn::InternalAuthenticator> authenticator;
-  std::map<IconType, IconInfo> icon_infos;
+  IconInfo payment_instrument_icon_info;
+  std::vector<IconInfo> payment_entities_logos_infos;
   std::unique_ptr<SecurePaymentConfirmationCredential> credential;
 };
 
@@ -346,36 +345,50 @@
       // the old (experimental) network_info/issuer_info fields, and the new
       // payment_entities_logos field. Both are flag-guarded, and only one flow
       // is supported at a time, so to simplify the rest of the logic we
-      // consolidate payment_entities_logos (if set) into
-      // issuer_info/network_info.
+      // consolidate issuer_info/network_info (if set) into
+      // payment_entities_logos.
       //
-      // If both flags are turned on (and payment_entities_logos is provided),
-      // then payment_entities_logos will 'win' and overwrite network_info and
-      // issuer_info.
+      // If both flags are turned on then payment_entities_logos will 'win' and
+      // network_info and issuer_info will be ignored.
       //
-      // TODO(crbug.com/417683819): Switch to using an array of logos in
-      // SecurePaymentConfirmationAppFactory, and invert the logic here.
+      // TODO(crbug.com/417683819): Remove this code once network_info and
+      // issuer_info have been fully deprecated and removed.
       mojom::SecurePaymentConfirmationRequestPtr spc_request =
           method_data->secure_payment_confirmation.Clone();
-      if (base::FeatureList::IsEnabled(
+      if (!base::FeatureList::IsEnabled(
               blink::features::kSecurePaymentConfirmationUxRefresh) &&
-          !spc_request->payment_entities_logos.empty()) {
-        const mojom::PaymentEntityLogoPtr& first_logo =
-            spc_request->payment_entities_logos[0];
-        spc_request->network_info = mojom::NetworkOrIssuerInformation::New(
-            /*name=*/first_logo->label,
-            /*icon=*/first_logo->url);
+          (spc_request->network_info || spc_request->issuer_info)) {
+        spc_request->payment_entities_logos.clear();
 
-        if (spc_request->payment_entities_logos.size() > 1) {
-          const mojom::PaymentEntityLogoPtr& second_logo =
-              spc_request->payment_entities_logos[1];
-          spc_request->issuer_info = mojom::NetworkOrIssuerInformation::New(
-              /*name=*/second_logo->label,
-              /*icon=*/second_logo->url);
-
+        // We encode the network and issuer info as network first, issuer
+        // second. If network was not provided, we insert a placeholder so that
+        // later code can properly map the order back.
+        if (spc_request->network_info) {
+          spc_request->payment_entities_logos.emplace_back(
+              mojom::PaymentEntityLogo::New(
+                  /*url=*/spc_request->network_info->icon,
+                  /*label=*/spc_request->network_info->name));
         } else {
-          spc_request->issuer_info = nullptr;
+          spc_request->payment_entities_logos.emplace_back(
+              mojom::PaymentEntityLogo::New(/*url=*/GURL(), /*label=*/""));
         }
+
+        if (spc_request->issuer_info) {
+          spc_request->payment_entities_logos.emplace_back(
+              mojom::PaymentEntityLogo::New(
+                  /*url=*/spc_request->issuer_info->icon,
+                  /*label=*/spc_request->issuer_info->name));
+        }
+      }
+
+      // Only spc_request->payment_entities_logos should be used from here out.
+      spc_request->network_info = nullptr;
+      spc_request->issuer_info = nullptr;
+
+      // Since only the first 2 icons are shown, remove the remaining logos.
+      if (spc_request->payment_entities_logos.size() > 2) {
+        spc_request->payment_entities_logos.erase(
+            spc_request->payment_entities_logos.begin() + 2);
       }
 
       // Record if the user will be offered an opt-out experience. Technically
@@ -469,43 +482,58 @@
   if (!credentials.empty())
     request->credential = std::move(credentials.front());
 
-  // Download the icons for the payment instrument, network icon, and issuer
-  // icon. These download URLs were passed into the PaymentRequest API. If given
-  // icon URL wasn't specified, then DownloadImageInFrame will simply return an
-  // empty set of bitmaps.
+  // Download the icons for the payment instrument icon and the payment entity
+  // logos. These download URLs were passed into the PaymentRequest API. If
+  // given icon URL wasn't specified, then DownloadImageInFrame will simply
+  // return an empty set of bitmaps.
   //
   // Perform these downloads regardless of whether there is a matching
   // credential, so that the hosting server(s) cannot detect presence of the
   // credential on file.
   auto* request_ptr = request.get();
-  request_ptr->icon_infos[IconType::PAYMENT_INSTRUMENT] = {
+
+  request_ptr->payment_instrument_icon_info = {
       .url = request_ptr->mojo_request->instrument->icon};
-  if (request_ptr->mojo_request->network_info) {
-    request_ptr->icon_infos[IconType::NETWORK] = {
-        .url = request_ptr->mojo_request->network_info->icon};
-  }
-  if (request_ptr->mojo_request->issuer_info) {
-    request_ptr->icon_infos[IconType::ISSUER] = {
-        .url = request_ptr->mojo_request->issuer_info->icon};
+  for (const mojom::PaymentEntityLogoPtr& logo :
+       request_ptr->mojo_request->payment_entities_logos) {
+    request_ptr->payment_entities_logos_infos.push_back({.url = logo->url});
   }
 
   auto barrier_closure = base::BarrierClosure(
-      request_ptr->icon_infos.size(),
+      // The payment instrument icon download, plus any payment entity logos.
+      1 + request_ptr->payment_entities_logos_infos.size(),
       base::BindOnce(&SecurePaymentConfirmationAppFactory::DidDownloadAllIcons,
                      weak_ptr_factory_.GetWeakPtr(), std::move(request)));
 
   gfx::Size preferred_size(kSecurePaymentConfirmationIconMaximumWidthPx,
                            kSecurePaymentConfirmationIconHeightPx);
 
-  for (auto& [type, info] : request_ptr->icon_infos) {
-    info.request_id = request_ptr->web_contents()->DownloadImageInFrame(
-        request_ptr->delegate->GetInitiatorRenderFrameHostId(),
-        info.url,  // source URL
-        false,     // is_favicon
-        preferred_size,
-        0,      // no max size
-        false,  // normal cache policy (a.k.a. do not bypass cache)
-        base::BindOnce(&DidDownloadIcon, &info, barrier_closure));
+  request_ptr->payment_instrument_icon_info.request_id =
+      request_ptr->web_contents()->DownloadImageInFrame(
+          request_ptr->delegate->GetInitiatorRenderFrameHostId(),
+          request_ptr->payment_instrument_icon_info.url,  // source URL
+          false,                                          // is_favicon
+          preferred_size,
+          0,      // no max size
+          false,  // normal cache policy (a.k.a. do not bypass cache)
+          base::BindOnce(&DidDownloadIcon,
+                         &request_ptr->payment_instrument_icon_info,
+                         barrier_closure));
+
+  for (IconInfo& info : request_ptr->payment_entities_logos_infos) {
+    if (info.url.is_empty()) {
+      // This IconInfo is a placeholder value. No download is necessary.
+      barrier_closure.Run();
+    } else {
+      info.request_id = request_ptr->web_contents()->DownloadImageInFrame(
+          request_ptr->delegate->GetInitiatorRenderFrameHostId(),
+          info.url,  // source URL
+          false,     // is_favicon
+          preferred_size,
+          0,      // no max size
+          false,  // normal cache policy (a.k.a. do not bypass cache)
+          base::BindOnce(&DidDownloadIcon, &info, barrier_closure));
+    }
   }
 }
 
@@ -515,8 +543,7 @@
   if (!request->delegate || !request->web_contents())
     return;
 
-  SkBitmap payment_instrument_icon =
-      request->icon_infos[IconType::PAYMENT_INSTRUMENT].icon;
+  SkBitmap payment_instrument_icon = request->payment_instrument_icon_info.icon;
   if (payment_instrument_icon.drawsNothing()) {
     // If the option iconMustBeShown is true, which it is by default, in the
     // case of a failed instrument icon download/decode, we reject the show()
@@ -547,24 +574,31 @@
   std::u16string payment_instrument_label =
       base::UTF8ToUTF16(request->mojo_request->instrument->display_name);
 
+  // We are currently migrating from the old network_info/issuer_info fields, to
+  // the new sequence of payment entity logos. Convert from the sequence to the
+  // individual fields, to set them on the payment app.
+  //
+  // TODO(crbug.com/417683819): Switch to using an array of logos in
+  // SecurePaymentConfirmationApp, and remove this conversion.
   std::u16string network_label = u"";
   std::unique_ptr<SkBitmap> network_icon;
-  if (request->mojo_request->network_info) {
-    network_label =
-        base::UTF8ToUTF16(request->mojo_request->network_info->name);
-    if (!request->icon_infos[IconType::NETWORK].icon.drawsNothing()) {
+  if (!request->mojo_request->payment_entities_logos.empty()) {
+    network_label = base::UTF8ToUTF16(
+        request->mojo_request->payment_entities_logos[0]->label);
+    if (!request->payment_entities_logos_infos[0].icon.drawsNothing()) {
       network_icon = std::make_unique<SkBitmap>(
-          request->icon_infos[IconType::NETWORK].icon);
+          request->payment_entities_logos_infos[0].icon);
     }
   }
 
   std::u16string issuer_label = u"";
   std::unique_ptr<SkBitmap> issuer_icon;
-  if (request->mojo_request->issuer_info) {
-    issuer_label = base::UTF8ToUTF16(request->mojo_request->issuer_info->name);
-    if (!request->icon_infos[IconType::ISSUER].icon.drawsNothing()) {
+  if (request->mojo_request->payment_entities_logos.size() > 1) {
+    issuer_label = base::UTF8ToUTF16(
+        request->mojo_request->payment_entities_logos[1]->label);
+    if (!request->payment_entities_logos_infos[1].icon.drawsNothing()) {
       issuer_icon = std::make_unique<SkBitmap>(
-          request->icon_infos[IconType::ISSUER].icon);
+          request->payment_entities_logos_infos[1].icon);
     }
   }
 
diff --git a/components/payments/content/secure_payment_confirmation_app_factory_unittest.cc b/components/payments/content/secure_payment_confirmation_app_factory_unittest.cc
index fb49fb2..ff35d1a5 100644
--- a/components/payments/content/secure_payment_confirmation_app_factory_unittest.cc
+++ b/components/payments/content/secure_payment_confirmation_app_factory_unittest.cc
@@ -45,6 +45,9 @@
 using ::testing::_;
 using ::testing::ByMove;
 using ::testing::Eq;
+using ::testing::Matcher;
+using ::testing::Pointer;
+using ::testing::Property;
 using ::testing::Return;
 using ::testing::ReturnRef;
 
@@ -588,12 +591,16 @@
         kWebDataServiceHandle, std::move(result));
   }
 
-  void FakeImageDownloaded(GURL image_url, bool succeeded = true) {
+  // The height can be set here, and expected in a test using
+  // IsSkBitmapWithHeight().
+  void FakeImageDownloaded(GURL image_url,
+                           bool succeeded = true,
+                           int height = 32) {
     std::vector<gfx::Size> icon_sizes({{32, 32}});
     std::vector<SkBitmap> icon_bitmaps;
     if (succeeded) {
       icon_bitmaps.emplace_back();
-      icon_bitmaps[0].allocN32Pixels(/*width=*/32, /*height=*/32);
+      icon_bitmaps[0].allocN32Pixels(/*width=*/32, /*height=*/height);
     }
 
     ASSERT_TRUE(static_cast<content::TestWebContents*>(web_contents_.get())
@@ -607,6 +614,10 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
+Matcher<const SkBitmap*> IsSkBitmapWithHeight(int height) {
+  return Pointer(Property(&SkBitmap::height, height));
+}
+
 // Tests that when neither the network nor issuer icons are specified, they are
 // not present on the final PaymentApp.
 TEST_F(SecurePaymentConfirmationAppFactoryNetworkAndIssuerIconsTest,
@@ -765,8 +776,130 @@
   EXPECT_TRUE(created_payment_app->issuer_bitmap());
 }
 
-// TODO(crbug.com/417683819): Add tests verifying that paymentEntitiesLogos is
-// correctly converted into icons to be downloaded.
+// Class wrapping tests relating to payment entity logos support in
+// SecurePaymentConfirmationAppFactory.
+class SecurePaymentConfirmationAppFactoryPaymentEntitiesLogosTest
+    : public SecurePaymentConfirmationAppFactoryNetworkAndIssuerIconsTest {
+ public:
+  const GURL kPaymentEntity1LogoUrl =
+      GURL("https://payment-entity-1.example/icon.png");
+  const GURL kPaymentEntity2LogoUrl =
+      GURL("https://payment-entity-2.example/icon.png");
+  const GURL kPaymentEntity3LogoUrl =
+      GURL("https://payment-entity-3.example/icon.png");
+
+ private:
+  base::test::ScopedFeatureList feature_list_{
+      blink::features::kSecurePaymentConfirmationUxRefresh};
+};
+
+// Tests that when the feature flag is on, logos specified in
+// payment_entities_logos are downloaded and placed in the network and issuer
+// logo fields in the created PaymentApp. The first logo is placed in the
+// network logo field and the second logo is placed in the issuer field.
+TEST_F(SecurePaymentConfirmationAppFactoryPaymentEntitiesLogosTest,
+       ConvertedToNetworkAndIssuerIcons) {
+  auto method_data = mojom::PaymentMethodData::New();
+  method_data->supported_method = "secure-payment-confirmation";
+  mojom::SecurePaymentConfirmationRequestPtr spc_request =
+      CreateSecurePaymentConfirmationRequest();
+  spc_request->payment_entities_logos.push_back(mojom::PaymentEntityLogo::New(
+      kPaymentEntity1LogoUrl, "Payment Entity 1"));
+  spc_request->payment_entities_logos.push_back(mojom::PaymentEntityLogo::New(
+      kPaymentEntity2LogoUrl, "Payment Entity 2"));
+  method_data->secure_payment_confirmation = std::move(spc_request);
+
+  std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate =
+      CreateMockDelegate(std::move(method_data));
+
+  std::unique_ptr<PaymentApp> created_payment_app;
+  EXPECT_CALL(*mock_delegate, OnPaymentAppCreated(_))
+      .WillOnce(MoveArg<0>(&created_payment_app));
+
+  secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr());
+
+  FakeCredentialFetchedFromDatabase(credential_id_bytes_);
+  FakeImageDownloaded(kInstrumentIconUrl);
+  FakeImageDownloaded(kPaymentEntity1LogoUrl, /*succeeded=*/true,
+                      /*height=*/50);
+  FakeImageDownloaded(kPaymentEntity2LogoUrl, /*succeeded=*/true,
+                      /*height=*/60);
+
+  // The payment entity logos should have been placed into the network_bitmap
+  // and issuer_bitmap.
+  ASSERT_TRUE(created_payment_app);
+  EXPECT_THAT(created_payment_app->network_bitmap(), IsSkBitmapWithHeight(50));
+  EXPECT_THAT(created_payment_app->issuer_bitmap(), IsSkBitmapWithHeight(60));
+}
+
+// Tests that the first entry in payment_entities_logos maps to the network
+// bitmap (and thus that implicitly the second entry will map to the issuer
+// bitmap).
+TEST_F(SecurePaymentConfirmationAppFactoryPaymentEntitiesLogosTest,
+       SinglePaymentEntityLogoConvertsToNetworkIcon) {
+  auto method_data = mojom::PaymentMethodData::New();
+  method_data->supported_method = "secure-payment-confirmation";
+  mojom::SecurePaymentConfirmationRequestPtr spc_request =
+      CreateSecurePaymentConfirmationRequest();
+  spc_request->payment_entities_logos.push_back(mojom::PaymentEntityLogo::New(
+      kPaymentEntity1LogoUrl, "Payment Entity 1"));
+  method_data->secure_payment_confirmation = std::move(spc_request);
+
+  std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate =
+      CreateMockDelegate(std::move(method_data));
+
+  std::unique_ptr<PaymentApp> created_payment_app;
+  EXPECT_CALL(*mock_delegate, OnPaymentAppCreated(_))
+      .WillOnce(MoveArg<0>(&created_payment_app));
+
+  secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr());
+
+  FakeCredentialFetchedFromDatabase(credential_id_bytes_);
+  FakeImageDownloaded(kInstrumentIconUrl);
+  FakeImageDownloaded(kPaymentEntity1LogoUrl);
+
+  // The payment entity logo should have been placed into the network_bitmap.
+  ASSERT_TRUE(created_payment_app);
+  EXPECT_TRUE(created_payment_app->network_bitmap());
+  EXPECT_FALSE(created_payment_app->issuer_bitmap());
+}
+
+// Tests that at most two PaymentEntityLogos are accepted by
+// SecurePaymentConfirmationAppFactory, and that additional logos are just
+// silently dropped.
+TEST_F(SecurePaymentConfirmationAppFactoryPaymentEntitiesLogosTest,
+       MoreThanTwoPaymentEntityLogos) {
+  auto method_data = mojom::PaymentMethodData::New();
+  method_data->supported_method = "secure-payment-confirmation";
+  mojom::SecurePaymentConfirmationRequestPtr spc_request =
+      CreateSecurePaymentConfirmationRequest();
+  spc_request->payment_entities_logos.push_back(mojom::PaymentEntityLogo::New(
+      kPaymentEntity1LogoUrl, "Payment Entity 1"));
+  spc_request->payment_entities_logos.push_back(mojom::PaymentEntityLogo::New(
+      kPaymentEntity2LogoUrl, "Payment Entity 2"));
+  spc_request->payment_entities_logos.push_back(mojom::PaymentEntityLogo::New(
+      kPaymentEntity3LogoUrl, "Payment Entity 3"));
+  method_data->secure_payment_confirmation = std::move(spc_request);
+
+  std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate =
+      CreateMockDelegate(std::move(method_data));
+
+  std::unique_ptr<PaymentApp> created_payment_app;
+  EXPECT_CALL(*mock_delegate, OnPaymentAppCreated(_))
+      .WillOnce(MoveArg<0>(&created_payment_app));
+
+  secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr());
+
+  FakeCredentialFetchedFromDatabase(credential_id_bytes_);
+  FakeImageDownloaded(kInstrumentIconUrl);
+  FakeImageDownloaded(kPaymentEntity1LogoUrl);
+  FakeImageDownloaded(kPaymentEntity2LogoUrl);
+
+  // Even though the third entity logo was not downloaded (and was not attempted
+  // to be downloaded), the first two should be sufficient and the payment app
+  // should be created.
+  ASSERT_TRUE(created_payment_app);
+}
 
 class SecurePaymentConfirmationAppFactoryUsingCredentialStoreAPIsTest
     : public SecurePaymentConfirmationAppFactoryTest {
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/SelectParserRelaxationEnabled.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/SelectParserRelaxationEnabled.yaml
index 8f4cd8db..fb66a56 100644
--- a/components/policy/resources/templates/policy_definitions/Miscellaneous/SelectParserRelaxationEnabled.yaml
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/SelectParserRelaxationEnabled.yaml
@@ -1,6 +1,6 @@
 caption: Controls whether the new HTML parser behavior for the &lt;select&gt; element is enabled
 desc: |2-
-  The HTML parser is being changed to allow additional HTML tags inside the &lt;select&gt; element. This policy allows the old HTML parser behavior to be used until M136.
+  The HTML parser is being changed to allow additional HTML tags inside the &lt;select&gt; element. This policy allows the old HTML parser behavior to be used until M138.
 
   If this policy is enabled or not set, then the HTML parser will allow additional tags inside the &lt;select&gt; element.
 
@@ -22,11 +22,11 @@
 schema:
   type: boolean
 supported_on:
-- chrome.*:131-
-- chrome_os:131-
-- android:131-
-- webview_android:131-
+- chrome.*:131-138
+- chrome_os:131-138
+- android:131-138
+- webview_android:131-138
 tags: []
 type: main
-deprecated: false
+deprecated: true
 device_only: false
diff --git a/components/policy/test/data/pref_mapping/SelectParserRelaxationEnabled.json b/components/policy/test/data/pref_mapping/SelectParserRelaxationEnabled.json
index ab323ce1..5868d79 100644
--- a/components/policy/test/data/pref_mapping/SelectParserRelaxationEnabled.json
+++ b/components/policy/test/data/pref_mapping/SelectParserRelaxationEnabled.json
@@ -1,20 +1,5 @@
 [
   {
-    "os": [
-      "win",
-      "linux",
-      "mac",
-      "chromeos",
-      "android",
-      "fuchsia"
-    ],
-    "simple_policy_pref_mapping_test": {
-      "pref_name": "policy.select_parser_relaxation_enabled",
-      "default_value": true,
-      "values_to_test": [
-        true,
-        false
-      ]
-    }
+    "reason_for_missing_test": "Policy was removed"
   }
 ]
diff --git a/components/safe_browsing/content/browser/client_side_detection_service.cc b/components/safe_browsing/content/browser/client_side_detection_service.cc
index 0d731614..98cb559 100644
--- a/components/safe_browsing/content/browser/client_side_detection_service.cc
+++ b/components/safe_browsing/content/browser/client_side_detection_service.cc
@@ -392,8 +392,7 @@
     LogAuthenticatedCookieResets(
         *resource_request,
         SafeBrowsingAuthenticatedEndpoint::kClientSideDetection);
-    SetAccessTokenAndClearCookieInResourceRequest(resource_request.get(),
-                                                  access_token);
+    SetAccessToken(resource_request.get(), access_token);
   }
 
   resource_request->url = GetClientReportUrl(kClientReportPhishingUrl);
diff --git a/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc b/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc
index 70e8a659..e26c50b8 100644
--- a/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc
+++ b/components/safe_browsing/content/browser/password_protection/password_protection_service_unittest.cc
@@ -1050,18 +1050,15 @@
        TestPasswordOnFocusRequestEnhancedProtectionShouldHaveToken) {
   histograms_.ExpectTotalCount(kPasswordOnFocusRequestWithTokenHistogram, 0);
   SetEnhancedProtectionPrefForTests(&test_pref_service_, true);
-  SetFeatures(
-      /*enable_features*/ {kSafeBrowsingRemoveCookiesInAuthRequests},
-      /*disable_features*/ {});
   std::string access_token = "fake access token";
   test_url_loader_factory_.SetInterceptor(
       base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
         EXPECT_THAT(
             request.headers.GetHeader(net::HttpRequestHeaders::kAuthorization),
             testing::Optional("Bearer " + access_token));
-        // Cookies should be removed when token is set.
+        // Cookies should still be included when token is set.
         EXPECT_EQ(request.credentials_mode,
-                  network::mojom::CredentialsMode::kOmit);
+                  network::mojom::CredentialsMode::kInclude);
       }));
   // Set up mock call to token fetcher.
   SafeBrowsingTokenFetcher::Callback cb;
diff --git a/components/safe_browsing/core/browser/password_protection/password_protection_request.cc b/components/safe_browsing/core/browser/password_protection/password_protection_request.cc
index 47f33480..47e154bd 100644
--- a/components/safe_browsing/core/browser/password_protection/password_protection_request.cc
+++ b/components/safe_browsing/core/browser/password_protection/password_protection_request.cc
@@ -362,8 +362,7 @@
     LogAuthenticatedCookieResets(
         *resource_request,
         SafeBrowsingAuthenticatedEndpoint::kPasswordProtection);
-    SetAccessTokenAndClearCookieInResourceRequest(resource_request.get(),
-                                                  access_token);
+    SetAccessToken(resource_request.get(), access_token);
   }
   resource_request->url =
       PasswordProtectionServiceBase::GetPasswordProtectionRequestUrl();
diff --git a/components/safe_browsing/core/browser/ping_manager.cc b/components/safe_browsing/core/browser/ping_manager.cc
index 2e107953..999da91 100644
--- a/components/safe_browsing/core/browser/ping_manager.cc
+++ b/components/safe_browsing/core/browser/ping_manager.cc
@@ -468,8 +468,7 @@
   if (!access_token.empty()) {
     LogAuthenticatedCookieResets(
         *resource_request, SafeBrowsingAuthenticatedEndpoint::kThreatDetails);
-    SetAccessTokenAndClearCookieInResourceRequest(resource_request.get(),
-                                                  access_token);
+    SetAccessToken(resource_request.get(), access_token);
   }
   base::UmaHistogramBoolean(
       "SafeBrowsing.ClientSafeBrowsingReport.RequestHasToken",
diff --git a/components/safe_browsing/core/browser/ping_manager_unittest.cc b/components/safe_browsing/core/browser/ping_manager_unittest.cc
index 7b4f872..3860ec1e 100644
--- a/components/safe_browsing/core/browser/ping_manager_unittest.cc
+++ b/components/safe_browsing/core/browser/ping_manager_unittest.cc
@@ -68,8 +68,7 @@
   void RunReportThreatDetailsTest(
       bool expect_access_token,
       std::optional<ChromeUserPopulation> expected_user_population,
-      std::optional<std::string> expected_page_load_token_value,
-      bool expect_cookies_removed);
+      std::optional<std::string> expected_page_load_token_value);
   PingManager* ping_manager();
   void SetNewPingManager(
       std::optional<base::RepeatingCallback<bool()>>
@@ -81,7 +80,6 @@
           get_page_load_token_callback,
       std::optional<base::RepeatingCallback<bool()>>
           get_should_send_persisted_report);
-  void SetUpFeatureList(bool should_enable_remove_cookies);
   // Returns a copy of the serialized persisted report that can be used to
   // verify the data sent through URL loader.
   std::string CallPersistThreatDetails(const std::string& url);
@@ -144,17 +142,6 @@
           base::BindRepeating([]() { return false; }))));
 }
 
-void PingManagerTest::SetUpFeatureList(bool should_enable_remove_cookies) {
-  std::vector<base::test::FeatureRef> enabled_features;
-  std::vector<base::test::FeatureRef> disabled_features;
-  if (should_enable_remove_cookies) {
-    enabled_features.push_back(kSafeBrowsingRemoveCookiesInAuthRequests);
-  } else {
-    disabled_features.push_back(kSafeBrowsingRemoveCookiesInAuthRequests);
-  }
-  feature_list_.InitWithFeatures(enabled_features, disabled_features);
-}
-
 std::string PingManagerTest::CallPersistThreatDetails(const std::string& url) {
   std::unique_ptr<ClientSafeBrowsingReportRequest> report =
       std::make_unique<ClientSafeBrowsingReportRequest>();
@@ -190,8 +177,7 @@
 void PingManagerTest::RunReportThreatDetailsTest(
     bool expect_access_token,
     std::optional<ChromeUserPopulation> expected_user_population,
-    std::optional<std::string> expected_page_load_token_value,
-    bool expect_cookies_removed) {
+    std::optional<std::string> expected_page_load_token_value) {
   base::HistogramTester histogram_tester;
   TestSafeBrowsingTokenFetcher* raw_token_fetcher = SetUpTokenFetcher();
   std::string input_report_content;
@@ -230,9 +216,7 @@
                                  testing::Optional("Bearer " + access_token),
                                  std::nullopt));
         EXPECT_EQ(request.credentials_mode,
-                  expect_cookies_removed
-                      ? network::mojom::CredentialsMode::kOmit
-                      : network::mojom::CredentialsMode::kInclude);
+                  network::mojom::CredentialsMode::kInclude);
         histogram_tester.ExpectUniqueSample(
             "SafeBrowsing.ClientSafeBrowsingReport.RequestHasToken",
             /*sample=*/expect_access_token,
@@ -539,11 +523,9 @@
       /*get_user_population_callback=*/std::nullopt,
       /*get_page_load_token_callback=*/std::nullopt,
       /*get_should_send_persisted_report=*/std::nullopt);
-  SetUpFeatureList(/*should_enable_remove_cookies=*/true);
   RunReportThreatDetailsTest(/*expect_access_token=*/true,
                              /*expected_user_population=*/std::nullopt,
-                             /*expected_page_load_token_value=*/std::nullopt,
-                             /*expect_cookies_removed=*/true);
+                             /*expected_page_load_token_value=*/std::nullopt);
 }
 TEST_F(PingManagerTest,
        ReportThreatDetailsWithAccessToken_RemoveCookiesFeatureDisabled) {
@@ -553,11 +535,9 @@
       /*get_user_population_callback=*/std::nullopt,
       /*get_page_load_token_callback=*/std::nullopt,
       /*get_should_send_persisted_report=*/std::nullopt);
-  SetUpFeatureList(/*should_enable_remove_cookies=*/false);
   RunReportThreatDetailsTest(/*expect_access_token=*/true,
                              /*expected_user_population=*/std::nullopt,
-                             /*expected_page_load_token_value=*/std::nullopt,
-                             /*expect_cookies_removed=*/false);
+                             /*expected_page_load_token_value=*/std::nullopt);
 }
 TEST_F(PingManagerTest, ReportThreatDetailsWithUserPopulation) {
   SetNewPingManager(
@@ -573,8 +553,7 @@
   population.set_user_population(ChromeUserPopulation::SAFE_BROWSING);
   RunReportThreatDetailsTest(/*expect_access_token=*/false,
                              /*expected_user_population=*/population,
-                             /*expected_page_load_token_value=*/std::nullopt,
-                             /*expect_cookies_removed=*/false);
+                             /*expected_page_load_token_value=*/std::nullopt);
 }
 TEST_F(PingManagerTest, ReportThreatDetailsWithPageLoadToken) {
   base::HistogramTester histogram_tester;
@@ -590,8 +569,7 @@
   RunReportThreatDetailsTest(
       /*expect_access_token=*/false,
       /*expected_user_population=*/std::nullopt,
-      /*expected_page_load_token_value=*/"testing_page_load_token",
-      /*expect_cookies_removed=*/false);
+      /*expected_page_load_token_value=*/"testing_page_load_token");
 }
 
 TEST_F(PingManagerTest, PersistThreatDetailsAtShutdown) {
diff --git a/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc b/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc
index ce07457..bfd765f 100644
--- a/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc
+++ b/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc
@@ -712,8 +712,7 @@
     LogAuthenticatedCookieResets(
         *resource_request,
         SafeBrowsingAuthenticatedEndpoint::kRealtimeUrlLookup);
-    SetAccessTokenAndClearCookieInResourceRequest(resource_request.get(),
-                                                  access_token_string);
+    SetAccessToken(resource_request.get(), access_token_string);
   }
   RecordBooleanWithAndWithoutSuffix("SafeBrowsing.RT.HasTokenInRequest",
                                     GetMetricSuffix(),
diff --git a/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc b/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
index d6e812b..b0820f5 100644
--- a/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
+++ b/components/safe_browsing/core/browser/realtime/url_lookup_service_unittest.cc
@@ -594,7 +594,7 @@
 
 TEST_F(RealTimeUrlLookupServiceTest, TestStartLookup_PendingRequestForSameUrl) {
   base::HistogramTester histograms;
-  EnableRealTimeUrlLookup({kSafeBrowsingRemoveCookiesInAuthRequests}, {});
+  EnableRealTimeUrlLookup({}, {});
   GURL url(kTestUrl);
   SetUpRTLookupResponse(RTLookupResponse::ThreatInfo::DANGEROUS,
                         RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, 60,
@@ -674,7 +674,7 @@
 TEST_F(RealTimeUrlLookupServiceTest,
        TestStartLookup_PingWithTokenUpdatesEsbProtegoPingWithTokenLastLogTime) {
   base::HistogramTester histograms;
-  EnableRealTimeUrlLookup({kSafeBrowsingRemoveCookiesInAuthRequests}, {});
+  EnableRealTimeUrlLookup({}, {});
   EnableTokenFetchesInClient();
   SetSafeBrowsingState(&test_pref_service_,
                        SafeBrowsingState::ENHANCED_PROTECTION);
@@ -704,7 +704,7 @@
 TEST_F(
     RealTimeUrlLookupServiceTest,
     TestStartLookup_PingWithoutTokenSetsEsbProtegoPingWithoutTokenLastLogTime) {
-  EnableRealTimeUrlLookup({kSafeBrowsingRemoveCookiesInAuthRequests}, {});
+  EnableRealTimeUrlLookup({}, {});
   DisableTokenFetchesInClient();
   SetSafeBrowsingState(&test_pref_service_,
                        SafeBrowsingState::ENHANCED_PROTECTION);
@@ -728,7 +728,7 @@
 TEST_F(
     RealTimeUrlLookupServiceTest,
     TestStartLookup_DoesNotSetEsbProtegoPingWithTokenLastLogTimeWhenCacheIsHit) {
-  EnableRealTimeUrlLookup({kSafeBrowsingRemoveCookiesInAuthRequests}, {});
+  EnableRealTimeUrlLookup({}, {});
   EnableTokenFetchesInClient();
   SetSafeBrowsingState(&test_pref_service_,
                        SafeBrowsingState::ENHANCED_PROTECTION);
@@ -753,7 +753,7 @@
 TEST_F(
     RealTimeUrlLookupServiceTest,
     TestStartLookup_DoesNotSetEsbProtegoPingWithoutTokenLastLogTimeWhenCacheIsHit) {
-  EnableRealTimeUrlLookup({kSafeBrowsingRemoveCookiesInAuthRequests}, {});
+  EnableRealTimeUrlLookup({}, {});
   DisableTokenFetchesInClient();
   SetSafeBrowsingState(&test_pref_service_,
                        SafeBrowsingState::ENHANCED_PROTECTION);
@@ -779,7 +779,7 @@
 TEST_F(
     RealTimeUrlLookupServiceTest,
     TestStartLookup_DoesNotSetEsbProtegoPingWithTokenLastLogTimeWhenEsbIsDisabled) {
-  EnableRealTimeUrlLookup({kSafeBrowsingRemoveCookiesInAuthRequests}, {});
+  EnableRealTimeUrlLookup({}, {});
   SetSafeBrowsingState(&test_pref_service_,
                        SafeBrowsingState::STANDARD_PROTECTION);
   EnableTokenFetchesInClient();
@@ -804,7 +804,7 @@
 TEST_F(
     RealTimeUrlLookupServiceTest,
     TestStartLookup_DoesNotSetEsbProtegoPingWithoutTokenLastLogTimeWhenEsbIsDisabled) {
-  EnableRealTimeUrlLookup({kSafeBrowsingRemoveCookiesInAuthRequests}, {});
+  EnableRealTimeUrlLookup({}, {});
   SetSafeBrowsingState(&test_pref_service_,
                        SafeBrowsingState::STANDARD_PROTECTION);
   DisableTokenFetchesInClient();
@@ -829,8 +829,7 @@
 TEST_F(RealTimeUrlLookupServiceTest,
        TestStartLookup_AttachTokenWhenWithTokenIsEnabled) {
   base::HistogramTester histograms;
-  EnableRealTimeUrlLookup(
-      {kSafeBrowsingRemoveCookiesInAuthRequests, kLocalIpAddressInEvents}, {});
+  EnableRealTimeUrlLookup({kLocalIpAddressInEvents}, {});
   EnableTokenFetchesInClient();
   GURL url(kTestUrl);
   SetUpRTLookupResponse(RTLookupResponse::ThreatInfo::DANGEROUS,
@@ -852,10 +851,9 @@
         EXPECT_FALSE(request_proto.has_profile_dm_token());
         EXPECT_FALSE(request_proto.has_client_reporting_metadata());
         EXPECT_TRUE(request_proto.local_ips().empty());
-
-        // Cookies should be removed when token is set.
+        // Cookies should still be included when token is set.
         EXPECT_EQ(request.credentials_mode,
-                  network::mojom::CredentialsMode::kOmit);
+                  network::mojom::CredentialsMode::kInclude);
         EXPECT_THAT(
             request.headers.GetHeader(net::HttpRequestHeaders::kAuthorization),
             testing::Optional(std::string("Bearer access_token_string")));
@@ -885,8 +883,7 @@
 TEST_F(RealTimeUrlLookupServiceTest,
        TestStartLookup_NoTokenWhenTokenIsUnavailable) {
   base::HistogramTester histograms;
-  EnableRealTimeUrlLookup(
-      {kSafeBrowsingRemoveCookiesInAuthRequests, kLocalIpAddressInEvents}, {});
+  EnableRealTimeUrlLookup({kLocalIpAddressInEvents}, {});
   EnableTokenFetchesInClient();
   GURL url(kTestUrl);
   SetUpRTLookupResponse(RTLookupResponse::ThreatInfo::DANGEROUS,
diff --git a/components/safe_browsing/core/common/BUILD.gn b/components/safe_browsing/core/common/BUILD.gn
index 092a289..95ba4a4 100644
--- a/components/safe_browsing/core/common/BUILD.gn
+++ b/components/safe_browsing/core/common/BUILD.gn
@@ -48,7 +48,6 @@
     "safe_browsing_policy_handler_unittest.cc",
     "safe_browsing_prefs_unittest.cc",
     "scheme_logger_unittest.cc",
-    "utils_unittest.cc",
   ]
 
   deps = [
diff --git a/components/safe_browsing/core/common/features.cc b/components/safe_browsing/core/common/features.cc
index 3bbe881..8b595d4c 100644
--- a/components/safe_browsing/core/common/features.cc
+++ b/components/safe_browsing/core/common/features.cc
@@ -296,10 +296,6 @@
     &kSafeBrowsingDailyPhishingReportsLimit,
     /*name=*/"kMaxReportsPerIntervalESB", /*default_value=*/10};
 
-BASE_FEATURE(kSafeBrowsingRemoveCookiesInAuthRequests,
-             "SafeBrowsingRemoveCookiesInAuthRequests",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 #if BUILDFLAG(IS_ANDROID)
 BASE_FEATURE(kSafeBrowsingSyncCheckerCheckAllowlist,
              "SafeBrowsingSyncCheckerCheckAllowlist",
@@ -379,7 +375,6 @@
       &kLocalListsUseSBv5,
       &kOnDeviceNotificationContentDetectionModel,
       &kReportNotificationContentDetectionData,
-      &kSafeBrowsingRemoveCookiesInAuthRequests,
       &kSafetyHubAbusiveNotificationRevocation,
       &kShowWarningsForSuspiciousNotifications,
       &kSuspiciousSiteTriggerQuotaFeature,
diff --git a/components/safe_browsing/core/common/features.h b/components/safe_browsing/core/common/features.h
index 2103696..4a6dc3b7 100644
--- a/components/safe_browsing/core/common/features.h
+++ b/components/safe_browsing/core/common/features.h
@@ -288,9 +288,6 @@
 // Specifies the CSD-Phishing daily reports limit for ESB users
 extern const base::FeatureParam<int> kSafeBrowsingDailyPhishingReportsLimitESB;
 
-// Controls whether cookies are removed when the access token is present.
-BASE_DECLARE_FEATURE(kSafeBrowsingRemoveCookiesInAuthRequests);
-
 #if BUILDFLAG(IS_ANDROID)
 // Enables sync checker to check allowlist first on Chrome on Android. This is
 // an optimization to improve the speed of Safe Browsing checks.
diff --git a/components/safe_browsing/core/common/utils.cc b/components/safe_browsing/core/common/utils.cc
index 6100f6e3..8aef94c4 100644
--- a/components/safe_browsing/core/common/utils.cc
+++ b/components/safe_browsing/core/common/utils.cc
@@ -160,15 +160,11 @@
                                   .InitWithNewPipeAndPassReceiver());
 }
 
-void SetAccessTokenAndClearCookieInResourceRequest(
-    network::ResourceRequest* resource_request,
-    const std::string& access_token) {
+void SetAccessToken(network::ResourceRequest* resource_request,
+                    const std::string& access_token) {
   resource_request->headers.SetHeader(
       net::HttpRequestHeaders::kAuthorization,
       base::StrCat({kAuthHeaderBearer, access_token}));
-  if (base::FeatureList::IsEnabled(kSafeBrowsingRemoveCookiesInAuthRequests)) {
-    resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
-  }
 }
 
 void RecordHttpResponseOrErrorCode(const char* metric_name,
diff --git a/components/safe_browsing/core/common/utils.h b/components/safe_browsing/core/common/utils.h
index afaf1af..5d59997 100644
--- a/components/safe_browsing/core/common/utils.h
+++ b/components/safe_browsing/core/common/utils.h
@@ -63,7 +63,7 @@
 bool CanGetReputationOfUrl(const GURL& url);
 
 // List of callers of
-// `SetAccessTokenAndClearCookieInResourceRequest`. This is used for
+// `SetAccessToken`. This is used for
 // logging the histogram SafeBrowsing.AuthenticatedCookieResetEndpoint.
 //
 // These values are persisted to logs. Entries should not be renumbered and
@@ -84,11 +84,9 @@
 void LogAuthenticatedCookieResets(network::ResourceRequest& resource_request,
                                   SafeBrowsingAuthenticatedEndpoint endpoint);
 
-// Set |access_token| in |resource_request|. Remove cookies in the request
-// since we only need one identifier.
-void SetAccessTokenAndClearCookieInResourceRequest(
-    network::ResourceRequest* resource_request,
-    const std::string& access_token);
+// Set |access_token| in |resource_request|.
+void SetAccessToken(network::ResourceRequest* resource_request,
+                    const std::string& access_token);
 
 // Record HTTP response code when there's no error in fetching an HTTP
 // request, and the error code, when there is.
diff --git a/components/safe_browsing/core/common/utils_unittest.cc b/components/safe_browsing/core/common/utils_unittest.cc
deleted file mode 100644
index 1ed2981..0000000
--- a/components/safe_browsing/core/common/utils_unittest.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/safe_browsing/core/common/utils.h"
-
-#include "base/test/scoped_feature_list.h"
-#include "components/safe_browsing/core/common/features.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace safe_browsing {
-
-TEST(SafeBrowsingUtilsTest, TestSetAccessTokenAndClearCookieInResourceRequest) {
-  auto resource_request = std::make_unique<network::ResourceRequest>();
-  std::string access_token = "123";
-  // Feature disabled
-  {
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitAndDisableFeature(
-        safe_browsing::kSafeBrowsingRemoveCookiesInAuthRequests);
-
-    SetAccessTokenAndClearCookieInResourceRequest(resource_request.get(),
-                                                  access_token);
-    EXPECT_THAT(resource_request->headers.GetHeader(
-                    net::HttpRequestHeaders::kAuthorization),
-                testing::Optional(std::string("Bearer 123")));
-    // Cookies are attached when the feature is disabled.
-    EXPECT_EQ(resource_request->credentials_mode,
-              network::mojom::CredentialsMode::kInclude);
-  }
-
-  // Feature enabled
-  {
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitAndEnableFeature(
-        safe_browsing::kSafeBrowsingRemoveCookiesInAuthRequests);
-
-    SetAccessTokenAndClearCookieInResourceRequest(resource_request.get(),
-                                                  access_token);
-    EXPECT_THAT(resource_request->headers.GetHeader(
-                    net::HttpRequestHeaders::kAuthorization),
-                testing::Optional(std::string("Bearer 123")));
-    // Cookies are removed when the feature is enabled.
-    EXPECT_EQ(resource_request->credentials_mode,
-              network::mojom::CredentialsMode::kOmit);
-  }
-
-  // Feature enabled, request omits cookies already.
-  {
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitAndEnableFeature(
-        safe_browsing::kSafeBrowsingRemoveCookiesInAuthRequests);
-    resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
-    SetAccessTokenAndClearCookieInResourceRequest(resource_request.get(),
-                                                  access_token);
-    EXPECT_THAT(resource_request->headers.GetHeader(
-                    net::HttpRequestHeaders::kAuthorization),
-                testing::Optional(std::string("Bearer 123")));
-    // The request should keep omitting cookies.
-    EXPECT_EQ(resource_request->credentials_mode,
-              network::mojom::CredentialsMode::kOmit);
-  }
-}
-
-}  // namespace safe_browsing
diff --git a/components/saved_tab_groups/public/string_utils.cc b/components/saved_tab_groups/public/string_utils.cc
index 11e35ec..786a3cf 100644
--- a/components/saved_tab_groups/public/string_utils.cc
+++ b/components/saved_tab_groups/public/string_utils.cc
@@ -17,11 +17,38 @@
     return l10n_util::GetStringUTF16(IDS_SAVED_TAB_GROUPS_CREATION_JUST_NOW);
   }
 
-  std::u16string elapsed_time_str = ui::TimeFormat::SimpleWithMonthAndYear(
-      ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_LONG,
-      elapsed_time_since_creation, true);
-  return l10n_util::GetStringFUTF16(IDS_SAVED_TAB_GROUPS_CREATION_FORMAT,
-                                    elapsed_time_str);
+  if (elapsed_time_since_creation < base::Hours(1)) {
+    return l10n_util::GetPluralStringFUTF16(
+        IDS_SAVED_TAB_GROUPS_CREATION_MINUTES_AGO_FORMAT,
+        elapsed_time_since_creation.InMinutes());
+  }
+
+  constexpr base::TimeDelta kDay = base::Days(1);
+
+  if (elapsed_time_since_creation < kDay) {
+    return l10n_util::GetPluralStringFUTF16(
+        IDS_SAVED_TAB_GROUPS_CREATION_HOURS_AGO_FORMAT,
+        elapsed_time_since_creation.InHours());
+  }
+
+  // An average month is a twelfth of an average year.
+  const float average_month_in_days = 365.25 / 12;
+
+  if (elapsed_time_since_creation < average_month_in_days * kDay) {
+    return l10n_util::GetPluralStringFUTF16(
+        IDS_SAVED_TAB_GROUPS_CREATION_DAYS_AGO_FORMAT,
+        elapsed_time_since_creation.InDays());
+  }
+
+  if (elapsed_time_since_creation < 365 * kDay) {
+    return l10n_util::GetPluralStringFUTF16(
+        IDS_SAVED_TAB_GROUPS_CREATION_MONTHS_AGO_FORMAT,
+        elapsed_time_since_creation.InDays() / average_month_in_days);
+  }
+
+  return l10n_util::GetPluralStringFUTF16(
+      IDS_SAVED_TAB_GROUPS_CREATION_YEARS_AGO_FORMAT,
+      elapsed_time_since_creation.InDays() / 365);
 }
 
 }  // namespace tab_groups
diff --git a/components/saved_tab_groups_strings.grdp b/components/saved_tab_groups_strings.grdp
index f371670..e78d5f2 100644
--- a/components/saved_tab_groups_strings.grdp
+++ b/components/saved_tab_groups_strings.grdp
@@ -1,9 +1,31 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
-  <message name="IDS_SAVED_TAB_GROUPS_CREATION_FORMAT" desc="Subtitle of a synced tab group in Tab Groups panel in Tab Grid representing the elapsed time since its creation.">
-    Created <ph name="DATE"><ex>2 days ago</ex>$1</ph>
-  </message>
   <message name="IDS_SAVED_TAB_GROUPS_CREATION_JUST_NOW" desc="Subtitle of a synced tab group in Tab Groups panel in Tab Grid that just got created.">
     Created just now
   </message>
+  <message name="IDS_SAVED_TAB_GROUPS_CREATION_MINUTES_AGO_FORMAT" desc="Subtitle of a synced tab group in Tab Groups panel in Tab Grid representing the elapsed time since its creation, when the time is counted in minutes.">
+    {NUM_MINS, plural,
+       =1 {Created 1 minute ago}
+       other {Created {NUM_MINS} minutes ago}}
+  </message>
+  <message name="IDS_SAVED_TAB_GROUPS_CREATION_HOURS_AGO_FORMAT" desc="Subtitle of a synced tab group in Tab Groups panel in Tab Grid representing the elapsed time since its creation, when the time is counted in hours.">
+    {NUM_HOURS, plural,
+       =1 {Created 1 hour ago}
+       other {Created {NUM_HOURS} hours ago}}
+  </message>
+  <message name="IDS_SAVED_TAB_GROUPS_CREATION_DAYS_AGO_FORMAT" desc="Subtitle of a synced tab group in Tab Groups panel in Tab Grid representing the elapsed time since its creation, when the time is counted in days.">
+    {NUM_DAYS, plural,
+       =1 {Created 1 day ago}
+       other {Created {NUM_DAYS} days ago}}
+  </message>
+  <message name="IDS_SAVED_TAB_GROUPS_CREATION_MONTHS_AGO_FORMAT" desc="Subtitle of a synced tab group in Tab Groups panel in Tab Grid representing the elapsed time since its creation, when the time is counted in months.">
+    {NUM_MONTHS, plural,
+       =1 {Created 1 month ago}
+       other {Created {NUM_MONTHS} months ago}}
+  </message>
+  <message name="IDS_SAVED_TAB_GROUPS_CREATION_YEARS_AGO_FORMAT" desc="Subtitle of a synced tab group in Tab Groups panel in Tab Grid representing the elapsed time since its creation, when the time is counted in years.">
+    {NUM_YEARS, plural,
+       =1 {Created 1 year ago}
+       other {Created {NUM_YEARS} years ago}}
+  </message>
 </grit-part>
diff --git a/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_DAYS_AGO_FORMAT.png.sha1 b/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_DAYS_AGO_FORMAT.png.sha1
new file mode 100644
index 0000000..88c8770
--- /dev/null
+++ b/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_DAYS_AGO_FORMAT.png.sha1
@@ -0,0 +1 @@
+cfe9ef9dc702efcf386bbae89b36e844e5f29b3e
\ No newline at end of file
diff --git a/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_FORMAT.png.sha1 b/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_FORMAT.png.sha1
deleted file mode 100644
index 53f2f012..0000000
--- a/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_FORMAT.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-508d5924dcd99f7062bb910980681af04dde67dd
\ No newline at end of file
diff --git a/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_HOURS_AGO_FORMAT.png.sha1 b/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_HOURS_AGO_FORMAT.png.sha1
new file mode 100644
index 0000000..88c8770
--- /dev/null
+++ b/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_HOURS_AGO_FORMAT.png.sha1
@@ -0,0 +1 @@
+cfe9ef9dc702efcf386bbae89b36e844e5f29b3e
\ No newline at end of file
diff --git a/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_MINUTES_AGO_FORMAT.png.sha1 b/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_MINUTES_AGO_FORMAT.png.sha1
new file mode 100644
index 0000000..88c8770
--- /dev/null
+++ b/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_MINUTES_AGO_FORMAT.png.sha1
@@ -0,0 +1 @@
+cfe9ef9dc702efcf386bbae89b36e844e5f29b3e
\ No newline at end of file
diff --git a/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_MONTHS_AGO_FORMAT.png.sha1 b/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_MONTHS_AGO_FORMAT.png.sha1
new file mode 100644
index 0000000..88c8770
--- /dev/null
+++ b/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_MONTHS_AGO_FORMAT.png.sha1
@@ -0,0 +1 @@
+cfe9ef9dc702efcf386bbae89b36e844e5f29b3e
\ No newline at end of file
diff --git a/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_YEARS_AGO_FORMAT.png.sha1 b/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_YEARS_AGO_FORMAT.png.sha1
new file mode 100644
index 0000000..88c8770
--- /dev/null
+++ b/components/saved_tab_groups_strings_grdp/IDS_SAVED_TAB_GROUPS_CREATION_YEARS_AGO_FORMAT.png.sha1
@@ -0,0 +1 @@
+cfe9ef9dc702efcf386bbae89b36e844e5f29b3e
\ No newline at end of file
diff --git a/components/search_engines/search_engine_choice/search_engine_choice_service.cc b/components/search_engines/search_engine_choice/search_engine_choice_service.cc
index 117acdbb..ebe4df0 100644
--- a/components/search_engines/search_engine_choice/search_engine_choice_service.cc
+++ b/components/search_engines/search_engine_choice/search_engine_choice_service.cc
@@ -493,7 +493,7 @@
                      : "no")
               : "no value");
 
-      NOTREACHED(base::NotFatalUntil::M138);
+      NOTREACHED(base::NotFatalUntil::M141);
       caller_trace_key.Clear();
     }
   }
diff --git a/components/services/on_device_translation/public/cpp/features.cc b/components/services/on_device_translation/public/cpp/features.cc
index e95bd745..2c8eeda 100644
--- a/components/services/on_device_translation/public/cpp/features.cc
+++ b/components/services/on_device_translation/public/cpp/features.cc
@@ -15,10 +15,6 @@
 
 namespace {
 
-// Limit the number of downloadable language packs to 5 during OT to mitigate
-// the risk of fingerprinting attacks.
-constexpr size_t kTranslationAPILimitLanguagePackCountMax = 5;
-
 base::FilePath GetPathFromCommandLine(const char* switch_name) {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (!command_line->HasSwitch(switch_name)) {
@@ -33,14 +29,6 @@
     &blink::features::kTranslationAPI, "TranslationAPILibraryMinimumVersion",
     "2025.1.10.0"};
 
-const base::FeatureParam<bool> kTranslationAPIAcceptLanguagesCheck{
-    &blink::features::kTranslationAPI, "TranslationAPIAcceptLanguagesCheck",
-    true};
-
-const base::FeatureParam<bool> kTranslationAPILimitLanguagePackCount{
-    &blink::features::kTranslationAPI, "TranslationAPILimitLanguagePackCount",
-    true};
-
 const base::FeatureParam<base::TimeDelta> kTranslationAPIServiceIdleTimeout{
     &blink::features::kTranslationAPI, "TranslationAPIServiceIdleTimeout",
     base::Minutes(1)};
@@ -53,17 +41,6 @@
   return GetPathFromCommandLine(kTranslateKitBinaryPath);
 }
 
-size_t GetInstallablePackageCount(size_t installed_package_count) {
-  if (base::FeatureList::IsEnabled(blink::features::kTranslationAPIV1) ||
-      !kTranslationAPILimitLanguagePackCount.Get()) {
-    return std::numeric_limits<size_t>::max();
-  }
-  if (installed_package_count >= kTranslationAPILimitLanguagePackCountMax) {
-    return 0;
-  }
-  return kTranslationAPILimitLanguagePackCountMax - installed_package_count;
-}
-
 bool IsValidTranslateKitVersion(std::string_view version_str) {
   base::Version minimum_version(kTranslationAPILibraryMinimumVersion.Get());
   CHECK(minimum_version.IsValid());
diff --git a/components/services/on_device_translation/public/cpp/features.h b/components/services/on_device_translation/public/cpp/features.h
index 1a80c7d..279e820 100644
--- a/components/services/on_device_translation/public/cpp/features.h
+++ b/components/services/on_device_translation/public/cpp/features.h
@@ -23,18 +23,6 @@
 // Any version string longer than this will be truncated.
 inline constexpr size_t kTranslationAPILibraryVersionStringSize = 14;
 
-// When this feature param is enabled, the Translation API will fail if neither
-// the source nor destination language is in the AcceptLanguages. This is
-// introduced to mitigate privacy concerns.
-extern const base::FeatureParam<bool> kTranslationAPIAcceptLanguagesCheck;
-
-// This feature limits the number of language components downloaded by
-// createTranslator() to 3.
-extern const base::FeatureParam<bool> kTranslationAPILimitLanguagePackCount;
-
-// Returns the number of additionally installable language packs.
-size_t GetInstallablePackageCount(size_t installed_package_count);
-
 // The duration that the OnDeviceTranslation service can remain idle before it
 // is terminated.
 extern const base::FeatureParam<base::TimeDelta>
diff --git a/components/sync/service/sync_service_utils.h b/components/sync/service/sync_service_utils.h
index 96aa9f10..a41904c 100644
--- a/components/sync/service/sync_service_utils.h
+++ b/components/sync/service/sync_service_utils.h
@@ -60,7 +60,9 @@
   kAccountMenu = 5,
   // From the Password Manager Settings (currently used only on iOS).
   kPasswordManagerSettings = 6,
-  kMaxValue = kPasswordManagerSettings
+  // From the passwords keyboard accessory (only used on Android).
+  kPasswordManagerKeyboardAccessory = 7,
+  kMaxValue = kPasswordManagerKeyboardAccessory
 };
 // LINT.ThenChange(/tools/metrics/histograms/metadata/sync/enums.xml:TrustedVaultUserActionTrigger)
 
diff --git a/components/tab_groups/android/java/res/values/colors.xml b/components/tab_groups/android/java/res/values/colors.xml
index 03c2724..2509ec5 100644
--- a/components/tab_groups/android/java/res/values/colors.xml
+++ b/components/tab_groups/android/java/res/values/colors.xml
@@ -48,4 +48,16 @@
     <color name="tab_group_card_text_color_purple">@color/tab_group_card_text_color_purple_gm3</color>
     <color name="tab_group_card_text_color_red">@color/tab_group_card_text_color_red_gm3</color>
     <color name="tab_group_card_text_color_yellow">@color/tab_group_card_text_color_yellow_gm3</color>
+
+    <!-- Tab group card placeholder related colors. -->
+    <color name="tab_group_card_placeholder_color_grey">@color/baseline_neutral_90</color>
+    <color name="tab_group_card_placeholder_color_blue">@color/tab_group_card_placeholder_color_blue_gm3</color>
+    <color name="tab_group_card_placeholder_color_cyan">@color/tab_group_card_placeholder_color_cyan_gm3</color>
+    <color name="tab_group_card_placeholder_color_green">@color/tab_group_card_placeholder_color_green_gm3</color>
+    <color name="tab_group_card_placeholder_color_orange">@color/tab_group_card_placeholder_color_orange_gm3</color>
+    <color name="tab_group_card_placeholder_color_pink">@color/tab_group_card_placeholder_color_pink_gm3</color>
+    <color name="tab_group_card_placeholder_color_purple">@color/tab_group_card_placeholder_color_purple_gm3</color>
+    <color name="tab_group_card_placeholder_color_red">@color/tab_group_card_placeholder_color_red_gm3</color>
+    <color name="tab_group_card_placeholder_color_yellow">@color/tab_group_card_placeholder_color_yellow_gm3</color>
+
 </resources>
\ No newline at end of file
diff --git a/components/tab_groups/android/java/src/org/chromium/components/tab_groups/TabGroupColorPickerUtils.java b/components/tab_groups/android/java/src/org/chromium/components/tab_groups/TabGroupColorPickerUtils.java
index b77713ac..6e6f06a 100644
--- a/components/tab_groups/android/java/src/org/chromium/components/tab_groups/TabGroupColorPickerUtils.java
+++ b/components/tab_groups/android/java/src/org/chromium/components/tab_groups/TabGroupColorPickerUtils.java
@@ -190,6 +190,54 @@
         };
     }
 
+    /**
+     * Get the color corresponding to the color id that is passed in. Adjust the color depending on
+     * light/dark/incognito mode as well as dynamic color themes. This function should only be used
+     * for retrieving items from the tab group card mini thumbnail placeholder color.
+     *
+     * @param context The current context.
+     * @param colorId The color id corresponding to the color item in the color picker.
+     * @param isIncognito Whether the current tab model is in incognito mode.
+     */
+    public static @ColorInt int getTabGroupCardMiniThumbnailPlaceholderColor(
+            Context context, @TabGroupColorId int colorId, boolean isIncognito) {
+        @ColorRes int colorRes = getTabGroupCardMiniThumbnailPlaceholderColorResource(colorId);
+        return resolveGroupRelatedColor(context, colorRes, isIncognito);
+    }
+
+    /**
+     * Get the color resource corresponding to the respective color item. This function should only
+     * be used for retrieving items from the tab group color.
+     *
+     * @param colorId The color id corresponding to the color of the Tab Group.
+     */
+    public static @ColorRes int getTabGroupCardMiniThumbnailPlaceholderColorResource(
+            @TabGroupColorId int colorId) {
+        switch (colorId) {
+            case TabGroupColorId.GREY:
+                return R.color.tab_group_card_placeholder_color_grey;
+            case TabGroupColorId.BLUE:
+                return R.color.tab_group_card_placeholder_color_blue;
+            case TabGroupColorId.RED:
+                return R.color.tab_group_card_placeholder_color_red;
+            case TabGroupColorId.YELLOW:
+                return R.color.tab_group_card_placeholder_color_yellow;
+            case TabGroupColorId.GREEN:
+                return R.color.tab_group_card_placeholder_color_green;
+            case TabGroupColorId.PINK:
+                return R.color.tab_group_card_placeholder_color_pink;
+            case TabGroupColorId.PURPLE:
+                return R.color.tab_group_card_placeholder_color_purple;
+            case TabGroupColorId.CYAN:
+                return R.color.tab_group_card_placeholder_color_cyan;
+            case TabGroupColorId.ORANGE:
+                return R.color.tab_group_card_placeholder_color_orange;
+            default:
+                assert false : "Invalid tab group text color id " + colorId;
+                return Resources.ID_NULL;
+        }
+    }
+
     private static @ColorInt int resolveGroupRelatedColor(
             Context context, @ColorRes int colorRes, boolean isIncognito) {
         @ColorInt int color = ContextCompat.getColor(context, colorRes);
diff --git a/components/variations/service/variations_field_trial_creator_base.h b/components/variations/service/variations_field_trial_creator_base.h
index baddb78d..c181f10 100644
--- a/components/variations/service/variations_field_trial_creator_base.h
+++ b/components/variations/service/variations_field_trial_creator_base.h
@@ -288,17 +288,6 @@
   // safe mode. Used for variations seed testing.
   void LoadSeedFromJsonFile(const base::FilePath& json_seed_path);
 
-  // Returns whether the conditions to activate the limited entropy synthetic
-  // trial are met.
-  bool ShouldActivateLimitedEntropySyntheticTrial(const VariationsSeed& seed);
-
-  // Registers the group assignment of the limited entropy synthetic trial if
-  // the activation condition are met (as given by
-  // ShouldActivateLimitedEntropySyntheticTrial()).
-  void RegisterLimitedEntropySyntheticTrialIfNeeded(
-      const VariationsSeed& seed,
-      SyntheticTrialRegistry* synthetic_trial_registry);
-
   // Returns the seed store. Virtual for testing.
   virtual VariationsSeedStore* GetSeedStore();
 
diff --git a/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc b/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc
index bbb2e71..15e846e 100644
--- a/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc
+++ b/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc
@@ -78,17 +78,6 @@
   return gmb;
 }
 
-void InProcessGpuMemoryBufferManager::CopyGpuMemoryBufferAsync(
-    gfx::GpuMemoryBufferHandle buffer_handle,
-    base::UnsafeSharedMemoryRegion memory_region,
-    base::OnceCallback<void(bool)> callback) {
-  std::move(callback).Run(false);
-}
-
-bool InProcessGpuMemoryBufferManager::IsConnected() {
-  return true;
-}
-
 bool InProcessGpuMemoryBufferManager::OnMemoryDump(
     const base::trace_event::MemoryDumpArgs& args,
     base::trace_event::ProcessMemoryDump* pmd) {
diff --git a/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h b/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h
index 8e6e3725..41e631a 100644
--- a/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h
+++ b/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h
@@ -55,11 +55,6 @@
       gfx::BufferUsage usage,
       gpu::SurfaceHandle surface_handle,
       base::WaitableEvent* shutdown_event) override;
-  void CopyGpuMemoryBufferAsync(
-      gfx::GpuMemoryBufferHandle buffer_handle,
-      base::UnsafeSharedMemoryRegion memory_region,
-      base::OnceCallback<void(bool)> callback) override;
-  bool IsConnected() override;
 
   // base::trace_event::MemoryDumpProvider:
   bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
diff --git a/components/viz/service/layers/layer_context_impl_unittest.cc b/components/viz/service/layers/layer_context_impl_unittest.cc
index e1bc9de..099f5724 100644
--- a/components/viz/service/layers/layer_context_impl_unittest.cc
+++ b/components/viz/service/layers/layer_context_impl_unittest.cc
@@ -14,6 +14,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/types/expected.h"
+#include "cc/layers/surface_layer_impl.h"
 #include "cc/layers/texture_layer_impl.h"
 #include "cc/trees/layer_tree_host_impl.h"
 #include "cc/trees/layer_tree_impl.h"
@@ -39,6 +40,15 @@
 const float kDefaultExternalPageScaleFactor = 1.0f;
 const float kDefaultDeviceScaleFactor = 1.0f;
 const float kDefaultPaintedDeviceScaleFactor = 1.0f;
+const FrameSinkId kDefaultFrameSinkId = FrameSinkId(1, 1);
+const LocalSurfaceId kDefaultLocalSurfaceId(
+    1,
+    base::UnguessableToken::CreateForTesting(2u, 3u));
+const SurfaceId kDefaultSurfaceId(kDefaultFrameSinkId, kDefaultLocalSurfaceId);
+const SurfaceRange kDefaultSurfaceRange(std::nullopt, kDefaultSurfaceId);
+
+// Default Layer property values
+const gfx::Size kDefaultLayerBounds(10, 10);
 
 // Default TextureLayer property values
 const bool kDefaultBlendBackgroundColor = false;
@@ -46,13 +56,19 @@
 const gfx::PointF kDefaultUVTopLeft = gfx::PointF();
 const gfx::PointF kDefaultUVBottomRight = gfx::PointF(1.f, 1.f);
 
+// Default SurfaceLayer property values
+const uint32_t kDefaultDeadlineInFrames = 0u;
+const bool kDefaultStretchContentToFillBounds = false;
+const bool kDefaultSurfaceHitTestable = false;
+const bool kDefaultHasPointerEventsNone = false;
+const bool kDefaultIsReflection = false;
+const bool kDefaultWillDrawNeedsReset = false;
+const bool kDefaultOverrideChildPaintFlags = false;
+
 class LayerContextImplTest : public testing::Test {
  public:
   LayerContextImplTest()
-      : frame_sink_manager_(FrameSinkManagerImpl::InitParams()),
-        default_local_surface_id_(
-            LocalSurfaceId(1,
-                           base::UnguessableToken::CreateForTesting(2u, 3u))) {}
+      : frame_sink_manager_(FrameSinkManagerImpl::InitParams()) {}
 
   void SetUp() override {
     compositor_frame_sink_support_ =
@@ -111,7 +127,7 @@
 
     // Other defaults
     update->display_color_spaces = gfx::DisplayColorSpaces();
-    update->local_surface_id_from_parent = default_local_surface_id_;
+    update->local_surface_id_from_parent = kDefaultLocalSurfaceId;
 
     base::TimeTicks now = base::TimeTicks::Now();
     base::TimeDelta interval = base::Milliseconds(16);
@@ -216,6 +232,19 @@
         extra->uv_bottom_right = kDefaultUVBottomRight;
         return mojom::LayerExtra::NewTextureLayerExtra(std::move(extra));
       }
+      case cc::mojom::LayerType::kSurface: {
+        auto extra = mojom::SurfaceLayerExtra::New();
+        extra->surface_range = kDefaultSurfaceRange;
+        extra->deadline_in_frames = kDefaultDeadlineInFrames;
+        extra->stretch_content_to_fill_bounds =
+            kDefaultStretchContentToFillBounds;
+        extra->surface_hit_testable = kDefaultSurfaceHitTestable;
+        extra->has_pointer_events_none = kDefaultHasPointerEventsNone;
+        extra->is_reflection = kDefaultIsReflection;
+        extra->will_draw_needs_reset = kDefaultWillDrawNeedsReset;
+        extra->override_child_paint_flags = kDefaultOverrideChildPaintFlags;
+        return mojom::LayerExtra::NewSurfaceLayerExtra(std::move(extra));
+      }
 
       default:
         // TODO(vmiura): Add each layer type's initialization.
@@ -238,6 +267,7 @@
     layer->transform_tree_index = cc::kSecondaryRootPropertyNodeId;
     layer->clip_tree_index = cc::kRootPropertyNodeId;
     layer->effect_tree_index = cc::kSecondaryRootPropertyNodeId;
+    layer->bounds = kDefaultLayerBounds;
     layer->layer_extra = CreateDefaultLayerExtra(type);
 
     update->layers.push_back(std::move(layer));
@@ -260,11 +290,8 @@
   }
 
  protected:
-  static constexpr FrameSinkId kDefaultFrameSinkId = FrameSinkId(1, 1);
-
   FakeCompositorFrameSinkClient dummy_client_;
   FrameSinkManagerImpl frame_sink_manager_;
-  LocalSurfaceId default_local_surface_id_;
 
   std::unique_ptr<CompositorFrameSinkSupport> compositor_frame_sink_support_;
   std::unique_ptr<LayerContextImpl> layer_context_impl_;
@@ -1645,7 +1672,7 @@
   mojom::LayerPtr CreateManualLayer(
       int id,
       cc::mojom::LayerType type = cc::mojom::LayerType::kLayer,
-      const gfx::Size& bounds = gfx::Size(10, 10),
+      const gfx::Size& bounds = kDefaultLayerBounds,
       int transform_idx = cc::kSecondaryRootPropertyNodeId,
       int clip_idx = cc::kRootPropertyNodeId,
       int effect_idx = cc::kSecondaryRootPropertyNodeId,
@@ -1669,10 +1696,6 @@
   constexpr int kLayerId3 = 4;
   constexpr int kNonExistentLayerId = 99;
 
-  (void)kLayerId2;
-  (void)kLayerId3;
-  (void)kNonExistentLayerId;
-
   // Test Case 1: Basic Layer Lifecycle (Create, Update Bounds, Remove)
   // Update 1: Create Layer ID kLayerId1.
   auto update1 = CreateDefaultUpdate();
@@ -1681,16 +1704,17 @@
   EXPECT_TRUE(
       layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value());
   VerifyLayerExists(kLayerId1, true);
-  VerifyLayerBounds(kLayerId1, gfx::Size(0, 0));  // Default bounds
+  VerifyLayerBounds(kLayerId1, kDefaultLayerBounds);  // Default bounds
 
   // Update 2: Update bounds of Layer ID kLayerId1.
   auto update2 = CreateDefaultUpdate();
+  const gfx::Size kUpdatedBounds1(20, 20);
   update2->layers.push_back(CreateManualLayer(
-      kLayerId1, cc::mojom::LayerType::kLayer, gfx::Size(20, 20)));
+      kLayerId1, cc::mojom::LayerType::kLayer, kUpdatedBounds1));
   EXPECT_TRUE(
       layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value());
   VerifyLayerExists(kLayerId1, true);
-  VerifyLayerBounds(kLayerId1, gfx::Size(20, 20));
+  VerifyLayerBounds(kLayerId1, kUpdatedBounds1);
 
   // Update 3: Remove Layer ID kLayerId1.
   auto update3 = CreateDefaultUpdate();
@@ -1721,12 +1745,13 @@
 
   // Update 6: Update Layer ID kLayerId1.
   auto update6 = CreateDefaultUpdate();
+  const gfx::Size kUpdatedBounds2(30, 30);
   update6->layers.push_back(CreateManualLayer(
-      kLayerId1, cc::mojom::LayerType::kLayer, gfx::Size(30, 30)));
+      kLayerId1, cc::mojom::LayerType::kLayer, kUpdatedBounds2));
   EXPECT_TRUE(
       layer_context_impl_->DoUpdateDisplayTree(std::move(update6)).has_value());
-  VerifyLayerBounds(kLayerId1, gfx::Size(30, 30));
-  VerifyLayerBounds(kLayerId2, gfx::Size(0, 0));  // Unaffected
+  VerifyLayerBounds(kLayerId1, kUpdatedBounds2);
+  VerifyLayerBounds(kLayerId2, kDefaultLayerBounds);  // Unaffected
 
   // Update 7: Remove Layer ID kLayerId1.
   auto update7 = CreateDefaultUpdate();
@@ -1756,13 +1781,17 @@
 
   // Update 10: Attempt to update kNonExistentLayerId.
   auto update10 = CreateDefaultUpdate();
+  const gfx::Size kUpdatedBounds3(5, 5);
   update10->layers.push_back(
       CreateManualLayer(kNonExistentLayerId, cc::mojom::LayerType::kLayer,
-                        gfx::Size(5, 5)));  // Update non-existent
+                        kUpdatedBounds3));  // Update non-existent
   auto result10 = layer_context_impl_->DoUpdateDisplayTree(std::move(update10));
   ASSERT_FALSE(result10.has_value());
   EXPECT_EQ(result10.error(), "Invalid layer ID");
   VerifyLayerExists(kLayerId1, true);  // Unaffected
+  VerifyLayerBounds(
+      kLayerId1,
+      kDefaultLayerBounds);  // Should be reset to default or last valid
   VerifyLayerExists(kNonExistentLayerId, false);
   VerifyLayerOrder({1, kLayerId1});
 
@@ -1776,8 +1805,9 @@
 
   // Update 12: Attempt to update kLayerId1 (removed).
   auto update12 = CreateDefaultUpdate();
+  const gfx::Size kUpdatedBounds4(40, 40);
   update12->layers.push_back(CreateManualLayer(
-      kLayerId1, cc::mojom::LayerType::kLayer, gfx::Size(40, 40)));
+      kLayerId1, cc::mojom::LayerType::kLayer, kUpdatedBounds4));
   auto result12 = layer_context_impl_->DoUpdateDisplayTree(std::move(update12));
   ASSERT_FALSE(result12.has_value());
   EXPECT_EQ(result12.error(), "Invalid layer ID");
@@ -1792,22 +1822,22 @@
   EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update13))
                   .has_value());
   VerifyLayerExists(kLayerId1, true);
-  VerifyLayerBounds(kLayerId1, gfx::Size(0, 0));
+  VerifyLayerBounds(kLayerId1, kDefaultLayerBounds);
   VerifyLayerOrder({1, kLayerId1});
 
   // Update 14: Try to add another instance of kLayerId1 with different bounds.
   auto update14 = CreateDefaultUpdate();
+  const gfx::Size kUpdatedBounds5(50, 50);
   update14->layers.push_back(CreateManualLayer(
-      kLayerId1, cc::mojom::LayerType::kLayer, gfx::Size(50, 50)));
+      kLayerId1, cc::mojom::LayerType::kLayer, kUpdatedBounds5));
   update14->layer_order = layer_order_;
   update14->layer_order->push_back(kLayerId1);
 
   auto result14 = layer_context_impl_->DoUpdateDisplayTree(std::move(update14));
   ASSERT_FALSE(result14.has_value());
   EXPECT_EQ(result14.error(), "Invalid or duplicate layer ID");
-
   VerifyLayerExists(kLayerId1, true);
-  VerifyLayerBounds(kLayerId1, gfx::Size(50, 50));  // Layer should be updated
+  VerifyLayerBounds(kLayerId1, kUpdatedBounds5);  // Layer should be updated
   VerifyLayerOrder({1, kLayerId1});  // Layer Order should not update
 
   // Update 15: Try to add a Non Existent layer to Layer Order
@@ -1824,7 +1854,7 @@
   auto update16 = CreateDefaultUpdate();
   update16->layers.push_back(
       CreateManualLayer(kLayerId2, cc::mojom::LayerType::kLayer,
-                        gfx::Size(10, 10), /*transform_idx=*/999));
+                        kDefaultLayerBounds, /*transform_idx=*/999));
   update16->layer_order = layer_order_;
   update16->layer_order->push_back(kLayerId2);
   EXPECT_FALSE(layer_context_impl_->DoUpdateDisplayTree(std::move(update16))
@@ -1940,9 +1970,7 @@
       }
       case cc::mojom::LayerType::kSurface: {
         auto extra = mojom::SurfaceLayerExtra::New();
-        extra->surface_range = SurfaceRange(
-            std::nullopt,
-            SurfaceId(kDefaultFrameSinkId, default_local_surface_id_));
+        extra->surface_range = kDefaultSurfaceRange;
         extra->deadline_in_frames = 0u;
         layer->layer_extra =
             mojom::LayerExtra::NewSurfaceLayerExtra(std::move(extra));
@@ -1986,6 +2014,8 @@
 }
 
 TEST_F(LayerContextImplLayerLifecycleTest, UpdateMultipleLayerProperties) {
+  const gfx::Size kUpdatedBounds(50, 50);
+
   auto update = CreateDefaultUpdate();
   int layer_id1 = AddDefaultLayerToUpdate(update.get());
   int layer_id2 = AddDefaultLayerToUpdate(update.get());
@@ -1994,7 +2024,7 @@
 
   auto update_props = CreateDefaultUpdate();
   auto layer1_props = CreateManualLayer(layer_id1);
-  layer1_props->bounds = gfx::Size(50, 50);
+  layer1_props->bounds = kUpdatedBounds;
   layer1_props->contents_opaque = true;
   layer1_props->contents_opaque_for_text = true;
   layer1_props->background_color = SkColors::kRed;
@@ -2012,7 +2042,7 @@
 
   cc::LayerImpl* layer1_impl = GetLayerFromActiveTree(layer_id1);
   ASSERT_NE(nullptr, layer1_impl);
-  EXPECT_EQ(layer1_impl->bounds(), gfx::Size(50, 50));
+  EXPECT_EQ(layer1_impl->bounds(), kUpdatedBounds);
   EXPECT_TRUE(layer1_impl->contents_opaque());
   EXPECT_TRUE(layer1_impl->contents_opaque_for_text());
   EXPECT_EQ(layer1_impl->background_color(), SkColors::kRed);
@@ -2284,8 +2314,8 @@
   // Test Case 1: Update with invalid transform_tree_index.
   auto update_invalid_transform = CreateDefaultUpdate();
   update_invalid_transform->layers.push_back(CreateManualLayer(
-      kLayerId, cc::mojom::LayerType::kLayer, gfx::Size(10, 10), kInvalidIndex,
-      kValidIndex, kValidIndex, kValidIndex));
+      kLayerId, cc::mojom::LayerType::kLayer, kDefaultLayerBounds,
+      kInvalidIndex, kValidIndex, kValidIndex, kValidIndex));
   auto result_transform = layer_context_impl_->DoUpdateDisplayTree(
       std::move(update_invalid_transform));
   ASSERT_FALSE(result_transform.has_value());
@@ -2295,7 +2325,7 @@
   // Test Case 2: Update with invalid clip_tree_index.
   auto update_invalid_clip = CreateDefaultUpdate();
   update_invalid_clip->layers.push_back(CreateManualLayer(
-      kLayerId, cc::mojom::LayerType::kLayer, gfx::Size(10, 10), kValidIndex,
+      kLayerId, cc::mojom::LayerType::kLayer, kDefaultLayerBounds, kValidIndex,
       kInvalidIndex, kValidIndex, kValidIndex));
   auto result_clip =
       layer_context_impl_->DoUpdateDisplayTree(std::move(update_invalid_clip));
@@ -2305,7 +2335,7 @@
   // Test Case 3: Update with invalid effect_tree_index (similar for scroll).
   auto update_invalid_effect = CreateDefaultUpdate();
   update_invalid_effect->layers.push_back(CreateManualLayer(
-      kLayerId, cc::mojom::LayerType::kLayer, gfx::Size(10, 10), kValidIndex,
+      kLayerId, cc::mojom::LayerType::kLayer, kDefaultLayerBounds, kValidIndex,
       kValidIndex, kInvalidIndex, kValidIndex));
   auto result_effect = layer_context_impl_->DoUpdateDisplayTree(
       std::move(update_invalid_effect));
@@ -2357,8 +2387,8 @@
 
   // Second update: Update UV rect.
   auto update2 = CreateDefaultUpdate();
-  auto layer_props = CreateManualLayer(
-      kTextureLayerId, cc::mojom::LayerType::kTexture, gfx::Size(10, 10));
+  auto layer_props =
+      CreateManualLayer(kTextureLayerId, cc::mojom::LayerType::kTexture);
   auto& texture_extra = layer_props->layer_extra->get_texture_layer_extra();
   texture_extra->uv_top_left = kUpdatedUVTopLeft;
   texture_extra->uv_bottom_right = kUpdatedUVBottomRight;
@@ -2392,8 +2422,8 @@
 
   // Second update: Update blend_background_color to true.
   auto update2 = CreateDefaultUpdate();
-  auto layer_props2 = CreateManualLayer(
-      kTextureLayerId, cc::mojom::LayerType::kTexture, gfx::Size(10, 10));
+  auto layer_props2 =
+      CreateManualLayer(kTextureLayerId, cc::mojom::LayerType::kTexture);
   auto& texture_extra2 = layer_props2->layer_extra->get_texture_layer_extra();
   texture_extra2->blend_background_color = kUpdatedBlendBackgroundColor;
   update2->layers.push_back(std::move(layer_props2));
@@ -2431,8 +2461,8 @@
 
   // Second update: Update force_texture_to_opaque to true.
   auto update2 = CreateDefaultUpdate();
-  auto layer_props2 = CreateManualLayer(
-      kTextureLayerId, cc::mojom::LayerType::kTexture, gfx::Size(10, 10));
+  auto layer_props2 =
+      CreateManualLayer(kTextureLayerId, cc::mojom::LayerType::kTexture);
   auto& texture_extra2 = layer_props2->layer_extra->get_texture_layer_extra();
   texture_extra2->force_texture_to_opaque = kUpdatedForceTextureToOpaque;
   update2->layers.push_back(std::move(layer_props2));
@@ -2514,4 +2544,149 @@
   EXPECT_TRUE(texture_layer_impl->transferable_resource().is_empty());
 }
 
+class LayerContextImplUpdateDisplayTreeSurfaceLayerTest
+    : public LayerContextImplLayerLifecycleTest {
+ protected:
+  cc::SurfaceLayerImpl* GetSurfaceLayerFromActiveTree(int layer_id) {
+    cc::LayerImpl* layer = GetLayerFromActiveTree(layer_id);
+    if (layer && layer->GetLayerType() == cc::mojom::LayerType::kSurface) {
+      return static_cast<cc::SurfaceLayerImpl*>(layer);
+    }
+    return nullptr;
+  }
+};
+
+TEST_F(LayerContextImplUpdateDisplayTreeSurfaceLayerTest,
+       UpdateBooleanProperties) {
+  constexpr int kSurfaceLayerId = 2;
+
+  // Initial update: Create SurfaceLayer with default boolean values.
+  auto update1 = CreateDefaultUpdate();
+  AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kSurface,
+                          kSurfaceLayerId);
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value());
+  VerifyLayerExists(kSurfaceLayerId, true);
+
+  cc::SurfaceLayerImpl* layer_impl =
+      GetSurfaceLayerFromActiveTree(kSurfaceLayerId);
+  ASSERT_NE(nullptr, layer_impl);
+
+  // Defaults should be false from CreateDefaultLayerExtra.
+  EXPECT_EQ(layer_impl->stretch_content_to_fill_bounds(),
+            kDefaultStretchContentToFillBounds);
+  EXPECT_EQ(layer_impl->surface_hit_testable(), kDefaultSurfaceHitTestable);
+  EXPECT_EQ(layer_impl->has_pointer_events_none(),
+            kDefaultHasPointerEventsNone);
+  EXPECT_EQ(layer_impl->is_reflection(), kDefaultIsReflection);
+  EXPECT_EQ(layer_impl->override_child_paint_flags(),
+            kDefaultOverrideChildPaintFlags);
+
+  // Second update: Update all boolean properties to true.
+  auto update2 = CreateDefaultUpdate();
+  auto layer_props2 =
+      CreateManualLayer(kSurfaceLayerId, cc::mojom::LayerType::kSurface);
+  auto& surface_extra2 = layer_props2->layer_extra->get_surface_layer_extra();
+  surface_extra2->stretch_content_to_fill_bounds = true;
+  surface_extra2->surface_hit_testable = true;
+  surface_extra2->has_pointer_events_none = true;
+  surface_extra2->is_reflection = true;
+  surface_extra2->override_child_paint_flags = true;
+  update2->layers.push_back(std::move(layer_props2));
+
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value());
+  ASSERT_NE(nullptr, layer_impl);
+  EXPECT_TRUE(layer_impl->stretch_content_to_fill_bounds());
+  EXPECT_TRUE(layer_impl->surface_hit_testable());
+  EXPECT_TRUE(layer_impl->has_pointer_events_none());
+  EXPECT_TRUE(layer_impl->is_reflection());
+  EXPECT_TRUE(layer_impl->override_child_paint_flags());
+
+  // Third update: Update all boolean properties back to false.
+  auto update3 = CreateDefaultUpdate();
+  auto layer_props3 =
+      CreateManualLayer(kSurfaceLayerId, cc::mojom::LayerType::kSurface);
+  auto& surface_extra3 = layer_props3->layer_extra->get_surface_layer_extra();
+  surface_extra3->stretch_content_to_fill_bounds = false;
+  surface_extra3->surface_hit_testable = false;
+  surface_extra3->has_pointer_events_none = false;
+  surface_extra3->is_reflection = false;
+  surface_extra3->override_child_paint_flags = false;
+  update3->layers.push_back(std::move(layer_props3));
+
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value());
+  ASSERT_NE(nullptr, layer_impl);
+  EXPECT_FALSE(layer_impl->stretch_content_to_fill_bounds());
+  EXPECT_FALSE(layer_impl->surface_hit_testable());
+  EXPECT_FALSE(layer_impl->has_pointer_events_none());
+  EXPECT_FALSE(layer_impl->is_reflection());
+  EXPECT_FALSE(layer_impl->override_child_paint_flags());
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeSurfaceLayerTest,
+       UpdateSurfaceRangeAndDeadline) {
+  constexpr int kSurfaceLayerId = 2;
+  constexpr uint32_t kUpdatedDeadlineInFrames = 5u;
+
+  // Initial update: Create SurfaceLayer with default range and deadline.
+  auto update1 = CreateDefaultUpdate();
+  AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kSurface,
+                          kSurfaceLayerId);
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value());
+  cc::SurfaceLayerImpl* layer_impl =
+      GetSurfaceLayerFromActiveTree(kSurfaceLayerId);
+  ASSERT_NE(nullptr, layer_impl);
+  EXPECT_EQ(layer_impl->range(), kDefaultSurfaceRange);
+  EXPECT_EQ(layer_impl->deadline_in_frames(), kDefaultDeadlineInFrames);
+
+  // Second update: Update surface_range and deadline_in_frames.
+  auto update2 = CreateDefaultUpdate();
+  auto layer_props2 =
+      CreateManualLayer(kSurfaceLayerId, cc::mojom::LayerType::kSurface);
+  auto& surface_extra2 = layer_props2->layer_extra->get_surface_layer_extra();
+  LocalSurfaceId new_lsi(4, base::UnguessableToken::CreateForTesting(5, 6));
+  surface_extra2->surface_range =
+      SurfaceRange(std::nullopt, SurfaceId(kDefaultFrameSinkId, new_lsi));
+  surface_extra2->deadline_in_frames = kUpdatedDeadlineInFrames;
+  update2->layers.push_back(std::move(layer_props2));
+
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value());
+  EXPECT_EQ(layer_impl->range().end(), SurfaceId(kDefaultFrameSinkId, new_lsi));
+  EXPECT_EQ(layer_impl->deadline_in_frames(), kUpdatedDeadlineInFrames);
+}
+
+TEST_F(LayerContextImplUpdateDisplayTreeSurfaceLayerTest,
+       UpdateWillDrawNeedsReset) {
+  constexpr int kSurfaceLayerId = 2;
+
+  // Initial update: Create SurfaceLayer with default will_draw_needs_reset.
+  auto update1 = CreateDefaultUpdate();
+  AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kSurface,
+                          kSurfaceLayerId);
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value());
+  cc::SurfaceLayerImpl* layer_impl1 =
+      GetSurfaceLayerFromActiveTree(kSurfaceLayerId);
+  ASSERT_NE(nullptr, layer_impl1);
+  EXPECT_EQ(layer_impl1->will_draw_needs_reset(), kDefaultWillDrawNeedsReset);
+
+  // Second update: Set will_draw_needs_reset to true.
+  auto update2 = CreateDefaultUpdate();
+  auto layer_props2 =
+      CreateManualLayer(kSurfaceLayerId, cc::mojom::LayerType::kSurface);
+  auto& surface_extra2 = layer_props2->layer_extra->get_surface_layer_extra();
+  surface_extra2->will_draw_needs_reset = true;
+  update2->layers.push_back(std::move(layer_props2));
+  EXPECT_TRUE(
+      layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value());
+  cc::SurfaceLayerImpl* layer_impl2 =
+      GetSurfaceLayerFromActiveTree(kSurfaceLayerId);
+  ASSERT_NE(nullptr, layer_impl2);
+  EXPECT_TRUE(layer_impl2->will_draw_needs_reset());
+}
+
 }  // namespace viz
diff --git a/components/viz/test/fake_skia_output_surface.cc b/components/viz/test/fake_skia_output_surface.cc
index 16ea22d5..680c7b9 100644
--- a/components/viz/test/fake_skia_output_surface.cc
+++ b/components/viz/test/fake_skia_output_surface.cc
@@ -343,7 +343,6 @@
   DCHECK(!image_context.mailbox().IsZero());
 
   auto* gl = context_provider()->ContextGL();
-  gl->WaitSyncTokenCHROMIUM(image_context.sync_token().GetConstData());
   auto texture_id = gl->CreateAndTexStorage2DSharedImageCHROMIUM(
       image_context.mailbox().name);
   auto gl_format_desc = gpu::GLFormatCaps().ToGLFormatDesc(
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index 304d26f..6fbdafe2 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -577,9 +577,10 @@
               observer.last_initiator_process_id());
   }
 
-  // The RenderFrameHost should have changed unless full site isolation and
-  // proactive BrowsingInstance swaps are both disabled.
-  if (!AreAllSitesIsolatedForTesting() &&
+  // The RenderFrameHost should have changed unless strict SiteInstances (either
+  // full site isolation or default SiteInstanceGroups) and proactive
+  // BrowsingInstance swaps are both disabled.
+  if (!AreStrictSiteInstancesEnabled() &&
       !CanCrossSiteNavigationsProactivelySwapBrowsingInstances()) {
     EXPECT_EQ(initial_rfh, current_frame_host());
   } else {
diff --git a/content/browser/renderer_host/render_frame_host_manager_browsertest.cc b/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
index 2cced0b..b118047c 100644
--- a/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_manager_browsertest.cc
@@ -103,7 +103,7 @@
 // Helper function that return true in cases where the current process model
 // will return the same SiteInstance for a cross-process navigation.
 bool ExpectSameSiteInstance() {
-  return !AreAllSitesIsolatedForTesting() &&
+  return !AreStrictSiteInstancesEnabled() &&
          !CanCrossSiteNavigationsProactivelySwapBrowsingInstances();
 }
 
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 33453d2f..68376bfc 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -472,8 +472,6 @@
       {wrf::EnableSharedWorker, switches::kDisableSharedWorkers, false},
       {wrf::EnableStandardizedBrowserZoom,
        blink::switches::kDisableStandardizedBrowserZoom, false},
-      {wrf::EnableSelectParserRelaxation,
-       blink::switches::kDisableSelectParserRelaxation, false},
       {wrf::EnableTextFragmentIdentifiers,
        switches::kDisableScrollToTextFragment, false},
       {wrf::EnableWebAuthenticationRemoteDesktopSupport,
diff --git a/content/public/browser/browsing_data_remover.h b/content/public/browser/browsing_data_remover.h
index 8e85f74..a479369 100644
--- a/content/public/browser/browsing_data_remover.h
+++ b/content/public/browser/browsing_data_remover.h
@@ -66,18 +66,15 @@
   // Mask used for Remove.
   using DataType = uint64_t;
   // Storage datatypes.
-  static constexpr DataType DATA_TYPE_APP_CACHE_DEPRECATED = 1 << 0;
-  static constexpr DataType DATA_TYPE_FILE_SYSTEMS = 1 << 1;
-  static constexpr DataType DATA_TYPE_INDEXED_DB = 1 << 2;
-  static constexpr DataType DATA_TYPE_LOCAL_STORAGE = 1 << 3;
-  static constexpr DataType DATA_TYPE_WEB_SQL_DEPRECATED = 1 << 4;
-  static constexpr DataType DATA_TYPE_SERVICE_WORKERS = 1 << 5;
-  static constexpr DataType DATA_TYPE_CACHE_STORAGE = 1 << 6;
-  // This is also persisted, keep with storage datatypes.
-  static constexpr DataType DATA_TYPE_BACKGROUND_FETCH = 1 << 14;
+  static constexpr DataType DATA_TYPE_FILE_SYSTEMS = 1 << 0;
+  static constexpr DataType DATA_TYPE_INDEXED_DB = 1 << 1;
+  static constexpr DataType DATA_TYPE_LOCAL_STORAGE = 1 << 2;
+  static constexpr DataType DATA_TYPE_SERVICE_WORKERS = 1 << 3;
+  static constexpr DataType DATA_TYPE_CACHE_STORAGE = 1 << 4;
+  static constexpr DataType DATA_TYPE_BACKGROUND_FETCH = 1 << 5;
 
   // Used to request the deletion of embedder-specific storage datatypes.
-  static constexpr DataType DATA_TYPE_EMBEDDER_DOM_STORAGE = 1 << 7;
+  static constexpr DataType DATA_TYPE_EMBEDDER_DOM_STORAGE = 1 << 6;
 
   // DOM-accessible storage (https://www.w3.org/TR/clear-site-data/#storage).
   // Has the same effect as selecting all storage datatypes listed above
@@ -88,81 +85,79 @@
       DATA_TYPE_EMBEDDER_DOM_STORAGE | DATA_TYPE_BACKGROUND_FETCH;
 
   // Other datatypes.
-  static constexpr DataType DATA_TYPE_COOKIES = 1 << 8;
-  static constexpr DataType DATA_TYPE_CACHE = 1 << 10;
-  static constexpr DataType DATA_TYPE_DOWNLOADS = 1 << 11;
-  static constexpr DataType DATA_TYPE_MEDIA_LICENSES = 1 << 12;
+  static constexpr DataType DATA_TYPE_COOKIES = 1 << 7;
+  static constexpr DataType DATA_TYPE_CACHE = 1 << 8;
+  static constexpr DataType DATA_TYPE_DOWNLOADS = 1 << 9;
+  static constexpr DataType DATA_TYPE_MEDIA_LICENSES = 1 << 10;
 
   // REMOVE_NOCHECKS intentionally does not check if the browser context is
   // prohibited from deleting history or downloads.
-  static constexpr DataType DATA_TYPE_NO_CHECKS = 1 << 13;
-
-  // 14 is already taken by DATA_TYPE_BACKGROUND_FETCH.
+  static constexpr DataType DATA_TYPE_NO_CHECKS = 1 << 11;
 
   // AVOID_CLOSING_CONNECTIONS is a pseudo-datatype indicating that when
   // deleting COOKIES, BrowsingDataRemover should skip
   // storage backends whose deletion would cause closing network connections.
   // TODO(crbug.com/41363015): Remove when fixed.
-  static constexpr DataType DATA_TYPE_AVOID_CLOSING_CONNECTIONS = 1 << 15;
+  static constexpr DataType DATA_TYPE_AVOID_CLOSING_CONNECTIONS = 1 << 12;
 
   // Trust Token API (https://github.com/wicg/trust-token-api) persistent
   // storage.
-  static constexpr DataType DATA_TYPE_TRUST_TOKENS = 1 << 16;
+  static constexpr DataType DATA_TYPE_TRUST_TOKENS = 1 << 13;
 
   // Attribution Reporting
   // (https://github.com/WICG/conversion-measurement-api) persistent
   // storage that was initiated by a site.
   static constexpr DataType DATA_TYPE_ATTRIBUTION_REPORTING_SITE_CREATED =
-      1 << 17;
+      1 << 14;
 
   // Aggregation Service
   // (https://github.com/WICG/attribution-reporting-api/blob/main/AGGREGATE.md#data-processing-through-a-secure-aggregation-service)
   // persistent storage.
-  static constexpr DataType DATA_TYPE_AGGREGATION_SERVICE = 1 << 18;
+  static constexpr DataType DATA_TYPE_AGGREGATION_SERVICE = 1 << 15;
 
   // Interest groups are stored as part of the Interest Group API experiment
   // Public explainer here:
   // https://github.com/WICG/turtledove/blob/main/FLEDGE.md
-  static constexpr DataType DATA_TYPE_INTEREST_GROUPS = 1 << 19;
+  static constexpr DataType DATA_TYPE_INTEREST_GROUPS = 1 << 16;
 
   // Shared storage API
   // (https://github.com/pythagoraskitty/shared-storage) persistent storage.
-  static constexpr DataType DATA_TYPE_SHARED_STORAGE = 1 << 20;
+  static constexpr DataType DATA_TYPE_SHARED_STORAGE = 1 << 17;
 
   // Similar to DATA_TYPE_ATTRIBUTION_REPORTING_SITE_INITIATED, but only
   // refers to data stored internally by the API, such as privacy budgeting
   // information.
-  static constexpr DataType DATA_TYPE_ATTRIBUTION_REPORTING_INTERNAL = 1 << 21;
+  static constexpr DataType DATA_TYPE_ATTRIBUTION_REPORTING_INTERNAL = 1 << 18;
 
   // Private Aggregation API
   // (https://github.com/alexmturner/private-aggregation-api) persistent
   // storage. This only refers to data stored internally by the API, such as
   // privacy budgeting information. Note that currently the API does not persist
   // any other data. Should only be cleared by user-initiated deletions.
-  static constexpr DataType DATA_TYPE_PRIVATE_AGGREGATION_INTERNAL = 1 << 22;
+  static constexpr DataType DATA_TYPE_PRIVATE_AGGREGATION_INTERNAL = 1 << 19;
 
   // Similar to DATA_TYPE_INTEREST_GROUPS, but only refers to data stored
   // internally by the API, such as k-Anonymity cache and rate limiting
   // information.
-  static constexpr DataType DATA_TYPE_INTEREST_GROUPS_INTERNAL = 1 << 23;
+  static constexpr DataType DATA_TYPE_INTEREST_GROUPS_INTERNAL = 1 << 20;
 
   // Permissions granted by Related Website Sets
   // (https://github.com/WICG/first-party-sets).
   static constexpr DataType DATA_TYPE_RELATED_WEBSITE_SETS_PERMISSIONS = 1
-                                                                         << 24;
+                                                                         << 21;
 
   // Device bound sessions
   // (https://github.com/WICG/dbsc/blob/main/README.md)
-  static constexpr DataType DATA_TYPE_DEVICE_BOUND_SESSIONS = 1 << 25;
+  static constexpr DataType DATA_TYPE_DEVICE_BOUND_SESSIONS = 1 << 22;
 
   // Interest group data that should be cleared in response to user action,
   // but not Clear-Site-Site data.
   // (https://github.com/WICG/turtledove/blob/main/FLEDGE.md)
-  static constexpr DataType DATA_TYPE_INTEREST_GROUPS_USER_CLEAR = 1 << 26;
+  static constexpr DataType DATA_TYPE_INTEREST_GROUPS_USER_CLEAR = 1 << 23;
 
   // Clear-Site-Data Interaction with Prefetch and Prerender.
-  static constexpr DataType DATA_TYPE_PREFETCH_CACHE = 1 << 27;
-  static constexpr DataType DATA_TYPE_PRERENDER_CACHE = 1 << 28;
+  static constexpr DataType DATA_TYPE_PREFETCH_CACHE = 1 << 24;
+  static constexpr DataType DATA_TYPE_PRERENDER_CACHE = 1 << 25;
 
   // Embedders can add more datatypes beyond this point.
   static constexpr DataType DATA_TYPE_CONTENT_END = DATA_TYPE_PRERENDER_CACHE;
@@ -190,9 +185,8 @@
   static constexpr DataType DATA_TYPE_ON_STORAGE_PARTITION =
       DATA_TYPE_DOM_STORAGE | DATA_TYPE_COOKIES |
       DATA_TYPE_AVOID_CLOSING_CONNECTIONS | DATA_TYPE_CACHE |
-      DATA_TYPE_APP_CACHE_DEPRECATED | DATA_TYPE_PRIVACY_SANDBOX |
-      DATA_TYPE_DEVICE_BOUND_SESSIONS | DATA_TYPE_PREFETCH_CACHE |
-      DATA_TYPE_PRERENDER_CACHE;
+      DATA_TYPE_PRIVACY_SANDBOX | DATA_TYPE_DEVICE_BOUND_SESSIONS |
+      DATA_TYPE_PREFETCH_CACHE | DATA_TYPE_PRERENDER_CACHE;
 
   using OriginType = uint64_t;
   // Web storage origins that StoragePartition recognizes as NOT protected
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index 71b5599..7cf7879 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -472,10 +472,16 @@
   // in CreateChildFrame() or similar.
   virtual std::optional<base::UnguessableToken> GetEmbeddingToken() = 0;
 
-  // Returns the assigned name of the frame, the name of the iframe tag
-  // declaring it. For example, <iframe name="framename">[...]</iframe>. It is
-  // quite possible for a frame to have no name, in which case GetFrameName will
-  // return an empty string.
+  // Returns this frame's browsing context name, i.e. its "window.name".
+  // Initially, if the <iframe> element had a `name` attribute, that value
+  // will be used. The initial value is snapshotted from the element
+  // attribute; changing the attribute later does not change the browsing
+  // context name.
+  // In addition to HTML attributes, the name can also be set via
+  // window.open(), the target attribute of an <a> element, or by frames
+  // in a frameset. Subsequent changes to window.name in the renderer will
+  // be reflected here, though they will not be pushed back to the element
+  // attribute. If the frame never had a name, this returns an empty string.
   virtual const std::string& GetFrameName() = 0;
 
   // Returns true if the frame is display: none.
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
index 7a2d251b..f58a8706 100644
--- a/content/public/renderer/content_renderer_client.h
+++ b/content/public/renderer/content_renderer_client.h
@@ -381,11 +381,11 @@
       const GURL& service_worker_scope,
       const GURL& script_url) {}
 
-  // Notifies that the main script of a service worker is about to evaluate.
+  // Notifies that a service work is about to prepare for script evaluation.
   // This function is called from the worker thread.
   // |context_proxy| is valid until
   // WillDestroyServiceWorkerContextOnWorkerThread() is called.
-  virtual void WillEvaluateServiceWorkerOnWorkerThread(
+  virtual void WillPrepareForEvaluationOnWorkerThread(
       blink::WebServiceWorkerContextProxy* context_proxy,
       v8::Local<v8::Context> v8_context,
       int64_t service_worker_version_id,
@@ -393,6 +393,9 @@
       const GURL& script_url,
       const blink::ServiceWorkerToken& service_worker_token) {}
 
+  // Notifies that the main script of a service worker is about to evaluate.
+  virtual void WillEvaluateServiceWorkerOnWorkerThread() {}
+
   // Notifies that a service worker context has finished executing its top-level
   // JavaScript. This function is called from the worker thread.
   virtual void DidStartServiceWorkerContextOnWorkerThread(
diff --git a/content/renderer/media/audio_decoder.cc b/content/renderer/media/audio_decoder.cc
index e07fd0d..c9f6862 100644
--- a/content/renderer/media/audio_decoder.cc
+++ b/content/renderer/media/audio_decoder.cc
@@ -12,6 +12,7 @@
 #include "base/containers/span_writer.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
+#include "base/strings/to_string.h"
 #include "base/time/time.h"
 #include "media/base/audio_bus.h"
 #include "media/base/limits.h"
@@ -80,7 +81,7 @@
   }
 
   DVLOG(1) << "Decoded file data (unknown duration)-"
-           << " data: " << data << " data size: " << data.size()
+           << " data: " << base::ToString(data) << " data size: " << data.size()
            << ", decoded duration: " << (number_of_frames / file_sample_rate)
            << ", number of frames: " << number_of_frames
            << ", estimated frames (if available): "
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index eff9f90..1afd40c 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -313,6 +313,14 @@
           proxy_, service_worker_scope_, script_url_);
 }
 
+void ServiceWorkerContextClient::WillPrepareForEvaluation(
+    v8::Local<v8::Context> v8_context) {
+  DCHECK(worker_task_runner_->RunsTasksInCurrentSequence());
+  GetContentClient()->renderer()->WillPrepareForEvaluationOnWorkerThread(
+      proxy_, v8_context, service_worker_version_id_, service_worker_scope_,
+      script_url_, service_worker_token_);
+}
+
 void ServiceWorkerContextClient::WillEvaluateScript(
     v8::Local<v8::Context> v8_context) {
   DCHECK(worker_task_runner_->RunsTasksInCurrentSequence());
@@ -331,9 +339,7 @@
   instance_host_->OnScriptEvaluationStart();
 
   DCHECK(proxy_);
-  GetContentClient()->renderer()->WillEvaluateServiceWorkerOnWorkerThread(
-      proxy_, v8_context, service_worker_version_id_, service_worker_scope_,
-      script_url_, service_worker_token_);
+  GetContentClient()->renderer()->WillEvaluateServiceWorkerOnWorkerThread();
 }
 
 void ServiceWorkerContextClient::DidEvaluateScript(bool success) {
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index ecff6ff7..5e964a9 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -166,6 +166,7 @@
   void WorkerContextStarted(
       blink::WebServiceWorkerContextProxy* proxy,
       scoped_refptr<base::SequencedTaskRunner> worker_task_runner) override;
+  void WillPrepareForEvaluation(v8::Local<v8::Context> v8_context) override;
   void WillEvaluateScript(v8::Local<v8::Context> v8_context) override;
   void DidEvaluateScript(bool success) override;
   void WillInitializeWorkerContext() override;
diff --git a/content/test/data/accessibility/html/permission-expected-blink.txt b/content/test/data/accessibility/html/permission-expected-blink.txt
index 4bc259d..1493f3db 100644
--- a/content/test/data/accessibility/html/permission-expected-blink.txt
+++ b/content/test/data/accessibility/html/permission-expected-blink.txt
@@ -3,13 +3,16 @@
 ++++genericContainer
 ++++++button focusable name='Use camera'
 ++++++++genericContainer ignored
-++++++++++staticText name='Use camera'
-++++++++++++inlineTextBox name='Use camera'
+++++++++++genericContainer ignored
+++++++++++++staticText name='Use camera'
+++++++++++++++inlineTextBox name='Use camera'
 ++++++button focusable name='Use microphone'
 ++++++++genericContainer ignored
-++++++++++staticText name='Use microphone'
-++++++++++++inlineTextBox name='Use microphone'
+++++++++++genericContainer ignored
+++++++++++++staticText name='Use microphone'
+++++++++++++++inlineTextBox name='Use microphone'
 ++++++button focusable name='Use microphone and camera'
 ++++++++genericContainer ignored
-++++++++++staticText name='Use microphone and camera'
-++++++++++++inlineTextBox name='Use microphone and camera'
+++++++++++genericContainer ignored
+++++++++++++staticText name='Use microphone and camera'
+++++++++++++++inlineTextBox name='Use microphone and camera'
diff --git a/device/vr/BUILD.gn b/device/vr/BUILD.gn
index 8314216..9b2c698 100644
--- a/device/vr/BUILD.gn
+++ b/device/vr/BUILD.gn
@@ -407,6 +407,16 @@
         "openxr/test/openxr_test_helper.h",
       ]
 
+      deps = [
+        ":vr_test_hook",
+        "//base",
+        "//components/version_info",
+        "//device/gamepad/public/cpp:shared_with_blink",
+        "//device/vr/public/mojom:test_mojom",
+        "//device/vr/public/mojom:vr_service",
+        "//third_party/openxr:openxr_headers",
+      ]
+
       if (is_win) {
         sources += [ "openxr/test/openxr.def" ]
         libs = [
@@ -420,18 +430,16 @@
                     rebase_path("//device/vr/openxr/test/openxr.lst",
                                 root_build_dir) ]
         inputs = [ "//device/vr/openxr/test/openxr.lst" ]
+        sources += [
+          "openxr/test/xr_test_gl.cc",
+          "openxr/test/xr_test_gl.h",
+        ]
+        libs = [
+          "EGL",
+          "GLESv2",
+        ]
       }
 
-      deps = [
-        ":vr_test_hook",
-        "//base",
-        "//components/version_info",
-        "//device/gamepad/public/cpp:shared_with_blink",
-        "//device/vr/public/mojom:test_mojom",
-        "//device/vr/public/mojom:vr_service",
-        "//third_party/openxr:openxr_headers",
-      ]
-
       public_deps = [ ":openxr_data" ]
 
       data_deps = [ "//device/vr:json_mock" ]
diff --git a/device/vr/openxr/test/fake_openxr_impl_api.cc b/device/vr/openxr/test/fake_openxr_impl_api.cc
index a658bda..3753da5 100644
--- a/device/vr/openxr/test/fake_openxr_impl_api.cc
+++ b/device/vr/openxr/test/fake_openxr_impl_api.cc
@@ -183,9 +183,23 @@
 #if BUILDFLAG(IS_WIN)
   RETURN_IF(create_info->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
             "XrInstanceCreateInfo next is not nullptr");
-#else
+#elif BUILDFLAG(IS_ANDROID)
   RETURN_IF(create_info->next == nullptr, XR_ERROR_VALIDATION_FAILURE,
             "XrInstanceCreateInfo next is nullptr");
+  const XrInstanceCreateInfoAndroidKHR* android_create_info =
+      reinterpret_cast<const XrInstanceCreateInfoAndroidKHR*>(
+          create_info->next);
+  RETURN_IF(
+      android_create_info->type != XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR,
+      XR_ERROR_VALIDATION_FAILURE,
+      "XrInstanceCreateInfoAndroidKHR type invalid");
+  RETURN_IF(android_create_info->applicationVM == nullptr,
+            XR_ERROR_VALIDATION_FAILURE,
+            "XrInstanceCreateInfoAndroidKHR applicationVM is nullptr");
+  // For testing purposes, assume applicationActivity is provided.
+  RETURN_IF(android_create_info->applicationActivity == nullptr,
+            XR_ERROR_VALIDATION_FAILURE,
+            "XrInstanceCreateInfoAndroidKHR applicationActivity is nullptr");
 #endif
 
   RETURN_IF(create_info->createFlags != 0, XR_ERROR_VALIDATION_FAILURE,
@@ -271,6 +285,18 @@
             "D3D11Device is nullptr");
 
   g_test_helper.SetD3DDevice(binding->device);
+#elif BUILDFLAG(IS_ANDROID)
+  const XrGraphicsBindingOpenGLESAndroidKHR* binding =
+      static_cast<const XrGraphicsBindingOpenGLESAndroidKHR*>(
+          create_info->next);
+  RETURN_IF(binding == nullptr, XR_ERROR_VALIDATION_FAILURE,
+            "XrGraphicsBindingOpenGLESAndroidKHR is nullptr");
+  RETURN_IF(binding->type != XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR,
+            XR_ERROR_VALIDATION_FAILURE,
+            "XrGraphicsBindingOpenGLESAndroidKHR type invalid");
+  RETURN_IF(binding->next != nullptr, XR_ERROR_VALIDATION_FAILURE,
+            "XrGraphicsBindingOpenGLESAndroidKHR next is not nullptr");
+  g_test_helper.SetOpenGLESInfo(binding->display, binding->context);
 #endif
   RETURN_IF(session == nullptr, XR_ERROR_VALIDATION_FAILURE,
             "XrSession is nullptr");
@@ -300,6 +326,10 @@
   RETURN_IF(create_info->format != DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
             XR_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED,
             "XrSwapchainCreateInfo format unsupported");
+#elif BUILDFLAG(IS_ANDROID)
+  RETURN_IF(create_info->format != OpenXrTestHelper::kSwapchainFormat,
+            XR_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED,
+            "XrSwapchainCreateInfo format unsupported");
 #endif
   RETURN_IF(create_info->sampleCount != OpenXrTestHelper::kSwapCount,
             XR_ERROR_VALIDATION_FAILURE,
@@ -616,6 +646,9 @@
 #if BUILDFLAG(IS_WIN)
   // This is what is hardcoded in `OpenXrGraphicsBindingD3D11`.
   formats[0] = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
+#elif BUILDFLAG(IS_ANDROID)
+  // This is what is hardcoded in `OpenXrGraphicsBindingOpenGLES`.
+  formats[0] = OpenXrTestHelper::kSwapchainFormat;
 #endif
 
   return XR_SUCCESS;
@@ -662,6 +695,23 @@
 
     image.texture = textures[i].Get();
   }
+#elif BUILDFLAG(IS_ANDROID)
+  const std::vector<uint32_t>& texture_ids =
+      g_test_helper.GetSwapchainTextureIDs();
+  DCHECK_EQ(texture_ids.size(), image_capacity_input);
+
+  for (uint32_t i = 0; i < image_capacity_input; i++) {
+    XrSwapchainImageOpenGLESKHR& image =
+        reinterpret_cast<XrSwapchainImageOpenGLESKHR*>(images)[i];
+
+    RETURN_IF(image.type != XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR,
+              XR_ERROR_VALIDATION_FAILURE,
+              "XrSwapchainImageOpenGLESKHR type invalid");
+    RETURN_IF(image.next != nullptr, XR_ERROR_VALIDATION_FAILURE,
+              "XrSwapchainImageOpenGLESKHR next is not nullptr");
+
+    image.image = texture_ids[i];
+  }
 #endif
 
   return XR_SUCCESS;
diff --git a/device/vr/openxr/test/openxr_test_helper.cc b/device/vr/openxr/test/openxr_test_helper.cc
index 9f3eb66..c95133e 100644
--- a/device/vr/openxr/test/openxr_test_helper.cc
+++ b/device/vr/openxr/test/openxr_test_helper.cc
@@ -13,6 +13,7 @@
 #include <limits>
 
 #include "base/containers/contains.h"
+#include "base/logging.h"
 #include "device/vr/openxr/openxr_interaction_profile_paths.h"
 #include "device/vr/openxr/openxr_platform.h"
 #include "device/vr/openxr/openxr_util.h"
@@ -21,6 +22,10 @@
 #include "ui/gfx/geometry/transform.h"
 #include "ui/gfx/geometry/transform_util.h"
 
+#if BUILDFLAG(IS_ANDROID)
+#include "device/vr/openxr/test/xr_test_gl.h"
+#endif
+
 namespace {
 bool PathContainsString(const std::string& path, const std::string& s) {
   return base::Contains(path, s);
@@ -43,6 +48,13 @@
   return ((index % 2 == 0) ? 1 : -1);
 }
 
+#if BUILDFLAG(IS_ANDROID)
+device::Color GetFirstColor(base::span<char> pixels) {
+  CHECK_GE(pixels.size(), 3u);
+  return device::Color(pixels[0], pixels[1], pixels[2], pixels[3]);
+}
+#endif
+
 }  // namespace
 
 OpenXrTestHelper::ActionProperties::ActionProperties()
@@ -105,6 +117,9 @@
 #if BUILDFLAG(IS_WIN)
   d3d_device_ = nullptr;
   textures_arr_.clear();
+#elif BUILDFLAG(IS_ANDROID)
+  opengl_es_textures_arr_.clear();
+  xr_gl_.reset();
 #endif
   acquired_swapchain_texture_ = 0;
   next_handle_ = 0;
@@ -143,6 +158,8 @@
 void OpenXrTestHelper::OnPresentedFrame() {
 #if BUILDFLAG(IS_WIN)
   DCHECK_NE(textures_arr_.size(), 0ull);
+#elif BUILDFLAG(IS_ANDROID)
+  DCHECK_NE(opengl_es_textures_arr_.size(), 0ull);
 #endif
 
   std::vector<device::ViewData> submitted_views;
@@ -176,15 +193,14 @@
 
 void OpenXrTestHelper::CopyTextureDataIntoFrameData(uint32_t x_start,
                                                     device::ViewData& data) {
+  constexpr uint32_t buffer_size = sizeof(device::ViewData::raw_buffer);
+  constexpr uint32_t buffer_size_pixels = buffer_size / sizeof(device::Color);
 #if BUILDFLAG(IS_WIN)
   DCHECK(d3d_device_);
   DCHECK_NE(textures_arr_.size(), 0ull);
   Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
   d3d_device_->GetImmediateContext(&context);
 
-  constexpr uint32_t buffer_size = sizeof(device::ViewData::raw_buffer);
-  constexpr uint32_t buffer_size_pixels = buffer_size / sizeof(device::Color);
-
   // We copy the submitted texture to a new texture, so we can map it, and
   // read back pixel data.
   auto desc = CD3D11_TEXTURE2D_DESC();
@@ -220,6 +236,33 @@
   memcpy(&data.raw_buffer, map_data.pData, buffer_size);
 
   context->Unmap(texture_destination.Get(), 0);
+#elif BUILDFLAG(IS_ANDROID)
+  DCHECK_NE(opengl_es_textures_arr_.size(), 0u);
+  DCHECK_NE(xr_gl_, nullptr);
+  DCHECK_LT(acquired_swapchain_texture_, opengl_es_textures_arr_.size());
+  base::span<char> out_buffer(data.raw_buffer);
+
+  // Generate a framebuffer to read from and attach the current texture to it.
+  GLuint fbo = 0;
+  xr_gl_->glGenFramebuffers_fn(1, &fbo);
+  xr_gl_->glBindFramebuffer_fn(GL_FRAMEBUFFER, fbo);
+  xr_gl_->glFramebufferTexture2D_fn(
+      GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+      opengl_es_textures_arr_[acquired_swapchain_texture_], 0);
+
+  GLenum status = xr_gl_->glCheckFramebufferStatus_fn(GL_FRAMEBUFFER);
+  if (status == GL_FRAMEBUFFER_COMPLETE) {
+    // Read a horizontal strip of pixels from the start of the texture; however
+    // many will fit.
+    xr_gl_->glReadPixels_fn(x_start, 0, buffer_size_pixels, 1, GL_RGBA,
+                            GL_UNSIGNED_BYTE, out_buffer.data());
+    data.color = GetFirstColor(data.raw_buffer);
+  } else {
+    DLOG(ERROR) << "Framebuffer not complete: " << std::hex << status;
+  }
+
+  xr_gl_->glBindFramebuffer_fn(GL_FRAMEBUFFER, 0);
+  xr_gl_->glDeleteFramebuffers_fn(1, &fbo);
 #endif
 }
 
@@ -693,6 +736,25 @@
 
     textures_arr_.push_back(texture);
   }
+#elif BUILDFLAG(IS_ANDROID)
+  DCHECK_NE(xr_gl_, nullptr);
+  opengl_es_textures_arr_.clear();
+  if (kMinSwapchainBuffering == 0) {
+    return;
+  }
+
+  opengl_es_textures_arr_.resize(kMinSwapchainBuffering);
+
+  xr_gl_->glGenTextures_fn(kMinSwapchainBuffering,
+                           opengl_es_textures_arr_.data());
+  for (GLuint texture_id : opengl_es_textures_arr_) {
+    xr_gl_->glBindTexture_fn(GL_TEXTURE_2D, texture_id);
+    // Allocate storage for the texture.
+    xr_gl_->glTexImage2D_fn(GL_TEXTURE_2D, 0, kSwapchainFormat, width, height,
+                            0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+  }
+  // Unbind the last texture.
+  xr_gl_->glBindTexture_fn(GL_TEXTURE_2D, 0);
 #endif
 }
 
@@ -708,6 +770,16 @@
   // is multiplied by 2 because WebXR uses a single double wide texture.
   CreateTextures(kPrimaryViewDimension * 2, kPrimaryViewDimension);
 }
+#elif BUILDFLAG(IS_ANDROID)
+void OpenXrTestHelper::SetOpenGLESInfo(EGLDisplay display, EGLContext context) {
+  // We don't seem to need to use these, but let's verify that we're passed in
+  // a valid display/context.
+  DCHECK_NE(display, EGL_NO_DISPLAY);
+  DCHECK_NE(context, EGL_NO_CONTEXT);
+  xr_gl_ = std::make_unique<XrTestGl>();
+
+  CreateTextures(kPrimaryViewDimension * 2, kPrimaryViewDimension);
+}
 #endif
 
 XrResult OpenXrTestHelper::AttachActionSets(
@@ -909,6 +981,10 @@
 OpenXrTestHelper::GetSwapchainTextures() const {
   return textures_arr_;
 }
+#elif BUILDFLAG(IS_ANDROID)
+const std::vector<uint32_t>& OpenXrTestHelper::GetSwapchainTextureIDs() const {
+  return opengl_es_textures_arr_;
+}
 #endif
 
 uint32_t OpenXrTestHelper::NextSwapchainImageIndex() {
@@ -917,7 +993,9 @@
       (acquired_swapchain_texture_ + 1) % textures_arr_.size();
   return acquired_swapchain_texture_;
 #else
-  return 0;
+  acquired_swapchain_texture_ =
+      (acquired_swapchain_texture_ + 1) % opengl_es_textures_arr_.size();
+  return acquired_swapchain_texture_;
 #endif
 }
 
diff --git a/device/vr/openxr/test/openxr_test_helper.h b/device/vr/openxr/test/openxr_test_helper.h
index 6f4492b..bb115ede 100644
--- a/device/vr/openxr/test/openxr_test_helper.h
+++ b/device/vr/openxr/test/openxr_test_helper.h
@@ -6,6 +6,7 @@
 #define DEVICE_VR_OPENXR_TEST_OPENXR_TEST_HELPER_H_
 
 #include <array>
+#include <memory>
 #include <optional>
 #include <queue>
 #include <unordered_map>
@@ -22,10 +23,17 @@
 #include <wrl.h>
 #endif
 
+#if BUILDFLAG(IS_ANDROID)
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#endif
+
 namespace gfx {
 class Transform;
 }  // namespace gfx
 
+class XrTestGl;
+
 class OpenXrTestHelper : public device::ServiceTestHook {
  public:
   OpenXrTestHelper();
@@ -111,6 +119,10 @@
   const std::vector<Microsoft::WRL::ComPtr<ID3D11Texture2D>>&
   GetSwapchainTextures() const;
 #endif
+#if BUILDFLAG(IS_ANDROID)
+  void SetOpenGLESInfo(EGLDisplay display, EGLContext context);
+  const std::vector<uint32_t>& GetSwapchainTextureIDs() const;
+#endif
 
   uint32_t NextSwapchainImageIndex();
   XrTime NextPredictedDisplayTime();
@@ -194,6 +206,10 @@
       XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT |
       XR_SPACE_LOCATION_POSITION_TRACKED_BIT;
 
+#if BUILDFLAG(IS_ANDROID)
+  static constexpr int kSwapchainFormat = GL_SRGB8_ALPHA8_EXT;
+#endif
+
  private:
   struct ActionProperties {
     std::unordered_map<XrPath, XrPath> profile_binding_map;
@@ -250,6 +266,9 @@
 #if BUILDFLAG(IS_WIN)
   Microsoft::WRL::ComPtr<ID3D11Device> d3d_device_;
   std::vector<Microsoft::WRL::ComPtr<ID3D11Texture2D>> textures_arr_;
+#elif BUILDFLAG(IS_ANDROID)
+  std::unique_ptr<XrTestGl> xr_gl_;
+  std::vector<uint32_t> opengl_es_textures_arr_;
 #endif
 
   // paths_ is used to keep tracked of strings that already has a corresponding
diff --git a/device/vr/openxr/test/xr_test_gl.cc b/device/vr/openxr/test/xr_test_gl.cc
new file mode 100644
index 0000000..2b0397f
--- /dev/null
+++ b/device/vr/openxr/test/xr_test_gl.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 "device/vr/openxr/test/xr_test_gl.h"
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <dlfcn.h>
+
+#include "base/logging.h"
+
+#define LOAD_GL_FN(name, upper_name) \
+  name##_fn =                        \
+      reinterpret_cast<PFN##upper_name##PROC>(dlsym(lib_gles_handle_, #name))
+#define LOAD_EGL_FN(name, upper_name) \
+  name##_fn =                         \
+      reinterpret_cast<PFN##upper_name##PROC>(dlsym(lib_egl_handle_, #name))
+
+XrTestGl::XrTestGl() {
+  lib_egl_handle_ = dlopen("libEGL.so", RTLD_LAZY | RTLD_LOCAL);
+  if (!lib_egl_handle_) {
+    LOG(ERROR) << "Failed to dlopen libEGL.so: " << dlerror();
+    return;
+  }
+
+  lib_gles_handle_ = dlopen("libGLESv2.so", RTLD_LAZY | RTLD_LOCAL);
+  if (!lib_gles_handle_) {
+    LOG(ERROR) << "Failed to dlopen libGLESv2.so: " << dlerror();
+    return;
+  }
+
+  LOAD_GL_FN(glGenTextures, GLGENTEXTURES);
+  LOAD_GL_FN(glBindTexture, GLBINDTEXTURE);
+  LOAD_GL_FN(glTexImage2D, GLTEXIMAGE2D);
+  LOAD_GL_FN(glGenFramebuffers, GLGENFRAMEBUFFERS);
+  LOAD_GL_FN(glBindFramebuffer, GLBINDFRAMEBUFFER);
+  LOAD_GL_FN(glFramebufferTexture2D, GLFRAMEBUFFERTEXTURE2D);
+  LOAD_GL_FN(glCheckFramebufferStatus, GLCHECKFRAMEBUFFERSTATUS);
+  LOAD_GL_FN(glReadPixels, GLREADPIXELS);
+  LOAD_GL_FN(glDeleteFramebuffers, GLDELETEFRAMEBUFFERS);
+
+  LOAD_EGL_FN(eglGetCurrentContext, EGLGETCURRENTCONTEXT);
+  LOAD_EGL_FN(eglGetCurrentDisplay, EGLGETCURRENTDISPLAY);
+  LOAD_EGL_FN(eglGetCurrentSurface, EGLGETCURRENTSURFACE);
+  LOAD_EGL_FN(eglGetError, EGLGETERROR);
+}
+
+XrTestGl::~XrTestGl() {
+  if (lib_gles_handle_) {
+    dlclose(lib_gles_handle_);
+    lib_gles_handle_ = nullptr;
+  }
+  if (lib_egl_handle_) {
+    dlclose(lib_egl_handle_);
+    lib_egl_handle_ = nullptr;
+  }
+}
+#undef LOAD_GL_FN
+#undef LOAD_EGL_FN
diff --git a/device/vr/openxr/test/xr_test_gl.h b/device/vr/openxr/test/xr_test_gl.h
new file mode 100644
index 0000000..3487bf10
--- /dev/null
+++ b/device/vr/openxr/test/xr_test_gl.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 DEVICE_VR_OPENXR_TEST_XR_TEST_GL_H_
+#define DEVICE_VR_OPENXR_TEST_XR_TEST_GL_H_
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include "base/memory/raw_ptr.h"
+
+// The mock OpenXR runtime is built as a standalone shared library. Because of
+// this many helpers for gl/egl can't be used as they expect quite a lot of
+// chrome infrastructure (command line, feature flags, RunLoop), to be setup.
+// This class is intended to help serve as an abstraction for the openxr mock
+// runtime to load/open the minimal set of GL/EGL functions needed by the tests.
+// Each instance of the class currently opens a new handle to the libraries.
+class XrTestGl {
+ public:
+  XrTestGl();
+  virtual ~XrTestGl();
+
+  // GLES function pointers
+  PFNGLGENTEXTURESPROC glGenTextures_fn = nullptr;
+  PFNGLBINDTEXTUREPROC glBindTexture_fn = nullptr;
+  PFNGLTEXIMAGE2DPROC glTexImage2D_fn = nullptr;
+  PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers_fn = nullptr;
+  PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer_fn = nullptr;
+  PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D_fn = nullptr;
+  PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus_fn = nullptr;
+  PFNGLREADPIXELSPROC glReadPixels_fn = nullptr;
+  PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers_fn = nullptr;
+
+  // EGL function pointers
+  PFNEGLGETCURRENTCONTEXTPROC eglGetCurrentContext_fn = nullptr;
+  PFNEGLGETCURRENTDISPLAYPROC eglGetCurrentDisplay_fn = nullptr;
+  PFNEGLGETCURRENTSURFACEPROC eglGetCurrentSurface_fn = nullptr;
+  PFNEGLGETERRORPROC eglGetError_fn = nullptr;
+
+ private:
+  raw_ptr<void> lib_gles_handle_ = nullptr;
+  raw_ptr<void> lib_egl_handle_ = nullptr;
+};
+#endif  // DEVICE_VR_OPENXR_TEST_XR_TEST_GL_H_
diff --git a/extensions/browser/api/execute_code_function.cc b/extensions/browser/api/execute_code_function.cc
index 90068d4..32e82f8 100644
--- a/extensions/browser/api/execute_code_function.cc
+++ b/extensions/browser/api/execute_code_function.cc
@@ -12,6 +12,7 @@
 #include <utility>
 
 #include "base/functional/bind.h"
+#include "base/strings/escape.h"
 #include "base/strings/string_util.h"
 #include "extensions/browser/extension_api_frame_id_map.h"
 #include "extensions/browser/extensions_browser_client.h"
@@ -211,7 +212,7 @@
     return false;
   }
 
-  script_url_ = extension()->ResolveExtensionURL(file);
+  script_url_ = extension()->ResolveExtensionURL(base::EscapePath(file));
 
   bool might_require_localization = is_css_injection;
 
diff --git a/extensions/browser/api/i18n/i18n_api.h b/extensions/browser/api/i18n/i18n_api.h
index b606f42..ced6fce 100644
--- a/extensions/browser/api/i18n/i18n_api.h
+++ b/extensions/browser/api/i18n/i18n_api.h
@@ -6,6 +6,9 @@
 #define EXTENSIONS_BROWSER_API_I18N_I18N_API_H_
 
 #include "extensions/browser/extension_function.h"
+#include "extensions/buildflags/buildflags.h"
+
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
 
 namespace extensions {
 
diff --git a/extensions/browser/api/offscreen/offscreen_api.h b/extensions/browser/api/offscreen/offscreen_api.h
index 3c2799e6..8eef0fc9 100644
--- a/extensions/browser/api/offscreen/offscreen_api.h
+++ b/extensions/browser/api/offscreen/offscreen_api.h
@@ -10,6 +10,9 @@
 #include "extensions/browser/extension_function_histogram_value.h"
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/extension_host_observer.h"
+#include "extensions/buildflags/buildflags.h"
+
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
 
 namespace extensions {
 
diff --git a/extensions/browser/api/runtime/runtime_api.h b/extensions/browser/api/runtime/runtime_api.h
index 16369d8..bbf1c1a6 100644
--- a/extensions/browser/api/runtime/runtime_api.h
+++ b/extensions/browser/api/runtime/runtime_api.h
@@ -24,9 +24,12 @@
 #include "extensions/browser/process_manager.h"
 #include "extensions/browser/process_manager_observer.h"
 #include "extensions/browser/update_observer.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/api/runtime.h"
 #include "extensions/common/extension_id.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace base {
 class Version;
 }
diff --git a/extensions/browser/api/user_scripts/user_scripts_api.cc b/extensions/browser/api/user_scripts/user_scripts_api.cc
index 84d5ad75..60ef969 100644
--- a/extensions/browser/api/user_scripts/user_scripts_api.cc
+++ b/extensions/browser/api/user_scripts/user_scripts_api.cc
@@ -12,6 +12,7 @@
 #include "base/format_macros.h"
 #include "base/functional/bind.h"
 #include "base/notreached.h"
+#include "base/strings/escape.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/types/optional_util.h"
@@ -763,7 +764,8 @@
       CHECK_LT(file_index, static_cast<int>(file_sources.size()));
       source = mojom::JSSource::New(
           std::move(*file_sources[file_index].data),
-          extension()->ResolveExtensionURL(file_sources[file_index].file_name));
+          extension()->ResolveExtensionURL(
+              base::EscapePath(file_sources[file_index].file_name)));
       file_index++;
     }
   }
diff --git a/extensions/browser/api/web_request/web_request_api.h b/extensions/browser/api/web_request/web_request_api.h
index 6ef7838..57fa93e 100644
--- a/extensions/browser/api/web_request/web_request_api.h
+++ b/extensions/browser/api/web_request/web_request_api.h
@@ -36,6 +36,7 @@
 #include "extensions/browser/extension_api_frame_id_map.h"
 #include "extensions/browser/extension_function.h"
 #include "extensions/browser/extension_registry_observer.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/extension_id.h"
 #include "ipc/ipc_sender.h"
 #include "net/base/auth.h"
@@ -45,6 +46,8 @@
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/network/public/mojom/websocket.mojom.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 class GURL;
 
 namespace content {
diff --git a/extensions/browser/event_listener_map.cc b/extensions/browser/event_listener_map.cc
index afd7bd0..1096313 100644
--- a/extensions/browser/event_listener_map.cc
+++ b/extensions/browser/event_listener_map.cc
@@ -12,6 +12,7 @@
 #include "base/memory/ptr_util.h"
 #include "content/public/browser/render_process_host.h"
 #include "extensions/browser/event_router.h"
+#include "extensions/browser/service_worker/worker_id.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension_id.h"
 #include "ipc/ipc_message.h"
@@ -302,13 +303,17 @@
 }
 
 void EventListenerMap::RemoveActiveServiceWorkerListenersForExtension(
-    const ExtensionId& extension_id) {
+    const WorkerId& worker_id) {
   RemoveListenersForExtensionImpl(
-      extension_id, /*removal_predicate=*/base::BindRepeating(
-          [](const ExtensionId& extension_id, EventListener* listener) {
-            return listener->extension_id() == extension_id &&
-                   listener->is_for_service_worker() && !listener->IsLazy();
-          }));
+      worker_id.extension_id, /*removal_predicate=*/base::BindRepeating(
+          [](const WorkerId& worker_id, const ExtensionId& extension_id,
+             EventListener* listener) {
+            return listener->extension_id() == worker_id.extension_id &&
+                   listener->is_for_service_worker() && !listener->IsLazy() &&
+                   listener->process()->GetDeprecatedID() ==
+                       worker_id.render_process_id;
+          },
+          worker_id));
 }
 
 void EventListenerMap::LoadUnfilteredLazyListeners(
diff --git a/extensions/browser/event_listener_map.h b/extensions/browser/event_listener_map.h
index 96ab4677..58ee9c4 100644
--- a/extensions/browser/event_listener_map.h
+++ b/extensions/browser/event_listener_map.h
@@ -28,6 +28,7 @@
 
 namespace extensions {
 struct Event;
+struct WorkerId;
 
 // A listener for an extension event. A listener is essentially an endpoint
 // that an event can be dispatched to.
@@ -233,7 +234,7 @@
 
   // Removes any active listeners that `extension_id` has added.
   void RemoveActiveServiceWorkerListenersForExtension(
-      const ExtensionId& extension_id);
+      const WorkerId& worker_id);
 
   // Adds unfiltered lazy listeners as described their serialised descriptions.
   // `event_names` the names of the lazy events.
diff --git a/extensions/browser/event_router.cc b/extensions/browser/event_router.cc
index a9fc0f41..1cb254e3 100644
--- a/extensions/browser/event_router.cc
+++ b/extensions/browser/event_router.cc
@@ -1609,8 +1609,7 @@
     const WorkerId& worker_id) {
   // Remove any active listeners since they are no longer guaranteed to be ready
   // to receive events.
-  listeners_.RemoveActiveServiceWorkerListenersForExtension(
-      worker_id.extension_id);
+  listeners_.RemoveActiveServiceWorkerListenersForExtension(worker_id);
 }
 
 void EventRouter::AddLazyEventListenerImpl(
diff --git a/extensions/common/manifest_handlers/extension_manifests_icon_variants_unittest.cc b/extensions/common/manifest_handlers/extension_manifests_icon_variants_unittest.cc
index 5306dcea..039581d3 100644
--- a/extensions/common/manifest_handlers/extension_manifests_icon_variants_unittest.cc
+++ b/extensions/common/manifest_handlers/extension_manifests_icon_variants_unittest.cc
@@ -341,4 +341,44 @@
   ASSERT_TRUE(extension->install_warnings().empty());
 }
 
+TEST_F(IconVariantsManifestTest, GetIconUrlWithSpecialChars) {
+  ManifestData manifest_data = ManifestData::FromJSON(
+      R"({
+        "name": "test",
+        "version": "1",
+        "manifest_version": 3,
+        "icons": {
+          "16": "#icons.16.png"
+        },
+        "icon_variants": [
+          {
+            "16": "#icon_variants.16.png"
+          },
+          {
+            "16": "#icon_variants.16.dark.png",
+            "color_schemes": ["dark"]
+          }
+        ]
+      })");
+  scoped_refptr<extensions::Extension> extension(
+      LoadAndExpectSuccess(manifest_data));
+
+  const GURL& icon_url = IconsInfo::GetIconURL(
+      extension.get(), extension_misc::EXTENSION_ICON_BITTY,
+      ExtensionIconSet::Match::kExactly);
+  EXPECT_EQ("%23icon_variants.16.png", icon_url.path().substr(1));
+
+  const GURL& icon_url_light = IconsInfo::GetIconURL(
+      extension.get(), extension_misc::EXTENSION_ICON_BITTY,
+      ExtensionIconSet::Match::kExactly,
+      ExtensionIconVariant::ColorScheme::kLight);
+  EXPECT_EQ("%23icon_variants.16.png", icon_url_light.path().substr(1));
+
+  const GURL& icon_url_dark = IconsInfo::GetIconURL(
+      extension.get(), extension_misc::EXTENSION_ICON_BITTY,
+      ExtensionIconSet::Match::kExactly,
+      ExtensionIconVariant::ColorScheme::kDark);
+  EXPECT_EQ("%23icon_variants.16.dark.png", icon_url_dark.path().substr(1));
+}
+
 }  // namespace extensions
diff --git a/extensions/common/manifest_handlers/icons_handler.cc b/extensions/common/manifest_handlers/icons_handler.cc
index ed35190e..c7ab4d1 100644
--- a/extensions/common/manifest_handlers/icons_handler.cc
+++ b/extensions/common/manifest_handlers/icons_handler.cc
@@ -9,6 +9,7 @@
 
 #include "base/files/file_util.h"
 #include "base/lazy_instance.h"
+#include "base/strings/escape.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
@@ -66,7 +67,8 @@
                            ExtensionIconVariant::ColorScheme color_scheme) {
   const std::string& path =
       GetIcons(*extension, color_scheme).Get(size_in_px, match_type);
-  return path.empty() ? GURL() : extension->ResolveExtensionURL(path);
+  return path.empty() ? GURL()
+                      : extension->ResolveExtensionURL(base::EscapePath(path));
 }
 
 bool IconsHandler::Parse(Extension* extension, std::u16string* error) {
diff --git a/extensions/common/utils/content_script_utils.cc b/extensions/common/utils/content_script_utils.cc
index a48de590..5ff699b 100644
--- a/extensions/common/utils/content_script_utils.cc
+++ b/extensions/common/utils/content_script_utils.cc
@@ -11,6 +11,7 @@
 #include <string_view>
 
 #include "base/files/file_util.h"
+#include "base/strings/escape.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -348,7 +349,8 @@
     result->js_scripts().reserve(js->size());
     for (const auto& source : *js) {
       if (source.file) {
-        GURL url = extension->ResolveExtensionURL(*source.file);
+        GURL url =
+            extension->ResolveExtensionURL(base::EscapePath(*source.file));
         ExtensionResource resource = extension->GetResource(*source.file);
         result->js_scripts().push_back(UserScript::Content::CreateFile(
             resource.extension_root(), resource.relative_path(), url));
@@ -377,7 +379,8 @@
     result->css_scripts().reserve(css->size());
     for (const auto& source : *css) {
       if (source.file) {
-        GURL url = extension->ResolveExtensionURL(*source.file);
+        GURL url =
+            extension->ResolveExtensionURL(base::EscapePath(*source.file));
         ExtensionResource resource = extension->GetResource(*source.file);
         result->css_scripts().push_back(UserScript::Content::CreateFile(
             resource.extension_root(), resource.relative_path(), url));
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 2bcc217..a984cf8c 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -81,6 +81,7 @@
 #include "extensions/renderer/module_system.h"
 #include "extensions/renderer/native_extension_bindings_system.h"
 #include "extensions/renderer/renderer_extension_registry.h"
+#include "extensions/renderer/renderer_frame_context_data.h"
 #include "extensions/renderer/safe_builtins.h"
 #include "extensions/renderer/script_context.h"
 #include "extensions/renderer/script_context_set.h"
@@ -602,7 +603,7 @@
   }
 }
 
-void Dispatcher::WillEvaluateServiceWorkerOnWorkerThread(
+void Dispatcher::WillPrepareForEvaluationOnWorkerThread(
     blink::WebServiceWorkerContextProxy* context_proxy,
     v8::Local<v8::Context> v8_context,
     int64_t service_worker_version_id,
@@ -730,7 +731,6 @@
   RequireGuestViewModules(context);
 #endif
 
-  WorkerThreadDispatcher::GetServiceWorkerData()->Init();
   g_worker_script_context_set.Get().Insert(base::WrapUnique(context));
 
   const base::TimeDelta elapsed = base::TimeTicks::Now() - start_time;
@@ -739,6 +739,22 @@
   service_worker_context_state = ServiceWorkerContextState::kInitialized;
 }
 
+void Dispatcher::WillEvaluateServiceWorkerOnWorkerThread() {
+  const int thread_id = content::WorkerThread::GetCurrentId();
+  CHECK_NE(thread_id, kMainThreadId);
+
+  // `WillPrepareForEvaluationOnWorkerThread` should have run and updated
+  // `service_worker_context_state`.
+  CHECK_NE(service_worker_context_state,
+           extensions::ServiceWorkerContextState::kDefault);
+
+  if (service_worker_context_state != ServiceWorkerContextState::kInitialized) {
+    return;
+  }
+
+  WorkerThreadDispatcher::GetServiceWorkerData()->Init();
+}
+
 void Dispatcher::WillReleaseScriptContext(
     blink::WebLocalFrame* frame,
     const v8::Local<v8::Context>& v8_context,
diff --git a/extensions/renderer/dispatcher.h b/extensions/renderer/dispatcher.h
index 9d4c74b7..dad2104 100644
--- a/extensions/renderer/dispatcher.h
+++ b/extensions/renderer/dispatcher.h
@@ -144,12 +144,12 @@
       const GURL& service_worker_scope,
       const GURL& script_url);
 
-  // This is called immediately before a service worker evaluates the
-  // toplevel script. This method installs extension API bindings.
+  // This is called before a service worker prepares script engine for
+  // evaluation.
   //
   // Runs on a different thread and should only use thread-safe member
   // variables.
-  void WillEvaluateServiceWorkerOnWorkerThread(
+  void WillPrepareForEvaluationOnWorkerThread(
       blink::WebServiceWorkerContextProxy* context_proxy,
       v8::Local<v8::Context> v8_context,
       int64_t service_worker_version_id,
@@ -157,6 +157,13 @@
       const GURL& script_url,
       const blink::ServiceWorkerToken& service_worker_token);
 
+  // This is called immediately before a service worker evaluates the
+  // toplevel script. This method installs extension API bindings.
+  //
+  // Runs on a different thread and should only use thread-safe member
+  // variables.
+  void WillEvaluateServiceWorkerOnWorkerThread();
+
   void WillReleaseScriptContext(blink::WebLocalFrame* frame,
                                 const v8::Local<v8::Context>& context,
                                 int32_t world_id);
diff --git a/extensions/renderer/module_system.cc b/extensions/renderer/module_system.cc
index 66eb37b..b4bee1e 100644
--- a/extensions/renderer/module_system.cc
+++ b/extensions/renderer/module_system.cc
@@ -198,9 +198,9 @@
     CHECK(GetPrivate(global, kModuleSystem, &dummy_value));
   }
 
-  if (context_->GetRenderFrame() &&
-      context_->context_type() == mojom::ContextType::kPrivilegedExtension &&
-      !context_->IsForServiceWorker() && ContextNeedsMojoBindings(context_)) {
+  if (context_->context_type() == mojom::ContextType::kPrivilegedExtension &&
+      ContextNeedsMojoBindings(context_) &&
+      blink::WebV8Features::IsSupported(context->v8_context())) {
     // Valid enablement code path, so need to ensure MojoJS is allowed for the
     // process before attempting to enable it.
     blink::WebV8Features::AllowMojoJSForProcess();
diff --git a/extensions/test/result_catcher.cc b/extensions/test/result_catcher.cc
index c5490bd..a06cd9ad 100644
--- a/extensions/test/result_catcher.cc
+++ b/extensions/test/result_catcher.cc
@@ -4,12 +4,14 @@
 
 #include "extensions/test/result_catcher.h"
 
+#include "base/logging.h"
 #include "base/run_loop.h"
 #include "content/public/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace extensions {
 
-ResultCatcher::ResultCatcher() : browser_context_restriction_(nullptr) {
+ResultCatcher::ResultCatcher() {
   test_api_observation_.Observe(TestApiObserverRegistry::GetInstance());
 }
 
@@ -25,43 +27,50 @@
     run_loop.Run();
   }
 
-  if (!results_.empty()) {
-    bool ret = results_.front();
-    results_.pop_front();
-    message_ = messages_.front();
-    messages_.pop_front();
-    return ret;
+  // Can happen if the test timed out and never produced a result.
+  if (results_.empty()) {
+    ADD_FAILURE() << "ResultCatcher never received a result.";
+    return false;
   }
 
-  DUMP_WILL_BE_NOTREACHED();
-  return false;
+  bool ret = results_.front();
+  results_.pop_front();
+  message_ = messages_.front();
+  messages_.pop_front();
+  return ret;
 }
 
 void ResultCatcher::OnTestPassed(content::BrowserContext* browser_context) {
-  if (browser_context_restriction_ &&
-      browser_context != browser_context_restriction_) {
+  if (IsRelevantBrowserContext(browser_context)) {
     return;
   }
 
   VLOG(1) << "Got chrome.test.notifyPass notification.";
   results_.push_back(true);
   messages_.push_back(std::string());
-  if (!quit_closure_.is_null())
+  if (quit_closure_) {
     std::move(quit_closure_).Run();
+  }
 }
 
 void ResultCatcher::OnTestFailed(content::BrowserContext* browser_context,
                                  const std::string& message) {
-  if (browser_context_restriction_ &&
-      browser_context != browser_context_restriction_) {
+  if (IsRelevantBrowserContext(browser_context)) {
     return;
   }
 
   VLOG(1) << "Got chrome.test.notifyFail notification.";
   results_.push_back(false);
   messages_.push_back(message);
-  if (!quit_closure_.is_null())
+  if (quit_closure_) {
     std::move(quit_closure_).Run();
+  }
+}
+
+bool ResultCatcher::IsRelevantBrowserContext(
+    content::BrowserContext* browser_context) const {
+  return browser_context_restriction_ &&
+         browser_context != browser_context_restriction_;
 }
 
 }  // namespace extensions
diff --git a/extensions/test/result_catcher.h b/extensions/test/result_catcher.h
index e266d486..5288956a 100644
--- a/extensions/test/result_catcher.h
+++ b/extensions/test/result_catcher.h
@@ -38,7 +38,7 @@
     browser_context_restriction_ = context;
   }
 
-  const std::string& message() { return message_; }
+  const std::string& message() const { return message_; }
 
  private:
   // TestApiObserver:
@@ -46,6 +46,8 @@
   void OnTestFailed(content::BrowserContext* browser_context,
                     const std::string& message) override;
 
+  bool IsRelevantBrowserContext(content::BrowserContext* browser_context) const;
+
   // A sequential list of pass/fail notifications from the test extension(s).
   base::circular_deque<bool> results_;
 
@@ -54,7 +56,7 @@
   std::string message_;
 
   // If non-NULL, we will listen to events from this BrowserContext only.
-  raw_ptr<content::BrowserContext> browser_context_restriction_;
+  raw_ptr<content::BrowserContext> browser_context_restriction_ = nullptr;
 
   // Only set if we're in a nested run loop waiting for results from
   // the extension.
diff --git a/gpu/command_buffer/client/client_shared_image.cc b/gpu/command_buffer/client/client_shared_image.cc
index 36bd748..1ae40cb8 100644
--- a/gpu/command_buffer/client/client_shared_image.cc
+++ b/gpu/command_buffer/client/client_shared_image.cc
@@ -556,6 +556,13 @@
       new RasterScopedAccess(raster_interface, this, sync_token, readonly));
 }
 
+std::unique_ptr<RasterScopedAccess>
+ClientSharedImage::BeginGLAccessForCopySharedImage(InterfaceBase* gl_interface,
+                                                   const SyncToken& sync_token,
+                                                   bool readonly) {
+  return BeginRasterAccess(gl_interface, sync_token, readonly);
+}
+
 #if BUILDFLAG(IS_WIN)
 void ClientSharedImage::SetUsePreMappedMemory(bool use_premapped_memory) {
   CHECK(gpu_memory_buffer_);
@@ -657,15 +664,6 @@
                                                   /*result=*/false));
 }
 
-bool ClientSharedImage::HelperGpuMemoryBufferManager::IsConnected() {
-  auto sii = GetSharedImageInterface();
-  if (!sii) {
-    DLOG(WARNING) << "No SharedImageInterface.";
-    return false;
-  }
-  return sii->IsConnected();
-}
-
 // Access the SharedImageInterface via the SharedImageInterfaceHolder.
 scoped_refptr<SharedImageInterface>
 ClientSharedImage::HelperGpuMemoryBufferManager::GetSharedImageInterface() {
diff --git a/gpu/command_buffer/client/client_shared_image.h b/gpu/command_buffer/client/client_shared_image.h
index b0d1179..29b26a7 100644
--- a/gpu/command_buffer/client/client_shared_image.h
+++ b/gpu/command_buffer/client/client_shared_image.h
@@ -244,6 +244,13 @@
       const SyncToken& sync_token,
       bool readonly);
 
+  // This is used for CopySharedImageToTextureINTERNAL, where we need GL access
+  // but do not create a GL texture.
+  std::unique_ptr<RasterScopedAccess> BeginGLAccessForCopySharedImage(
+      InterfaceBase* gl_interface,
+      const SyncToken& sync_token,
+      bool readonly);
+
 #if BUILDFLAG(IS_WIN)
   // Allows client to indicate the |gpu_memory_buffer_| to pre map its shared
   // memory region internally for performance optimization purposes. It is only
@@ -286,8 +293,6 @@
         base::UnsafeSharedMemoryRegion memory_region,
         base::OnceCallback<void(bool)> callback) final;
 
-    bool IsConnected() final;
-
    private:
     // Points to the parent ClientSharedImage. It will be used to access SII via
     // SII holder.
diff --git a/gpu/command_buffer/client/gpu_memory_buffer_manager.cc b/gpu/command_buffer/client/gpu_memory_buffer_manager.cc
index 3ec3d05..286bd42 100644
--- a/gpu/command_buffer/client/gpu_memory_buffer_manager.cc
+++ b/gpu/command_buffer/client/gpu_memory_buffer_manager.cc
@@ -10,16 +10,11 @@
 
 GpuMemoryBufferManager::~GpuMemoryBufferManager() = default;
 
-void GpuMemoryBufferManager::AddObserver(
-    GpuMemoryBufferManagerObserver* observer) {}
-
-void GpuMemoryBufferManager::RemoveObserver(
-    GpuMemoryBufferManagerObserver* observer) {}
-
-void GpuMemoryBufferManager::NotifyObservers() {
-  for (auto& observer : observers_) {
-    observer.OnGpuMemoryBufferManagerDestroyed();
-  }
+void GpuMemoryBufferManager::CopyGpuMemoryBufferAsync(
+    gfx::GpuMemoryBufferHandle buffer_handle,
+    base::UnsafeSharedMemoryRegion memory_region,
+    base::OnceCallback<void(bool)> callback) {
+  NOTREACHED();
 }
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/client/gpu_memory_buffer_manager.h b/gpu/command_buffer/client/gpu_memory_buffer_manager.h
index d78bd1c3..a47f88f 100644
--- a/gpu/command_buffer/client/gpu_memory_buffer_manager.h
+++ b/gpu/command_buffer/client/gpu_memory_buffer_manager.h
@@ -21,15 +21,6 @@
 
 namespace gpu {
 
-// Used to observe the destruction of GpuMemoryBufferManager.
-class GPU_EXPORT GpuMemoryBufferManagerObserver : public base::CheckedObserver {
- public:
-  virtual void OnGpuMemoryBufferManagerDestroyed() = 0;
-
- protected:
-  ~GpuMemoryBufferManagerObserver() override = default;
-};
-
 class GPU_EXPORT GpuMemoryBufferManager {
  public:
   GpuMemoryBufferManager();
@@ -50,20 +41,7 @@
   virtual void CopyGpuMemoryBufferAsync(
       gfx::GpuMemoryBufferHandle buffer_handle,
       base::UnsafeSharedMemoryRegion memory_region,
-      base::OnceCallback<void(bool)> callback) = 0;
-
-  // Checks if the GpuMemoryBufferManager is connected to the GPU Service
-  // Currently on GPU process crash the connection isn't restored.
-  virtual bool IsConnected() = 0;
-
-  // Implementations of GpuMemoryBufferManager can override below methods if
-  // they want to add/remove observers to notify its destruction.
-  virtual void AddObserver(GpuMemoryBufferManagerObserver* observer);
-  virtual void RemoveObserver(GpuMemoryBufferManagerObserver* observer);
-
- protected:
-  void NotifyObservers();
-  base::ObserverList<GpuMemoryBufferManagerObserver> observers_;
+      base::OnceCallback<void(bool)> callback);
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/client/shared_image_interface.cc b/gpu/command_buffer/client/shared_image_interface.cc
index d4566b0..2287acf 100644
--- a/gpu/command_buffer/client/shared_image_interface.cc
+++ b/gpu/command_buffer/client/shared_image_interface.cc
@@ -108,12 +108,6 @@
   NOTREACHED();
 }
 
-bool SharedImageInterface::CopyNativeGmbToSharedMemorySync(
-    gfx::GpuMemoryBufferHandle buffer_handle,
-    base::UnsafeSharedMemoryRegion memory_region) {
-  NOTREACHED();
-}
-
 void SharedImageInterface::CopyNativeGmbToSharedMemoryAsync(
     gfx::GpuMemoryBufferHandle buffer_handle,
     base::UnsafeSharedMemoryRegion memory_region,
@@ -121,10 +115,6 @@
   NOTREACHED();
 }
 
-bool SharedImageInterface::IsConnected() {
-  NOTREACHED();
-}
-
 void SharedImageInterface::Release() const {
   bool should_destroy = false;
 
diff --git a/gpu/command_buffer/client/shared_image_interface.h b/gpu/command_buffer/client/shared_image_interface.h
index 8145a25..ab612ff 100644
--- a/gpu/command_buffer/client/shared_image_interface.h
+++ b/gpu/command_buffer/client/shared_image_interface.h
@@ -236,23 +236,12 @@
   // is done to copy the content of |buffer_handle| into a shared memory
   // |memory_region| via below methods. This shared memory is mappable in any
   // process and is used internally during GpuMemoryBuffer::Map().
-  // This will block on calling client thread.
-  virtual bool CopyNativeGmbToSharedMemorySync(
-      gfx::GpuMemoryBufferHandle buffer_handle,
-      base::UnsafeSharedMemoryRegion memory_region);
-
-  // This is non-blocking version of above method. The |callback| will be run
-  // when the copy is done.
+  // The |callback| will be run when the copy is done.
   virtual void CopyNativeGmbToSharedMemoryAsync(
       gfx::GpuMemoryBufferHandle buffer_handle,
       base::UnsafeSharedMemoryRegion memory_region,
       base::OnceCallback<void(bool)> callback);
 
-  // Checks if the GpuChannel is connected to this interface. This is
-  // used on windows to find if SII is still connected to the GPU service so
-  // that GpuMemoryBufferManager can use it.
-  virtual bool IsConnected();
-
   // Destroys the shared image, unregistering its mailbox, after |sync_token|
   // has been released. After this call, the mailbox can't be used to reference
   // the image any more, however if the image was imported into other APIs,
diff --git a/gpu/command_buffer/client/test_gpu_memory_buffer_manager.cc b/gpu/command_buffer/client/test_gpu_memory_buffer_manager.cc
index 2ee21f4..4f2f548 100644
--- a/gpu/command_buffer/client/test_gpu_memory_buffer_manager.cc
+++ b/gpu/command_buffer/client/test_gpu_memory_buffer_manager.cc
@@ -188,15 +188,4 @@
   return result;
 }
 
-void TestGpuMemoryBufferManager::CopyGpuMemoryBufferAsync(
-    gfx::GpuMemoryBufferHandle buffer_handle,
-    base::UnsafeSharedMemoryRegion memory_region,
-    base::OnceCallback<void(bool)> callback) {
-  std::move(callback).Run(false);
-}
-
-bool TestGpuMemoryBufferManager::IsConnected() {
-  return true;
-}
-
 }  // namespace gpu
diff --git a/gpu/command_buffer/client/test_gpu_memory_buffer_manager.h b/gpu/command_buffer/client/test_gpu_memory_buffer_manager.h
index 74caf10..b8da466 100644
--- a/gpu/command_buffer/client/test_gpu_memory_buffer_manager.h
+++ b/gpu/command_buffer/client/test_gpu_memory_buffer_manager.h
@@ -41,11 +41,6 @@
       gfx::BufferUsage usage,
       gpu::SurfaceHandle surface_handle,
       base::WaitableEvent* shutdown_event) override;
-  void CopyGpuMemoryBufferAsync(
-      gfx::GpuMemoryBufferHandle buffer_handle,
-      base::UnsafeSharedMemoryRegion memory_region,
-      base::OnceCallback<void(bool)> callback) override;
-  bool IsConnected() override;
 
  private:
   // This class is called by multiple threads at the same time. Hold this lock
diff --git a/gpu/command_buffer/service/texture_base.h b/gpu/command_buffer/service/texture_base.h
index 5caac0ac..d5744af 100644
--- a/gpu/command_buffer/service/texture_base.h
+++ b/gpu/command_buffer/service/texture_base.h
@@ -7,7 +7,6 @@
 
 #include <stdint.h>
 
-#include "base/memory/raw_ptr.h"
 #include "gpu/gpu_export.h"
 
 namespace gpu {
diff --git a/gpu/ipc/client/client_shared_image_interface.cc b/gpu/ipc/client/client_shared_image_interface.cc
index acd14e8..53ec887 100644
--- a/gpu/ipc/client/client_shared_image_interface.cc
+++ b/gpu/ipc/client/client_shared_image_interface.cc
@@ -253,17 +253,6 @@
   proxy_->UpdateSharedImage(sync_token, std::move(d3d_shared_fence), mailbox);
 }
 
-bool ClientSharedImageInterface::CopyNativeGmbToSharedMemorySync(
-    gfx::GpuMemoryBufferHandle buffer_handle,
-    base::UnsafeSharedMemoryRegion memory_region) {
-  CHECK_EQ(buffer_handle.type, gfx::GpuMemoryBufferType::DXGI_SHARED_HANDLE);
-  CHECK(memory_region.IsValid());
-  bool status = false;
-  proxy_->CopyNativeGmbToSharedMemorySync(std::move(buffer_handle),
-                                          std::move(memory_region), &status);
-  return status;
-}
-
 void ClientSharedImageInterface::CopyNativeGmbToSharedMemoryAsync(
     gfx::GpuMemoryBufferHandle buffer_handle,
     base::UnsafeSharedMemoryRegion memory_region,
@@ -273,11 +262,7 @@
   proxy_->CopyNativeGmbToSharedMemoryAsync(
       std::move(buffer_handle), std::move(memory_region), std::move(callback));
 }
-
-bool ClientSharedImageInterface::IsConnected() {
-  return proxy_->IsConnected();
-}
-#endif
+#endif  // BUILDFLAG(IS_WIN)
 
 ClientSharedImageInterface::SwapChainSharedImages
 ClientSharedImageInterface::CreateSwapChain(viz::SharedImageFormat format,
diff --git a/gpu/ipc/client/client_shared_image_interface.h b/gpu/ipc/client/client_shared_image_interface.h
index d636706..928e943 100644
--- a/gpu/ipc/client/client_shared_image_interface.h
+++ b/gpu/ipc/client/client_shared_image_interface.h
@@ -87,15 +87,12 @@
   void UpdateSharedImage(const SyncToken& sync_token,
                          scoped_refptr<gfx::D3DSharedFence> d3d_shared_fence,
                          const Mailbox& mailbox) override;
-  bool CopyNativeGmbToSharedMemorySync(
-      gfx::GpuMemoryBufferHandle buffer_handle,
-      base::UnsafeSharedMemoryRegion memory_region) override;
   void CopyNativeGmbToSharedMemoryAsync(
       gfx::GpuMemoryBufferHandle buffer_handle,
       base::UnsafeSharedMemoryRegion memory_region,
       base::OnceCallback<void(bool)> callback) override;
-  bool IsConnected() override;
-#endif
+#endif  // BUILDFLAG(IS_WIN)
+
   SwapChainSharedImages CreateSwapChain(viz::SharedImageFormat format,
                                         const gfx::Size& size,
                                         const gfx::ColorSpace& color_space,
diff --git a/gpu/ipc/client/gpu_channel_host.cc b/gpu/ipc/client/gpu_channel_host.cc
index fd0aac0..d94ff231 100644
--- a/gpu/ipc/client/gpu_channel_host.cc
+++ b/gpu/ipc/client/gpu_channel_host.cc
@@ -139,14 +139,6 @@
       std::move(callback));
 }
 
-void GpuChannelHost::CopyNativeGmbToSharedMemorySync(
-    gfx::GpuMemoryBufferHandle buffer_handle,
-    base::UnsafeSharedMemoryRegion memory_region,
-    bool* status) {
-  GetGpuChannel().CopyNativeGmbToSharedMemorySync(
-      std::move(buffer_handle), std::move(memory_region), status);
-}
-
 void GpuChannelHost::CopyNativeGmbToSharedMemoryAsync(
     gfx::GpuMemoryBufferHandle buffer_handle,
     base::UnsafeSharedMemoryRegion memory_region,
@@ -160,11 +152,7 @@
   GetGpuChannel().CopyNativeGmbToSharedMemoryAsync(
       std::move(buffer_handle), std::move(memory_region), std::move(callback));
 }
-
-bool GpuChannelHost::IsConnected() {
-  return static_cast<bool>(gpu_channel_);
-}
-#endif
+#endif  // BUILDFLAG(IS_WIN)
 
 void GpuChannelHost::DelayedEnsureFlush(uint32_t deferred_message_id) {
   AutoLock lock(deferred_message_lock_);
diff --git a/gpu/ipc/client/gpu_channel_host.h b/gpu/ipc/client/gpu_channel_host.h
index 5c9c204..1fdb9be 100644
--- a/gpu/ipc/client/gpu_channel_host.h
+++ b/gpu/ipc/client/gpu_channel_host.h
@@ -151,16 +151,11 @@
       std::vector<SyncToken> sync_token_dependencies,
       uint64_t release_count,
       base::OnceCallback<void(bool)> callback);
-  void CopyNativeGmbToSharedMemorySync(
-      gfx::GpuMemoryBufferHandle buffer_handle,
-      base::UnsafeSharedMemoryRegion memory_region,
-      bool* status);
   void CopyNativeGmbToSharedMemoryAsync(
       gfx::GpuMemoryBufferHandle buffer_handle,
       base::UnsafeSharedMemoryRegion memory_region,
       base::OnceCallback<void(bool)> callback);
-  bool IsConnected();
-#endif
+#endif  // BUILDFLAG(IS_WIN)
 
   // Crashes the GPU process. This functionality is added here because
   // of instability when creating a new tab just to navigate to
diff --git a/gpu/ipc/client/shared_image_interface_proxy.cc b/gpu/ipc/client/shared_image_interface_proxy.cc
index 3cc66e4f..a384241 100644
--- a/gpu/ipc/client/shared_image_interface_proxy.cc
+++ b/gpu/ipc/client/shared_image_interface_proxy.cc
@@ -287,15 +287,6 @@
       std::move(dependencies), /*release_count=*/0);
 }
 
-void SharedImageInterfaceProxy::CopyNativeGmbToSharedMemorySync(
-    gfx::GpuMemoryBufferHandle buffer_handle,
-    base::UnsafeSharedMemoryRegion memory_region,
-    bool* status) {
-  mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
-  host_->CopyNativeGmbToSharedMemorySync(std::move(buffer_handle),
-                                         std::move(memory_region), status);
-}
-
 void SharedImageInterfaceProxy::CopyNativeGmbToSharedMemoryAsync(
     gfx::GpuMemoryBufferHandle buffer_handle,
     base::UnsafeSharedMemoryRegion memory_region,
@@ -303,10 +294,6 @@
   host_->CopyNativeGmbToSharedMemoryAsync(
       std::move(buffer_handle), std::move(memory_region), std::move(callback));
 }
-
-bool SharedImageInterfaceProxy::IsConnected() {
-  return host_->IsConnected();
-}
 #endif  // BUILDFLAG(IS_WIN)
 
 void SharedImageInterfaceProxy::UpdateSharedImage(const SyncToken& sync_token,
diff --git a/gpu/ipc/client/shared_image_interface_proxy.h b/gpu/ipc/client/shared_image_interface_proxy.h
index 1c0154f..30eb3e6f 100644
--- a/gpu/ipc/client/shared_image_interface_proxy.h
+++ b/gpu/ipc/client/shared_image_interface_proxy.h
@@ -84,15 +84,10 @@
   void UpdateSharedImage(const SyncToken& sync_token,
                          scoped_refptr<gfx::D3DSharedFence> d3d_shared_fence,
                          const Mailbox& mailbox);
-  void CopyNativeGmbToSharedMemorySync(
-      gfx::GpuMemoryBufferHandle buffer_handle,
-      base::UnsafeSharedMemoryRegion memory_region,
-      bool* status);
   void CopyNativeGmbToSharedMemoryAsync(
       gfx::GpuMemoryBufferHandle buffer_handle,
       base::UnsafeSharedMemoryRegion memory_region,
       base::OnceCallback<void(bool)> callback);
-  bool IsConnected();
 #endif  // BUILDFLAG(IS_WIN)
 
   void UpdateSharedImage(const SyncToken& sync_token, const Mailbox& mailbox);
diff --git a/gpu/ipc/common/gpu_channel.mojom b/gpu/ipc/common/gpu_channel.mojom
index 3a381ae..d49c3c6e 100644
--- a/gpu/ipc/common/gpu_channel.mojom
+++ b/gpu/ipc/common/gpu_channel.mojom
@@ -253,20 +253,11 @@
       Mailbox mailbox, array<SyncToken> sync_token_dependencies,
       uint64 release_count) => (bool success);
 
-  [Sync, EnableIf=is_win]
   // Copies GMB pixel data from the native handle to the |shared_memory|.
-  // Returns |true| if the copy has succeeded.
+  // Returns |true| asynchronously if the copy has succeeded.
   // Note that this method has some similar functionality but is different than
   // above CopyToGpuMemoryBufferAsync in the sense that above method is only
   // used for shared memory GMBs whereas this method is used for native GMBs.
-  // This has to be a blocking operation since it will be called during
-  // GpuMemoryBuffer::Map() operation which should provide updated data to
-  // clients before using it.
-  CopyNativeGmbToSharedMemorySync(gfx.mojom.GpuMemoryBufferHandle
-      buffer_handle, mojo_base.mojom.UnsafeSharedMemoryRegion shared_memory)
-                     => (bool success);
-
-  // This is non-blocking version of above method.
   [EnableIf=is_win]
   CopyNativeGmbToSharedMemoryAsync(gfx.mojom.GpuMemoryBufferHandle
       buffer_handle, mojo_base.mojom.UnsafeSharedMemoryRegion shared_memory)
diff --git a/gpu/ipc/common/mock_gpu_channel.h b/gpu/ipc/common/mock_gpu_channel.h
index aa9ed108..335e10b 100644
--- a/gpu/ipc/common/mock_gpu_channel.h
+++ b/gpu/ipc/common/mock_gpu_channel.h
@@ -74,10 +74,6 @@
                     const std::vector<SyncToken>&,
                     uint64_t,
                     CopyToGpuMemoryBufferAsyncCallback));
-  MOCK_METHOD3(CopyNativeGmbToSharedMemorySync,
-               void(gfx::GpuMemoryBufferHandle,
-                    base::UnsafeSharedMemoryRegion,
-                    CopyNativeGmbToSharedMemorySyncCallback));
   MOCK_METHOD3(CopyNativeGmbToSharedMemoryAsync,
                void(gfx::GpuMemoryBufferHandle,
                     base::UnsafeSharedMemoryRegion,
diff --git a/gpu/ipc/service/gpu_channel.cc b/gpu/ipc/service/gpu_channel.cc
index 9ec9036..7ce419b2 100644
--- a/gpu/ipc/service/gpu_channel.cc
+++ b/gpu/ipc/service/gpu_channel.cc
@@ -204,10 +204,6 @@
       const std::vector<gpu::SyncToken>& sync_token_dependencies,
       uint64_t release_count,
       CopyToGpuMemoryBufferAsyncCallback callback) override;
-  void CopyNativeGmbToSharedMemorySync(
-      gfx::GpuMemoryBufferHandle buffer_handle,
-      base::UnsafeSharedMemoryRegion shared_memory,
-      CopyNativeGmbToSharedMemorySyncCallback callback) override;
   void CopyNativeGmbToSharedMemoryAsync(
       gfx::GpuMemoryBufferHandle buffer_handle,
       base::UnsafeSharedMemoryRegion shared_memory,
@@ -661,15 +657,6 @@
                                            sync_token_dependencies, release));
 }
 
-void GpuChannelMessageFilter::CopyNativeGmbToSharedMemorySync(
-    gfx::GpuMemoryBufferHandle buffer_handle,
-    base::UnsafeSharedMemoryRegion shared_memory,
-    CopyNativeGmbToSharedMemorySyncCallback callback) {
-  std::move(callback).Run(
-      gpu_memory_buffer_factory_->FillSharedMemoryRegionWithBufferContents(
-          std::move(buffer_handle), std::move(shared_memory)));
-}
-
 void GpuChannelMessageFilter::CopyNativeGmbToSharedMemoryAsync(
     gfx::GpuMemoryBufferHandle buffer_handle,
     base::UnsafeSharedMemoryRegion shared_memory,
diff --git a/infra/inclusive_language_presubmit_exempt_dirs.txt b/infra/inclusive_language_presubmit_exempt_dirs.txt
index 6f877ff..ce3acfe 100644
--- a/infra/inclusive_language_presubmit_exempt_dirs.txt
+++ b/infra/inclusive_language_presubmit_exempt_dirs.txt
@@ -66,13 +66,29 @@
 chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox
 chrome/browser/ui/webui/settings
 chrome/browser/win
+chromecast
+chromecast/base
+chromecast/browser/devtools
+chromecast/common/mojom
+chromecast/media/cma/backend
+chromecast/media/cma/backend/alsa
+chromecast/public
 chrome/common
 chrome/common/chromeos/extensions
 chrome/common/extensions
 chrome/common/extensions/api
 chrome/docs
-chrome/install_static
 chrome/installer/gcapi_mac
+chrome/install_static
+chromeos/ash/components/carrier_lock
+chromeos/ash/components/network/onc
+chromeos/ash/components/telemetry_extension
+chromeos/ash/experiences/arc
+chromeos/ash/experiences/arc/mojom
+chromeos/ash/services/ime/public/cpp/shared_lib/proto
+chromeos/ash/services/ime/public/mojom
+chromeos/crosapi
+chromeos/process_proxy
 chrome/test/base/ash/interactive/bluetooth
 chrome/test/chromedriver
 chrome/test/chromedriver/chrome
@@ -94,22 +110,6 @@
 chrome/test/data/webui
 chrome/test/data/webui/chromeos
 chrome/updater/win
-chromecast
-chromecast/base
-chromecast/browser/devtools
-chromecast/common/mojom
-chromecast/media/cma/backend
-chromecast/media/cma/backend/alsa
-chromecast/public
-chromeos/ash/components/carrier_lock
-chromeos/ash/components/network/onc
-chromeos/ash/components/telemetry_extension
-chromeos/ash/experiences/arc
-chromeos/ash/experiences/arc/mojom
-chromeos/ash/services/ime/public/cpp/shared_lib/proto
-chromeos/ash/services/ime/public/mojom
-chromeos/crosapi
-chromeos/process_proxy
 codelabs/mojo_examples
 components
 components/android_autofill/browser/test_support/java/src/org/chromium/components/autofill
@@ -194,9 +194,9 @@
 docs/security/research/graphics
 docs/security/url_display_guidelines
 docs/speed
-docs/speed_metrics
 docs/speed/benchmark/harnesses
 docs/speed/binary_size
+docs/speed_metrics
 docs/speed/metrics_changelog
 docs/telemetry_extension
 docs/testing
@@ -287,7 +287,6 @@
 testing/scripts
 testing/scripts/variations_smoke_test_data/http_server
 third_party/abseil-cpp
-third_party/abseil-cpp/.github
 third_party/abseil-cpp/absl/base
 third_party/abseil-cpp/absl/base/internal
 third_party/abseil-cpp/absl/random
@@ -296,6 +295,7 @@
 third_party/abseil-cpp/ci
 third_party/abseil-cpp/CMake
 third_party/abseil-cpp/CMake/Googletest
+third_party/abseil-cpp/.github
 third_party/afl/src
 third_party/afl/src/docs
 third_party/afl/src/llvm_mode
@@ -424,13 +424,13 @@
 third_party/blink/web_tests/external/wpt/media-source/mp4
 third_party/blink/web_tests/external/wpt/payment-method-basic-card
 third_party/blink/web_tests/external/wpt/payment-request
-third_party/blink/web_tests/external/wpt/payment-request/payment-response
 third_party/blink/web_tests/external/wpt/payment-request/PaymentAddress
 third_party/blink/web_tests/external/wpt/payment-request/PaymentRequestUpdateEvent
+third_party/blink/web_tests/external/wpt/payment-request/payment-response
 third_party/blink/web_tests/external/wpt/payment-request/PaymentValidationErrors
-third_party/blink/web_tests/external/wpt/resource-timing
 third_party/blink/web_tests/external/wpt/resources
 third_party/blink/web_tests/external/wpt/resources/chromium
+third_party/blink/web_tests/external/wpt/resource-timing
 third_party/blink/web_tests/external/wpt/sanitizer-api/support
 third_party/blink/web_tests/external/wpt/wai-aria/scripts
 third_party/blink/web_tests/external/wpt/webaudio/js
@@ -480,8 +480,6 @@
 third_party/expat
 third_party/fdlibm
 third_party/fusejs/dist
-third_party/google_input_tools/src/chrome/os/sounds
-third_party/google_input_tools/third_party/closure_library/closure/goog/html
 third_party/google-closure-library
 third_party/google-closure-library/closure-deps
 third_party/google-closure-library/closure-deps/lib
@@ -497,16 +495,18 @@
 third_party/google-closure-library/doc/js
 third_party/google-closure-library/scripts
 third_party/google-closure-library/scripts/ci
+third_party/google_input_tools/src/chrome/os/sounds
+third_party/google_input_tools/third_party/closure_library/closure/goog/html
 third_party/google-java-format
 third_party/google-java-format/local_modifications/java/com/google/googlejavaformat/java
 third_party/google-java-format/originals/java/com/google/googlejavaformat/java
 third_party/googletest
 third_party/grpc/source/include/grpc
 third_party/grpc/source/include/grpc/impl
-third_party/grpc/source/include/grpc/support
 third_party/grpc/source/include/grpcpp
 third_party/grpc/source/include/grpcpp/ext
 third_party/grpc/source/include/grpcpp/support
+third_party/grpc/source/include/grpc/support
 third_party/grpc/source/src/compiler
 third_party/grpc/source/src/core/channelz
 third_party/grpc/source/src/core/ext/filters/http/client
@@ -551,8 +551,8 @@
 third_party/lzma_sdk/google
 third_party/markdown
 third_party/markdown/extensions
-third_party/material_web_components/components-chromium/node_modules/@types/trusted-types
 third_party/material_web_components/components-chromium/node_modules/lit-html
+third_party/material_web_components/components-chromium/node_modules/@types/trusted-types
 third_party/mediapipe/src
 third_party/mediapipe/src/.github/ISSUE_TEMPLATE
 third_party/mediapipe/src/mediapipe/calculators/core
@@ -615,13 +615,13 @@
 third_party/polymer/v3_0/components-chromium/paper-tooltip
 third_party/polymer/v3_0/components-chromium/polymer
 third_party/polymer/v3_0/components-chromium/polymer/lib/elements
-third_party/private_membership
 third_party/private-join-and-compute
+third_party/private_membership
 third_party/protobuf
-third_party/protobuf/.github/workflows
 third_party/protobuf/bazel/toolchains
 third_party/protobuf/build_defs
 third_party/protobuf/docs
+third_party/protobuf/.github/workflows
 third_party/protobuf/objectivec/Tests
 third_party/protobuf/php/ext/google/protobuf
 third_party/protobuf/python
@@ -654,9 +654,9 @@
 third_party/rust/chromium_crates_io/vendor/combine-v4/src
 third_party/rust/chromium_crates_io/vendor/crc32fast-v1/.github/workflows
 third_party/rust/chromium_crates_io/vendor/cxx-v1
-third_party/rust/chromium_crates_io/vendor/cxx-v1/.github/workflows
 third_party/rust/chromium_crates_io/vendor/cxx-v1/book
 third_party/rust/chromium_crates_io/vendor/cxx-v1/book/src
+third_party/rust/chromium_crates_io/vendor/cxx-v1/.github/workflows
 third_party/rust/chromium_crates_io/vendor/cxx-v1/src
 third_party/rust/chromium_crates_io/vendor/displaydoc-v0_2
 third_party/rust/chromium_crates_io/vendor/displaydoc-v0_2/src
@@ -740,7 +740,6 @@
 third_party/six/src
 third_party/six/src/documentation
 third_party/sqlite
-third_party/sqlite/fuzz/db_corpus
 third_party/tensorflow-text/src/tensorflow_text/core/kernels
 third_party/test_fonts
 third_party/tflite_support/src
@@ -781,10 +780,10 @@
 third_party/wpt_tools
 third_party/wpt_tools/wpt/resources
 third_party/wpt_tools/wpt/tools/lint
-third_party/wpt_tools/wpt/tools/third_party_modified/mozlog/mozlog/pytest_mozlog
-third_party/wpt_tools/wpt/tools/third_party_modified/mozlog/mozlog/unstructured
 third_party/wpt_tools/wpt/tools/third_party/atomicwrites
 third_party/wpt_tools/wpt/tools/third_party/html5lib
+third_party/wpt_tools/wpt/tools/third_party_modified/mozlog/mozlog/pytest_mozlog
+third_party/wpt_tools/wpt/tools/third_party_modified/mozlog/mozlog/unstructured
 third_party/wpt_tools/wpt/tools/third_party/websockets/src/websockets/legacy
 third_party/wpt_tools/wpt/tools/webdriver/webdriver
 third_party/wpt_tools/wpt/tools/wpt
@@ -848,3 +847,4 @@
 ui/ozone/platform/drm/gpu
 ui/ozone/platform/drm/host
 ui/views/controls/tabbed_pane
+url/third_party/mozilla
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/fallback_coordinator_egtest.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/fallback_coordinator_egtest.mm
index 1664e71..0f1c10d 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/fallback_coordinator_egtest.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/fallback_coordinator_egtest.mm
@@ -291,9 +291,10 @@
 // destroyed. Waiting for dealloc was causing a race condition with the
 // autorelease pool, and some times a DCHECK will be hit.
 - (void)testOpeningIncognitoTabsDoNotLeak {
+  [AutofillAppInterface saveExampleProfile];
+
   const GURL URL = self.testServer->GetURL(kFormHTMLFile);
   std::string webViewText("Profile form");
-  [AutofillAppInterface saveExampleProfile];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElementWithId(kFormElementCity)];
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_background_color_picker_mediator.mm b/ios/chrome/browser/home_customization/coordinator/home_customization_background_color_picker_mediator.mm
index 66302fe..095108e 100644
--- a/ios/chrome/browser/home_customization/coordinator/home_customization_background_color_picker_mediator.mm
+++ b/ios/chrome/browser/home_customization/coordinator/home_customization_background_color_picker_mediator.mm
@@ -7,62 +7,7 @@
 #import <Foundation/Foundation.h>
 
 #import "ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_consumer.h"
-#import "skia/ext/skia_utils_ios.h"
-#import "third_party/material_color_utilities/src/cpp/cam/hct.h"
-#import "third_party/material_color_utilities/src/cpp/palettes/core.h"
-#import "third_party/material_color_utilities/src/cpp/utils/utils.h"
-#import "ui/color/dynamic_color/palette_factory.h"
-
-// Define constants within the namespace
-namespace {
-
-// A block type that provides a dynamic color based on the current trait
-// collection.
-typedef UIColor* (^DynamicColorProviderBlock)(UITraitCollection* traits);
-
-// Represents a pair of tone values for a given color tone,
-// with separate values for light and dark UI modes.
-struct ToneSet {
-  // The tone value to use in light mode.
-  int light_mode;
-
-  // The tone value to use in dark mode.
-  int dark_mode;
-};
-
-// The tone value used for generating a light variant of the seed color.
-const ToneSet kLightTone = {
-    /*light_mode=*/90,
-    /*dark_mode=*/30,
-};
-
-// The tone value used for generating a medium variant of the seed color.
-const ToneSet kMediumTone = {
-    /*light_mode=*/80,
-    /*dark_mode=*/50,
-};
-
-// The tone value used for generating a dark variant of the seed color.
-const ToneSet kDarkTone = {
-    /*light_mode=*/40,
-    /*dark_mode=*/80,
-};
-
-// Returns a dynamic `UIColor` that adapts to the system's light or dark
-// appearance using tones derived from the given `TonalPalette`.
-DynamicColorProviderBlock GetDynamicProviderForPrimary(
-    const ui::TonalPalette& primary,
-    const ToneSet& toneSet) {
-  uint32_t lightARGB = primary.get(toneSet.light_mode);
-  uint32_t darkARGB = primary.get(toneSet.dark_mode);
-
-  return ^UIColor*(UITraitCollection* traits) {
-    BOOL isDark = (traits.userInterfaceStyle == UIUserInterfaceStyleDark);
-    return skia::UIColorFromSkColor(isDark ? darkARGB : lightARGB);
-  };
-}
-
-}  // namespace
+#import "ios/chrome/browser/home_customization/ui/home_customization_color_palette_util.h"
 
 @implementation HomeCustomizationBackgroundColorPickerMediator
 
@@ -76,44 +21,10 @@
     [UIColor orangeColor], [UIColor purpleColor]
   ] enumerateObjectsUsingBlock:^(UIColor* seedColor, NSUInteger index,
                                  BOOL* stop) {
-    [configs addObject:[self configurationForSeedColor:seedColor]];
+    [configs addObject:CreateColorPaletteConfigurationFromSeedColor(seedColor)];
   }];
 
   [_consumer setColorPaletteConfigurations:configs];
 }
 
-#pragma mark - Private
-
-// Creates and returns a color palette configuration from a seed color.
-- (HomeCustomizationColorPaletteConfiguration*)configurationForSeedColor:
-    (UIColor*)seedColor {
-  HomeCustomizationColorPaletteConfiguration* config =
-      [[HomeCustomizationColorPaletteConfiguration alloc] init];
-
-  CGFloat red = 0.0;
-  CGFloat green = 0.0;
-  CGFloat blue = 0.0;
-  CGFloat alpha = 0.0;
-  [seedColor getRed:&red green:&green blue:&blue alpha:&alpha];
-
-  SkColor skColor =
-      SkColorSetARGB(alpha * 255.0, red * 255.0, green * 255.0, blue * 255.0);
-
-  std::unique_ptr<ui::Palette> palette = ui::GeneratePalette(
-      skColor, ui::ColorProviderKey::SchemeVariant::kTonalSpot);
-  ui::TonalPalette primary = palette->primary();
-
-  config.seedColor = seedColor;
-  config.lightColor =
-      [UIColor colorWithDynamicProvider:GetDynamicProviderForPrimary(
-                                            primary, kLightTone)];
-  config.mediumColor =
-      [UIColor colorWithDynamicProvider:GetDynamicProviderForPrimary(
-                                            primary, kMediumTone)];
-  config.darkColor =
-      [UIColor colorWithDynamicProvider:GetDynamicProviderForPrimary(
-                                            primary, kDarkTone)];
-  return config;
-}
-
 @end
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator.mm b/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator.mm
index 399b850..b819c492 100644
--- a/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator.mm
+++ b/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator.mm
@@ -11,6 +11,8 @@
 #import "ios/chrome/browser/home_customization/coordinator/home_customization_mediator.h"
 #import "ios/chrome/browser/home_customization/ui/home_customization_background_color_picker_view_controller.h"
 #import "ios/chrome/browser/home_customization/ui/home_customization_background_picker_presentation_delegate.h"
+#import "ios/chrome/browser/home_customization/ui/home_customization_color_palette_provider.h"
+#import "ios/chrome/browser/home_customization/ui/home_customization_color_palette_util.h"
 #import "ios/chrome/browser/home_customization/ui/home_customization_discover_view_controller.h"
 #import "ios/chrome/browser/home_customization/ui/home_customization_logo_vendor_provider.h"
 #import "ios/chrome/browser/home_customization/ui/home_customization_magic_stack_view_controller.h"
@@ -39,7 +41,8 @@
 @interface HomeCustomizationCoordinator () <
     UISheetPresentationControllerDelegate,
     HomeCustomizationBackgroundPickerPresentationDelegate,
-    HomeCustomizationLogoVendorProvider> {
+    HomeCustomizationLogoVendorProvider,
+    HomeCustomizationColorPaletteProvider> {
   // Displays the background picker action sheet.
   HomeCustomizationBackgroundPickerActionSheetCoordinator*
       _backgroundPickerActionSheetCoordinator;
@@ -186,6 +189,7 @@
       self.mainViewController.backgroundPickerPresentationDelegate = self;
       self.mainViewController.mutator = _mediator;
       self.mainViewController.logoVendorProvider = self;
+      self.mainViewController.colorPaletteProvider = self;
       self.mediator.mainPageConsumer = self.mainViewController;
       [self.mediator configureMainPageData];
       menuPage = self.mainViewController;
@@ -268,4 +272,11 @@
       self.browser, self.browser->GetWebStateList()->GetActiveWebState());
 }
 
+#pragma mark - HomeCustomizationColorPaletteProvider
+
+- (HomeCustomizationColorPaletteConfiguration*)provideColorPaletteFromSeedColor:
+    (UIColor*)seedColor {
+  return CreateColorPaletteConfigurationFromSeedColor(seedColor);
+}
+
 @end
diff --git a/ios/chrome/browser/home_customization/ui/BUILD.gn b/ios/chrome/browser/home_customization/ui/BUILD.gn
index 408d1536..d2a5b2b 100644
--- a/ios/chrome/browser/home_customization/ui/BUILD.gn
+++ b/ios/chrome/browser/home_customization/ui/BUILD.gn
@@ -27,6 +27,9 @@
     "home_customization_collection_configurator.mm",
     "home_customization_color_palette_configuration.h",
     "home_customization_color_palette_configuration.mm",
+    "home_customization_color_palette_provider.h",
+    "home_customization_color_palette_util.h",
+    "home_customization_color_palette_util.mm",
     "home_customization_discover_consumer.h",
     "home_customization_discover_view_controller.h",
     "home_customization_discover_view_controller.mm",
@@ -62,7 +65,10 @@
     "//ios/chrome/common/ui/util",
     "//ios/chrome/common/ui/util:dynamic_type_util",
     "//ios/public/provider/chrome/browser/ui_utils:ui_utils_api",
+    "//skia",
+    "//third_party/material_color_utilities",
     "//ui/base",
+    "//ui/color/dynamic_color",
     "//url",
   ]
   frameworks = [ "UIKit.framework" ]
diff --git a/ios/chrome/browser/home_customization/ui/DEPS b/ios/chrome/browser/home_customization/ui/DEPS
index 9945b12..0ad36fc 100644
--- a/ios/chrome/browser/home_customization/ui/DEPS
+++ b/ios/chrome/browser/home_customization/ui/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+ios/chrome/browser/ntp/ui_bundled",
+  "+third_party/material_color_utilities/src/cpp",
 ]
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_background_cell.h b/ios/chrome/browser/home_customization/ui/home_customization_background_cell.h
index 95c8522..4d38a7d8 100644
--- a/ios/chrome/browser/home_customization/ui/home_customization_background_cell.h
+++ b/ios/chrome/browser/home_customization/ui/home_customization_background_cell.h
@@ -9,6 +9,7 @@
 
 @protocol LogoVendor;
 @protocol HomeCustomizationMutator;
+@class HomeCustomizationColorPaletteConfiguration;
 
 // Represents a mini preview of how the NTP will look with a particular
 // background selected. This cell is part of the background customization
@@ -26,7 +27,10 @@
 // Configures the cell using the given background customization configuration.
 - (void)configureWithBackgroundOption:
             (BackgroundCustomizationConfiguration*)backgroundConfiguration
-                           logoVendor:(id<LogoVendor>)logoVendor;
+                           logoVendor:(id<LogoVendor>)logoVendor
+                         colorPalette:
+                             (HomeCustomizationColorPaletteConfiguration*)
+                                 colorPalette;
 
 // Updates the background image displayed behind the cell’s content.
 - (void)updateBackgroundImage:(UIImage*)image;
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_background_cell.mm b/ios/chrome/browser/home_customization/ui/home_customization_background_cell.mm
index bf4fd7e..7901a86 100644
--- a/ios/chrome/browser/home_customization/ui/home_customization_background_cell.mm
+++ b/ios/chrome/browser/home_customization/ui/home_customization_background_cell.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/home_customization/ui/home_customization_background_cell.h"
 
 #import "ios/chrome/browser/home_customization/model/background_customization_configuration.h"
+#import "ios/chrome/browser/home_customization/ui/home_customization_color_palette_configuration.h"
 #import "ios/chrome/browser/home_customization/ui/home_customization_mutator.h"
 #import "ios/chrome/browser/ntp/ui_bundled/logo_vendor.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
@@ -194,16 +195,26 @@
   ]];
 }
 
-- (void)configureWithBackgroundOption:
-            (BackgroundCustomizationConfiguration*)option
-                           logoVendor:(id<LogoVendor>)logoVendor {
+- (void)
+    configureWithBackgroundOption:(BackgroundCustomizationConfiguration*)option
+                       logoVendor:(id<LogoVendor>)logoVendor
+                     colorPalette:(HomeCustomizationColorPaletteConfiguration*)
+                                      colorPalette {
   if (_isConfigured) {
     return;
   }
   _backgroundConfiguration = option;
+  BOOL imageBackground = !option.thumbnailURL.is_empty();
+
+  logoVendor.usesMonochromeLogo = colorPalette || imageBackground;
   UIView* logoView = logoVendor.view;
   logoView.translatesAutoresizingMaskIntoConstraints = NO;
 
+  // Change the tint of the logo based on the background.
+  logoView.tintColor = imageBackground ? [UIColor whiteColor]
+                       : colorPalette  ? colorPalette.darkColor
+                                       : logoView.tintColor;
+
   // Insert the logo view right after the spacer.
   [self.innerContentView insertArrangedSubview:logoView atIndex:1];
 
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_view_controller.mm b/ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_view_controller.mm
index 35870f3..03ca7de 100644
--- a/ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_view_controller.mm
+++ b/ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_view_controller.mm
@@ -300,7 +300,8 @@
   id<LogoVendor> logoVendor = [self.logoVendorProvider provideLogoVendor];
 
   [cell configureWithBackgroundOption:backgroundConfiguration
-                           logoVendor:logoVendor];
+                           logoVendor:logoVendor
+                         colorPalette:nil];
 
   if ([itemIdentifier isEqualToString:_selectedBackgroundId]) {
     [_collectionView selectItemAtIndexPath:indexPath
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_color_palette_provider.h b/ios/chrome/browser/home_customization/ui/home_customization_color_palette_provider.h
new file mode 100644
index 0000000..1b869623
--- /dev/null
+++ b/ios/chrome/browser/home_customization/ui/home_customization_color_palette_provider.h
@@ -0,0 +1,21 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_COLOR_PALETTE_PROVIDER_H_
+#define IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_COLOR_PALETTE_PROVIDER_H_
+
+#import <UIKit/UIKit.h>
+
+@class HomeCustomizationColorPaletteConfiguration;
+
+// A protocol for providing a color palette used in Home customization.
+@protocol HomeCustomizationColorPaletteProvider
+
+// Provides the color palette object from a seed color.
+- (HomeCustomizationColorPaletteConfiguration*)provideColorPaletteFromSeedColor:
+    (UIColor*)seedColor;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_COLOR_PALETTE_PROVIDER_H_
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_color_palette_util.h b/ios/chrome/browser/home_customization/ui/home_customization_color_palette_util.h
new file mode 100644
index 0000000..ead5d01
--- /dev/null
+++ b/ios/chrome/browser/home_customization/ui/home_customization_color_palette_util.h
@@ -0,0 +1,16 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_COLOR_PALETTE_UTIL_H_
+#define IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_COLOR_PALETTE_UTIL_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/chrome/browser/home_customization/ui/home_customization_color_palette_configuration.h"
+
+// Creates and returns a color palette configuration from a seed color.
+HomeCustomizationColorPaletteConfiguration*
+CreateColorPaletteConfigurationFromSeedColor(UIColor* seedColor);
+
+#endif  // IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_COLOR_PALETTE_UTIL_H_
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_color_palette_util.mm b/ios/chrome/browser/home_customization/ui/home_customization_color_palette_util.mm
new file mode 100644
index 0000000..11aaa8e
--- /dev/null
+++ b/ios/chrome/browser/home_customization/ui/home_customization_color_palette_util.mm
@@ -0,0 +1,101 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/home_customization/ui/home_customization_color_palette_util.h"
+
+#import <Foundation/Foundation.h>
+
+#import "ios/chrome/browser/home_customization/ui/home_customization_color_palette_configuration.h"
+#import "skia/ext/skia_utils_ios.h"
+#import "third_party/material_color_utilities/src/cpp/cam/hct.h"
+#import "third_party/material_color_utilities/src/cpp/palettes/core.h"
+#import "third_party/material_color_utilities/src/cpp/utils/utils.h"
+#import "third_party/skia/include/core/SkColor.h"
+#import "ui/color/dynamic_color/palette_factory.h"
+
+// Define constants within the namespace
+namespace {
+
+// A block type that provides a dynamic color based on the current trait
+// collection.
+typedef UIColor* (^DynamicColorProviderBlock)(UITraitCollection* traits);
+
+// Represents a pair of tone values for a given color tone,
+// with separate values for light and dark UI modes.
+struct ToneSet {
+  // The tone value to use in light mode.
+  int light_mode;
+
+  // The tone value to use in dark mode.
+  int dark_mode;
+};
+
+// The tone value used for generating a light variant of the seed color.
+const ToneSet kLightTone = {
+    /*light_mode=*/90,
+    /*dark_mode=*/30,
+};
+
+// The tone value used for generating a medium variant of the seed color.
+const ToneSet kMediumTone = {
+    /*light_mode=*/80,
+    /*dark_mode=*/50,
+};
+
+// The tone value used for generating a dark variant of the seed color.
+const ToneSet kDarkTone = {
+    /*light_mode=*/40,
+    /*dark_mode=*/80,
+};
+
+// Returns a dynamic `UIColor` that adapts to the system's light or dark
+// appearance using tones derived from the given `TonalPalette`.
+DynamicColorProviderBlock GetDynamicProviderForPrimary(
+    const ui::TonalPalette& primary,
+    const ToneSet& toneSet) {
+  uint32_t lightARGB = primary.get(toneSet.light_mode);
+  uint32_t darkARGB = primary.get(toneSet.dark_mode);
+
+  return ^UIColor*(UITraitCollection* traits) {
+    BOOL isDark = (traits.userInterfaceStyle == UIUserInterfaceStyleDark);
+    return skia::UIColorFromSkColor(isDark ? darkARGB : lightARGB);
+  };
+}
+
+}  // namespace
+
+// Creates and returns a color palette configuration from a seed color.
+HomeCustomizationColorPaletteConfiguration*
+CreateColorPaletteConfigurationFromSeedColor(UIColor* seedColor) {
+  if (!seedColor) {
+    return nil;
+  }
+
+  HomeCustomizationColorPaletteConfiguration* config =
+      [[HomeCustomizationColorPaletteConfiguration alloc] init];
+
+  CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
+  [seedColor getRed:&red green:&green blue:&blue alpha:&alpha];
+
+  SkColor skColor = SkColorSetARGB(
+      static_cast<U8CPU>(alpha * 255.0), static_cast<U8CPU>(red * 255.0),
+      static_cast<U8CPU>(green * 255.0), static_cast<U8CPU>(blue * 255.0));
+
+  std::unique_ptr<ui::Palette> palette = ui::GeneratePalette(
+      skColor, ui::ColorProviderKey::SchemeVariant::kTonalSpot);
+  const ui::TonalPalette& primary = palette->primary();
+
+  config.seedColor = seedColor;
+  config.lightColor =
+      [UIColor colorWithDynamicProvider:GetDynamicProviderForPrimary(
+                                            primary, kLightTone)];
+  config.mediumColor =
+      [UIColor colorWithDynamicProvider:GetDynamicProviderForPrimary(
+                                            primary, kMediumTone)];
+  config.darkColor =
+      [UIColor colorWithDynamicProvider:GetDynamicProviderForPrimary(
+                                            primary, kDarkTone)];
+
+  return config;
+}
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.h b/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.h
index 66e251e0..ce67266 100644
--- a/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.h
+++ b/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.h
@@ -13,6 +13,7 @@
 @protocol HomeCustomizationMutator;
 @protocol HomeCustomizationBackgroundPickerPresentationDelegate;
 @protocol HomeCustomizationLogoVendorProvider;
+@protocol HomeCustomizationColorPaletteProvider;
 
 // The view controller representing the first page of the Home customization
 // menu.
@@ -34,6 +35,10 @@
 @property(nonatomic, weak) id<HomeCustomizationLogoVendorProvider>
     logoVendorProvider;
 
+// A provider responsible for supplying a color palette object.
+@property(nonatomic, weak) id<HomeCustomizationColorPaletteProvider>
+    colorPaletteProvider;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_MAIN_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.mm b/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.mm
index f517bec..d8cfc7e 100644
--- a/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.mm
+++ b/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.mm
@@ -9,6 +9,7 @@
 #import "ios/chrome/browser/home_customization/ui/home_customization_background_cell.h"
 #import "ios/chrome/browser/home_customization/ui/home_customization_background_picker_cell.h"
 #import "ios/chrome/browser/home_customization/ui/home_customization_collection_configurator.h"
+#import "ios/chrome/browser/home_customization/ui/home_customization_color_palette_provider.h"
 #import "ios/chrome/browser/home_customization/ui/home_customization_logo_vendor_provider.h"
 #import "ios/chrome/browser/home_customization/ui/home_customization_mutator.h"
 #import "ios/chrome/browser/home_customization/ui/home_customization_toggle_cell.h"
@@ -376,9 +377,14 @@
   BackgroundCustomizationConfiguration* backgroundConfiguration =
       _backgroundCustomizationConfigurationMap[itemIdentifier];
   id<LogoVendor> logoVendor = [self.logoVendorProvider provideLogoVendor];
+  HomeCustomizationColorPaletteConfiguration* colorPalette =
+      [self.colorPaletteProvider
+          provideColorPaletteFromSeedColor:backgroundConfiguration
+                                               .backgroundColor];
 
   [cell configureWithBackgroundOption:backgroundConfiguration
-                           logoVendor:logoVendor];
+                           logoVendor:logoVendor
+                         colorPalette:colorPalette];
 
   if ([itemIdentifier isEqualToString:_selectedBackgroundId]) {
     [self.collectionView
diff --git a/ios/chrome/browser/intelligence/bwg/coordinator/bwg_coordinator.mm b/ios/chrome/browser/intelligence/bwg/coordinator/bwg_coordinator.mm
index 7ba271d..5f5bba59 100644
--- a/ios/chrome/browser/intelligence/bwg/coordinator/bwg_coordinator.mm
+++ b/ios/chrome/browser/intelligence/bwg/coordinator/bwg_coordinator.mm
@@ -79,6 +79,7 @@
   _handler = nil;
   _mediator = nil;
   _prefService = nil;
+  _tracker = nil;
   [super stop];
 }
 
@@ -95,7 +96,7 @@
   // If promo was shown outside the promos manager, ensure the promo doesn't
   // show through the promos manager.
   if (_entryPoint != bwg::EntryPointPromo) {
-    _prefService->SetBoolean(prefs::kIOSAIHubShown, YES);
+    _prefService->SetBoolean(prefs::kIOSBWGManualPromo, true);
     _tracker->UnregisterPriorityNotificationHandler(
         feature_engagement::kIPHIOSBWGPromoFeature);
   }
@@ -154,12 +155,12 @@
 
 // If YES, BWG Promo should be shown.
 - (BOOL)shouldShowBWGPromo {
-  BOOL AIHubShown = _prefService->GetBoolean(prefs::kIOSAIHubShown);
-  BOOL promoShown = _tracker->HasEverTriggered(
+  BOOL promoShownManually = _prefService->GetBoolean(prefs::kIOSBWGManualPromo);
+  BOOL promoTriggered = _tracker->HasEverTriggered(
       feature_engagement::kIPHIOSBWGPromoFeature, true);
-  BOOL isPromo = _entryPoint == bwg::EntryPointPromo;
+  BOOL isPromoEntry = _entryPoint == bwg::EntryPointPromo;
 
-  return isPromo || (!promoShown && !AIHubShown);
+  return isPromoEntry || (!promoTriggered && !promoShownManually);
 }
 
 @end
diff --git a/ios/chrome/browser/intelligence/bwg/ui/bwg_navigation_controller.mm b/ios/chrome/browser/intelligence/bwg/ui/bwg_navigation_controller.mm
index 12668566..c3655f6 100644
--- a/ios/chrome/browser/intelligence/bwg/ui/bwg_navigation_controller.mm
+++ b/ios/chrome/browser/intelligence/bwg/ui/bwg_navigation_controller.mm
@@ -96,6 +96,7 @@
                                     height:kFullDetentHeight]
   ];
 
+  self.modalInPresentation = YES;
   self.modalPresentationStyle = UIModalPresentationPageSheet;
 
   self.sheetPresentationController.preferredCornerRadius =
diff --git a/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm b/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm
index 29437840..755f1e5 100644
--- a/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm
+++ b/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm
@@ -150,8 +150,7 @@
                   profile_path.Append(
                       optimization_guide::kOptimizationGuideHintStore),
                   base::ThreadPool::CreateSequencedTaskRunner(
-                      {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
-                  pref_service)
+                      {base::MayBlock(), base::TaskPriority::BEST_EFFORT}))
             : nullptr;
     hint_store = hint_store_ ? hint_store_->AsWeakPtr() : nullptr;
   }
diff --git a/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/password_suggestion_bottom_sheet_egtest.mm b/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/password_suggestion_bottom_sheet_egtest.mm
index 83a14111..0098e0b 100644
--- a/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/password_suggestion_bottom_sheet_egtest.mm
+++ b/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/password_suggestion_bottom_sheet_egtest.mm
@@ -77,6 +77,20 @@
       IDS_IOS_PASSWORD_BOTTOM_SHEET_USE_KEYBOARD);
 }
 
+// Returns the matcher for the "Show details" context menu item.
+id<GREYMatcher> ShowDetailsContextMenuItem() {
+  return grey_allOf(chrome_test_util::ContextMenuItemWithAccessibilityLabelId(
+                        IDS_IOS_PASSWORD_BOTTOM_SHEET_SHOW_DETAILS),
+                    grey_interactable(), nullptr);
+}
+
+// Returns the matcher for the "Password Manager" context menu item.
+id<GREYMatcher> PasswordManagerContextMenuItem() {
+  return grey_allOf(chrome_test_util::ContextMenuItemWithAccessibilityLabelId(
+                        IDS_IOS_PASSWORD_BOTTOM_SHEET_PASSWORD_MANAGER),
+                    grey_interactable(), nullptr);
+}
+
 // Get the top presented view controller, in this case the bottom sheet view
 // controller.
 UIViewController* TopPresentedViewController() {
@@ -133,6 +147,26 @@
   }
 }
 
+// Checks that the number of stored credentials is as expected.
+void CheckNumberOfStoredCredentials(int expected_count) {
+  int credentials_count = [PasswordManagerAppInterface storedCredentialsCount];
+  GREYAssertEqual(expected_count, credentials_count,
+                  @"Wrong number of stored credentials.");
+}
+
+// Waits for the element associated with `matcher` to appear. Then taps on it.
+void TapElementOnceVisible(id<GREYMatcher> matcher) {
+  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:matcher];
+  [[EarlGrey selectElementWithMatcher:matcher] performAction:grey_tap()];
+}
+
+// Waits for the element associated with `matcher` to appear. Then long presses
+// it.
+void LongPressElementOnceVisible(id<GREYMatcher> matcher) {
+  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:matcher];
+  [[EarlGrey selectElementWithMatcher:matcher] performAction:grey_longPress()];
+}
+
 }  // namespace
 
 @interface PasswordSuggestionBottomSheetEGTest : ChromeTestCase
@@ -154,6 +188,15 @@
   chrome_test_util::GREYAssertErrorNil(
       [MetricsAppInterface setupHistogramTester]);
   [MetricsAppInterface overrideMetricsAndCrashReportingForTesting];
+
+  // Sign in.
+  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
+
+  // Set up reauth module.
+  [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
+  [PasswordSuggestionBottomSheetAppInterface
+      mockReauthenticationModuleExpectedResult:ReauthenticationResult::
+                                                   kSuccess];
 }
 
 - (void)tearDownHelper {
@@ -206,28 +249,61 @@
 
 #pragma mark - Helper methods
 
+// Returns the GURL for the simple login page.
+- (GURL)loginPageURL {
+  return self.testServer->GetURL("/simple_login_form_empty.html");
+}
+
+// Returns the GURL for the simple login autofocus page.
+- (GURL)loginAutofocusPageURL {
+  return self.testServer->GetURL("/simple_login_form_empty_autofocus.html");
+}
+
+// Returns the GURL for the simple login passkey page.
+- (GURL)loginPasskeyPageURL {
+  return self.testServer->GetURL("/simple_login_form_empty_passkey.html");
+}
+
 // Loads simple page on localhost.
 - (void)loadLoginPage {
   // Loads simple page. It is on localhost so it is considered a secure context.
-  [ChromeEarlGrey
-      loadURL:self.testServer->GetURL("/simple_login_form_empty.html")];
+  [ChromeEarlGrey loadURL:[self loginPageURL]];
   [ChromeEarlGrey waitForWebStateContainingText:"Login form."];
 }
 
 - (void)loadLoginAutofocusPage {
   // Loads simple page. It is on localhost so it is considered a secure context.
-  [ChromeEarlGrey loadURL:self.testServer->GetURL(
-                              "/simple_login_form_empty_autofocus.html")];
+  [ChromeEarlGrey loadURL:[self loginAutofocusPageURL]];
   [ChromeEarlGrey waitForWebStateContainingText:"Login form."];
 }
 
 - (void)loadLoginPasskeyPage {
   // Loads simple page. It is on localhost so it is considered a secure context.
-  [ChromeEarlGrey
-      loadURL:self.testServer->GetURL("/simple_login_form_empty_passkey.html")];
+  [ChromeEarlGrey loadURL:[self loginPasskeyPageURL]];
   [ChromeEarlGrey waitForWebStateContainingText:"Login form."];
 }
 
+// Saves a generic password (i.e., without special arguments) to the store and
+// loads the simple login page.
+- (void)saveGenericPasswordAndLoadLoginPage {
+  [PasswordManagerAppInterface
+      storeCredentialWithUsername:@"user"
+                         password:@"password"
+                              URL:net::NSURLWithGURL([self loginPageURL])];
+  [self loadLoginPage];
+}
+
+// Saves a generic password (i.e., without special arguments) to the store and
+// loads the simple login autofocus page.
+- (void)saveGenericPasswordAndLoadLoginAutofocusPage {
+  [PasswordManagerAppInterface
+      storeCredentialWithUsername:@"user"
+                         password:@"password"
+                              URL:net::NSURLWithGURL(
+                                      [self loginAutofocusPageURL])];
+  [self loadLoginAutofocusPage];
+}
+
 - (void)verifyPasswordFieldsHaveBeenFilled:(NSString*)username {
   // Verify that the username has been filled.
   NSString* condition = [NSString
@@ -245,17 +321,11 @@
 #pragma mark - Tests
 
 - (void)testOpenPasswordBottomSheetUsePassword {
-  [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
-  [PasswordSuggestionBottomSheetAppInterface
-      mockReauthenticationModuleExpectedResult:ReauthenticationResult::
-                                                   kSuccess];
-
-  GURL URL = self.testServer->GetURL("/simple_login_form_empty.html");
+  GURL URL = [self loginPageURL];
   [PasswordManagerAppInterface
       storeCredentialWithUsername:@"user"
                          password:@"password"
                               URL:net::NSURLWithGURL(URL)];
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
   [self loadLoginPage];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
@@ -288,18 +358,7 @@
 // fill data flow feature is enabled. This tests the combination of the 2
 // features.
 - (void)testOpenPasswordBottomSheetUsePassword_V2_StatelessFillDataFlow {
-  [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
-  [PasswordSuggestionBottomSheetAppInterface
-      mockReauthenticationModuleExpectedResult:ReauthenticationResult::
-                                                   kSuccess];
-
-  GURL URL = self.testServer->GetURL("/simple_login_form_empty.html");
-  [PasswordManagerAppInterface
-      storeCredentialWithUsername:@"user"
-                         password:@"password"
-                              URL:net::NSURLWithGURL(URL)];
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-  [self loadLoginPage];
+  [self saveGenericPasswordAndLoadLoginPage];
 
   // Wait a bit to let things settle. Waiting on content to be loaded on the
   // page isn't 100% reliable as trying to interact with that content at that
@@ -313,7 +372,8 @@
       waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user")];
 
   // Verify that the subtitle string appears.
-  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:SubtitleString(URL)];
+  [ChromeEarlGrey
+      waitForUIElementToAppearWithMatcher:SubtitleString([self loginPageURL])];
 
   [[EarlGrey selectElementWithMatcher:UsePasswordButton()]
       performAction:grey_tap()];
@@ -335,14 +395,7 @@
 // This test verifies that the bottom sheet opens on autofocus events, when the
 // kIOSPasswordBottomSheetAutofocus feature is enabled.
 - (void)testOpenPasswordBottomOnAutofocus {
-  [PasswordManagerAppInterface
-      storeCredentialWithUsername:@"user"
-                         password:@"password"
-                              URL:net::NSURLWithGURL(self.testServer->GetURL(
-                                      "/simple_login_form_empty_autofocus."
-                                      "html"))];
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-  [self loadLoginAutofocusPage];
+  [self saveGenericPasswordAndLoadLoginAutofocusPage];
 
   [ChromeEarlGrey waitForUIElementToAppearWithMatcher:UsePasswordButton()];
 }
@@ -355,15 +408,7 @@
     EARL_GREY_TEST_DISABLED(@"Test is flaky on iPad.")
   }
 
-  [PasswordManagerAppInterface
-      storeCredentialWithUsername:@"user"
-                         password:@"password"
-                              URL:net::NSURLWithGURL(self.testServer->GetURL(
-                                      "/simple_login_form_empty_autofocus."
-                                      "html"))];
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-
-  [self loadLoginAutofocusPage];
+  [self saveGenericPasswordAndLoadLoginAutofocusPage];
 
   [ChromeEarlGrey waitForKeyboardToAppear];
 }
@@ -379,10 +424,8 @@
   [PasswordManagerAppInterface
       storeCredentialWithUsername:@"user"
                          password:@"password"
-                              URL:net::NSURLWithGURL(self.testServer->GetURL(
-                                      "/simple_login_form_empty_passkey."
-                                      "html"))];
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
+                              URL:net::NSURLWithGURL(
+                                      [self loginPasskeyPageURL])];
 
   [self loadLoginPasskeyPage];
 
@@ -395,16 +438,10 @@
 // This test will allow us to know if we're using a coherent browser state to
 // open the bottom sheet in incognito mode.
 - (void)testOpenPasswordBottomSheetUsePasswordIncognito {
-  [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
-  [PasswordSuggestionBottomSheetAppInterface
-      mockReauthenticationModuleExpectedResult:ReauthenticationResult::
-                                                   kSuccess];
   [PasswordManagerAppInterface
       storeCredentialWithUsername:@"user"
                          password:@"password"
-                              URL:net::NSURLWithGURL(self.testServer->GetURL(
-                                      "/simple_login_form_empty.html"))];
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
+                              URL:net::NSURLWithGURL([self loginPageURL])];
 
   [ChromeEarlGrey openNewIncognitoTab];
   [self loadLoginPage];
@@ -427,13 +464,7 @@
     EARL_GREY_TEST_DISABLED(@"Test is flaky on iPad.")
   }
 
-  [PasswordManagerAppInterface
-      storeCredentialWithUsername:@"user"
-                         password:@"password"
-                              URL:net::NSURLWithGURL(self.testServer->GetURL(
-                                      "/simple_login_form_empty.html"))];
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-  [self loadLoginPage];
+  [self saveGenericPasswordAndLoadLoginPage];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
@@ -454,13 +485,7 @@
     EARL_GREY_TEST_DISABLED(@"Test is flaky on iPad.")
   }
 
-  [PasswordManagerAppInterface
-      storeCredentialWithUsername:@"user"
-                         password:@"password"
-                              URL:net::NSURLWithGURL(self.testServer->GetURL(
-                                      "/simple_login_form_empty.html"))];
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-  [self loadLoginPage];
+  [self saveGenericPasswordAndLoadLoginPage];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
@@ -475,39 +500,24 @@
 }
 
 - (void)testOpenPasswordBottomSheetOpenPasswordManager {
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-  NSURL* URL = net::NSURLWithGURL(
-      self.testServer->GetURL("/simple_login_form_empty.html"));
-  [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
-  [PasswordSuggestionBottomSheetAppInterface
-      mockReauthenticationModuleExpectedResult:ReauthenticationResult::
-                                                   kSuccess];
+  NSURL* URL = net::NSURLWithGURL([self loginPageURL]);
   [PasswordManagerAppInterface storeCredentialWithUsername:@"user"
                                                   password:@"password"
                                                        URL:URL];
   [PasswordManagerAppInterface storeCredentialWithUsername:@"user2"
                                                   password:@"password2"
                                                        URL:URL];
-  int credentialsCount = [PasswordManagerAppInterface storedCredentialsCount];
-  GREYAssertEqual(2, credentialsCount, @"Wrong number of stored credentials.");
+  CheckNumberOfStoredCredentials(/*expected_count=*/2);
 
   [self loadLoginPage];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
 
-  [ChromeEarlGrey
-      waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user")];
-
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user")]
-      performAction:grey_tap()];
-
-  [ChromeEarlGrey
-      waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user2")];
+  TapElementOnceVisible(grey_accessibilityID(@"user"));
 
   // Long press to open context menu.
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user2")]
-      performAction:grey_longPress()];
+  LongPressElementOnceVisible(grey_accessibilityID(@"user2"));
 
   [ChromeEarlGreyUI waitForAppToIdle];
 
@@ -516,12 +526,8 @@
   [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
                                     ReauthenticationResult::kSuccess];
 
-  [[EarlGrey
-      selectElementWithMatcher:
-          grey_allOf(chrome_test_util::ButtonWithAccessibilityLabel(
-                         l10n_util::GetNSString(
-                             IDS_IOS_PASSWORD_BOTTOM_SHEET_PASSWORD_MANAGER)),
-                     grey_interactable(), nullptr)] performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:PasswordManagerContextMenuItem()]
+      performAction:grey_tap()];
 
   [ChromeEarlGreyUI waitForAppToIdle];
 
@@ -534,38 +540,25 @@
                                                         origin]),
                                    grey_sufficientlyVisible(), nil)]
       assertWithMatcher:grey_notNil()];
-
-  [PasswordSettingsAppInterface removeMockReauthenticationModule];
 }
 
 // Disabled due to flakes across builders; see https://crbug.com/374961324.
 - (void)testOpenPasswordBottomSheetOpenPasswordDetails {
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-  NSURL* URL = net::NSURLWithGURL(
-      self.testServer->GetURL("/simple_login_form_empty.html"));
-  [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
-  [PasswordSuggestionBottomSheetAppInterface
-      mockReauthenticationModuleExpectedResult:ReauthenticationResult::
-                                                   kSuccess];
+  NSURL* URL = net::NSURLWithGURL([self loginPageURL]);
   [PasswordManagerAppInterface storeCredentialWithUsername:@"user"
                                                   password:@"password"
                                                        URL:URL];
   [PasswordManagerAppInterface storeCredentialWithUsername:@"user2"
                                                   password:@"password2"
                                                        URL:URL];
-  int credentialsCount = [PasswordManagerAppInterface storedCredentialsCount];
-  GREYAssertEqual(2, credentialsCount, @".");
+  CheckNumberOfStoredCredentials(/*expected_count=*/2);
 
   [self loadLoginPage];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
 
-  [ChromeEarlGrey
-      waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user")];
-
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user")]
-      performAction:grey_tap()];
+  TapElementOnceVisible(grey_accessibilityID(@"user"));
 
   [ChromeEarlGrey
       waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user2")];
@@ -583,11 +576,8 @@
   [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user2")]
       performAction:grey_longPress()];
 
-  [[EarlGrey
-      selectElementWithMatcher:
-          grey_allOf(chrome_test_util::ContextMenuItemWithAccessibilityLabelId(
-                         IDS_IOS_PASSWORD_BOTTOM_SHEET_SHOW_DETAILS),
-                     grey_interactable(), nullptr)] performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:ShowDetailsContextMenuItem()]
+      performAction:grey_tap()];
 
   [ChromeEarlGreyUI waitForAppToIdle];
 
@@ -630,29 +620,21 @@
 // Verifies that Password Details is not revealed when local authentication
 // fails.
 - (void)testOpenPasswordBottomSheetOpenPasswordDetailsWithFailedAuthentication {
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-  NSURL* URL = net::NSURLWithGURL(
-      self.testServer->GetURL("/simple_login_form_empty.html"));
-
+  NSURL* URL = net::NSURLWithGURL([self loginPageURL]);
   [PasswordManagerAppInterface storeCredentialWithUsername:@"user"
                                                   password:@"password"
                                                        URL:URL];
   [PasswordManagerAppInterface storeCredentialWithUsername:@"user2"
                                                   password:@"password2"
                                                        URL:URL];
-  int credentialsCount = [PasswordManagerAppInterface storedCredentialsCount];
-  GREYAssertEqual(2, credentialsCount, @".");
+  CheckNumberOfStoredCredentials(/*expected_count=*/2);
 
   [self loadLoginPage];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
 
-  [ChromeEarlGrey
-      waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user")];
-
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user")]
-      performAction:grey_tap()];
+  TapElementOnceVisible(grey_accessibilityID(@"user"));
 
   [ChromeEarlGrey
       waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user2")];
@@ -670,11 +652,8 @@
   [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user2")]
       performAction:grey_longPress()];
 
-  [[EarlGrey
-      selectElementWithMatcher:
-          grey_allOf(chrome_test_util::ContextMenuItemWithAccessibilityLabelId(
-                         IDS_IOS_PASSWORD_BOTTOM_SHEET_SHOW_DETAILS),
-                     grey_interactable(), nullptr)] performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:ShowDetailsContextMenuItem()]
+      performAction:grey_tap()];
 
   [ChromeEarlGreyUI waitForAppToIdle];
 
@@ -702,39 +681,24 @@
 }
 
 - (void)testOpenPasswordBottomSheetDeletePassword {
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-  NSURL* URL = net::NSURLWithGURL(
-      self.testServer->GetURL("/simple_login_form_empty.html"));
-  [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
-  [PasswordSuggestionBottomSheetAppInterface
-      mockReauthenticationModuleExpectedResult:ReauthenticationResult::
-                                                   kSuccess];
+  NSURL* URL = net::NSURLWithGURL([self loginPageURL]);
   [PasswordManagerAppInterface storeCredentialWithUsername:@"user"
                                                   password:@"password"
                                                        URL:URL];
   [PasswordManagerAppInterface storeCredentialWithUsername:@"user2"
                                                   password:@"password2"
                                                        URL:URL];
-  int credentialsCount = [PasswordManagerAppInterface storedCredentialsCount];
-  GREYAssertEqual(2, credentialsCount, @"Wrong number of stored credentials.");
+  CheckNumberOfStoredCredentials(/*expected_count=*/2);
 
   [self loadLoginPage];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
 
-  [ChromeEarlGrey
-      waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user")];
-
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user")]
-      performAction:grey_tap()];
-
-  [ChromeEarlGrey
-      waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user2")];
+  TapElementOnceVisible(grey_accessibilityID(@"user"));
 
   // Long press to open context menu.
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user2")]
-      performAction:grey_longPress()];
+  LongPressElementOnceVisible(grey_accessibilityID(@"user2"));
 
   [ChromeEarlGreyUI waitForAppToIdle];
 
@@ -742,11 +706,8 @@
   [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
                                     ReauthenticationResult::kSuccess];
 
-  [[EarlGrey
-      selectElementWithMatcher:
-          grey_allOf(chrome_test_util::ContextMenuItemWithAccessibilityLabelId(
-                         IDS_IOS_PASSWORD_BOTTOM_SHEET_SHOW_DETAILS),
-                     grey_interactable(), nullptr)] performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:ShowDetailsContextMenuItem()]
+      performAction:grey_tap()];
 
   [ChromeEarlGreyUI waitForAppToIdle];
 
@@ -765,41 +726,26 @@
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
 
-  [ChromeEarlGrey
-      waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user")];
-
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user")]
-      performAction:grey_tap()];
+  TapElementOnceVisible(grey_accessibilityID(@"user"));
 
   [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user2")]
       assertWithMatcher:grey_nil()];
 }
 
 - (void)testOpenPasswordBottomSheetSelectPassword {
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-  NSURL* URL = net::NSURLWithGURL(
-      self.testServer->GetURL("/simple_login_form_empty.html"));
-  [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
-  [PasswordSuggestionBottomSheetAppInterface
-      mockReauthenticationModuleExpectedResult:ReauthenticationResult::
-                                                   kSuccess];
+  NSURL* URL = net::NSURLWithGURL([self loginPageURL]);
   [PasswordManagerAppInterface storeCredentialWithUsername:@"user"
                                                   password:@"password"
                                                        URL:URL];
-  int credentialsCount = [PasswordManagerAppInterface storedCredentialsCount];
-  GREYAssertEqual(1, credentialsCount, @"Wrong number of stored credentials.");
+  CheckNumberOfStoredCredentials(/*expected_count=*/1);
 
   [self loadLoginPage];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
 
-  [ChromeEarlGrey
-      waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user")];
-
   // Tapping the single item doesn't change anything.
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user")]
-      performAction:grey_tap()];
+  TapElementOnceVisible(grey_accessibilityID(@"user"));
 
   [[EarlGrey selectElementWithMatcher:UsePasswordButton()]
       performAction:grey_tap()];
@@ -811,8 +757,7 @@
   [PasswordManagerAppInterface storeCredentialWithUsername:@"user2"
                                                   password:@"password2"
                                                        URL:URL];
-  credentialsCount = [PasswordManagerAppInterface storedCredentialsCount];
-  GREYAssertEqual(2, credentialsCount, @"Wrong number of stored credentials.");
+  CheckNumberOfStoredCredentials(/*expected_count=*/2);
 
   // Reload the page, now with 2 credentials.
   [self loadLoginPage];
@@ -820,18 +765,11 @@
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
 
-  [ChromeEarlGrey
-      waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user")];
-
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user")]
-      performAction:grey_tap()];
-
-  [ChromeEarlGrey
-      waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user2")];
+  // Select the first item.
+  TapElementOnceVisible(grey_accessibilityID(@"user"));
 
   // Select the second item.
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user2")]
-      performAction:grey_tap()];
+  TapElementOnceVisible(grey_accessibilityID(@"user2"));
 
   [[EarlGrey selectElementWithMatcher:UsePasswordButton()]
       performAction:grey_tap()];
@@ -856,33 +794,22 @@
 
 // TODO(crbug.com/40279461): Fix flaky test & re-enable.
 - (void)DISABLED_testOpenPasswordBottomSheetExpand {
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-  NSURL* URL = net::NSURLWithGURL(
-      self.testServer->GetURL("/simple_login_form_empty.html"));
-  [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
-  [PasswordSuggestionBottomSheetAppInterface
-      mockReauthenticationModuleExpectedResult:ReauthenticationResult::
-                                                   kSuccess];
+  NSURL* URL = net::NSURLWithGURL([self loginPageURL]);
   for (int i = 1; i <= 9; i++) {
     [PasswordManagerAppInterface
         storeCredentialWithUsername:[NSString stringWithFormat:@"user%i", i]
                            password:@"password"
                                 URL:URL];
   }
-  int credentialsCount = [PasswordManagerAppInterface storedCredentialsCount];
-  GREYAssertEqual(9, credentialsCount, @"Wrong number of stored credentials.");
+  CheckNumberOfStoredCredentials(/*expected_count=*/9);
 
   [self loadLoginPage];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
 
-  [ChromeEarlGrey
-      waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user1")];
-
   // Tap to expand.
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user1")]
-      performAction:grey_tap()];
+  TapElementOnceVisible(grey_accessibilityID(@"user1"));
 
   // Scroll to the last password.
   [[EarlGrey selectElementWithMatcher:
@@ -890,11 +817,7 @@
                      kConfirmationAlertUnderTitleViewAccessibilityIdentifier)]
       performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];
 
-  [ChromeEarlGrey
-      waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user9")];
-
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user9")]
-      performAction:grey_tap()];
+  TapElementOnceVisible(grey_accessibilityID(@"user9"));
 
   [[EarlGrey selectElementWithMatcher:UsePasswordButton()]
       performAction:grey_tap()];
@@ -908,15 +831,8 @@
     EARL_GREY_TEST_DISABLED(@"Test is flaky on iPad.")
   }
 
-  [PasswordManagerAppInterface
-      storeCredentialWithUsername:@"user"
-                         password:@"password"
-                              URL:net::NSURLWithGURL(self.testServer->GetURL(
-                                      "/simple_login_form_empty.html"))];
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-
   // Dismiss #1.
-  [self loadLoginPage];
+  [self saveGenericPasswordAndLoadLoginPage];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
       performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
@@ -966,16 +882,10 @@
 
 // TODO(crbug.com/40279461): Fix flaky test & re-enable.
 - (void)DISABLED_testOpenPasswordBottomSheetNoUsername {
-  [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
-  [PasswordSuggestionBottomSheetAppInterface
-      mockReauthenticationModuleExpectedResult:ReauthenticationResult::
-                                                   kSuccess];
   [PasswordManagerAppInterface
       storeCredentialWithUsername:@""
                          password:@"password"
-                              URL:net::NSURLWithGURL(self.testServer->GetURL(
-                                      "/simple_login_form_empty.html"))];
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
+                              URL:net::NSURLWithGURL([self loginPageURL])];
   [self loadLoginPage];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
@@ -1006,17 +916,7 @@
 // collection to larger content size.
 - (void)testOpenPasswordBottomSheetUsePasswordAfterTraitCollectionChange {
   if (@available(iOS 17.0, *)) {
-    [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
-    [PasswordSuggestionBottomSheetAppInterface
-        mockReauthenticationModuleExpectedResult:ReauthenticationResult::
-                                                     kSuccess];
-    [PasswordManagerAppInterface
-        storeCredentialWithUsername:@"user"
-                           password:@"password"
-                                URL:net::NSURLWithGURL(self.testServer->GetURL(
-                                        "/simple_login_form_empty.html"))];
-    [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-    [self loadLoginPage];
+    [self saveGenericPasswordAndLoadLoginPage];
 
     [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
         performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
@@ -1052,13 +952,7 @@
 
 // TODO(crbug.com/361518360): Unflake the test.
 - (void)DISABLED_testOpenPasswordBottomSheetWithSingleSharedPassword {
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-  NSURL* URL = net::NSURLWithGURL(
-      self.testServer->GetURL("/simple_login_form_empty.html"));
-  [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
-  [PasswordSuggestionBottomSheetAppInterface
-      mockReauthenticationModuleExpectedResult:ReauthenticationResult::
-                                                   kSuccess];
+  NSURL* URL = net::NSURLWithGURL([self loginPageURL]);
 
   // Save 1 password that has been received via sharing and the other not.
   [PasswordManagerAppInterface storeCredentialWithUsername:@"user1"
@@ -1069,8 +963,7 @@
                                                   password:@"password2"
                                                        URL:URL
                                                     shared:NO];
-  int credentialsCount = [PasswordManagerAppInterface storedCredentialsCount];
-  GREYAssertEqual(2, credentialsCount, @"Wrong number of stored credentials.");
+  CheckNumberOfStoredCredentials(/*expected_count=*/2);
 
   [self loadLoginPage];
 
@@ -1093,10 +986,7 @@
       performAction:grey_swipeSlowInDirection(kGREYDirectionUp)];
   [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user1")]
       performAction:grey_tap()];
-  [ChromeEarlGrey
-      waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user2")];
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"user2")]
-      performAction:grey_tap()];
+  TapElementOnceVisible(grey_accessibilityID(@"user2"));
   [[EarlGrey selectElementWithMatcher:UsePasswordButton()]
       performAction:grey_tap()];
   [self verifyPasswordFieldsHaveBeenFilled:@"user2"];
@@ -1121,13 +1011,7 @@
 }
 
 - (void)testOpenPasswordBottomSheetWithMultipleSharedPasswords {
-  [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
-  [PasswordSuggestionBottomSheetAppInterface
-      mockReauthenticationModuleExpectedResult:ReauthenticationResult::
-                                                   kSuccess];
-
-  NSURL* URL = net::NSURLWithGURL(
-      self.testServer->GetURL("/simple_login_form_empty.html"));
+  NSURL* URL = net::NSURLWithGURL([self loginPageURL]);
   [PasswordManagerAppInterface storeCredentialWithUsername:@"user1"
                                                   password:@"password1"
                                                        URL:URL
@@ -1136,7 +1020,6 @@
                                                   password:@"password2"
                                                        URL:URL
                                                     shared:YES];
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
   [self loadLoginPage];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
@@ -1166,20 +1049,13 @@
 
 // TODO(crbug.com/361518360): Unflake the test.
 - (void)DISABLED_testOpenPasswordBottomSheetWithSharedPasswordsAndUseKeyboard {
-  [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
-  [PasswordSuggestionBottomSheetAppInterface
-      mockReauthenticationModuleExpectedResult:ReauthenticationResult::
-                                                   kSuccess];
-
   // Save a password that has been received via sharing.
   [PasswordManagerAppInterface
       storeCredentialWithUsername:@"user1"
                          password:@"password1"
-                              URL:net::NSURLWithGURL(self.testServer->GetURL(
-                                      "/simple_login_form_empty.html"))
+                              URL:net::NSURLWithGURL([self loginPageURL])
                            shared:YES];
 
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
   [self loadLoginPage];
 
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
@@ -1222,16 +1098,9 @@
 
 // Tests that the bottom sheet isn't displayed when the user uses the omnibox.
 - (void)testBottomSheetWithOmnibox {
-  GURL URL = self.testServer->GetURL("/simple_login_form_empty.html");
-
-  // Put a credential in the store so the sheet can trigger.
-  [PasswordManagerAppInterface
-      storeCredentialWithUsername:@"user"
-                         password:@"password"
-                              URL:net::NSURLWithGURL(URL)];
-  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
-
-  [self loadLoginPage];
+  // Put a credential in the store so the sheet can trigger, and load the
+  // webpage.
+  [self saveGenericPasswordAndLoadLoginPage];
 
   // Display the omnibox UI.
   [ChromeEarlGreyUI focusOmnibox];
diff --git a/ios/chrome/browser/price_notifications/ui_bundled/BUILD.gn b/ios/chrome/browser/price_notifications/ui_bundled/BUILD.gn
index 38c6aa6..e81d351 100644
--- a/ios/chrome/browser/price_notifications/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/price_notifications/ui_bundled/BUILD.gn
@@ -24,6 +24,8 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/bookmarks/model",
     "//ios/chrome/browser/commerce/model:shopping_service",
+    "//ios/chrome/browser/first_run/public:best_features_item",
+    "//ios/chrome/browser/first_run/public:features",
     "//ios/chrome/browser/price_insights/coordinator:delegates",
     "//ios/chrome/browser/price_insights/ui:price_insights_ui",
     "//ios/chrome/browser/price_notifications/ui_bundled/cells",
diff --git a/ios/chrome/browser/price_notifications/ui_bundled/DEPS b/ios/chrome/browser/price_notifications/ui_bundled/DEPS
index 8165051..060a395 100644
--- a/ios/chrome/browser/price_notifications/ui_bundled/DEPS
+++ b/ios/chrome/browser/price_notifications/ui_bundled/DEPS
@@ -5,4 +5,6 @@
   "+ios/chrome/browser/price_insights",
   "+ios/chrome/browser/push_notification/model",
   "+ios/chrome/browser/tabs/model/tab_title_util.h",
-]
\ No newline at end of file
+  "+ios/chrome/browser/first_run/public/best_features_item.h",
+  "+ios/chrome/browser/first_run/public/features.h",
+]
diff --git a/ios/chrome/browser/price_notifications/ui_bundled/price_notifications_price_tracking_mediator.mm b/ios/chrome/browser/price_notifications/ui_bundled/price_notifications_price_tracking_mediator.mm
index 519a3e3..a0c9183b 100644
--- a/ios/chrome/browser/price_notifications/ui_bundled/price_notifications_price_tracking_mediator.mm
+++ b/ios/chrome/browser/price_notifications/ui_bundled/price_notifications_price_tracking_mediator.mm
@@ -17,6 +17,8 @@
 #import "components/power_bookmarks/core/power_bookmark_utils.h"
 #import "components/power_bookmarks/core/proto/power_bookmark_meta.pb.h"
 #import "components/power_bookmarks/core/proto/shopping_specifics.pb.h"
+#import "ios/chrome/browser/first_run/public/best_features_item.h"
+#import "ios/chrome/browser/first_run/public/features.h"
 #import "ios/chrome/browser/price_insights/coordinator/price_insights_consumer.h"
 #import "ios/chrome/browser/price_notifications/ui_bundled/cells/price_notifications_table_view_item.h"
 #import "ios/chrome/browser/price_notifications/ui_bundled/price_notifications_alert_presenter.h"
@@ -330,6 +332,12 @@
   [self recordProductStatusFromSource:PriceNotificationTrackingSource::
                                           kPriceTracking
                                status:PriceNotificationProductStatus::kTrack];
+
+  // Notify Welcome Back to remove Price Tracking and Insights from the eligible
+  // features.
+  if (IsWelcomeBackInFirstRunEnabled()) {
+    MarkWelcomeBackFeatureUsed(BestFeaturesItemType::kPriceTrackingAndInsights);
+  }
 }
 
 // This function handles the response from the user attempting to unsubscribe to
diff --git a/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm b/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm
index 541bf203..0797d5e8 100644
--- a/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm
+++ b/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm
@@ -636,8 +636,8 @@
   // BWG promo handler.
   if (IsPageActionMenuEnabled()) {
     PrefService* prefService = self.profile->GetPrefs();
-    BOOL AIHubShown = prefService->GetBoolean(prefs::kIOSAIHubShown);
-    if (!AIHubShown) {
+    BOOL manualPromoShown = prefService->GetBoolean(prefs::kIOSBWGManualPromo);
+    if (!manualPromoShown) {
       _displayHandlerPromos[promos_manager::Promo::BWGPromo] =
           [[BWGPromoDisplayHandler alloc] init];
     }
diff --git a/ios/chrome/browser/safe_browsing/model/tailored_security/BUILD.gn b/ios/chrome/browser/safe_browsing/model/tailored_security/BUILD.gn
index f72e9a64..33d7ac5 100644
--- a/ios/chrome/browser/safe_browsing/model/tailored_security/BUILD.gn
+++ b/ios/chrome/browser/safe_browsing/model/tailored_security/BUILD.gn
@@ -20,6 +20,8 @@
     "//components/safe_browsing/core/common:safe_browsing_policy_handler",
     "//components/safe_browsing/core/common:safe_browsing_prefs",
     "//components/signin/public/identity_manager",
+    "//ios/chrome/browser/first_run/public:best_features_item",
+    "//ios/chrome/browser/first_run/public:features",
     "//ios/chrome/browser/infobars/model",
     "//ios/chrome/browser/overlays/model",
     "//ios/chrome/browser/shared/model/profile",
@@ -41,6 +43,8 @@
     "//components/safe_browsing/core/browser/tailored_security_service",
     "//components/safe_browsing/core/common:safe_browsing_prefs",
     "//ios/chrome/app/strings",
+    "//ios/chrome/browser/first_run/public:best_features_item",
+    "//ios/chrome/browser/first_run/public:features",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/components/security_interstitials/safe_browsing",
     "//ios/web/public",
diff --git a/ios/chrome/browser/safe_browsing/model/tailored_security/DEPS b/ios/chrome/browser/safe_browsing/model/tailored_security/DEPS
new file mode 100644
index 0000000..66d16c2
--- /dev/null
+++ b/ios/chrome/browser/safe_browsing/model/tailored_security/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+ios/chrome/browser/first_run/public/best_features_item.h",
+  "+ios/chrome/browser/first_run/public/features.h",
+]
diff --git a/ios/chrome/browser/safe_browsing/model/tailored_security/tailored_security_service_infobar_delegate.mm b/ios/chrome/browser/safe_browsing/model/tailored_security/tailored_security_service_infobar_delegate.mm
index b5dc7f5..47e395b1 100644
--- a/ios/chrome/browser/safe_browsing/model/tailored_security/tailored_security_service_infobar_delegate.mm
+++ b/ios/chrome/browser/safe_browsing/model/tailored_security/tailored_security_service_infobar_delegate.mm
@@ -7,6 +7,8 @@
 #import "base/metrics/histogram_functions.h"
 #import "components/safe_browsing/core/browser/tailored_security_service/tailored_security_outcome.h"
 #import "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#import "ios/chrome/browser/first_run/public/best_features_item.h"
+#import "ios/chrome/browser/first_run/public/features.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/grit/ios_branded_strings.h"
 #import "ios/chrome/grit/ios_strings.h"
@@ -104,6 +106,12 @@
             profile->GetPrefs(),
             safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION,
             /*is_esb_enabled_by_account_integration=*/false);
+        // Notify Welcome Back to remove Enhanced Safe Browsing from the
+        // eligible features.
+        if (IsWelcomeBackInFirstRunEnabled()) {
+          MarkWelcomeBackFeatureUsed(
+              BestFeaturesItemType::kEnhancedSafeBrowsing);
+        }
         break;
     }
   }
diff --git a/ios/chrome/browser/safe_browsing/model/tailored_security/tailored_security_tab_helper.mm b/ios/chrome/browser/safe_browsing/model/tailored_security/tailored_security_tab_helper.mm
index 4edff87..c819618 100644
--- a/ios/chrome/browser/safe_browsing/model/tailored_security/tailored_security_tab_helper.mm
+++ b/ios/chrome/browser/safe_browsing/model/tailored_security/tailored_security_tab_helper.mm
@@ -10,6 +10,8 @@
 #import "components/safe_browsing/core/browser/tailored_security_service/tailored_security_service_observer_util.h"
 #import "components/safe_browsing/core/browser/tailored_security_service/tailored_security_service_util.h"
 #import "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#import "ios/chrome/browser/first_run/public/best_features_item.h"
+#import "ios/chrome/browser/first_run/public/features.h"
 #import "ios/chrome/browser/infobars/model/infobar_ios.h"
 #import "ios/chrome/browser/infobars/model/infobar_manager_impl.h"
 #import "ios/chrome/browser/safe_browsing/model/tailored_security/tailored_security_service_infobar_delegate.h"
@@ -96,6 +98,11 @@
   if (is_enabled) {
     ShowInfoBar(safe_browsing::TailoredSecurityServiceMessageState::
                     kConsentedAndFlowEnabled);
+    // Notify Welcome Back to remove Enhanced Safe Browsing from the eligible
+    // features.
+    if (IsWelcomeBackInFirstRunEnabled()) {
+      MarkWelcomeBackFeatureUsed(BestFeaturesItemType::kEnhancedSafeBrowsing);
+    }
   } else {
     ShowInfoBar(safe_browsing::TailoredSecurityServiceMessageState::
                     kConsentedAndFlowDisabled);
diff --git a/ios/chrome/browser/search_engine_choice/ui_bundled/search_engine_choice_ui_util.mm b/ios/chrome/browser/search_engine_choice/ui_bundled/search_engine_choice_ui_util.mm
index e49c2eee..586614a2 100644
--- a/ios/chrome/browser/search_engine_choice/ui_bundled/search_engine_choice_ui_util.mm
+++ b/ios/chrome/browser/search_engine_choice/ui_bundled/search_engine_choice_ui_util.mm
@@ -40,6 +40,15 @@
   if (resource_it != std::end(kSearchEnginesScaledResources)) {
     return resource_it->id;
   }
+
+  if (resource_name == "IDR_SEARCH_ENGINE_GOOGLE_IMAGE") {
+    // Unlike the other logos which are in `kSearchEnginesScaledResources`,
+    // the Google logo is included via
+    // `components/resources/search_engine_choice_scaled_resources.grdp`
+    // TODO(crbug.com/422992330): Fix this discrepancy now that all OSE assets
+    // are restricted to branded builds.
+    return IDR_SEARCH_ENGINE_GOOGLE_IMAGE;
+  }
 #endif
 
   return IDR_DEFAULT_FAVICON;
diff --git a/ios/chrome/browser/settings/ui_bundled/privacy/BUILD.gn b/ios/chrome/browser/settings/ui_bundled/privacy/BUILD.gn
index 0a8ccbb..192b819 100644
--- a/ios/chrome/browser/settings/ui_bundled/privacy/BUILD.gn
+++ b/ios/chrome/browser/settings/ui_bundled/privacy/BUILD.gn
@@ -35,6 +35,8 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/browsing_data/model:feature_flags",
     "//ios/chrome/browser/feature_engagement/model",
+    "//ios/chrome/browser/first_run/public:best_features_item",
+    "//ios/chrome/browser/first_run/public:features",
     "//ios/chrome/browser/incognito_interstitial/ui_bundled:constants",
     "//ios/chrome/browser/incognito_reauth/ui_bundled:features",
     "//ios/chrome/browser/incognito_reauth/ui_bundled:incognito_reauth_util",
diff --git a/ios/chrome/browser/settings/ui_bundled/privacy/DEPS b/ios/chrome/browser/settings/ui_bundled/privacy/DEPS
index b1009ef..5370765 100644
--- a/ios/chrome/browser/settings/ui_bundled/privacy/DEPS
+++ b/ios/chrome/browser/settings/ui_bundled/privacy/DEPS
@@ -1,3 +1,5 @@
 include_rules = [
   "+ios/chrome/browser/incognito_interstitial/ui_bundled/incognito_interstitial_constants.h",
+  "+ios/chrome/browser/first_run/public/best_features_item.h",
+  "+ios/chrome/browser/first_run/public/features.h",
 ]
diff --git a/ios/chrome/browser/settings/ui_bundled/privacy/privacy_safe_browsing_mediator.mm b/ios/chrome/browser/settings/ui_bundled/privacy/privacy_safe_browsing_mediator.mm
index ae84c60..6c674d43 100644
--- a/ios/chrome/browser/settings/ui_bundled/privacy/privacy_safe_browsing_mediator.mm
+++ b/ios/chrome/browser/settings/ui_bundled/privacy/privacy_safe_browsing_mediator.mm
@@ -14,6 +14,8 @@
 #import "components/safe_browsing/core/common/features.h"
 #import "components/safe_browsing/core/common/hashprefix_realtime/hash_realtime_utils.h"
 #import "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#import "ios/chrome/browser/first_run/public/best_features_item.h"
+#import "ios/chrome/browser/first_run/public/features.h"
 #import "ios/chrome/browser/policy/model/policy_util.h"
 #import "ios/chrome/browser/settings/model/sync/utils/sync_util.h"
 #import "ios/chrome/browser/settings/ui_bundled/cells/settings_image_detail_text_item.h"
@@ -118,6 +120,11 @@
   switch (type) {
     case ItemTypeSafeBrowsingEnhancedProtection:
       safeBrowsingState = safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION;
+      // Notify Welcome Back to remove Enhanced Safe Browsing from the eligible
+      // features.
+      if (IsWelcomeBackInFirstRunEnabled()) {
+        MarkWelcomeBackFeatureUsed(BestFeaturesItemType::kEnhancedSafeBrowsing);
+      }
       break;
     case ItemTypeSafeBrowsingStandardProtection:
       safeBrowsingState = safe_browsing::SafeBrowsingState::STANDARD_PROTECTION;
diff --git a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
index 5dd74a2..53e8ce8 100644
--- a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
@@ -1043,7 +1043,7 @@
 
   registry->RegisterBooleanPref(prefs::kIOSBwgConsent, false);
 
-  registry->RegisterBooleanPref(prefs::kIOSAIHubShown, false);
+  registry->RegisterBooleanPref(prefs::kIOSBWGManualPromo, false);
 
   registry->RegisterTimePref(prefs::kIosSyncInfobarErrorLastDismissedTimestamp,
                              base::Time());
diff --git a/ios/chrome/browser/shared/model/prefs/pref_names.h b/ios/chrome/browser/shared/model/prefs/pref_names.h
index f4d0054..9e00b24d 100644
--- a/ios/chrome/browser/shared/model/prefs/pref_names.h
+++ b/ios/chrome/browser/shared/model/prefs/pref_names.h
@@ -770,8 +770,8 @@
 // A boolean specifying whether the bwg consent form has been accepted.
 inline constexpr char kIOSBwgConsent[] = "ios.bwg.consent";
 
-// A boolean specifying whether the AI Hub was shown.
-inline constexpr char kIOSAIHubShown[] = "ios.ai_hub.shown";
+// A boolean specifying whether the BWG Promo was shown manually.
+inline constexpr char kIOSBWGManualPromo[] = "ios.bwg.manual_promo";
 
 // A time object storing the first browser startup with a managed primary
 // identity in the personal profile after multi-profile becomes supported. Used
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/BUILD.gn b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/BUILD.gn
index cd0267002..a88fc18 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/BUILD.gn
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/BUILD.gn
@@ -42,6 +42,8 @@
     "//ios/chrome/browser/data_sharing/model:observer_bridge",
     "//ios/chrome/browser/drag_and_drop/model",
     "//ios/chrome/browser/favicon/model",
+    "//ios/chrome/browser/first_run/public:best_features_item",
+    "//ios/chrome/browser/first_run/public:features",
     "//ios/chrome/browser/main/model",
     "//ios/chrome/browser/net/model:crurl",
     "//ios/chrome/browser/policy/model:policy_util",
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/DEPS b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/DEPS
index 4922458..f5f4a6df 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/DEPS
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/DEPS
@@ -10,6 +10,8 @@
   "+ios/chrome/browser/net/model",
   "+ios/chrome/browser/saved_tab_groups/ui",
   "+ios/chrome/browser/toolbar/ui_bundled",
+  "+ios/chrome/browser/first_run/public/best_features_item.h",
+  "+ios/chrome/browser/first_run/public/features.h",
 ]
 
 specific_include_rules = {
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_coordinator.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_coordinator.mm
index 0a9f20d0..5865177 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_coordinator.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_coordinator.mm
@@ -19,6 +19,8 @@
 #import "ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.h"
 #import "ios/chrome/browser/collaboration/model/messaging/messaging_backend_service_factory.h"
 #import "ios/chrome/browser/data_sharing/model/data_sharing_service_factory.h"
+#import "ios/chrome/browser/first_run/public/best_features_item.h"
+#import "ios/chrome/browser/first_run/public/features.h"
 #import "ios/chrome/browser/saved_tab_groups/model/ios_tab_group_sync_util.h"
 #import "ios/chrome/browser/saved_tab_groups/model/tab_group_sync_service_factory.h"
 #import "ios/chrome/browser/share_kit/model/share_kit_face_pile_configuration.h"
@@ -111,6 +113,12 @@
   ProfileIOS* profile = self.profile;
   Browser* browser = self.browser;
 
+  // Notify Welcome Back to remove Tab Groups from the eligible
+  // features.
+  if (IsWelcomeBackInFirstRunEnabled()) {
+    MarkWelcomeBackFeatureUsed(BestFeaturesItemType::kTabGroups);
+  }
+
   tab_groups::TabGroupSyncService* tabGroupSyncService =
       tab_groups::TabGroupSyncServiceFactory::GetForProfile(profile);
   ShareKitService* shareKitService =
diff --git a/ios_internal b/ios_internal
index f9357573..f570cdf 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit f93575738db651614557a7f7c6841142661b9e21
+Subproject commit f570cdfd61b09e2357cc640bb2de2ba7455fad1f
diff --git a/media/base/demuxer.h b/media/base/demuxer.h
index d52946c..e118c81 100644
--- a/media/base/demuxer.h
+++ b/media/base/demuxer.h
@@ -13,7 +13,6 @@
 
 #include "base/time/time.h"
 #include "media/base/container_names.h"
-#include "media/base/data_source.h"
 #include "media/base/demuxer_stream.h"
 #include "media/base/eme_constants.h"
 #include "media/base/media_export.h"
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index c41c37c9..53f3c1dc 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -32,6 +32,7 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
+#include "media/base/data_source.h"
 #include "media/base/decrypt_config.h"
 #include "media/base/demuxer.h"
 #include "media/base/demuxer_memory_limit.h"
diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h
index f28026a..4fe9d0c52 100644
--- a/media/filters/ffmpeg_demuxer.h
+++ b/media/filters/ffmpeg_demuxer.h
@@ -54,6 +54,7 @@
 
 namespace media {
 
+class DataSource;
 class MediaLog;
 class FFmpegBitstreamConverter;
 class FFmpegDemuxer;
diff --git a/media/filters/hls_manifest_demuxer_engine.h b/media/filters/hls_manifest_demuxer_engine.h
index 5f617c4..8eb7aa6b 100644
--- a/media/filters/hls_manifest_demuxer_engine.h
+++ b/media/filters/hls_manifest_demuxer_engine.h
@@ -13,6 +13,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/threading/sequence_bound.h"
 #include "base/time/time.h"
+#include "media/base/data_source.h"
 #include "media/base/media_export.h"
 #include "media/base/media_log.h"
 #include "media/base/media_track.h"
diff --git a/media/gpu/test/local_gpu_memory_buffer_manager.cc b/media/gpu/test/local_gpu_memory_buffer_manager.cc
index 1f483228..f4e96e2 100644
--- a/media/gpu/test/local_gpu_memory_buffer_manager.cc
+++ b/media/gpu/test/local_gpu_memory_buffer_manager.cc
@@ -276,17 +276,6 @@
   return std::make_unique<GpuMemoryBufferImplGbm>(format, buffer_object);
 }
 
-void LocalGpuMemoryBufferManager::CopyGpuMemoryBufferAsync(
-    gfx::GpuMemoryBufferHandle buffer_handle,
-    base::UnsafeSharedMemoryRegion memory_region,
-    base::OnceCallback<void(bool)> callback) {
-  std::move(callback).Run(false);
-}
-
-bool LocalGpuMemoryBufferManager::IsConnected() {
-  return true;
-}
-
 std::unique_ptr<gfx::GpuMemoryBuffer> LocalGpuMemoryBufferManager::ImportDmaBuf(
     const gfx::NativePixmapHandle& handle,
     const gfx::Size& size,
diff --git a/media/gpu/test/local_gpu_memory_buffer_manager.h b/media/gpu/test/local_gpu_memory_buffer_manager.h
index 7750f7c..10a0900 100644
--- a/media/gpu/test/local_gpu_memory_buffer_manager.h
+++ b/media/gpu/test/local_gpu_memory_buffer_manager.h
@@ -34,19 +34,15 @@
   LocalGpuMemoryBufferManager& operator=(const LocalGpuMemoryBufferManager&) =
       delete;
 
-  // gpu::GpuMemoryBufferManager implementation
   ~LocalGpuMemoryBufferManager() override;
+
+  // gpu::GpuMemoryBufferManager implementation
   std::unique_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBuffer(
       const gfx::Size& size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
       gpu::SurfaceHandle surface_handle,
       base::WaitableEvent* shutdown_event) override;
-  void CopyGpuMemoryBufferAsync(
-      gfx::GpuMemoryBufferHandle buffer_handle,
-      base::UnsafeSharedMemoryRegion memory_region,
-      base::OnceCallback<void(bool)> callback) override;
-  bool IsConnected() override;
 
   // Imports a DmaBuf as a GpuMemoryBuffer to be able to map it. The
   // GBM_BO_USE_SW_READ_OFTEN usage is specified so that the user of the
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc
index 175858b..0de9eec 100644
--- a/media/renderers/paint_canvas_video_renderer.cc
+++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -240,10 +240,12 @@
   context_support->SignalQuery(query_id, std::move(on_query_done_cb));
 }
 
-void SynchronizeVideoFrameRead(scoped_refptr<VideoFrame> video_frame,
-                               gpu::gles2::GLES2Interface* gl,
-                               gpu::ContextSupport* context_support) {
-  WaitAndReplaceSyncTokenClient client(gl);
+void SynchronizeVideoFrameRead(
+    scoped_refptr<VideoFrame> video_frame,
+    gpu::gles2::GLES2Interface* gl,
+    gpu::ContextSupport* context_support,
+    std::unique_ptr<gpu::RasterScopedAccess> ri_access = nullptr) {
+  WaitAndReplaceSyncTokenClient client(gl, std::move(ri_access));
   video_frame->UpdateReleaseSyncToken(&client);
   if (!video_frame->metadata().read_lock_fences_enabled)
     return;
@@ -1447,8 +1449,8 @@
     BindAndTexImage2D(destination_gl, target, texture, internal_format, format,
                       type, /*level=*/0, video_frame->visible_rect().size());
 
-    destination_gl->WaitSyncTokenCHROMIUM(
-        video_frame->acquire_sync_token().GetConstData());
+    auto destination_access = shared_image->BeginGLAccessForCopySharedImage(
+        destination_gl, video_frame->acquire_sync_token(), /*readonly=*/true);
 
     // Copy shared image to gl texture for hardware video decode with
     // multiplanar shared image formats.
@@ -1460,7 +1462,8 @@
         shared_image->mailbox().name);
 
     SynchronizeVideoFrameRead(std::move(video_frame), destination_gl,
-                              raster_context_provider->ContextSupport());
+                              raster_context_provider->ContextSupport(),
+                              std::move(destination_access));
     return true;
   }
 
diff --git a/media/test/pipeline_integration_test_base.h b/media/test/pipeline_integration_test_base.h
index 79af40f2..234ee88 100644
--- a/media/test/pipeline_integration_test_base.h
+++ b/media/test/pipeline_integration_test_base.h
@@ -17,6 +17,7 @@
 #include "base/time/time.h"
 #include "media/audio/clockless_audio_sink.h"
 #include "media/audio/null_audio_sink.h"
+#include "media/base/data_source.h"
 #include "media/base/demuxer.h"
 #include "media/base/media_switches.h"
 #include "media/base/mock_media_log.h"
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
index 322ebc4..37a112c 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -96,9 +96,7 @@
 
 {#--- Constants #}
 {%-  for constant in interface.constants %}
-{%-   if constant.kind|is_string_kind %}
-const char {{interface.name}}::{{constant.name}}[{{constant|constant_length}}] = {{constant|constant_value}};
-{%-   else %}
+{%-   if not constant.kind|is_string_kind %}
 constexpr {{constant.kind|cpp_pod_type}} {{interface.name}}::{{constant.name}};
 {%-   endif %}
 {%- endfor %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
index 1a52181..2b038c7 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -55,19 +55,10 @@
 
 {{namespace_begin(namespaces_as_array, variant)}}
 
-{#--- Constants #}
-{%- for constant in module.constants %}
-{%-   if constant.kind|is_string_kind %}
-const char {{constant.name}}[] = {{constant|constant_value}};
-{%-   endif %}
-{%- endfor %}
-
 {#--- Struct Constants #}
 {%- for struct in structs %}
 {%-   for constant in struct.constants %}
-{%-     if constant.kind|is_string_kind %}
-const char {{struct.name}}::{{constant.name}}[] = {{constant|constant_value}};
-{%-     else %}
+{%-     if not constant.kind|is_string_kind %}
 constexpr {{constant.kind|cpp_pod_type}} {{struct.name}}::{{constant.name}};
 {%-     endif %}
 {%-   endfor %}
diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
index 195fb9e..caf848a 100644
--- a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -746,11 +746,10 @@
   def _FormatConstantDeclaration(self, constant, nested=False):
     if mojom.IsStringKind(constant.kind):
       if nested:
-        return "const char %s[%s]" % (constant.name,
-                                      self._ConstantLength(constant))
-      return "%sextern const char %s[%s]" % \
-          ((self.export_attribute + " ") if self.export_attribute else "",
-           constant.name, self._ConstantLength(constant))
+        return "constexpr char %s[] = %s" % (constant.name,
+                                             self._ConstantValue(constant))
+      return "inline constexpr char %s[] = %s" % \
+          (constant.name, self._ConstantValue(constant))
     return "constexpr %s %s = %s" % (GetCppPodType(
         constant.kind), constant.name, self._ConstantValue(constant))
 
diff --git a/net/cookies/canonical_cookie.h b/net/cookies/canonical_cookie.h
index 5d8a39e..6b417ef 100644
--- a/net/cookies/canonical_cookie.h
+++ b/net/cookies/canonical_cookie.h
@@ -285,24 +285,7 @@
   bool IsEquivalent(const CanonicalCookie& ecc) const {
     // It seems like it would make sense to take secure, httponly, and samesite
     // into account, but the RFC doesn't specify this.
-    return IsEquivalent(RefUniqueKey(), ecc);
-  }
-
-  // This function exists to help optimize the case of when a single cookie is
-  // being compared multiple times against other cookies. E.x.:
-  //
-  // cookie1.IsEquivalent(cookie2), cookie1.IsEquivalent(cookie3),
-  // cookie1.IsEquivalent(cookie4), etc.
-  //
-  // Doing the above re-computes cookie1's UniqueKey each time.
-  //
-  // The function allows the caller to cache cookie1's UniqueKey and reuse it
-  // as `this_key`. This function is preferable to manually comparing cookie's
-  // `UniqueKey` as it helps keep the comparison logic in one place.
-  bool IsEquivalent(const RefUniqueCookieKey& this_key,
-                    const CanonicalCookie& ecc) const {
-    DCHECK(this_key == RefUniqueKey());
-    return this_key == ecc.RefUniqueKey();
+    return RefUniqueKey() == ecc.RefUniqueKey();
   }
 
   // Checks a looser set of equivalency rules than 'IsEquivalent()' in order
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index 590cb2eb..698be82 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -1531,8 +1531,7 @@
     }
     // If cookie's domain is in legacy mode, check to make sure we are not
     // setting an aliasing cookie.
-    if (cookie_being_set.IsEquivalent(cookie_being_set_key,
-                                      *cur_existing_cookie) ||
+    if (cookie_being_set_key == cur_existing_cookie->RefUniqueKey() ||
         (cookie_being_set_scope_semantics == CookieScopeSemantics::LEGACY &&
          cookie_being_set.LegacyUniqueKey() ==
              cur_existing_cookie->LegacyUniqueKey())) {
diff --git a/net/device_bound_sessions/session_service_impl.cc b/net/device_bound_sessions/session_service_impl.cc
index ba4109e..0d239cc 100644
--- a/net/device_bound_sessions/session_service_impl.cc
+++ b/net/device_bound_sessions/session_service_impl.cc
@@ -140,23 +140,26 @@
                                 result);
 }
 
-std::pair<SessionServiceImpl::SessionsMap::iterator,
-          SessionServiceImpl::SessionsMap::iterator>
+std::ranges::subrange<SessionServiceImpl::SessionsMap::iterator>
 SessionServiceImpl::GetSessionsForSite(const SchemefulSite& site) {
   const auto now = base::Time::Now();
   auto [begin, end] = unpartitioned_sessions_.equal_range(site);
   for (auto it = begin; it != end;) {
-    if (now >= it->second->expiry_date()) {
+    auto curit = it;
+    ++it;
+
+    if (now >= curit->second->expiry_date()) {
       // Since this deletion is not due to a request, we do not need to
       // provide a per-request callback here.
-      it = DeleteSessionAndNotifyInternal(it, base::NullCallback());
+      DeleteSessionAndNotifyInternal(curit, base::NullCallback());
     } else {
-      it->second->RecordAccess();
-      it++;
+      curit->second->RecordAccess();
     }
   }
 
-  return unpartitioned_sessions_.equal_range(site);
+  auto sessions_for_site = unpartitioned_sessions_.equal_range(site);
+  return std::ranges::subrange<SessionsMap::iterator>(sessions_for_site.first,
+                                                      sessions_for_site.second);
 }
 
 std::optional<SessionService::DeferralParams> SessionServiceImpl::ShouldDefer(
@@ -168,17 +171,15 @@
   SchemefulSite site(request->url());
   const base::flat_set<SessionKey>& previous_deferrals =
       request->device_bound_session_deferrals();
-  auto range = GetSessionsForSite(site);
-  for (auto it = range.first; it != range.second; ++it) {
-    if (previous_deferrals.find({site, it->second->id()}) !=
+  for (const auto& [_, session] : GetSessionsForSite(site)) {
+    if (previous_deferrals.find({site, session->id()}) !=
         previous_deferrals.end()) {
       continue;
     }
-    if (it->second->ShouldDeferRequest(request, first_party_set_metadata)) {
+    if (session->ShouldDeferRequest(request, first_party_set_metadata)) {
       NotifySessionAccess(request->device_bound_session_access_callback(),
-                          SessionAccess::AccessType::kUpdate, site,
-                          *it->second);
-      return DeferralParams(it->second->id());
+                          SessionAccess::AccessType::kUpdate, site, *session);
+      return DeferralParams(session->id());
     }
   }
 
@@ -301,13 +302,11 @@
   }
 
   SchemefulSite site(request_url);
-  auto range = GetSessionsForSite(site);
-  for (auto it = range.first; it != range.second; ++it) {
-    if (it->second->id().value() == param.session_id()) {
+  for (const auto& [_, session] : GetSessionsForSite(site)) {
+    if (session->id().value() == param.session_id()) {
       NotifySessionAccess(on_access_callback,
-                          SessionAccess::AccessType::kUpdate, site,
-                          *it->second);
-      it->second->set_cached_challenge(param.challenge());
+                          SessionAccess::AccessType::kUpdate, site, *session);
+      session->set_cached_challenge(param.challenge());
       return;
     }
   }
@@ -339,7 +338,7 @@
   auto range = unpartitioned_sessions_.equal_range(site);
   for (auto it = range.first; it != range.second; ++it) {
     if (it->second->id() == id) {
-      std::ignore = DeleteSessionAndNotifyInternal(it, per_request_callback);
+      DeleteSessionAndNotifyInternal(it, per_request_callback);
       return;
     }
   }
@@ -376,11 +375,12 @@
     base::OnceClosure completion_callback) {
   for (auto it = unpartitioned_sessions_.begin();
        it != unpartitioned_sessions_.end();) {
-    if (SessionMatchesFilter(it->first, *it->second, created_after_time,
+    auto curit = it;
+    ++it;
+
+    if (SessionMatchesFilter(curit->first, *curit->second, created_after_time,
                              created_before_time, origin_and_site_matcher)) {
-      it = DeleteSessionAndNotifyInternal(it, base::NullCallback());
-    } else {
-      ++it;
+      DeleteSessionAndNotifyInternal(curit, base::NullCallback());
     }
   }
 
@@ -398,8 +398,7 @@
   return subscription;
 }
 
-SessionServiceImpl::SessionsMap::iterator
-SessionServiceImpl::DeleteSessionAndNotifyInternal(
+void SessionServiceImpl::DeleteSessionAndNotifyInternal(
     SessionServiceImpl::SessionsMap::iterator it,
     SessionService::OnAccessCallback per_request_callback) {
   if (session_store_) {
@@ -410,7 +409,7 @@
                       SessionAccess::AccessType::kTermination, it->first,
                       *it->second);
 
-  return unpartitioned_sessions_.erase(it);
+  unpartitioned_sessions_.erase(it);
 }
 
 void SessionServiceImpl::NotifySessionAccess(
diff --git a/net/device_bound_sessions/session_service_impl.h b/net/device_bound_sessions/session_service_impl.h
index ab850253..2fd88843 100644
--- a/net/device_bound_sessions/session_service_impl.h
+++ b/net/device_bound_sessions/session_service_impl.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <memory>
 #include <optional>
+#include <ranges>
 #include <string>
 #include <unordered_map>
 #include <vector>
@@ -143,14 +144,13 @@
 
   // Get all the unexpired sessions for a given site. This also removes
   // expired sessions for the site and extends the TTL of used sessions.
-  std::pair<SessionsMap::iterator, SessionsMap::iterator> GetSessionsForSite(
+  std::ranges::subrange<SessionsMap::iterator> GetSessionsForSite(
       const SchemefulSite& site);
 
   // Remove a session from the session map. It also clears the session
   // from `session_store_` and notifies any observers (including
   // `per_request_callback`) about the termination.
-  // Return the iterator to the next session in the map.
-  [[nodiscard]] SessionsMap::iterator DeleteSessionAndNotifyInternal(
+  void DeleteSessionAndNotifyInternal(
       SessionsMap::iterator it,
       SessionService::OnAccessCallback per_request_callback);
 
diff --git a/services/data_decoder/BUILD.gn b/services/data_decoder/BUILD.gn
index 28cee86..f0dd371 100644
--- a/services/data_decoder/BUILD.gn
+++ b/services/data_decoder/BUILD.gn
@@ -29,13 +29,6 @@
     "xml_parser.h",
   ]
 
-  if (is_chromeos) {
-    sources += [
-      "ble_scan_parser_impl.cc",
-      "ble_scan_parser_impl.h",
-    ]
-  }
-
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
   deps = [
@@ -57,6 +50,13 @@
 
   public_deps = [ "//services/data_decoder/public/mojom" ]
 
+  if (is_chromeos) {
+    sources += [
+      "ble_scan_parser_impl.cc",
+      "ble_scan_parser_impl.h",
+    ]
+  }
+
   if (use_blink) {
     sources += [
       "image_decoder_impl.cc",
@@ -80,10 +80,6 @@
     "xml_parser_unittest.cc",
   ]
 
-  if (is_chromeos) {
-    sources += [ "ble_scan_parser_impl_unittest.cc" ]
-  }
-
   deps = [
     ":lib",
     "//base",
@@ -98,6 +94,15 @@
     "//ui/gfx",
   ]
 
+  if (is_chromeos) {
+    sources += [ "ble_scan_parser_impl_unittest.cc" ]
+
+    deps += [
+      "//services/data_decoder/ble_scan_parser:lib",
+      "//services/data_decoder/ble_scan_parser:parser",
+    ]
+  }
+
   if (is_ios) {
     deps += [ "//components/test:web_package_test_bundle_data" ]
   } else {
diff --git a/services/data_decoder/ble_scan_parser/BUILD.gn b/services/data_decoder/ble_scan_parser/BUILD.gn
new file mode 100644
index 0000000..20568de2
--- /dev/null
+++ b/services/data_decoder/ble_scan_parser/BUILD.gn
@@ -0,0 +1,56 @@
+# Copyright 2025 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/rust/rust_static_library.gni")
+
+rust_static_library("lib") {
+  crate_root = "lib.rs"
+  sources = [
+    "cxx.rs",
+    "lib.rs",
+  ]
+
+  visibility = [
+    ":*",
+    "//services/data_decoder:*",
+  ]
+
+  allow_unsafe = true
+  cxx_bindings = [ "cxx.rs" ]
+  public_deps = [ ":wrapper_functions" ]
+}
+
+source_set("wrapper_functions") {
+  sources = [
+    "wrapper_functions.cc",
+    "wrapper_functions.h",
+  ]
+
+  visibility = [
+    ":*",
+    "//services/data_decoder:*",
+  ]
+
+  deps = [
+    "//base",
+    "//build/rust:cxx_cppdeps",
+  ]
+  public_deps = [ "//services/data_decoder/public/mojom" ]
+}
+
+source_set("parser") {
+  sources = [
+    "parser.cc",
+    "parser.h",
+  ]
+
+  deps = [
+    ":lib",
+    ":wrapper_functions",
+  ]
+  public_deps = [
+    "//base",
+    "//services/data_decoder/public/mojom",
+  ]
+}
diff --git a/services/data_decoder/ble_scan_parser/DEPS b/services/data_decoder/ble_scan_parser/DEPS
new file mode 100644
index 0000000..6a25be5c
--- /dev/null
+++ b/services/data_decoder/ble_scan_parser/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+third_party/rust/cxx/v1/cxx.h"
+]
diff --git a/services/data_decoder/ble_scan_parser/cxx.rs b/services/data_decoder/ble_scan_parser/cxx.rs
new file mode 100644
index 0000000..6d7ff8d
--- /dev/null
+++ b/services/data_decoder/ble_scan_parser/cxx.rs
@@ -0,0 +1,49 @@
+// 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.
+
+#[cxx::bridge(namespace=ble_scan_parser_bridge)]
+pub mod ffi {
+    // Note: this is only exposed for testing purposes.
+    #[derive(Clone, Copy)]
+    enum UuidFormat {
+        // The UUID is the third and fourth bytes of a UUID with this pattern:
+        // 0000____-0000-1000-8000-00805F9B34FB
+        With16Bits,
+        // The UUID is the first four bytes of a UUID with this pattern:
+        // ________-0000-1000-8000-00805F9B34FB
+        With32Bits,
+        // The UUID is a standard UUID
+        With128Bits,
+    }
+
+    unsafe extern "C++" {
+        include!("services/data_decoder/ble_scan_parser/wrapper_functions.h");
+
+        pub type ScanRecord;
+
+        fn set_advertising_flags(record: Pin<&mut ScanRecord>, flags: i8);
+        fn set_tx_power(record: Pin<&mut ScanRecord>, power: i8);
+        fn set_advertisement_name(record: Pin<&mut ScanRecord>, name: &str);
+        fn add_service_uuid(record: Pin<&mut ScanRecord>, uuid: &[u8; 16]);
+        fn add_service_data(record: Pin<&mut ScanRecord>, uuid: &[u8; 16], data: &[u8]);
+        fn add_manufacturer_data(record: Pin<&mut ScanRecord>, company_code: u16, data: &[u8]);
+
+        pub type UuidListBuilderForTest;
+        fn add_uuid(self: Pin<&mut UuidListBuilderForTest>, uuid: &[u8; 16]);
+    }
+
+    extern "Rust" {
+        fn parse(advertising_data: &[u8], record: Pin<&mut ScanRecord>) -> bool;
+        fn parse_service_uuids_for_test(
+            bytes: &[u8],
+            format: UuidFormat,
+            uuid_list_builder: Pin<&mut UuidListBuilderForTest>,
+        ) -> bool;
+        fn parse_uuid_for_test(bytes: &[u8], format: UuidFormat, out_uuid: &mut [u8; 16]) -> bool;
+    }
+}
+
+use crate::parse;
+use crate::parse_service_uuids_for_test;
+use crate::parse_uuid_for_test;
diff --git a/services/data_decoder/ble_scan_parser/lib.rs b/services/data_decoder/ble_scan_parser/lib.rs
new file mode 100644
index 0000000..0737d87
--- /dev/null
+++ b/services/data_decoder/ble_scan_parser/lib.rs
@@ -0,0 +1,198 @@
+// 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.
+
+mod cxx;
+
+use std::pin::Pin;
+
+use crate::cxx::ffi;
+
+// Definitions of the data type flags:
+// https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/
+const DATA_TYPE_FLAGS: u8 = 0x01;
+const DATA_TYPE_SERVICE_UUIDS_16BIT_PARTIAL: u8 = 0x02;
+const DATA_TYPE_SERVICE_UUIDS_16BIT_COMPLETE: u8 = 0x03;
+const DATA_TYPE_SERVICE_UUIDS_32BIT_PARTIAL: u8 = 0x04;
+const DATA_TYPE_SERVICE_UUIDS_32BIT_COMPLETE: u8 = 0x05;
+const DATA_TYPE_SERVICE_UUIDS_128BIT_PARTIAL: u8 = 0x06;
+const DATA_TYPE_SERVICE_UUIDS_128BIT_COMPLETE: u8 = 0x07;
+const DATA_TYPE_LOCAL_NAME_SHORT: u8 = 0x08;
+const DATA_TYPE_LOCAL_NAME_COMPLETE: u8 = 0x09;
+const DATA_TYPE_TX_POWER_LEVEL: u8 = 0x0A;
+const DATA_TYPE_SERVICE_DATA: u8 = 0x16;
+const DATA_TYPE_MANUFACTURER_DATA: u8 = 0xFF;
+
+const UUID_PLACEHOLDER: [u8; 16] = [
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
+];
+
+pub fn parse(advertisement_data: &[u8], record: Pin<&mut ffi::ScanRecord>) -> bool {
+    parse_impl(advertisement_data, record).is_some()
+}
+
+fn parse_impl<'a>(
+    mut advertisement_data: &'a [u8],
+    mut record: Pin<&mut ffi::ScanRecord>,
+) -> Option<()> {
+    // A reference for BLE advertising data:
+    // https://community.silabs.com/s/article/kba-bt-0201-bluetooth-advertising-data-basics
+    // The data consists of a sequence of lengths and packets, where:
+    // - a single byte <length> describes the length of the following packet.
+    //   Packets must be at least 2 bytes in length; a packet must contain at least
+    //   one byte of data.
+    // - a packet starting with a single byte <type> that describes the data type.
+    // - and the remainder is the packet data, interpreted according to <type>.
+
+    // TODO(dcheng): The C++ style guide very clearly discourages treating signed
+    // types as bitfields, and yet here we are.
+    let mut advertising_flags: i8 = -1;
+    let mut advertisement_name: &'a str = "";
+    // TODO(dcheng): It's unclear what the correct default value here is, as the
+    // original C++ implementation does not bother initializing this.
+    let mut tx_power: i8 = 0;
+
+    loop {
+        let Some((&length, remainder)) = advertisement_data.split_first() else {
+            break;
+        };
+
+        let length: usize = length.into();
+
+        if length <= 1 || length > remainder.len() {
+            return None;
+        }
+
+        let packet;
+        (packet, advertisement_data) = remainder.split_at(length);
+        // Length must be at least one, so this is always safe.
+        let (&data_type, data) = packet.split_first().unwrap();
+
+        match data_type {
+            // For flags, additional bytes past the first are silently ignored. The unwrap() is
+            // guaranteed to succeed, due to the length <= 1 check above.
+            DATA_TYPE_FLAGS => advertising_flags = *data.first().unwrap() as i8,
+            // TODO(dcheng): The core supplement says:
+            // Two Service or Service Class UUID data types are assigned to each size of
+            // Service UUID. One Service or Service Class UUID data type indicates that the
+            // Service or Service Class UUID list is incomplete and the other indicates the
+            // Service or Service Class UUID list is complete.
+            //
+            // A packet or data block shall not contain more than one instance for each
+            // Service or Service Class UUID data size.
+            //
+            // But the parser does not seem to consider that.
+            DATA_TYPE_SERVICE_UUIDS_16BIT_PARTIAL | DATA_TYPE_SERVICE_UUIDS_16BIT_COMPLETE => {
+                parse_service_uuids(data, UuidFormat::With16Bits, |uuid| {
+                    ffi::add_service_uuid(record.as_mut(), uuid);
+                })?
+            }
+            DATA_TYPE_SERVICE_UUIDS_32BIT_PARTIAL | DATA_TYPE_SERVICE_UUIDS_32BIT_COMPLETE => {
+                parse_service_uuids(data, UuidFormat::With32Bits, |uuid| {
+                    ffi::add_service_uuid(record.as_mut(), uuid);
+                })?
+            }
+            DATA_TYPE_SERVICE_UUIDS_128BIT_PARTIAL | DATA_TYPE_SERVICE_UUIDS_128BIT_COMPLETE => {
+                parse_service_uuids(data, UuidFormat::With128Bits, |uuid| {
+                    ffi::add_service_uuid(record.as_mut(), uuid);
+                })?
+            }
+            DATA_TYPE_LOCAL_NAME_SHORT | DATA_TYPE_LOCAL_NAME_COMPLETE => {
+                // TODO(dcheng): The Core Specification Supplement, Part A, Section 1.2, states
+                // that this should be a valid UTF-8 string. Do we need to be lenient with our
+                // parsing here though? The original C++ implementation certainly never bothered
+                // to validate...
+                advertisement_name = str::from_utf8(data).ok()?;
+            }
+            // For TX power, additional bytes past the first are silently ignored. The unwrap() is
+            // guaranteed to succeed, due to the length <= 1 check above.
+            DATA_TYPE_TX_POWER_LEVEL => tx_power = *data.first().unwrap() as i8,
+            DATA_TYPE_SERVICE_DATA => {
+                if data.len() < 4 {
+                    return None;
+                }
+                let (uuid, data) = data.split_at(2);
+                let uuid = parse_uuid(uuid, UuidFormat::With16Bits)?;
+                ffi::add_service_data(record.as_mut(), &uuid, data);
+            }
+            DATA_TYPE_MANUFACTURER_DATA => {
+                if data.len() < 4 {
+                    return None;
+                }
+                // This unwrap() is safe due to the length check above.
+                let (key_bytes, data) = data.split_first_chunk::<2>().unwrap();
+                let manufacturer_key = u16::from_le_bytes(*key_bytes);
+                ffi::add_manufacturer_data(record.as_mut(), manufacturer_key, data);
+            }
+            // Unknown packet types are silently ignored.
+            _ => (),
+        }
+    }
+
+    ffi::set_advertising_flags(record.as_mut(), advertising_flags);
+    ffi::set_tx_power(record.as_mut(), tx_power);
+    ffi::set_advertisement_name(record.as_mut(), advertisement_name);
+    Some(())
+}
+
+use ffi::UuidFormat;
+
+impl UuidFormat {
+    fn get_offset_and_len(self) -> (usize, usize) {
+        match self {
+            UuidFormat::With16Bits => (2, 2),
+            UuidFormat::With32Bits => (0, 4),
+            UuidFormat::With128Bits => (0, 16),
+            _ => unreachable!(),
+        }
+    }
+}
+
+/// Parses
+fn parse_service_uuids<F>(bytes: &[u8], format: UuidFormat, mut f: F) -> Option<()>
+where
+    F: FnMut(&[u8; 16]),
+{
+    let (_, len) = format.get_offset_and_len();
+    let chunks = bytes.chunks_exact(len);
+    if !chunks.remainder().is_empty() {
+        return None;
+    }
+    for chunk in chunks {
+        let uuid = parse_uuid(chunk, format)?;
+        f(&uuid);
+    }
+    Some(())
+}
+
+fn parse_uuid(bytes: &[u8], format: UuidFormat) -> Option<[u8; 16]> {
+    let (offset, len) = format.get_offset_and_len();
+    if bytes.len() != len {
+        return None;
+    }
+    let mut uuid = UUID_PLACEHOLDER;
+    for (src, dst) in bytes.iter().rev().zip(uuid[offset..offset + len].iter_mut()) {
+        *dst = *src;
+    }
+    Some(uuid)
+}
+
+fn parse_service_uuids_for_test(
+    bytes: &[u8],
+    format: UuidFormat,
+    mut uuid_list_builder: Pin<&mut ffi::UuidListBuilderForTest>,
+) -> bool {
+    parse_service_uuids(bytes, format, |uuid| {
+        uuid_list_builder.as_mut().add_uuid(uuid);
+    })
+    .is_some()
+}
+
+fn parse_uuid_for_test(bytes: &[u8], format: UuidFormat, out: &mut [u8; 16]) -> bool {
+    if let Some(uuid) = parse_uuid(bytes, format) {
+        *out = uuid;
+        true
+    } else {
+        false
+    }
+}
diff --git a/services/data_decoder/ble_scan_parser/parser.cc b/services/data_decoder/ble_scan_parser/parser.cc
new file mode 100644
index 0000000..bb0d9b9
--- /dev/null
+++ b/services/data_decoder/ble_scan_parser/parser.cc
@@ -0,0 +1,23 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/data_decoder/ble_scan_parser/parser.h"
+
+#include <utility>
+
+#include "base/containers/span.h"
+#include "base/containers/span_rust.h"
+#include "services/data_decoder/ble_scan_parser/cxx.rs.h"
+
+namespace ble_scan_parser {
+
+data_decoder::mojom::ScanRecordPtr Parse(base::span<const uint8_t> bytes) {
+  data_decoder::mojom::ScanRecordPtr record =
+      data_decoder::mojom::ScanRecord::New();
+  bool result =
+      ble_scan_parser_bridge::parse(base::SpanToRustSlice(bytes), *record);
+  return result ? std::move(record) : nullptr;
+}
+
+}  // namespace ble_scan_parser
diff --git a/services/data_decoder/ble_scan_parser/parser.h b/services/data_decoder/ble_scan_parser/parser.h
new file mode 100644
index 0000000..e99dcbe
--- /dev/null
+++ b/services/data_decoder/ble_scan_parser/parser.h
@@ -0,0 +1,19 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_DATA_DECODER_BLE_SCAN_PARSER_PARSER_H_
+#define SERVICES_DATA_DECODER_BLE_SCAN_PARSER_PARSER_H_
+
+#include <stdint.h>
+
+#include "base/containers/span.h"
+#include "services/data_decoder/public/mojom/ble_scan_parser.mojom.h"
+
+namespace ble_scan_parser {
+
+data_decoder::mojom::ScanRecordPtr Parse(base::span<const uint8_t> bytes);
+
+}
+
+#endif  // SERVICES_DATA_DECODER_BLE_SCAN_PARSER_PARSER_H_
diff --git a/services/data_decoder/ble_scan_parser/wrapper_functions.cc b/services/data_decoder/ble_scan_parser/wrapper_functions.cc
new file mode 100644
index 0000000..9ab9003
--- /dev/null
+++ b/services/data_decoder/ble_scan_parser/wrapper_functions.cc
@@ -0,0 +1,54 @@
+// 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 "services/data_decoder/ble_scan_parser/wrapper_functions.h"
+
+#include <stdint.h>
+
+#include <array>
+#include <string>
+#include <vector>
+
+#include "base/containers/to_vector.h"
+#include "device/bluetooth/public/cpp/bluetooth_uuid.h"
+#include "third_party/rust/cxx/v1/cxx.h"
+
+namespace ble_scan_parser_bridge {
+
+UuidListBuilderForTest::UuidListBuilderForTest() = default;
+UuidListBuilderForTest::~UuidListBuilderForTest() = default;
+
+void UuidListBuilderForTest::add_uuid(const std::array<uint8_t, 16>& uuid) {
+  uuids.push_back(device::BluetoothUUID(uuid));
+}
+
+void set_advertising_flags(ScanRecord& record, int8_t flags) {
+  record.advertising_flags = flags;
+}
+
+void set_tx_power(ScanRecord& record, int8_t power) {
+  record.tx_power = power;
+}
+
+void set_advertisement_name(ScanRecord& record, rust::Str name) {
+  record.advertisement_name = std::string(name);
+}
+
+void add_service_uuid(ScanRecord& record, const std::array<uint8_t, 16>& uuid) {
+  record.service_uuids.push_back(device::BluetoothUUID(uuid));
+}
+
+void add_service_data(ScanRecord& record,
+                      const std::array<uint8_t, 16>& uuid,
+                      rust::Slice<const uint8_t> data) {
+  record.service_data_map[device::BluetoothUUID(uuid)] = base::ToVector(data);
+}
+
+void add_manufacturer_data(ScanRecord& record,
+                           uint16_t company_code,
+                           rust::Slice<const uint8_t> data) {
+  record.manufacturer_data_map[company_code] = base::ToVector(data);
+}
+
+}  // namespace ble_scan_parser_bridge
diff --git a/services/data_decoder/ble_scan_parser/wrapper_functions.h b/services/data_decoder/ble_scan_parser/wrapper_functions.h
new file mode 100644
index 0000000..21a6e9b
--- /dev/null
+++ b/services/data_decoder/ble_scan_parser/wrapper_functions.h
@@ -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.
+
+#ifndef SERVICES_DATA_DECODER_BLE_SCAN_PARSER_WRAPPER_FUNCTIONS_H_
+#define SERVICES_DATA_DECODER_BLE_SCAN_PARSER_WRAPPER_FUNCTIONS_H_
+
+#include <stdint.h>
+
+#include <array>
+#include <vector>
+
+#include "device/bluetooth/public/cpp/bluetooth_uuid.h"
+#include "services/data_decoder/public/mojom/ble_scan_parser.mojom.h"
+#include "third_party/rust/cxx/v1/cxx.h"
+
+namespace ble_scan_parser_bridge {
+
+using ScanRecord = data_decoder::mojom::ScanRecord;
+
+void set_advertising_flags(ScanRecord& record, int8_t flags);
+void set_tx_power(ScanRecord& record, int8_t power);
+void set_advertisement_name(ScanRecord& record, rust::Str name);
+void add_service_uuid(ScanRecord& record, const std::array<uint8_t, 16>& uuid);
+void add_service_data(ScanRecord& record,
+                      const std::array<uint8_t, 16>& uuid,
+                      rust::Slice<const uint8_t> data);
+void add_manufacturer_data(ScanRecord& record,
+                           uint16_t company_code,
+                           rust::Slice<const uint8_t> data);
+
+struct UuidListBuilderForTest {
+  UuidListBuilderForTest();
+  ~UuidListBuilderForTest();
+
+  void add_uuid(const std::array<uint8_t, 16>& uuid);
+
+  std::vector<device::BluetoothUUID> uuids;
+};
+
+}  // namespace ble_scan_parser_bridge
+
+#endif  // SERVICES_DATA_DECODER_BLE_SCAN_PARSER_WRAPPER_FUNCTIONS_H_
diff --git a/services/data_decoder/ble_scan_parser_impl_unittest.cc b/services/data_decoder/ble_scan_parser_impl_unittest.cc
index 781e481..c96d463 100644
--- a/services/data_decoder/ble_scan_parser_impl_unittest.cc
+++ b/services/data_decoder/ble_scan_parser_impl_unittest.cc
@@ -4,6 +4,15 @@
 
 #include "services/data_decoder/ble_scan_parser_impl.h"
 
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/containers/span.h"
+#include "base/containers/span_rust.h"
+#include "base/notreached.h"
+#include "services/data_decoder/ble_scan_parser/cxx.rs.h"
+#include "services/data_decoder/ble_scan_parser/parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace data_decoder {
@@ -48,7 +57,45 @@
   }
 };
 
-using ParserImpls = ::testing::Types<CxxParserTraits>;
+struct RustParserTraits {
+  static ble_scan_parser_bridge::UuidFormat ToRust(UuidFormat format) {
+    switch (format) {
+      case UuidFormat::kFormat16Bit:
+        return ble_scan_parser_bridge::UuidFormat::With16Bits;
+      case UuidFormat::kFormat32Bit:
+        return ble_scan_parser_bridge::UuidFormat::With32Bits;
+      case UuidFormat::kFormat128Bit:
+        return ble_scan_parser_bridge::UuidFormat::With128Bits;
+      case UuidFormat::kFormatInvalid:
+        NOTREACHED();
+    }
+    NOTREACHED();
+  }
+
+  static device::BluetoothUUID ParseUuid(base::span<const uint8_t> bytes,
+                                         UuidFormat format) {
+    std::array<uint8_t, 16> uuid_bytes;
+    bool result = ble_scan_parser_bridge::parse_uuid_for_test(
+        base::SpanToRustSlice(bytes), ToRust(format), uuid_bytes);
+    return result ? device::BluetoothUUID(uuid_bytes) : device::BluetoothUUID();
+  }
+
+  static bool ParseServiceUuids(base::span<const uint8_t> bytes,
+                                UuidFormat format,
+                                std::vector<device::BluetoothUUID>& out) {
+    ble_scan_parser_bridge::UuidListBuilderForTest builder;
+    bool result = ble_scan_parser_bridge::parse_service_uuids_for_test(
+        base::SpanToRustSlice(bytes), ToRust(format), builder);
+    out = std::move(builder.uuids);
+    return result;
+  }
+
+  static mojom::ScanRecordPtr ParseBleScan(base::span<const uint8_t> bytes) {
+    return ble_scan_parser::Parse(bytes);
+  }
+};
+
+using ParserImpls = ::testing::Types<CxxParserTraits, RustParserTraits>;
 TYPED_TEST_SUITE(BleScanParserImplTest, ParserImpls);
 
 TYPED_TEST(BleScanParserImplTest, ParseBadUuidLengthReturnsEmptyString) {
diff --git a/services/webnn/coreml/context_impl_coreml.mm b/services/webnn/coreml/context_impl_coreml.mm
index 3343a65..4d70405c 100644
--- a/services/webnn/coreml/context_impl_coreml.mm
+++ b/services/webnn/coreml/context_impl_coreml.mm
@@ -61,6 +61,14 @@
                           "Creation of constant tensors is not supported.")));
     return;
   }
+  // TODO(crbug.com/345352987): implement WebGPU interop tensors for CoreML
+  // backend.
+  if (tensor_info->usage.Has(MLTensorUsageFlags::kWebGpuInterop)) {
+    std::move(callback).Run(base::unexpected(
+        mojom::Error::New(mojom::Error::Code::kNotSupportedError,
+                          "WebGPU Interop is not supported.")));
+    return;
+  }
   std::move(callback).Run(TensorImplCoreml::Create(std::move(receiver), this,
                                                    std::move(tensor_info)));
 }
diff --git a/services/webnn/public/cpp/graph_validation_utils.cc b/services/webnn/public/cpp/graph_validation_utils.cc
index e05459ac..b941f04 100644
--- a/services/webnn/public/cpp/graph_validation_utils.cc
+++ b/services/webnn/public/cpp/graph_validation_utils.cc
@@ -10,6 +10,7 @@
 #include <variant>
 #include <vector>
 
+#include "base/check.h"
 #include "base/check_op.h"
 #include "base/containers/contains.h"
 #include "base/notreached.h"
@@ -1084,7 +1085,7 @@
     return base::unexpected(ErrorWithLabel(
         label, "The input shape is not broadcastable to the new shape."));
   }
-  CHECK_EQ(new_shape, base::span<const uint32_t>(*output_shape));
+  CHECK(new_shape == *output_shape);
 
   return OperandDescriptor::Create(context_properties, input.data_type(),
                                    *output_shape, label);
diff --git a/services/webnn/tflite/context_impl_tflite.cc b/services/webnn/tflite/context_impl_tflite.cc
index eab9d202..d6c11816 100644
--- a/services/webnn/tflite/context_impl_tflite.cc
+++ b/services/webnn/tflite/context_impl_tflite.cc
@@ -57,6 +57,14 @@
                           "Creation of constant tensors is not supported.")));
     return;
   }
+  // TODO(crbug.com/345352987): implement WebGPU interop tensors for TFLite
+  // backend.
+  if (tensor_info->usage.Has(MLTensorUsageFlags::kWebGpuInterop)) {
+    std::move(callback).Run(base::unexpected(
+        mojom::Error::New(mojom::Error::Code::kNotSupportedError,
+                          "WebGPU Interop is not supported.")));
+    return;
+  }
   std::move(callback).Run(TensorImplTflite::Create(std::move(receiver), this,
                                                    std::move(tensor_info)));
 }
diff --git a/services/webnn/webnn_graph_mojolpm_fuzzer_seed_corpus/simple.textproto b/services/webnn/webnn_graph_mojolpm_fuzzer_seed_corpus/simple.textproto
index 2d70c329..49a53dfe 100644
--- a/services/webnn/webnn_graph_mojolpm_fuzzer_seed_corpus/simple.textproto
+++ b/services/webnn/webnn_graph_mojolpm_fuzzer_seed_corpus/simple.textproto
@@ -51,6 +51,7 @@
           values: {
             value:  {
               new {
+                id: 0
                 m_id: 0
               }
             }
@@ -60,6 +61,7 @@
           values: {
             value: {
               new {
+                id: 1
                 m_id: 1
               }
             }
@@ -75,11 +77,13 @@
                     id: 1
                     m_input_operand_id: {
                       new {
+                        id: 0
                         m_id: 0
                       }
                     }
                     m_output_operand_id: {
                       new {
+                        id: 1
                         m_id: 1
                       }
                     }
@@ -91,6 +95,7 @@
           }
         }
         m_constant_operand_ids_to_handles: {}
+        m_id_to_constant_tensor_operand_map: {}
       }
     }
   }
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index db023988..3372629 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -919,6 +919,21 @@
             ]
         }
     ],
+    "AndroidTabStripLayoutOptimization": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "TabStripLayoutOptimization"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidUseFrameIntervalDeciderAdaptiveFrameRate": [
         {
             "platforms": [
@@ -20499,26 +20514,6 @@
             ]
         }
     ],
-    "SafeBrowsingRemoveCookiesInAuthRequests": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows",
-                "android",
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SafeBrowsingRemoveCookiesInAuthRequests"
-                    ]
-                }
-            ]
-        }
-    ],
     "SafeBrowsingSyncCheckerCheckAllowlist": [
         {
             "platforms": [
@@ -23896,21 +23891,6 @@
             ]
         }
     ],
-    "TabStripLayoutOptimization": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "TabStripLayoutOptimization"
-                    ]
-                }
-            ]
-        }
-    ],
     "TabstripComboButton": [
         {
             "platforms": [
diff --git a/third_party/angle b/third_party/angle
index 3d81e45..3a15fab 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 3d81e45d2bb674c7ba81cf2facc31336571b8a2d
+Subproject commit 3a15fab06cff664a04188959f35fff33a37c5edb
diff --git a/third_party/blink/common/switches.cc b/third_party/blink/common/switches.cc
index 10c7a55..72f4107 100644
--- a/third_party/blink/common/switches.cc
+++ b/third_party/blink/common/switches.cc
@@ -117,12 +117,6 @@
 extern const char kCSSCustomStateDeprecatedSyntaxEnabled[] =
     "css-custom-state-deprecated-syntax-enabled";
 
-// Used to communicate managed policy for SelectParserRelaxation. This feature
-// is typically controlled by a RuntimeEnabledFeature, but requires an
-// enterprise policy override.
-extern const char kDisableSelectParserRelaxation[] =
-    "disable-select-parser-relaxation";
-
 // Sets the timeout seconds of the network-quiet timers in IdlenessDetector.
 // Used by embedders who want to change the timeout time in order to run web
 // contents on various embedded devices and changeable network bandwidths in
diff --git a/third_party/blink/public/common/messaging/DEPS b/third_party/blink/public/common/messaging/DEPS
index 0e55736f56..8c0eda17e 100644
--- a/third_party/blink/public/common/messaging/DEPS
+++ b/third_party/blink/public/common/messaging/DEPS
@@ -1,3 +1,5 @@
 include_rules = [
+"+components/viz/common/resources/shared_image_format.h",
 "+components/viz/common/resources/shared_image_format_utils.h",
+"+gpu/command_buffer/common/sync_token.h",
 ]
diff --git a/third_party/blink/public/common/messaging/accelerated_image_info.h b/third_party/blink/public/common/messaging/accelerated_image_info.h
index 08cdbbba..d22928f57 100644
--- a/third_party/blink/public/common/messaging/accelerated_image_info.h
+++ b/third_party/blink/public/common/messaging/accelerated_image_info.h
@@ -6,12 +6,13 @@
 #define THIRD_PARTY_BLINK_PUBLIC_COMMON_MESSAGING_ACCELERATED_IMAGE_INFO_H_
 
 #include "base/functional/callback.h"
+#include "components/viz/common/resources/shared_image_format.h"
 #include "gpu/command_buffer/client/client_shared_image.h"
-#include "gpu/command_buffer/common/mailbox_holder.h"
-#include "gpu/command_buffer/common/shared_image_usage.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "gpu/command_buffer/common/sync_token.h"
 #include "third_party/blink/public/common/common_export.h"
-#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkAlphaType.h"
+#include "ui/gfx/color_space.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace blink {
 
diff --git a/third_party/blink/public/common/switches.h b/third_party/blink/public/common/switches.h
index 26776a7..ec821cc 100644
--- a/third_party/blink/public/common/switches.h
+++ b/third_party/blink/public/common/switches.h
@@ -45,7 +45,6 @@
 BLINK_COMMON_EXPORT extern const char kMaxUntiledLayerWidth[];
 BLINK_COMMON_EXPORT extern const char kDisableBlobUrlPartitioning[];
 BLINK_COMMON_EXPORT extern const char kCSSCustomStateDeprecatedSyntaxEnabled[];
-BLINK_COMMON_EXPORT extern const char kDisableSelectParserRelaxation[];
 BLINK_COMMON_EXPORT extern const char kMinHeightForGpuRasterTile[];
 BLINK_COMMON_EXPORT extern const char kNetworkQuietTimeout[];
 BLINK_COMMON_EXPORT extern const char kSharedArrayBufferAllowedOrigins[];
diff --git a/third_party/blink/public/mojom/content_extraction/ai_page_content.mojom b/third_party/blink/public/mojom/content_extraction/ai_page_content.mojom
index 2159883..86ccc75 100644
--- a/third_party/blink/public/mojom/content_extraction/ai_page_content.mojom
+++ b/third_party/blink/public/mojom/content_extraction/ai_page_content.mojom
@@ -116,6 +116,31 @@
   AIPageContentSelection? selection;
 };
 
+// Provides the reasons which make an element clickable.
+enum AIPageContentClickabilityReason {
+  // The node is a native form control.
+  kClickableControl,
+
+  // The node has a click event handler.
+  kClickEvents,
+
+  // The node has mouse event handlers like mouseup, mousedown, mouseover and
+  // mouseenter.
+  kMouseEvents,
+
+  // The node has keyboard event handlers like keydown, keypress, keyup.
+  kKeyEvents,
+
+  // The node's text can be edited.
+  kEditable,
+
+  // The node uses cursor css to indicate the element is interactive.
+  kCursorPointer,
+
+  // The node has a clickable aria role.
+  kAriaRole
+};
+
 struct AIPageContentNodeInteractionInfo {
   // If this node supports scrolling, provides information about its current
   // state.
@@ -155,6 +180,11 @@
   //
   // Note: This value is only set for nodes in the viewport.
   int32? document_scoped_z_order;
+
+  // If this node is detected to be is_clickable, provides the reason which
+  // caused that.
+  // Note: This may not capture all the reasons.
+  array<AIPageContentClickabilityReason> debug_clickability_reasons;
 };
 
 // The coordinate space for the dimensions here is physical coordinates (not
diff --git a/third_party/blink/public/mojom/on_device_translation/translation_manager.mojom b/third_party/blink/public/mojom/on_device_translation/translation_manager.mojom
index 44cb7b66..455ac51 100644
--- a/third_party/blink/public/mojom/on_device_translation/translation_manager.mojom
+++ b/third_party/blink/public/mojom/on_device_translation/translation_manager.mojom
@@ -38,23 +38,16 @@
   // language pair is not supported.
   kNoNotSupportedLanguage = 5,
 
-  // The translator cannot be created because the Accept-Language check failed.
-  kNoAcceptLanguagesCheckFailed = 6,
-
-  // The translator cannot be created. New models need to be downloaded,
-  // but the number of downloaded language packs will exceedd the limit.
-  kNoExceedsLanguagePackCountLimitation = 7,
-
   // The translator cannot be created, because the translator service crashed.
-  kNoServiceCrashed = 8,
+  kNoServiceCrashed = 6,
 
   // The translator cannot be created, because the use of Translator API is
   // disallowed by `TranslatorAPIAllowed` Enterprise policy
-  kNoDisallowedByPolicy = 9,
+  kNoDisallowedByPolicy = 7,
 
   // The translator cannot be created, because the number of services exceeds
   // the limitation.
-  kNoExceedsServiceCountLimitation = 10,
+  kNoExceedsServiceCountLimitation = 8,
 };
 
 // The error of TranslationManager's CreateTranslator IPC.
@@ -74,30 +67,23 @@
   // translator.
   kFailedToCreateTranslator = 4,
 
-  // The translator cannot be created because the Accept-Language check failed.
-  kAcceptLanguagesCheckFailed = 5,
-
-  // The translator cannot be created. New models need to be downloaded,
-  // but the number of downloaded language packs will exceedd the limit.
-  kExceedsLanguagePackCountLimitation = 6,
-
   // The translator cannot be created, because the translator service crashed.
-  kServiceCrashed = 7,
+  kServiceCrashed = 5,
 
   // The translator cannot be created, because the use of Translator API is
   // disallowed by `TranslatorAPIAllowed` Enterprise policy
-  kDisallowedByPolicy = 8,
+  kDisallowedByPolicy = 6,
 
   // The translator cannot be created, because the number of services exceeds
   // the limitation.
-  kExceedsServiceCountLimitation = 9,
+  kExceedsServiceCountLimitation = 7,
 
   // The translator cannot be created, because the number of pending tasks
   // exceeds the limitation.
-  kExceedsPendingTaskCountLimitation = 10,
+  kExceedsPendingTaskCountLimitation =  8,
 
   // The translator cannot be created because the library version is invalid.
-  kInvalidVersion = 11,
+  kInvalidVersion = 9,
 };
 
 // The result of TranslationManager's CreateTranslator IPC.
diff --git a/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h b/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h
index f96781a04..9b2dc42 100644
--- a/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h
+++ b/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h
@@ -113,6 +113,9 @@
       WebServiceWorkerContextProxy*,
       scoped_refptr<base::SequencedTaskRunner> worker_task_runner) {}
 
+  // Called before preparing v8 script engine for evaluation.
+  virtual void WillPrepareForEvaluation(v8::Local<v8::Context> v8_context) {}
+
   // Called immediately before V8 script evaluation starts for the main script.
   // This means all setup is finally complete: the script has been loaded, the
   // worker thread has started, the script has been passed to the worker thread,
diff --git a/third_party/blink/public/web/web_v8_features.h b/third_party/blink/public/web/web_v8_features.h
index 6e540fe..f735897 100644
--- a/third_party/blink/public/web/web_v8_features.h
+++ b/third_party/blink/public/web/web_v8_features.h
@@ -58,6 +58,9 @@
   // Send isolate priority change notification to worker thread isolates.
   static void SetIsolatePriority(base::Process::Priority priority);
 
+  // Whether `WebV8Features` is supported for the given v8 context.
+  static bool IsSupported(v8::Local<v8::Context>);
+
  private:
   WebV8Features() = delete;
 };
diff --git a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
index a0998ff9..e89caff 100644
--- a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
@@ -54,6 +54,7 @@
   (property.valid_for_position_try and 'kValidForPositionTry' or ''),
   (property.valid_for_page_context and 'kValidForPageContext' or ''),
   (property.valid_for_permission_element and 'kValidForPermissionElement' or ''),
+  (property.valid_for_permission_icon and 'kValidForPermissionIcon' or ''),
   (property.valid_for_visited and 'kValidForVisited' or ''),
   (is_surrogate and 'kSurrogate' or ''),
   (property.font and 'kAffectsFont' or ''),
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index c2eabe4..9b26546 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -803,6 +803,13 @@
       valid_type: "bool",
     },
 
+    // Whether the property can be applied to ::permission-icon elements.
+    // See https://github.com/WICG/PEPC/blob/main/explainer.md#locking-the-pepc-style
+    valid_for_permission_icon: {
+      default: false,
+      valid_type: "bool",
+    },
+
     // - valid_for_position_try: true
     //
     // Whether the property can be used in a @position-try rule
@@ -3165,6 +3172,7 @@
       // so we turn off incremental style for all them all.
       supports_incremental_style: false,
       valid_for_permission_element: true,
+      valid_for_permission_icon: true,
       invalidate: ["layout", "paint"],
     },
     {
@@ -3211,6 +3219,7 @@
       style_builder_custom_functions: ["value"],
       valid_for_highlight_legacy: true,
       valid_for_highlight: true,
+      valid_for_permission_icon: true,
       invalidate: ["paint"],
     },
     {
@@ -3534,6 +3543,7 @@
       supports_incremental_style: true,
       valid_for_position_try: true,
       valid_for_permission_element: true,
+      valid_for_permission_icon: true,
       valid_for_page_context: true,
       invalidate: ["layout", "scroll-anchor"],
       affected_by_zoom: true,
@@ -3896,6 +3906,7 @@
       },
       supports_incremental_style: true,
       valid_for_permission_element: true,
+      valid_for_permission_icon: true,
       valid_for_page_context: true,
       invalidate: ["margin", "out-of-flow", "scroll-anchor"],
       anchor_mode: "width",
@@ -3920,6 +3931,7 @@
       },
       supports_incremental_style: true,
       valid_for_permission_element: true,
+      valid_for_permission_icon: true,
       valid_for_page_context: true,
       invalidate: ["margin", "out-of-flow", "scroll-anchor"],
       anchor_mode: "width",
@@ -4140,6 +4152,7 @@
       supports_incremental_style: true,
       valid_for_position_try: true,
       valid_for_permission_element: true,
+      valid_for_permission_icon: true,
       valid_for_page_context: true,
       invalidate: ["layout", "scroll-anchor"],
       affected_by_zoom: true,
@@ -4162,6 +4175,7 @@
       supports_incremental_style: true,
       valid_for_position_try: true,
       valid_for_permission_element: true,
+      valid_for_permission_icon: true,
       valid_for_page_context: true,
       invalidate: ["layout", "scroll-anchor"],
       affected_by_zoom: true,
@@ -4183,6 +4197,7 @@
       supports_incremental_style: true,
       valid_for_position_try: true,
       valid_for_permission_element: true,
+      valid_for_permission_icon: true,
       valid_for_page_context: true,
       invalidate: ["layout", "scroll-anchor"],
       affected_by_zoom: true,
@@ -4204,6 +4219,7 @@
       supports_incremental_style: true,
       valid_for_position_try: true,
       valid_for_permission_element: true,
+      valid_for_permission_icon: true,
       valid_for_page_context: true,
       invalidate: ["layout", "scroll-anchor"],
       affected_by_zoom: true,
@@ -5512,6 +5528,7 @@
       style_builder_custom_functions: ["value"],
       valid_for_highlight_legacy: true,
       valid_for_highlight: true,
+      valid_for_permission_icon: true,
       invalidate: ["paint", "stroke"],
     },
     {
@@ -5616,6 +5633,7 @@
       typedom_types: ["Length", "Percentage"],
       valid_for_highlight_legacy: true,
       valid_for_highlight: true,
+      valid_for_permission_icon: true,
       invalidate: ["layout", "paint"],
       affected_by_zoom: true,
     },
@@ -7282,6 +7300,7 @@
       valid_for_position_try: true,
       affected_by_zoom: true,
       valid_for_permission_element: true,
+      valid_for_permission_icon: true,
       valid_for_page_context: true,
       invalidate: ["layout", "scroll-anchor"],
     },
@@ -7447,6 +7466,7 @@
       valid_for_first_letter: true,
       valid_for_position_try: true,
       valid_for_permission_element: true,
+      valid_for_permission_icon: true,
       valid_for_page_context: true,
     },
     {
@@ -8578,6 +8598,7 @@
       valid_for_position_try: true,
       valid_for_permission_element: true,
       valid_for_page_context: true,
+      valid_for_permission_icon: true,
     },
     {
       name: "margin-block",
diff --git a/third_party/blink/renderer/core/css/properties/css_property.h b/third_party/blink/renderer/core/css/properties/css_property.h
index 9d7e9e9..3a02c205 100644
--- a/third_party/blink/renderer/core/css/properties/css_property.h
+++ b/third_party/blink/renderer/core/css/properties/css_property.h
@@ -277,6 +277,8 @@
     // 1ull << 35 is taken by kNotLegacyOverlapping above.
     // Whether this property is valid in a :visited selector.
     kValidForVisited = 1ull << 36,
+    // See valid_for_permission_icon in css_properties.json5
+    kValidForPermissionIcon = 1ull << 37,
   };
 
   constexpr CSSProperty(CSSPropertyID property_id,
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 0058150..d573397 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -3151,7 +3151,11 @@
   // https://dom.spec.whatwg.org/#dom-element-toggleattribute
   // 1. If qualifiedName does not match the Name production in XML, then throw
   // an "InvalidCharacterError" DOMException.
-  if (!Document::IsValidName(qualified_name)) {
+  bool is_valid =
+      RuntimeEnabledFeatures::RelaxDOMValidNamesEnabled()
+          ? Document::IsValidAttributeLocalNameNewSpec(qualified_name)
+          : Document::IsValidName(qualified_name);
+  if (!is_valid) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidCharacterError,
         "'" + qualified_name + "' is not a valid attribute name.");
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 0ddee90..3c142b5 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -1733,6 +1733,8 @@
   GCedHeapVector<Member<Element>>* ElementsFromAttributeOrInternals(
       const QualifiedName& attribute) const;
 
+  bool IsClickableControl() { return IsClickableControl(this); }
+
  protected:
   bool HasElementData() const { return static_cast<bool>(element_data_); }
   const ElementData* GetElementData() const { return element_data_.Get(); }
diff --git a/third_party/blink/renderer/core/exported/web_v8_features.cc b/third_party/blink/renderer/core/exported/web_v8_features.cc
index 160cec84..1ce89f4 100644
--- a/third_party/blink/renderer/core/exported/web_v8_features.cc
+++ b/third_party/blink/renderer/core/exported/web_v8_features.cc
@@ -43,7 +43,8 @@
   }
   v8::Isolate* isolate = context->GetIsolate();
   ScriptState* script_state = ScriptState::From(isolate, context);
-  DCHECK(script_state->World().IsMainWorld());
+  DCHECK(script_state->World().IsMainWorld() ||
+         script_state->World().IsWorkerOrWorkletWorld());
   ContextFeatureSettings::From(
       ExecutionContext::From(script_state),
       ContextFeatureSettings::CreationMode::kCreateIfNotExists)
@@ -138,4 +139,9 @@
   WorkerBackingThread::SetWorkerThreadIsolatesPriority(isolate_priority);
 }
 
+// static
+bool WebV8Features::IsSupported(v8::Local<v8::Context> context) {
+  return blink::ExecutionContext::From(context);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
index 6003a753..4725f68 100644
--- a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
+++ b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
@@ -345,35 +345,31 @@
 
   data.Append(ChooserResourceLoader::GetPickerCommonStyleSheet());
   data.Append(ChooserResourceLoader::GetListPickerStyleSheet());
-  if (taller_options_) {
-    int padding = static_cast<int>(roundf(4 * scale_factor));
-    int min_height = static_cast<int>(roundf(24 * scale_factor));
-    PagePopupClient::AddString(String::Format("option, optgroup {"
-                                              "padding-top: %dpx;"
-                                              "}\n"
-                                              "option {"
-                                              "padding-bottom: %dpx;"
-                                              "min-block-size: %dpx;"
-                                              "display: flex;"
-                                              "align-items: center;"
-                                              "}\n",
-                                              padding, padding, min_height),
-                               data);
-    // Sets the min target size of <option> to 24x24 CSS pixels to meet
-    // Accessibility standards.
-    if (RuntimeEnabledFeatures::SelectOptionAccessibilityTargetSizeEnabled()) {
-      PagePopupClient::AddString(
-          String::Format("option {"
-                         "display: block;"
-                         "align-content: center;"
-                         "min-inline-size: %dpx;"
-                         "min-block-size: %dpx;"
-                         "box-sizing: border-box;"
-                         "}\n",
-                         min_height, std::max(24, min_height)),
-          data);
-    }
-  }
+  int padding = static_cast<int>(roundf(4 * scale_factor));
+  int min_height = static_cast<int>(roundf(24 * scale_factor));
+  PagePopupClient::AddString(String::Format("option, optgroup {"
+                                            "padding-top: %dpx;"
+                                            "}\n"
+                                            "option {"
+                                            "padding-bottom: %dpx;"
+                                            "min-block-size: %dpx;"
+                                            "display: flex;"
+                                            "align-items: center;"
+                                            "}\n",
+                                            padding, padding, min_height),
+                             data);
+  // Sets the min target size of <option> to 24x24 CSS pixels to meet
+  // Accessibility standards.
+  PagePopupClient::AddString(
+      String::Format("option {"
+                     "display: block;"
+                     "align-content: center;"
+                     "min-inline-size: %dpx;"
+                     "min-block-size: %dpx;"
+                     "box-sizing: border-box;"
+                     "}\n",
+                     min_height, std::max(24, min_height)),
+      data);
 
   PagePopupClient::AddString(
       "</style></head><body><div id=main>Loading...</div><script>\n"
@@ -669,10 +665,6 @@
 
 void InternalPopupMenu::Show(PopupMenu::ShowEventType type) {
   DCHECK(!popup_);
-  taller_options_ =
-      type == PopupMenu::kTouch ||
-      RuntimeEnabledFeatures::ForceTallerSelectPopupEnabled() ||
-      RuntimeEnabledFeatures::SelectOptionAccessibilityTargetSizeEnabled();
   popup_ = chrome_client_->OpenPagePopup(this);
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/internal_popup_menu.h b/third_party/blink/renderer/core/html/forms/internal_popup_menu.h
index 3d28cc6..15a5b7c 100644
--- a/third_party/blink/renderer/core/html/forms/internal_popup_menu.h
+++ b/third_party/blink/renderer/core/html/forms/internal_popup_menu.h
@@ -77,7 +77,6 @@
   Member<HTMLSelectElement> owner_element_;
   PagePopup* popup_;
   bool needs_update_;
-  bool taller_options_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_permission_element.cc b/third_party/blink/renderer/core/html/html_permission_element.cc
index b1969dc8..762e1cb 100644
--- a/third_party/blink/renderer/core/html/html_permission_element.cc
+++ b/third_party/blink/renderer/core/html/html_permission_element.cc
@@ -445,6 +445,7 @@
 void HTMLPermissionElement::Trace(Visitor* visitor) const {
   visitor->Trace(permission_service_);
   visitor->Trace(embedded_permission_control_receiver_);
+  visitor->Trace(permission_container_);
   visitor->Trace(permission_text_span_);
   visitor->Trace(permission_internal_icon_);
   visitor->Trace(intersection_observer_);
@@ -782,16 +783,20 @@
 }
 
 void HTMLPermissionElement::DidAddUserAgentShadowRoot(ShadowRoot& root) {
+  permission_container_ = MakeGarbageCollected<HTMLDivElement>(GetDocument());
+  permission_container_->SetShadowPseudoId(
+      shadow_element_names::kPseudoInternalPermissionContainer);
+  root.AppendChild(permission_container_);
   if (RuntimeEnabledFeatures::PermissionElementIconEnabled(
           GetDocument().GetExecutionContext())) {
     permission_internal_icon_ =
         MakeGarbageCollected<HTMLPermissionIconElement>(GetDocument());
-    root.AppendChild(permission_internal_icon_);
+    permission_container_->AppendChild(permission_internal_icon_);
   }
   permission_text_span_ = MakeGarbageCollected<HTMLSpanElement>(GetDocument());
   permission_text_span_->SetShadowPseudoId(
       shadow_element_names::kPseudoInternalPermissionTextSpan);
-  root.AppendChild(permission_text_span_);
+  permission_container_->AppendChild(permission_text_span_);
 }
 
 void HTMLPermissionElement::AdjustStyle(ComputedStyleBuilder& builder) {
@@ -1728,11 +1733,7 @@
   // time.
   UserAgentShadowRoot()->AppendChild(
       MakeGarbageCollected<HTMLSlotElement>(GetDocument()));
-  UserAgentShadowRoot()->RemoveChild(permission_text_span_);
-  if (RuntimeEnabledFeatures::PermissionElementIconEnabled(
-          GetDocument().GetExecutionContext())) {
-    UserAgentShadowRoot()->RemoveChild(permission_internal_icon_);
-  }
+  UserAgentShadowRoot()->RemoveChild(permission_container_);
   MaybeDispatchValidationChangeEvent();
 }
 
diff --git a/third_party/blink/renderer/core/html/html_permission_element.h b/third_party/blink/renderer/core/html/html_permission_element.h
index 7341cd1..b25a5e41 100644
--- a/third_party/blink/renderer/core/html/html_permission_element.h
+++ b/third_party/blink/renderer/core/html/html_permission_element.h
@@ -17,6 +17,7 @@
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/frame/cached_permission_status.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/html/html_div_element.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/html/html_permission_icon_element.h"
 #include "third_party/blink/renderer/core/intersection_observer/intersection_observer.h"
@@ -535,6 +536,8 @@
   // |base::TimeTicks::Max()| if it's indefinite.
   HashMap<DisableReason, base::TimeTicks> clicking_disabled_reasons_;
 
+  // A element which contains the internal permission elements(text and icon).
+  Member<HTMLDivElement> permission_container_;
   Member<HTMLSpanElement> permission_text_span_;
   Member<HTMLPermissionIconElement> permission_internal_icon_;
   Member<IntersectionObserver> intersection_observer_;
diff --git a/third_party/blink/renderer/core/html/html_permission_icon_element.h b/third_party/blink/renderer/core/html/html_permission_icon_element.h
index f7f392a..8c8a300 100644
--- a/third_party/blink/renderer/core/html/html_permission_icon_element.h
+++ b/third_party/blink/renderer/core/html/html_permission_icon_element.h
@@ -15,6 +15,10 @@
  public:
   explicit HTMLPermissionIconElement(Document&);
 
+  CascadeFilter GetCascadeFilter() const override {
+    // Reject all properties for which 'kValidForPermissionIcon' is false.
+    return CascadeFilter(CSSProperty::kValidForPermissionIcon);
+  }
   void SetIcon(mojom::blink::PermissionName permission_type,
                bool is_precise_location);
 
diff --git a/third_party/blink/renderer/core/html/resources/images/permission_icon_location_precise.svg b/third_party/blink/renderer/core/html/resources/images/permission_icon_location_precise.svg
index 15123c9..bdcae760 100644
--- a/third_party/blink/renderer/core/html/resources/images/permission_icon_location_precise.svg
+++ b/third_party/blink/renderer/core/html/resources/images/permission_icon_location_precise.svg
@@ -1,3 +1,3 @@
 <svg xmlns="http://www.w3.org/2000/svg" height="100%" viewBox="0 -960 960 960" width="100%">
-  <path d="M440-42v-80q-125-14-214.5-103.5T122-440H42v-80h80q14-125 103.5-214.5T440-838v-80h80v80q125 14 214.5 103.5T838-520h80v80h-80q-14 125-103.5 214.5T520-122v80h-80Zm40-158q116 0 198-82t82-198q0-116-82-198t-198-82q-116 0-198 82t-82 198q0 116 82 198t198 82Zm0-120q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47Zm0-80q33 0 56.5-23.5T560-480q0-33-23.5-56.5T480-560q-33 0-56.5 23.5T400-480q0 33 23.5 56.5T480-400Zm0-80Z"/>
+  <path d="M440-42v-80q-125-14-214.5-103.5T122-440H42v-80h80q14-125 103.5-214.5T440-838v-80h80v80q125 14 214.5 103.5T838-520h80v80h-80q-14 125-103.5 214.5T520-122v80h-80Zm40-158q116 0 198-82t82-198q0-116-82-198t-198-82q-116 0-198 82t-82 198q0 116 82 198t198 82Zm0-120q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47Z"/>
 </svg>
diff --git a/third_party/blink/renderer/core/html/resources/permission.css b/third_party/blink/renderer/core/html/resources/permission.css
index 40c9121..81b2431f 100644
--- a/third_party/blink/renderer/core/html/resources/permission.css
+++ b/third_party/blink/renderer/core/html/resources/permission.css
@@ -36,24 +36,36 @@
   font-size: inherit;
   font-style: inherit;
   font-weight: inherit;
-  height: 100%;
+  height: fit-content;
+  width: fit-content;
   hyphenate-character: auto;
   line-height: normal;
-  margin: auto;
   min-height: inherit;
   max-height: inherit;
   outline: none;
   user-select:none;
   white-space:nowrap;
-  width: fit-content;
   word-wrap: normal;
   word-spacing: inherit;
 }
 
-::permission-icon {
+permission::-internal-permission-container {
   display: flex;
+  flex-flow: row nowrap;
+  justify-content: center;
+  align-items: center;
+  height: 100%;
+  width: fit-content;
+  min-height: inherit;
+  max-height: inherit;
+  margin: auto;
+}
+
+::permission-icon {
   width: 1.3em;
   height: 1.3em;
-  margin: auto;
-  float: inline-start;
+  margin-inline-end: 5px;
+  margin-block-start: 0px !important;
+  margin-block-end: 0px !important;
+  margin-inline-start: 0px !important;
 }
diff --git a/third_party/blink/renderer/core/html/shadow/shadow_element_names.json5 b/third_party/blink/renderer/core/html/shadow/shadow_element_names.json5
index 3b14b04..00ebddaf 100644
--- a/third_party/blink/renderer/core/html/shadow/shadow_element_names.json5
+++ b/third_party/blink/renderer/core/html/shadow/shadow_element_names.json5
@@ -175,6 +175,10 @@
       Symbol: "kPseudoInternalPermissionTextSpan",
     },
     {
+      name: "-internal-permission-container",
+      Symbol: "kPseudoInternalPermissionContainer",
+    },
+    {
       name: "-internal-optgroup-label",
       Symbol: "kIdOptGroupLabel",
     },
diff --git a/third_party/blink/renderer/core/layout/length_utils.cc b/third_party/blink/renderer/core/layout/length_utils.cc
index e76525b..d6bc725 100644
--- a/third_party/blink/renderer/core/layout/length_utils.cc
+++ b/third_party/blink/renderer/core/layout/length_utils.cc
@@ -153,6 +153,11 @@
               (available_size - margins.InlineSum()).ClampNegativeToZero());
     }
     case Length::kAuto:
+      if (length_type == LengthTypeInternal::kMin &&
+          RuntimeEnabledFeatures::LayoutMinSizeAutoIndefiniteEnabled()) {
+        return border_padding.InlineSum();
+      }
+      [[fallthrough]];
     case Length::kNone:
       return kIndefiniteSize;
     case Length::kFlex:
@@ -278,6 +283,11 @@
       return intrinsic_size;
     }
     case Length::kAuto:
+      if (length_type == LengthTypeInternal::kMin &&
+          RuntimeEnabledFeatures::LayoutMinSizeAutoIndefiniteEnabled()) {
+        return border_padding.BlockSum();
+      }
+      [[fallthrough]];
     case Length::kNone:
       return kIndefiniteSize;
     case Length::kFlex:
diff --git a/third_party/blink/renderer/core/navigation_api/navigation_api.cc b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
index 06a234f..d6ec04a 100644
--- a/third_party/blink/renderer/core/navigation_api/navigation_api.cc
+++ b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
@@ -811,16 +811,13 @@
   init->setUserInitiated(params->involvement !=
                          UserNavigationInvolvement::kNone);
   if (params->source_element) {
+    auto* control =
+        DynamicTo<HTMLFormControlElement>(params->source_element.Get());
     HTMLFormElement* form =
-        DynamicTo<HTMLFormElement>(params->source_element.Get());
-    if (!form) {
-      if (auto* control =
-              DynamicTo<HTMLFormControlElement>(params->source_element.Get())) {
-        form = control->formOwner();
-      }
-    }
+        control ? control->formOwner()
+                : DynamicTo<HTMLFormElement>(params->source_element.Get());
     if (form && form->Method() == FormSubmission::kPostMethod) {
-      init->setFormData(FormData::Create(form, ASSERT_NO_EXCEPTION));
+      init->setFormData(FormData::Create(form, control, ASSERT_NO_EXCEPTION));
     }
   }
   if (ongoing_api_method_tracker_) {
diff --git a/third_party/blink/renderer/core/paint/box_fragment_painter.cc b/third_party/blink/renderer/core/paint/box_fragment_painter.cc
index 783682a..46812fb 100644
--- a/third_party/blink/renderer/core/paint/box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/box_fragment_painter.cc
@@ -1378,8 +1378,6 @@
         paint_info, paint_offset, layout_box, box_fragment_.GetFragmentData());
     paint_rect.Move(contents_paint_state_for_hidden->PaintOffset());
 
-    background_client = &layout_box.GetScrollableArea()
-                             ->GetScrollingBackgroundDisplayItemClient();
     visual_rect = layout_box.GetScrollableArea()->ScrollingBackgroundVisualRect(
         paint_offset);
   } else {
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
index 0580966..123d1e46 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
@@ -276,6 +276,9 @@
   InitContentSecurityPolicyFromVector(std::move(csp_list));
   BindContentSecurityPolicyToExecutionContext();
 
+  // Allows `ContextFeatureSettings` to update before preparing script engine.
+  ReportingProxy().WillPrepareForEvaluation();
+
   // This should be called after OriginTrialContext::AddTokens() to install
   // origin trial features in JavaScript's global object.
   // DedicatedWorkerGlobalScope inherits the outside's OriginTrialTokens in the
diff --git a/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc b/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
index 905f866..fc26ac9a 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/shared_worker_global_scope.cc
@@ -113,6 +113,9 @@
 
   OriginTrialContext::AddTokens(this, response_origin_trial_tokens);
 
+  // Allows `ContextFeatureSettings` to update before preparing script engine.
+  ReportingProxy().WillPrepareForEvaluation();
+
   // This should be called after OriginTrialContext::AddTokens() to install
   // origin trial features in JavaScript's global object.
   ScriptController()->PrepareForEvaluation();
diff --git a/third_party/blink/renderer/core/workers/worker_reporting_proxy.h b/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
index 54c24e6..0c14796 100644
--- a/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
+++ b/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
@@ -85,6 +85,9 @@
   // or InstalledScriptsManager).
   virtual void DidFailToFetchModuleScript() {}
 
+  // Invoked before preparing script engine for evaluation.
+  virtual void WillPrepareForEvaluation() {}
+
   // Invoked when the main classic/module script is about to be evaluated.
   virtual void WillEvaluateScript() {}
 
diff --git a/third_party/blink/renderer/modules/ai/ai_metrics.cc b/third_party/blink/renderer/modules/ai/ai_metrics.cc
index ea2052e..a51f01f 100644
--- a/third_party/blink/renderer/modules/ai/ai_metrics.cc
+++ b/third_party/blink/renderer/modules/ai/ai_metrics.cc
@@ -72,4 +72,52 @@
   return base::StrCat({"AI.Session.", GetAISessionTypeName(session_type),
                        ".PromptResponseCallbackCount"});
 }
+
+// static
+AIMetrics::LanguageModelInputType AIMetrics::ToLanguageModelInputType(
+    mojom::blink::AILanguageModelPromptContent::Tag type) {
+  using MojoEnum = mojom::blink::AILanguageModelPromptContent::Tag;
+  using MetricsEnum = AIMetrics::LanguageModelInputType;
+  switch (type) {
+    case MojoEnum::kText:
+      return MetricsEnum::kText;
+    case MojoEnum::kBitmap:
+      return MetricsEnum::kImage;
+    case MojoEnum::kAudio:
+      return MetricsEnum::kAudio;
+  }
+  NOTREACHED();
+}
+
+// static
+AIMetrics::LanguageModelInputType AIMetrics::ToLanguageModelInputType(
+    V8LanguageModelMessageType::Enum type) {
+  using V8Enum = V8LanguageModelMessageType::Enum;
+  using MetricsEnum = AIMetrics::LanguageModelInputType;
+  switch (type) {
+    case V8Enum::kText:
+      return MetricsEnum::kText;
+    case V8Enum::kImage:
+      return MetricsEnum::kImage;
+    case V8Enum::kAudio:
+      return MetricsEnum::kAudio;
+  }
+  NOTREACHED();
+}
+
+// static
+AIMetrics::LanguageModelInputRole AIMetrics::ToLanguageModelInputRole(
+    mojom::blink::AILanguageModelPromptRole role) {
+  using MojoEnum = mojom::blink::AILanguageModelPromptRole;
+  using MetricsEnum = AIMetrics::LanguageModelInputRole;
+  switch (role) {
+    case MojoEnum::kSystem:
+      return MetricsEnum::kSystem;
+    case MojoEnum::kUser:
+      return MetricsEnum::kUser;
+    case MojoEnum::kAssistant:
+      return MetricsEnum::kAssistant;
+  }
+  NOTREACHED();
+}
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/ai/ai_metrics.h b/third_party/blink/renderer/modules/ai/ai_metrics.h
index 6d93a65..50b8bb8 100644
--- a/third_party/blink/renderer/modules/ai/ai_metrics.h
+++ b/third_party/blink/renderer/modules/ai/ai_metrics.h
@@ -5,8 +5,12 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_AI_AI_METRICS_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_AI_AI_METRICS_H_
 
+#include <optional>
 #include <string>
 
+#include "third_party/blink/public/mojom/ai/ai_language_model.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_language_model_message_content.h"
+
 namespace blink {
 
 class AIMetrics {
@@ -59,6 +63,24 @@
   };
   // LINT.ThenChange(//tools/metrics/histograms/metadata/ai/enums.xml:AIAPI)
 
+  // LINT.IfChange(LanguageModelInputType)
+  enum class LanguageModelInputType {
+    kText = 0,
+    kImage = 1,
+    kAudio = 2,
+    kMaxValue = kAudio,
+  };
+  // LINT.ThenChange(//tools/metrics/histograms/metadata/ai/enums.xml:LanguageModelInputType)
+
+  // LINT.IfChange(LanguageModelInputRole)
+  enum class LanguageModelInputRole {
+    kSystem = 0,
+    kUser = 1,
+    kAssistant = 2,
+    kMaxValue = kAssistant,
+  };
+  // LINT.ThenChange(//tools/metrics/histograms/metadata/ai/enums.xml:LanguageModelInputRole)
+
   static std::string GetAIAPIUsageMetricName(AISessionType session_type);
   static std::string GetAvailabilityMetricName(AISessionType session_type);
   static std::string GetAISessionRequestSizeMetricName(
@@ -69,6 +91,15 @@
       AISessionType session_type);
   static std::string GetAISessionResponseCallbackCountMetricName(
       AISessionType session_type);
+
+  // Enum mappings from mojo/V8 enums to metric enums. Returns nullopt if the
+  // enum is not mapped.
+  static LanguageModelInputType ToLanguageModelInputType(
+      mojom::blink::AILanguageModelPromptContent::Tag type);
+  static LanguageModelInputType ToLanguageModelInputType(
+      V8LanguageModelMessageType::Enum type);
+  static LanguageModelInputRole ToLanguageModelInputRole(
+      mojom::blink::AILanguageModelPromptRole role);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/ai/availability.cc b/third_party/blink/renderer/modules/ai/availability.cc
index 6d63421..46b7632 100644
--- a/third_party/blink/renderer/modules/ai/availability.cc
+++ b/third_party/blink/renderer/modules/ai/availability.cc
@@ -89,9 +89,6 @@
           execution_context, AIMetrics::AISessionType::kTranslator,
           mojom::blink::ModelAvailabilityCheckResult::
               kUnavailableUnsupportedLanguage);
-    case mojom::blink::CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
-    case mojom::blink::CanCreateTranslatorResult::
-        kNoExceedsLanguagePackCountLimitation:
     case mojom::blink::CanCreateTranslatorResult::kNoServiceCrashed:
     case mojom::blink::CanCreateTranslatorResult::kNoDisallowedByPolicy:
     case mojom::blink::CanCreateTranslatorResult::
diff --git a/third_party/blink/renderer/modules/ai/language_model.cc b/third_party/blink/renderer/modules/ai/language_model.cc
index 0ac91724..7f8ca41 100644
--- a/third_party/blink/renderer/modules/ai/language_model.cc
+++ b/third_party/blink/renderer/modules/ai/language_model.cc
@@ -8,6 +8,7 @@
 #include "base/containers/span.h"
 #include "base/functional/callback_forward.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/metrics/metrics_hashes.h"
 #include "base/notreached.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/types/expected_macros.h"
@@ -61,6 +62,77 @@
   resolver->Reject(value);
 }
 
+// Logs (mojo converted) prompt message metrics.
+void LogPromptMessageMetrics(
+    const WTF::Vector<mojom::blink::AILanguageModelPromptPtr>& prompts) {
+  for (const auto& prompt : prompts) {
+    std::string prefix =
+        base::StrCat({AIMetrics::GetAIAPIUsageMetricName(
+                          AIMetrics::AISessionType::kLanguageModel),
+                      ".Messages"});
+    base::UmaHistogramEnumeration(
+        base::StrCat({prefix, ".Role"}),
+        AIMetrics::ToLanguageModelInputRole(prompt->role));
+    for (const auto& content : prompt->content) {
+      base::UmaHistogramEnumeration(
+          base::StrCat({prefix, ".Type"}),
+          AIMetrics::ToLanguageModelInputType(content->which()));
+      if (content->is_text()) {
+        base::UmaHistogramCounts1M(
+            base::StrCat({prefix, ".Text.Length"}),
+            static_cast<int>(content->get_text().length()));
+      }
+    }
+  }
+}
+
+// Logs create option metrics.
+void LogCreateOptionMetrics(
+    const LanguageModelCreateCoreOptions& create_options,
+    const std::string& function_name) {
+  std::string prefix =
+      base::StrCat({AIMetrics::GetAIAPIUsageMetricName(
+                        AIMetrics::AISessionType::kLanguageModel),
+                    ".", function_name});
+  if (create_options.hasTopK()) {
+    base::UmaHistogramCounts1000(base::StrCat({prefix, ".TopK"}),
+                                 static_cast<int>(create_options.topK()));
+  }
+  if (create_options.hasTemperature()) {
+    // Temperature is generally in the range of [0.0,2.0].
+    base::UmaHistogramCustomCounts(
+        base::StrCat({prefix, ".TemperatureX1000"}),
+        static_cast<int>(create_options.temperature() * 1000.0f), 1, 2000, 200);
+  }
+  // Logs metrics for a list of expected inputs or outputs.
+  auto log_expected_metrics =
+      [&](const std::string& expected_type,
+          const HeapVector<Member<LanguageModelExpected>>& expected_inputs) {
+        for (const auto& input : expected_inputs) {
+          if (input->hasType()) {
+            base::UmaHistogramEnumeration(
+                base::StrCat({prefix, ".", expected_type, ".Type"}),
+                AIMetrics::ToLanguageModelInputType(input->type().AsEnum()));
+          }
+          for (const auto& lang : input->getLanguagesOr(Vector<String>())) {
+            base::UmaHistogramSparse(
+                base::StrCat({prefix, ".", expected_type, ".Language"}),
+                static_cast<base::HistogramBase::Sample32>(
+                    base::HashMetricName(lang.Ascii())));
+          }
+        }
+      };
+
+  // LINT.IfChange(LanguageModelExpectedInputOrOutput)
+  if (create_options.hasExpectedInputs()) {
+    log_expected_metrics("ExpectedInputs", create_options.expectedInputs());
+  }
+  if (create_options.hasExpectedOutputs()) {
+    log_expected_metrics("ExpectedOutputs", create_options.expectedOutputs());
+  }
+  // LINT.ThenChange(//tools/metrics/histograms/metadata/ai/histograms.xml:LanguageModelExpectedInputOrOutput)
+}
+
 class CloneLanguageModelClient
     : public GarbageCollected<CloneLanguageModelClient>,
       public mojom::blink::AIManagerCreateLanguageModelClient,
@@ -168,6 +240,7 @@
           complete_callback,
       base::RepeatingClosure overflow_callback,
       WTF::Vector<mojom::blink::AILanguageModelPromptPtr> input) {
+    LogPromptMessageMetrics(input);
     MakeGarbageCollected<AppendClient>(
         std::move(script_state), std::move(language_model), std::move(resolver),
         std::move(input), std::move(signal), std::move(complete_callback),
@@ -334,6 +407,7 @@
       MakeGarbageCollected<ScriptPromiseResolver<LanguageModel>>(script_state);
   auto promise = resolver->Promise();
 
+  LogCreateOptionMetrics(*options, "create");
   base::UmaHistogramEnumeration(AIMetrics::GetAIAPIUsageMetricName(
                                     AIMetrics::AISessionType::kLanguageModel),
                                 AIMetrics::AIAPI::kCreateSession);
@@ -370,6 +444,7 @@
       MakeGarbageCollected<ScriptPromiseResolver<V8Availability>>(script_state);
   auto promise = resolver->Promise();
 
+  LogCreateOptionMetrics(*options, "availability");
   base::UmaHistogramEnumeration(AIMetrics::GetAIAPIUsageMetricName(
                                     AIMetrics::AISessionType::kLanguageModel),
                                 AIMetrics::AIAPI::kCanCreateSession);
@@ -535,6 +610,7 @@
     mojo::PendingRemote<mojom::blink::ModelStreamingResponder>
         pending_responder,
     WTF::Vector<mojom::blink::AILanguageModelPromptPtr> prompts) {
+  LogPromptMessageMetrics(prompts);
   if (!language_model_remote_) {
     if (std::holds_alternative<ScriptPromiseResolverBase*>(
             resolver_or_stream)) {
diff --git a/third_party/blink/renderer/modules/ai/language_model_create_client.cc b/third_party/blink/renderer/modules/ai/language_model_create_client.cc
index d96c4a4..08e9f1b 100644
--- a/third_party/blink/renderer/modules/ai/language_model_create_client.cc
+++ b/third_party/blink/renderer/modules/ai/language_model_create_client.cc
@@ -83,7 +83,8 @@
     expected_in = ToMojoExpectations(options_->expectedInputs());
     for (const auto& expected : expected_in) {
       if (expected->type != mojom::blink::AILanguageModelPromptType::kText &&
-          !RuntimeEnabledFeatures::AIPromptAPIMultimodalInputEnabled()) {
+          !RuntimeEnabledFeatures::AIPromptAPIMultimodalInputEnabled(
+              GetExecutionContext())) {
         GetResolver()->Reject(DOMException::Create(
             kExceptionMessageUnableToCreateSession,
             DOMException::GetErrorName(DOMExceptionCode::kNotSupportedError)));
diff --git a/third_party/blink/renderer/modules/ai/language_model_create_options.idl b/third_party/blink/renderer/modules/ai/language_model_create_options.idl
index 462f0b3..897abc1 100644
--- a/third_party/blink/renderer/modules/ai/language_model_create_options.idl
+++ b/third_party/blink/renderer/modules/ai/language_model_create_options.idl
@@ -23,9 +23,13 @@
   required LanguageModelMessageValue value;
 };
 
+// LINT.IfChange
 enum LanguageModelMessageRole { "system", "user", "assistant" };
+// LINT.ThenChange(//third_party/blink/renderer/modules/ai/ai_metrics.h:LanguageModelInputRole)
 
+// LINT.IfChange
 enum LanguageModelMessageType { "text", "image", "audio" };
+// LINT.ThenChange(//third_party/blink/renderer/modules/ai/ai_metrics.h:LanguageModelInputType)
 
 typedef (
   ImageBitmapSource
diff --git a/third_party/blink/renderer/modules/ai/language_model_prompt_builder.cc b/third_party/blink/renderer/modules/ai/language_model_prompt_builder.cc
index b49ecc7..adc919f 100644
--- a/third_party/blink/renderer/modules/ai/language_model_prompt_builder.cc
+++ b/third_party/blink/renderer/modules/ai/language_model_prompt_builder.cc
@@ -252,7 +252,8 @@
       }
     }
     if (is_multimodal) {
-      if (!RuntimeEnabledFeatures::AIPromptAPIMultimodalInputEnabled()) {
+      if (!RuntimeEnabledFeatures::AIPromptAPIMultimodalInputEnabled(
+              ExecutionContext::From(script_state_))) {
         v8::Isolate* isolate = script_state_->GetIsolate();
         Reject(ScriptValue(isolate, V8ThrowException::CreateTypeError(
                                         isolate, "Input type not supported")));
diff --git a/third_party/blink/renderer/modules/ai/on_device_translation/create_translator_client.cc b/third_party/blink/renderer/modules/ai/on_device_translation/create_translator_client.cc
index 3b36977..ad43f45b 100644
--- a/third_party/blink/renderer/modules/ai/on_device_translation/create_translator_client.cc
+++ b/third_party/blink/renderer/modules/ai/on_device_translation/create_translator_client.cc
@@ -21,10 +21,6 @@
 
 const char kExceptionMessageUnableToCreateTranslator[] =
     "Unable to create translator for the given source and target language.";
-const char kLinkToDocument[] =
-    "See "
-    "https://developer.chrome.com/docs/ai/translator-api?#supported-languages "
-    "for more details.";
 
 String ConvertCreateTranslatorErrorToDebugString(CreateTranslatorError error) {
   switch (error) {
@@ -36,14 +32,6 @@
       return "Failed to initialize the translation library.";
     case CreateTranslatorError::kFailedToCreateTranslator:
       return "The translation library failed to create a translator.";
-    case CreateTranslatorError::kAcceptLanguagesCheckFailed:
-      return String(base::StrCat(
-          {"The preferred languages check for Translator API failed. ",
-           kLinkToDocument}));
-    case CreateTranslatorError::kExceedsLanguagePackCountLimitation:
-      return String(base::StrCat(
-          {"The Translator API language pack count exceeded the limitation. ",
-           kLinkToDocument}));
     case CreateTranslatorError::kServiceCrashed:
       return "The translation service crashed.";
     case CreateTranslatorError::kDisallowedByPolicy:
@@ -70,13 +58,6 @@
       NOTREACHED();
     case CanCreateTranslatorResult::kNoNotSupportedLanguage:
       return "The language pair is unsupported.";
-    case CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
-      equivalent_error = CreateTranslatorError::kAcceptLanguagesCheckFailed;
-      break;
-    case CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation:
-      equivalent_error =
-          CreateTranslatorError::kExceedsLanguagePackCountLimitation;
-      break;
     case CanCreateTranslatorResult::kNoServiceCrashed:
       equivalent_error = CreateTranslatorError::kServiceCrashed;
       break;
@@ -101,8 +82,6 @@
       return true;
     case CanCreateTranslatorResult::kReadily:
     case CanCreateTranslatorResult::kNoNotSupportedLanguage:
-    case CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
-    case CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation:
     case CanCreateTranslatorResult::kNoServiceCrashed:
     case CanCreateTranslatorResult::kNoDisallowedByPolicy:
     case CanCreateTranslatorResult::kNoExceedsServiceCountLimitation:
@@ -120,8 +99,6 @@
     case CanCreateTranslatorResult::kAfterDownloadTranslatorCreationRequired:
       return false;
     case CanCreateTranslatorResult::kNoNotSupportedLanguage:
-    case CanCreateTranslatorResult::kNoAcceptLanguagesCheckFailed:
-    case CanCreateTranslatorResult::kNoExceedsLanguagePackCountLimitation:
     case CanCreateTranslatorResult::kNoServiceCrashed:
     case CanCreateTranslatorResult::kNoDisallowedByPolicy:
     case CanCreateTranslatorResult::kNoExceedsServiceCountLimitation:
@@ -253,8 +230,7 @@
   // they lack the ability to do so.
   CHECK(window != nullptr || context->IsServiceWorkerGlobalScope());
 
-  if (RuntimeEnabledFeatures::TranslationAPIV1Enabled() &&
-      !context->IsServiceWorkerGlobalScope() &&
+  if (!context->IsServiceWorkerGlobalScope() &&
       RequiresUserActivation(result) &&
       !LocalFrame::ConsumeTransientUserActivation(window->GetFrame())) {
     GetResolver()->RejectWithDOMException(
diff --git a/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent.cc b/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent.cc
index 11d545a..2d8750a 100644
--- a/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent.cc
+++ b/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent.cc
@@ -213,6 +213,49 @@
   return object.Style()->Visibility() == EVisibility::kVisible;
 }
 
+void AddClickabilityReasons(Element& element,
+                            mojom::blink::AIPageContentAttributes& attributes) {
+  auto& interaction_info = *attributes.node_interaction_info;
+
+  using Reason = mojom::blink::AIPageContentClickabilityReason;
+
+  if (element.IsClickableControl()) {
+    interaction_info.debug_clickability_reasons.push_back(
+        Reason::kClickableControl);
+  }
+
+  if (element.HasJSBasedEventListeners(event_type_names::kClick)) {
+    interaction_info.debug_clickability_reasons.push_back(Reason::kClickEvents);
+  }
+
+  if (element.HasJSBasedEventListeners(event_type_names::kMouseover) ||
+      element.HasJSBasedEventListeners(event_type_names::kMouseenter) ||
+      element.HasJSBasedEventListeners(event_type_names::kMouseup) ||
+      element.HasJSBasedEventListeners(event_type_names::kMousedown)) {
+    interaction_info.debug_clickability_reasons.push_back(Reason::kMouseEvents);
+  }
+
+  if (element.HasJSBasedEventListeners(event_type_names::kKeydown) ||
+      element.HasJSBasedEventListeners(event_type_names::kKeypress) ||
+      element.HasJSBasedEventListeners(event_type_names::kKeyup)) {
+    interaction_info.debug_clickability_reasons.push_back(Reason::kKeyEvents);
+  }
+
+  if (IsEditable(element)) {
+    interaction_info.debug_clickability_reasons.push_back(Reason::kEditable);
+  }
+
+  const ComputedStyle& style = element.ComputedStyleRef();
+  if (style.Cursor() == ECursor::kPointer && !style.CursorIsInherited()) {
+    interaction_info.debug_clickability_reasons.push_back(
+        Reason::kCursorPointer);
+  }
+
+  if (ui::IsClickable(*attributes.aria_role)) {
+    interaction_info.debug_clickability_reasons.push_back(Reason::kAriaRole);
+  }
+}
+
 bool ShouldSkipSubtree(const LayoutObject& object) {
   auto* layout_embedded_content = DynamicTo<LayoutEmbeddedContent>(object);
   if (layout_embedded_content) {
@@ -1401,7 +1444,8 @@
     }
   }
 
-  if (auto* element = DynamicTo<Element>(object.GetNode())) {
+  auto* element = DynamicTo<Element>(object.GetNode());
+  if (element) {
     node_interaction_info->is_focusable = element->IsFocusable();
     node_interaction_info->is_clickable =
         element->IsMaybeClickable() || ui::IsClickable(*attributes.aria_role);
@@ -1430,6 +1474,9 @@
   }
 
   attributes.node_interaction_info = std::move(node_interaction_info);
+  if (attributes.node_interaction_info->is_clickable) {
+    AddClickabilityReasons(*element, attributes);
+  }
 }
 
 AIPageContentAgent::ContentBuilder::RecursionData::RecursionData(
diff --git a/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent_unittest.cc b/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent_unittest.cc
index f5ad2a2..e087fb9 100644
--- a/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent_unittest.cc
+++ b/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent_unittest.cc
@@ -3724,5 +3724,184 @@
   EXPECT_TRUE(select.content_attributes->node_interaction_info->is_clickable);
 }
 
+TEST_F(AIPageContentAgentTest, ClickabilityReasonClickableControl) {
+  frame_test_helpers::LoadHTMLString(
+      helper_.LocalMainFrame(),
+      "<body><button id='testButton'>Click Me</button></body>",
+      url_test_helpers::ToKURL("http://foobar.com"));
+
+  GetAIPageContentWithActionableElements();
+
+  const auto& button_node = *ContentRootNode().children_nodes[0];
+  ASSERT_TRUE(button_node.content_attributes->node_interaction_info);
+  EXPECT_THAT(
+      button_node.content_attributes->node_interaction_info
+          ->debug_clickability_reasons,
+      testing::Contains(
+          mojom::blink::AIPageContentClickabilityReason::kClickableControl));
+}
+
+TEST_F(AIPageContentAgentTest, ClickabilityReasonClickEvents) {
+  frame_test_helpers::LoadHTMLString(
+      helper_.LocalMainFrame(),
+      "<body><div id='testDiv'>Clickable</div>"
+      "<script>document.getElementById('testDiv').onclick = "
+      "function(){};</script>"
+      "</body>",
+      url_test_helpers::ToKURL("http://foobar.com"));
+
+  GetAIPageContentWithActionableElements();
+
+  const auto& div_node = *ContentRootNode().children_nodes[0];
+  ASSERT_TRUE(div_node.content_attributes->node_interaction_info);
+  EXPECT_THAT(div_node.content_attributes->node_interaction_info
+                  ->debug_clickability_reasons,
+              testing::Contains(
+                  mojom::blink::AIPageContentClickabilityReason::kClickEvents));
+}
+
+TEST_F(AIPageContentAgentTest, ClickabilityReasonMouseEvents) {
+  // An element with various mouse event listeners.
+  frame_test_helpers::LoadHTMLString(
+      helper_.LocalMainFrame(),
+      "<body><div id='testDiv'>Mouse Events</div>"
+      "<script>"
+      "  const div = document.getElementById('testDiv');"
+      "  div.onmouseover = function(){};"
+      "</script>"
+      "</body>",
+      url_test_helpers::ToKURL("http://foobar.com"));
+
+  GetAIPageContentWithActionableElements();
+
+  const auto& div_node = *ContentRootNode().children_nodes[0];
+  ASSERT_TRUE(div_node.content_attributes->node_interaction_info);
+  EXPECT_THAT(div_node.content_attributes->node_interaction_info
+                  ->debug_clickability_reasons,
+              testing::Contains(
+                  mojom::blink::AIPageContentClickabilityReason::kMouseEvents));
+}
+
+TEST_F(AIPageContentAgentTest, ClickabilityReasonKeyEvents) {
+  // An element with keyboard event listeners.
+  frame_test_helpers::LoadHTMLString(
+      helper_.LocalMainFrame(),
+      "<body><input type='text' id='testInput'>"
+      "<script>"
+      "  const input = document.getElementById('testInput');"
+      "  input.onkeydown = function(){};"
+      "</script>"
+      "</body>",
+      url_test_helpers::ToKURL("http://foobar.com"));
+
+  GetAIPageContentWithActionableElements();
+
+  const auto& input_node = *ContentRootNode().children_nodes[0];
+  ASSERT_TRUE(input_node.content_attributes->node_interaction_info);
+  EXPECT_THAT(input_node.content_attributes->node_interaction_info
+                  ->debug_clickability_reasons,
+              testing::Contains(
+                  mojom::blink::AIPageContentClickabilityReason::kKeyEvents));
+}
+
+TEST_F(AIPageContentAgentTest, ClickabilityReasonEditable) {
+  // A div with contenteditable attribute.
+  frame_test_helpers::LoadHTMLString(
+      helper_.LocalMainFrame(),
+      "<body><div contenteditable='true'>Editable Content</div></body>",
+      url_test_helpers::ToKURL("http://foobar.com"));
+
+  GetAIPageContentWithActionableElements();
+
+  const auto& div_node = *ContentRootNode().children_nodes[0];
+  ASSERT_TRUE(div_node.content_attributes->node_interaction_info);
+  EXPECT_TRUE(div_node.content_attributes->node_interaction_info->is_editable);
+  EXPECT_THAT(div_node.content_attributes->node_interaction_info
+                  ->debug_clickability_reasons,
+              testing::Contains(
+                  mojom::blink::AIPageContentClickabilityReason::kEditable));
+}
+
+TEST_F(AIPageContentAgentTest, ClickabilityReasonCursorPointer) {
+  frame_test_helpers::LoadHTMLString(
+      helper_.LocalMainFrame(),
+      "<body><div style='cursor: pointer;'>Pointer Cursor</div></body>",
+      url_test_helpers::ToKURL("http://foobar.com"));
+
+  GetAIPageContentWithActionableElements();
+
+  const auto& div_node = *ContentRootNode().children_nodes[0];
+  ASSERT_TRUE(div_node.content_attributes->node_interaction_info);
+  EXPECT_THAT(
+      div_node.content_attributes->node_interaction_info
+          ->debug_clickability_reasons,
+      testing::Contains(
+          mojom::blink::AIPageContentClickabilityReason::kCursorPointer));
+}
+
+TEST_F(AIPageContentAgentTest, ClickabilityReasonAriaRole) {
+  frame_test_helpers::LoadHTMLString(
+      helper_.LocalMainFrame(), "<body><div role='link'>ARIA Link</div></body>",
+      url_test_helpers::ToKURL("http://foobar.com"));
+
+  GetAIPageContentWithActionableElements();
+
+  const auto& div_node = *ContentRootNode().children_nodes[0];
+  ASSERT_TRUE(div_node.content_attributes->node_interaction_info);
+  EXPECT_EQ(div_node.content_attributes->aria_role,
+            ax::mojom::blink::Role::kLink);
+  EXPECT_THAT(div_node.content_attributes->node_interaction_info
+                  ->debug_clickability_reasons,
+              testing::Contains(
+                  mojom::blink::AIPageContentClickabilityReason::kAriaRole));
+}
+
+TEST_F(AIPageContentAgentTest, ClickabilityReasonMultipleReasons) {
+  frame_test_helpers::LoadHTMLString(
+      helper_.LocalMainFrame(),
+      "<body>"
+      "<button id='multiReasonBtn' contenteditable='true' style='cursor: "
+      "pointer;' role='menuitem'>"
+      "Multi-Reason Button"
+      "</button>"
+      "<script>"
+      "  const btn = document.getElementById('multiReasonBtn');"
+      "  btn.onclick = function(){};"
+      "  btn.onmouseover = function(){};"
+      "  btn.onkeydown = function(){};"
+      "</script>"
+      "</body>",
+      url_test_helpers::ToKURL("http://foobar.com"));
+
+  GetAIPageContentWithActionableElements();
+
+  const auto& button_node = *ContentRootNode().children_nodes[0];
+  ASSERT_TRUE(button_node.content_attributes->node_interaction_info);
+  EXPECT_THAT(
+      button_node.content_attributes->node_interaction_info
+          ->debug_clickability_reasons,
+      testing::UnorderedElementsAre(
+          mojom::blink::AIPageContentClickabilityReason::kClickableControl,
+          mojom::blink::AIPageContentClickabilityReason::kClickEvents,
+          mojom::blink::AIPageContentClickabilityReason::kMouseEvents,
+          mojom::blink::AIPageContentClickabilityReason::kKeyEvents,
+          mojom::blink::AIPageContentClickabilityReason::kEditable,
+          mojom::blink::AIPageContentClickabilityReason::kCursorPointer,
+          mojom::blink::AIPageContentClickabilityReason::kAriaRole));
+}
+
+TEST_F(AIPageContentAgentTest, ClickabilityReasonNoReasons) {
+  frame_test_helpers::LoadHTMLString(
+      helper_.LocalMainFrame(), "<body><div>Plain Div</div></body>",
+      url_test_helpers::ToKURL("http://foobar.com"));
+
+  GetAIPageContentWithActionableElements();
+
+  const auto& div_node = *ContentRootNode().children_nodes[0];
+  ASSERT_TRUE(div_node.content_attributes->node_interaction_info);
+  EXPECT_TRUE(div_node.content_attributes->node_interaction_info
+                  ->debug_clickability_reasons.empty());
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h b/third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h
index ca0ecc7..ea4bf36d 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h
@@ -29,17 +29,17 @@
 // which is owned by AudioTrackRecorder.
 class AudioTrackEncoder {
  public:
-  using OnEncodedAudioCB = base::RepeatingCallback<void(
+  using OnEncodedAudioCB = WTF::CrossThreadRepeatingFunction<void(
       const media::AudioParameters& params,
       scoped_refptr<media::DecoderBuffer> encoded_data,
       std::optional<media::AudioEncoder::CodecDescription> codec_desc,
       base::TimeTicks capture_time)>;
 
-  using OnEncodedAudioErrorCB = media::EncoderStatus::Callback;
+  using OnEncodedAudioErrorCB =
+      WTF::CrossThreadOnceFunction<void(media::EncoderStatus error_status)>;
 
-  explicit AudioTrackEncoder(
-      OnEncodedAudioCB on_encoded_audio_cb,
-      OnEncodedAudioErrorCB on_encoded_audio_error_cb = base::DoNothing());
+  AudioTrackEncoder(OnEncodedAudioCB on_encoded_audio_cb,
+                    OnEncodedAudioErrorCB on_encoded_audio_error_cb);
   virtual ~AudioTrackEncoder() = default;
 
   AudioTrackEncoder(const AudioTrackEncoder&) = delete;
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder.cc
index cef3b2a..145e5f1 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder.cc
@@ -181,11 +181,9 @@
 }
 
 void AudioTrackMojoEncoder::NotifyError(media::EncoderStatus error) {
-  if (on_encoded_audio_error_cb_.is_null()) {
-    return;
+  if (on_encoded_audio_error_cb_) {
+    std::move(on_encoded_audio_error_cb_).Run(std::move(error));
   }
-
-  std::move(on_encoded_audio_error_cb_).Run(std::move(error));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder_unittest.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder_unittest.cc
index d646d80..84ebe4d1 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder_unittest.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder_unittest.cc
@@ -206,7 +206,7 @@
       scheduler::GetSequencedTaskRunnerForTesting(),
       AudioTrackRecorder::CodecId::kAac,
       /*on_encoded_audio_cb=*/
-      base::BindLambdaForTesting(
+      WTF::CrossThreadBindRepeating(base::BindLambdaForTesting(
           [this](const media::AudioParameters& /*params*/,
                  scoped_refptr<media::DecoderBuffer> /*encoded_data*/,
                  std::optional<
@@ -214,13 +214,14 @@
                  base::TimeTicks capture_time) {
             ++output_count_;
             capture_times_.push_back(capture_time);
-          }),
+          })),
       /*on_encoded_audio_error_cb=*/
-      base::BindLambdaForTesting([this](media::EncoderStatus status) {
-        ASSERT_EQ(error_code_, media::EncoderStatus::Codes::kOk);
-        ASSERT_FALSE(status.is_ok());
-        error_code_ = status.code();
-      })};
+      WTF::CrossThreadBindOnce(
+          base::BindLambdaForTesting([this](media::EncoderStatus status) {
+            ASSERT_EQ(error_code_, media::EncoderStatus::Codes::kOk);
+            ASSERT_FALSE(status.is_ok());
+            error_code_ = status.code();
+          }))};
 };
 
 TEST_F(AudioTrackMojoEncoderTest, InputArrivingAfterInitialization) {
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.cc
index efede70b..c0358cd3 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.cc
@@ -228,10 +228,8 @@
 }
 
 void AudioTrackOpusEncoder::NotifyError(media::EncoderStatus error) {
-  if (on_encoded_audio_error_cb_.is_null()) {
-    return;
+  if (on_encoded_audio_error_cb_) {
+    std::move(on_encoded_audio_error_cb_).Run(std::move(error));
   }
-
-  std::move(on_encoded_audio_error_cb_).Run(std::move(error));
 }
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.cc
index c546f62..06f4dc8 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.cc
@@ -27,7 +27,7 @@
 
   if (!input_params.IsValid()) {
     DLOG(ERROR) << "Invalid params: " << input_params.AsHumanReadableString();
-    if (!on_encoded_audio_error_cb_.is_null()) {
+    if (on_encoded_audio_error_cb_) {
       std::move(on_encoded_audio_error_cb_)
           .Run(media::EncoderStatus::Codes::kEncoderUnsupportedConfig);
     }
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc
index e81dd3a..6b67c284 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.h"
+
 #include <memory>
 
 #include "base/check_op.h"
@@ -12,7 +13,6 @@
 #include "base/time/time.h"
 #include "media/base/audio_bus.h"
 #include "media/base/audio_parameters.h"
-#include "third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h"
 #include "third_party/blink/renderer/modules/mediarecorder/audio_track_mojo_encoder.h"
 #include "third_party/blink/renderer/modules/mediarecorder/audio_track_opus_encoder.h"
 #include "third_party/blink/renderer/modules/mediarecorder/audio_track_pcm_encoder.h"
@@ -21,7 +21,9 @@
 #include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
 #include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+#include "third_party/blink/renderer/platform/wtf/bind_post_task.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_media.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_copier_std.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -42,10 +44,10 @@
 namespace WTF {
 
 template <>
-struct CrossThreadCopier<media::AudioParameters> {
+struct CrossThreadCopier<std::optional<media::AudioEncoder::CodecDescription>>
+    : public CrossThreadCopierPassThrough<
+          std::optional<media::AudioEncoder::CodecDescription>> {
   STATIC_ONLY(CrossThreadCopier);
-  using Type = media::AudioParameters;
-  static Type Copy(Type pointer) { return pointer; }
 };
 
 }  // namespace WTF
@@ -74,20 +76,20 @@
                         WrapPersistent(callback_interface)))),
       track_(track),
       encoder_task_runner_(std::move(encoder_task_runner)),
-      encoder_(encoder_task_runner_,
-               CreateAudioEncoder(
-                   codec,
-                   encoder_task_runner_,
-                   base::BindPostTask(
-                       main_thread_task_runner,
-                       WTF::BindRepeating(&CallbackInterface::OnEncodedAudio,
-                                          WrapPersistent(callback_interface))),
-                   base::BindPostTask(
-                       main_thread_task_runner,
-                       WTF::BindOnce(&CallbackInterface::OnAudioEncodingError,
-                                     WrapPersistent(callback_interface))),
-                   bits_per_second,
-                   bitrate_mode)),
+      encoder_(CreateAudioEncoder(
+          codec,
+          WTF::BindPostTask(
+              main_thread_task_runner,
+              WTF::CrossThreadBindRepeating(
+                  &CallbackInterface::OnEncodedAudio,
+                  MakeUnwrappingCrossThreadHandle(callback_interface))),
+          WTF::BindPostTask(
+              main_thread_task_runner,
+              WTF::CrossThreadBindOnce(
+                  &CallbackInterface::OnAudioEncodingError,
+                  MakeUnwrappingCrossThreadHandle(callback_interface))),
+          bits_per_second,
+          bitrate_mode)),
       callback_interface_(callback_interface) {
   DCHECK(IsMainThread());
   DCHECK(track_);
@@ -104,31 +106,32 @@
 
 // Creates an audio encoder from the codec. Returns nullptr if the codec is
 // invalid.
-std::unique_ptr<AudioTrackEncoder> AudioTrackRecorder::CreateAudioEncoder(
+WTF::SequenceBound<AudioTrackEncoder> AudioTrackRecorder::CreateAudioEncoder(
     CodecId codec,
-    scoped_refptr<base::SequencedTaskRunner> encoder_task_runner,
-    OnEncodedAudioCB on_encoded_audio_cb,
-    OnEncodedAudioErrorCB on_encoded_audio_error_cb,
+    AudioTrackEncoder::OnEncodedAudioCB on_encoded_audio_cb,
+    AudioTrackEncoder::OnEncodedAudioErrorCB on_encoded_audio_error_cb,
     uint32_t bits_per_second,
     BitrateMode bitrate_mode) {
-  std::unique_ptr<AudioTrackEncoder> encoder;
   switch (codec) {
     case CodecId::kPcm:
-      return std::make_unique<AudioTrackPcmEncoder>(
-          std::move(on_encoded_audio_cb), std::move(on_encoded_audio_error_cb));
+      return WTF::SequenceBound<AudioTrackPcmEncoder>(
+          encoder_task_runner_, std::move(on_encoded_audio_cb),
+          std::move(on_encoded_audio_error_cb));
     case CodecId::kAac:
 #if HAS_AAC_ENCODER
-      return std::make_unique<AudioTrackMojoEncoder>(
-          encoder_task_runner, codec, std::move(on_encoded_audio_cb),
-          std::move(on_encoded_audio_error_cb), bits_per_second);
+      return WTF::SequenceBound<AudioTrackMojoEncoder>(
+          encoder_task_runner_, encoder_task_runner_, codec,
+          std::move(on_encoded_audio_cb), std::move(on_encoded_audio_error_cb),
+          bits_per_second);
 #else
       NOTREACHED() << "AAC encoder is not supported.";
 #endif
     case CodecId::kOpus:
     default:
-      return std::make_unique<AudioTrackOpusEncoder>(
-          std::move(on_encoded_audio_cb), std::move(on_encoded_audio_error_cb),
-          bits_per_second, bitrate_mode == BitrateMode::kVariable);
+      return WTF::SequenceBound<AudioTrackOpusEncoder>(
+          encoder_task_runner_, std::move(on_encoded_audio_cb),
+          std::move(on_encoded_audio_error_cb), bits_per_second,
+          bitrate_mode == BitrateMode::kVariable);
   }
 }
 
diff --git a/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.h b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.h
index e54532e..a69d022a 100644
--- a/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/audio_track_recorder.h
@@ -17,6 +17,7 @@
 #include "media/base/audio_encoder.h"
 #include "media/base/decoder_buffer.h"
 #include "third_party/blink/public/platform/modules/mediastream/web_media_stream_audio_sink.h"
+#include "third_party/blink/renderer/modules/mediarecorder/audio_track_encoder.h"
 #include "third_party/blink/renderer/modules/mediarecorder/track_recorder.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
@@ -29,8 +30,6 @@
 }  // namespace media
 
 namespace blink {
-
-class AudioTrackEncoder;
 class MediaStreamComponent;
 
 // AudioTrackRecorder is a MediaStreamAudioSink that encodes the audio buses
@@ -72,14 +71,6 @@
     virtual void OnSourceReadyStateChanged() = 0;
   };
 
-  using OnEncodedAudioCB = base::RepeatingCallback<void(
-      const media::AudioParameters& params,
-      scoped_refptr<media::DecoderBuffer> encoded_data,
-      std::optional<media::AudioEncoder::CodecDescription> codec_description,
-      base::TimeTicks capture_time)>;
-
-  using OnEncodedAudioErrorCB = media::EncoderStatus::Callback;
-
   static CodecId GetPreferredCodecId(MediaTrackContainerType container_type);
 
   AudioTrackRecorder(
@@ -113,11 +104,10 @@
  private:
   // Creates an audio encoder from |codec|. Returns nullptr if the codec is
   // invalid.
-  static std::unique_ptr<AudioTrackEncoder> CreateAudioEncoder(
+  WTF::SequenceBound<AudioTrackEncoder> CreateAudioEncoder(
       CodecId codec,
-      scoped_refptr<base::SequencedTaskRunner> encoder_task_runner,
-      OnEncodedAudioCB on_encoded_audio_cb,
-      OnEncodedAudioErrorCB on_encoded_audio_error_cb,
+      AudioTrackEncoder::OnEncodedAudioCB on_encoded_audio_cb,
+      AudioTrackEncoder::OnEncodedAudioErrorCB on_encoded_audio_error_cb,
       uint32_t bits_per_second,
       BitrateMode bitrate_mode);
 
@@ -133,7 +123,7 @@
   const scoped_refptr<base::SequencedTaskRunner> encoder_task_runner_;
 
   // Thin wrapper around the chosen encoder.
-  WTF::SequenceBound<std::unique_ptr<AudioTrackEncoder>> encoder_;
+  WTF::SequenceBound<AudioTrackEncoder> encoder_;
 
   // Number of frames per chunked buffer passed to the encoder.
   int frames_per_chunk_ = 0;
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper.cc
index 3b163d1..4a4f26e5 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper.cc
@@ -11,7 +11,6 @@
 #include "media/base/video_frame.h"
 #include "media/media_buildflags.h"
 #include "media/video/alpha_video_encoder_wrapper.h"
-#include "media/video/gpu_video_accelerator_factories.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 
 namespace blink {
@@ -39,17 +38,17 @@
     media::VideoCodecProfile profile,
     uint32_t bits_per_second,
     bool is_screencast,
-    media::GpuVideoAcceleratorFactories* gpu_factories,
+    bool is_hardware_encoder,
     CreateEncoderCB create_encoder_cb,
     VideoTrackRecorder::OnEncodedVideoCB on_encoded_video_cb,
     OnErrorCB on_error_cb)
     : Encoder(std::move(encoding_task_runner),
-              on_encoded_video_cb,
+              std::move(on_encoded_video_cb),
               bits_per_second),
-      gpu_factories_(gpu_factories),
+      is_hardware_encoder_(is_hardware_encoder),
       profile_(profile),
       codec_(media::VideoCodecProfileToVideoCodec(profile_)),
-      create_encoder_cb_(create_encoder_cb),
+      create_encoder_cb_(std::move(create_encoder_cb)),
       on_error_cb_(std::move(on_error_cb)) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
   CHECK(create_encoder_cb_);
@@ -84,8 +83,8 @@
 
 bool MediaRecorderEncoderWrapper::CanEncodeAlphaChannel() const {
   // Alpha encoding is supported only with VP8 and VP9 software encoders.
-  return !gpu_factories_ && (codec_ == media::VideoCodec::kVP8 ||
-                             codec_ == media::VideoCodec::kVP9);
+  return !is_hardware_encoder_ && (codec_ == media::VideoCodec::kVP8 ||
+                                   codec_ == media::VideoCodec::kVP9);
 }
 
 bool MediaRecorderEncoderWrapper::IsScreenContentEncodingForTesting() const {
@@ -93,8 +92,7 @@
          *options_.content_hint == media::VideoEncoder::ContentHint::Screen;
 }
 
-void MediaRecorderEncoderWrapper::EnterErrorState(
-    const media::EncoderStatus& status) {
+void MediaRecorderEncoderWrapper::EnterErrorState(media::EncoderStatus status) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (state_ == State::kInError) {
     CHECK(!on_error_cb_);
@@ -147,13 +145,13 @@
 
   if (encode_alpha_) {
     CHECK(CanEncodeAlphaChannel());
-    auto yuv_encoder = create_encoder_cb_.Run(gpu_factories_);
-    auto alpha_encoder = create_encoder_cb_.Run(gpu_factories_);
+    auto yuv_encoder = create_encoder_cb_.Run();
+    auto alpha_encoder = create_encoder_cb_.Run();
     CHECK(yuv_encoder && alpha_encoder);
     encoder_ = std::make_unique<media::AlphaVideoEncoderWrapper>(
         std::move(yuv_encoder), std::move(alpha_encoder));
   } else {
-    encoder_ = create_encoder_cb_.Run(gpu_factories_);
+    encoder_ = create_encoder_cb_.Run();
   }
   CHECK(encoder_);
 
@@ -161,7 +159,7 @@
   // because a given |on_encoded_video_cb_| already hops a thread.
   encoder_->DisablePostedCallbacks();
   metrics_provider_->Initialize(profile_, options_.frame_size,
-                                /*is_hardware_encoder=*/gpu_factories_);
+                                is_hardware_encoder_);
   encoder_->Initialize(
       profile_, options_,
       WTF::BindRepeating(&MediaRecorderEncoderWrapper::OnVideoEncoderInfo,
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper.h b/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper.h
index 49f380e..df684bb 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper.h
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper.h
@@ -12,10 +12,6 @@
 #include "third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h"
 #include "third_party/blink/renderer/platform/wtf/deque.h"
 
-namespace media {
-class GpuVideoAcceleratorFactories;
-}  // namespace media
-
 namespace blink {
 
 // VideoTrackRecorder::Encoder class encodes h264, hevc, vp8, vp9 and av1 using
@@ -24,16 +20,15 @@
     : public VideoTrackRecorder::Encoder {
  public:
   using CreateEncoderCB =
-      base::RepeatingCallback<std::unique_ptr<media::VideoEncoder>(
-          media::GpuVideoAcceleratorFactories*)>;
-  using OnErrorCB = base::OnceCallback<void(const media::EncoderStatus&)>;
+      WTF::CrossThreadFunction<std::unique_ptr<media::VideoEncoder>()>;
+  using OnErrorCB = WTF::CrossThreadOnceFunction<void(media::EncoderStatus)>;
 
   MediaRecorderEncoderWrapper(
       scoped_refptr<base::SequencedTaskRunner> encoding_task_runner,
       media::VideoCodecProfile profile,
       uint32_t bits_per_second,
       bool is_screencast,
-      media::GpuVideoAcceleratorFactories* gpu_factories,
+      bool is_hardware_encoder,
       CreateEncoderCB create_encoder_cb,
       VideoTrackRecorder::OnEncodedVideoCB on_encoded_video_cb,
       OnErrorCB on_error_cb);
@@ -79,7 +74,7 @@
                    bool request_keyframe) override;
   bool CanEncodeAlphaChannel() const override;
 
-  void EnterErrorState(const media::EncoderStatus& status);
+  void EnterErrorState(media::EncoderStatus status);
   void Reconfigure(const gfx::Size& frame_size, bool encode_alpha);
 
   // (Re)creates |encoder_| and initialize the encoder with |frame_size|.
@@ -95,7 +90,7 @@
       media::VideoEncoderOutput output,
       std::optional<media::VideoEncoder::CodecDescription> description);
 
-  const raw_ptr<media::GpuVideoAcceleratorFactories> gpu_factories_;
+  const bool is_hardware_encoder_;
 
   const media::VideoCodecProfile profile_;
   const media::VideoCodec codec_;
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper_unittest.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper_unittest.cc
index e2dcb225..b471a902 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper_unittest.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_encoder_wrapper_unittest.cc
@@ -128,11 +128,10 @@
 
  protected:
   MOCK_METHOD(void, CreateEncoder, (), ());
-  MOCK_METHOD(void, OnError, (const media::EncoderStatus&), ());
+  MOCK_METHOD(void, OnError, (media::EncoderStatus), ());
   MOCK_METHOD(void, MockVideoEncoderWrapperDtor, (), ());
 
-  std::unique_ptr<media::VideoEncoder> CreateMockVideoEncoder(
-      media::GpuVideoAcceleratorFactories* /*gpu_factories*/) {
+  std::unique_ptr<media::VideoEncoder> CreateMockVideoEncoder() {
     CreateEncoder();
     return std::make_unique<MockVideoEncoderWrapper>(
         &mock_encoder_,
@@ -145,14 +144,15 @@
     encoder_wrapper_ = std::make_unique<MediaRecorderEncoderWrapper>(
         scheduler::GetSingleThreadTaskRunnerForTesting(), profile_,
         kDefaultBitrate, is_screencast,
-        /*gpu_factories=*/nullptr,
-        WTF::BindRepeating(
+        /*is_hardware_encoder=*/false,
+        WTF::CrossThreadBindRepeating(
             &MediaRecorderEncoderWrapperTest::CreateMockVideoEncoder,
-            base::Unretained(this)),
-        WTF::BindRepeating(&MediaRecorderEncoderWrapperTest::OnEncodedVideo,
-                           base::Unretained(this)),
-        WTF::BindRepeating(&MediaRecorderEncoderWrapperTest::OnError,
-                           base::Unretained(this)));
+            WTF::CrossThreadUnretained(this)),
+        WTF::CrossThreadBindRepeating(
+            &MediaRecorderEncoderWrapperTest::OnEncodedVideo,
+            WTF::CrossThreadUnretained(this)),
+        WTF::CrossThreadBindOnce(&MediaRecorderEncoderWrapperTest::OnError,
+                                 WTF::CrossThreadUnretained(this)));
     EXPECT_EQ(is_screencast,
               encoder_wrapper_->IsScreenContentEncodingForTesting());
     auto metrics_provider =
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
index a100de1..d8c871d 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
@@ -1201,7 +1201,7 @@
 }
 
 void MediaRecorderHandler::OnVideoEncodingError(
-    const media::EncoderStatus& error_status) {
+    media::EncoderStatus error_status) {
   if (recorder_) {
     recorder_->OnError(DOMExceptionCode::kEncodingError,
                        String(media::EncoderStatusCodeToString(error_status)));
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h
index ea5348c4..db0c891 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h
@@ -130,7 +130,7 @@
                           base::TimeTicks timestamp) override;
   std::unique_ptr<media::VideoEncoderMetricsProvider>
   CreateVideoEncoderMetricsProvider() override;
-  void OnVideoEncodingError(const media::EncoderStatus& error_status) override;
+  void OnVideoEncodingError(media::EncoderStatus error_status) override;
   // AudioTrackRecorder::CallbackInterface overrides.
   void OnEncodedAudio(
       const media::AudioParameters& params,
diff --git a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
index 3665d97..e4b9349 100644
--- a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
@@ -29,6 +29,7 @@
 #include "media/base/video_frame.h"
 #include "media/base/video_util.h"
 #include "media/media_buildflags.h"
+#include "media/muxers/muxer.h"
 #include "media/muxers/webm_muxer.h"
 #include "media/renderers/paint_canvas_video_renderer.h"
 #include "media/video/gpu_video_accelerator_factories.h"
@@ -45,7 +46,9 @@
 #include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
 #include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/wtf/bind_post_task.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_media.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_copier_std.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -65,10 +68,11 @@
 using video_track_recorder::kVEAEncoderMinResolutionWidth;
 
 namespace WTF {
+
 template <>
-struct CrossThreadCopier<std::vector<scoped_refptr<media::VideoFrame>>>
+struct CrossThreadCopier<std::optional<media::VideoEncoder::CodecDescription>>
     : public CrossThreadCopierPassThrough<
-          std::vector<scoped_refptr<media::VideoFrame>>> {
+          std::optional<media::VideoEncoder::CodecDescription>> {
   STATIC_ONLY(CrossThreadCopier);
 };
 
@@ -78,6 +82,14 @@
           blink::KeyFrameRequestProcessor::Configuration> {
   STATIC_ONLY(CrossThreadCopier);
 };
+
+template <>
+struct CrossThreadCopier<media::Muxer::VideoParameters>
+    : public CrossThreadCopierByValuePassThrough<
+          media::Muxer::VideoParameters> {
+  STATIC_ONLY(CrossThreadCopier);
+};
+
 }  // namespace WTF
 
 namespace blink {
@@ -171,12 +183,6 @@
       media::VideoEncodeAccelerator::SupportedProfiles());
 }
 
-VideoTrackRecorderImpl::CodecEnumerator* GetCodecEnumerator() {
-  static VideoTrackRecorderImpl::CodecEnumerator* enumerator =
-      new VideoTrackRecorderImpl::CodecEnumerator(GetVEASupportedProfiles());
-  return enumerator;
-}
-
 void UmaHistogramForCodec(bool uses_acceleration, CodecId codec_id) {
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
@@ -296,13 +302,16 @@
 }
 
 MediaRecorderEncoderWrapper::CreateEncoderCB
-GetCreateHardwareVideoEncoderCallback(CodecId codec_id) {
+GetCreateHardwareVideoEncoderCallback(
+    CodecId codec_id,
+    media::GpuVideoAcceleratorFactories* gpu_factories) {
+  CHECK(gpu_factories);
   auto required_encoder_type =
       media::MayHaveAndAllowSelectOSSoftwareEncoder(
           MediaVideoCodecFromCodecId(codec_id))
           ? media::VideoEncodeAccelerator::Config::EncoderType::kNoPreference
           : media::VideoEncodeAccelerator::Config::EncoderType::kHardware;
-  return ConvertToBaseRepeatingCallback(WTF::CrossThreadBindRepeating(
+  return WTF::CrossThreadBindRepeating(
       [](media::VideoEncodeAccelerator::Config::EncoderType
              required_encoder_type,
          media::GpuVideoAcceleratorFactories* gpu_factories)
@@ -314,7 +323,7 @@
                 base::SequencedTaskRunner::GetCurrentDefault(),
                 required_encoder_type));
       },
-      required_encoder_type));
+      required_encoder_type, WTF::CrossThreadUnretained(gpu_factories));
 }
 
 MediaRecorderEncoderWrapper::CreateEncoderCB
@@ -322,28 +331,25 @@
   switch (codec_id) {
 #if BUILDFLAG(ENABLE_OPENH264)
     case CodecId::kH264:
-      return ConvertToBaseRepeatingCallback(WTF::CrossThreadBindRepeating(
-          [](media::GpuVideoAcceleratorFactories* /*gpu_factories*/)
-              -> std::unique_ptr<media::VideoEncoder> {
+      return WTF::CrossThreadBindRepeating(
+          []() -> std::unique_ptr<media::VideoEncoder> {
             return std::make_unique<media::OpenH264VideoEncoder>();
-          }));
+          });
 #endif  // BUILDFLAG(ENABLE_OPENH264)
 #if BUILDFLAG(ENABLE_LIBVPX)
     case CodecId::kVp8:
     case CodecId::kVp9:
-      return ConvertToBaseRepeatingCallback(WTF::CrossThreadBindRepeating(
-          [](media::GpuVideoAcceleratorFactories* /*gpu_factories*/)
-              -> std::unique_ptr<media::VideoEncoder> {
+      return WTF::CrossThreadBindRepeating(
+          []() -> std::unique_ptr<media::VideoEncoder> {
             return std::make_unique<media::VpxVideoEncoder>();
-          }));
+          });
 #endif
 #if BUILDFLAG(ENABLE_LIBAOM)
     case CodecId::kAv1:
-      return ConvertToBaseRepeatingCallback(WTF::CrossThreadBindRepeating(
-          [](media::GpuVideoAcceleratorFactories* /*gpu_factories*/)
-              -> std::unique_ptr<media::VideoEncoder> {
+      return WTF::CrossThreadBindRepeating(
+          []() -> std::unique_ptr<media::VideoEncoder> {
             return std::make_unique<media::Av1VideoEncoder>();
-          }));
+          });
 #endif  // BUILDFLAG(ENABLE_LIBAOM)
     default:
       NOTREACHED() << "Unsupported codec=" << static_cast<int>(codec_id);
@@ -386,96 +392,6 @@
     media::VideoCodecLevel level)
     : codec_id(codec_id), profile(profile), level(level) {}
 
-VideoTrackRecorderImpl::CodecEnumerator::CodecEnumerator(
-    const media::VideoEncodeAccelerator::SupportedProfiles&
-        vea_supported_profiles) {
-  for (const auto& supported_profile : vea_supported_profiles) {
-    const media::VideoCodecProfile codec = supported_profile.profile;
-    for (auto& codec_id_and_profile : kPreferredCodecIdAndVEAProfiles) {
-      if (codec >= codec_id_and_profile.min_profile &&
-          codec <= codec_id_and_profile.max_profile) {
-        DVLOG(2) << "Accelerated codec found: " << media::GetProfileName(codec)
-                 << ", min_resolution: "
-                 << supported_profile.min_resolution.ToString()
-                 << ", max_resolution: "
-                 << supported_profile.max_resolution.ToString()
-                 << ", max_framerate: "
-                 << supported_profile.max_framerate_numerator << "/"
-                 << supported_profile.max_framerate_denominator;
-        auto iter = supported_profiles_.find(codec_id_and_profile.codec_id);
-        if (iter == supported_profiles_.end()) {
-          auto result = supported_profiles_.insert(
-              codec_id_and_profile.codec_id,
-              media::VideoEncodeAccelerator::SupportedProfiles());
-          result.stored_value->value.push_back(supported_profile);
-        } else {
-          iter->value.push_back(supported_profile);
-        }
-        if (preferred_codec_id_ == CodecId::kLast) {
-          preferred_codec_id_ = codec_id_and_profile.codec_id;
-        }
-      }
-    }
-  }
-}
-
-VideoTrackRecorderImpl::CodecEnumerator::~CodecEnumerator() = default;
-
-std::pair<media::VideoCodecProfile, bool>
-VideoTrackRecorderImpl::CodecEnumerator::FindSupportedVideoCodecProfile(
-    CodecId codec,
-    media::VideoCodecProfile profile) const {
-  const auto profiles = supported_profiles_.find(codec);
-  if (profiles == supported_profiles_.end()) {
-    return {media::VIDEO_CODEC_PROFILE_UNKNOWN, false};
-  }
-  for (const auto& p : profiles->value) {
-    if (p.profile == profile) {
-      const bool vbr_support =
-          p.rate_control_modes & media::VideoEncodeAccelerator::kVariableMode;
-      return {profile, vbr_support};
-    }
-  }
-  return {media::VIDEO_CODEC_PROFILE_UNKNOWN, false};
-}
-
-VideoTrackRecorderImpl::CodecId
-VideoTrackRecorderImpl::CodecEnumerator::GetPreferredCodecId(
-    MediaTrackContainerType type) const {
-  if (preferred_codec_id_ == CodecId::kLast) {
-    if (type == MediaTrackContainerType::kVideoMp4 ||
-        type == MediaTrackContainerType::kAudioMp4) {
-      return CodecId::kVp9;
-    }
-    return CodecId::kVp8;
-  }
-
-  return preferred_codec_id_;
-}
-
-std::pair<media::VideoCodecProfile, bool>
-VideoTrackRecorderImpl::CodecEnumerator::GetFirstSupportedVideoCodecProfile(
-    CodecId codec) const {
-  const auto profile = supported_profiles_.find(codec);
-  if (profile == supported_profiles_.end()) {
-    return {media::VIDEO_CODEC_PROFILE_UNKNOWN, false};
-  }
-
-  const auto& supported_profile = profile->value.front();
-  const bool vbr_support = supported_profile.rate_control_modes &
-                           media::VideoEncodeAccelerator::kVariableMode;
-  return {supported_profile.profile, vbr_support};
-}
-
-media::VideoEncodeAccelerator::SupportedProfiles
-VideoTrackRecorderImpl::CodecEnumerator::GetSupportedProfiles(
-    CodecId codec) const {
-  const auto profile = supported_profiles_.find(codec);
-  return profile == supported_profiles_.end()
-             ? media::VideoEncodeAccelerator::SupportedProfiles()
-             : profile->value;
-}
-
 VideoTrackRecorderImpl::Counter::Counter() : count_(0u) {}
 
 VideoTrackRecorderImpl::Counter::~Counter() = default;
@@ -495,15 +411,15 @@
 
 VideoTrackRecorderImpl::Encoder::Encoder(
     scoped_refptr<base::SequencedTaskRunner> encoding_task_runner,
-    const OnEncodedVideoCB& on_encoded_video_cb,
+    OnEncodedVideoCB on_encoded_video_cb,
     uint32_t bits_per_second)
     : encoding_task_runner_(std::move(encoding_task_runner)),
-      on_encoded_video_cb_(on_encoded_video_cb),
+      on_encoded_video_cb_(std::move(on_encoded_video_cb)),
       bits_per_second_(bits_per_second),
       num_frames_in_encode_(
           std::make_unique<VideoTrackRecorderImpl::Counter>()) {
   CHECK(encoding_task_runner_);
-  DCHECK(!on_encoded_video_cb_.is_null());
+  DCHECK(on_encoded_video_cb_);
 }
 
 VideoTrackRecorderImpl::Encoder::~Encoder() = default;
@@ -768,7 +684,37 @@
 // static
 VideoTrackRecorderImpl::CodecId VideoTrackRecorderImpl::GetPreferredCodecId(
     MediaTrackContainerType type) {
-  return GetCodecEnumerator()->GetPreferredCodecId(type);
+  const auto preferred_codec_id = []() {
+    for (const auto& supported_profile : GetVEASupportedProfiles()) {
+      const media::VideoCodecProfile codec = supported_profile.profile;
+      for (auto& codec_id_and_profile : kPreferredCodecIdAndVEAProfiles) {
+        if (codec >= codec_id_and_profile.min_profile &&
+            codec <= codec_id_and_profile.max_profile) {
+          DVLOG(2) << "Accelerated codec found: "
+                   << media::GetProfileName(codec) << ", min_resolution: "
+                   << supported_profile.min_resolution.ToString()
+                   << ", max_resolution: "
+                   << supported_profile.max_resolution.ToString()
+                   << ", max_framerate: "
+                   << supported_profile.max_framerate_numerator << "/"
+                   << supported_profile.max_framerate_denominator;
+          return codec_id_and_profile.codec_id;
+        }
+      }
+    }
+    return CodecId::kLast;
+  }();
+
+  if (preferred_codec_id != CodecId::kLast) {
+    return preferred_codec_id;
+  }
+
+  if (type == MediaTrackContainerType::kVideoMp4 ||
+      type == MediaTrackContainerType::kAudioMp4) {
+    return CodecId::kVp9;
+  }
+
+  return CodecId::kVp8;
 }
 
 // static
@@ -786,20 +732,37 @@
     }
   }
 
-  const auto profiles =
-      GetCodecEnumerator()->GetSupportedProfiles(codec_profile.codec_id);
-  if (profiles.empty()) {
-    return false;
-  }
-
-  for (const auto& profile : profiles) {
-    if (profile.profile == media::VIDEO_CODEC_PROFILE_UNKNOWN) {
-      return false;
+  const auto media_codec_id = [](CodecId codec) {
+    switch (codec) {
+      case VideoTrackRecorder::CodecId::kVp8:
+        return media::VideoCodec::kVP8;
+      case VideoTrackRecorder::CodecId::kVp9:
+        return media::VideoCodec::kVP9;
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+      case VideoTrackRecorder::CodecId::kH264:
+        return media::VideoCodec::kH264;
+#endif
+#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
+      case VideoTrackRecorder::CodecId::kHevc:
+        return media::VideoCodec::kHEVC;
+#endif
+      case VideoTrackRecorder::CodecId::kAv1:
+        return media::VideoCodec::kAV1;
+      case VideoTrackRecorder::CodecId::kLast:
+        return media::VideoCodec::kUnknown;
     }
-    // Skip other profiles if the profile is specified.
-    if (codec_profile.profile && *codec_profile.profile != profile.profile) {
+  }(codec_profile.codec_id);
+
+  for (const auto& profile : GetVEASupportedProfiles()) {
+    DCHECK_NE(profile.profile, media::VIDEO_CODEC_PROFILE_UNKNOWN);
+
+    // Skip other profiles if the profile is specified or skip on codec.
+    if ((codec_profile.profile && *codec_profile.profile != profile.profile) ||
+        media_codec_id !=
+            media::VideoCodecProfileToVideoCodec(profile.profile)) {
       continue;
     }
+
     // Skip if profile is OS software encoder profile and we don't allow use
     // OS software encoder.
     if (profile.is_software_codec &&
@@ -851,10 +814,6 @@
       key_frame_config_(key_frame_config),
       codec_profile_(codec_profile),
       bits_per_second_(bits_per_second),
-      on_encoded_video_cb_(base::BindPostTask(
-          main_thread_task_runner_,
-          WTF::BindRepeating(&CallbackInterface::OnEncodedVideo,
-                             WrapPersistent(callback_interface)))),
       frame_buffer_pool_limit_(frame_buffer_pool_limit) {
   TRACE_EVENT("media", "VideoTrackRecorderImpl::VideoTrackRecorderImpl");
   CHECK(main_thread_task_runner_);
@@ -981,8 +940,7 @@
   encoder_.AsyncCall(&Encoder::ForceKeyFrameForNextFrameForTesting);
 }
 
-std::unique_ptr<VideoTrackRecorder::Encoder>
-VideoTrackRecorderImpl::CreateMediaVideoEncoder(
+void VideoTrackRecorderImpl::CreateMediaVideoEncoder(
     scoped_refptr<base::SequencedTaskRunner> encoding_task_runner,
     CodecProfile codec_profile,
     bool is_screencast,
@@ -997,26 +955,32 @@
     // TODO(crbug.com/1441395): This should be handled by using
     // media::VideoEncoderFallback. This should be achieved after refactoring
     // VideoTrackRecorder to call media::VideoEncoder directly.
-    on_error_cb = base::BindPostTask(
-        main_thread_task_runner_,
-        WTF::BindOnce(&VideoTrackRecorderImpl::OnHardwareEncoderError,
-                      weak_factory_.GetWeakPtr()));
+    on_error_cb =
+        WTF::BindPostTask(main_thread_task_runner_,
+                          WTF::CrossThreadBindOnce(
+                              &VideoTrackRecorderImpl::OnHardwareEncoderError,
+                              weak_factory_.GetWeakPtr()));
   } else {
-    on_error_cb = base::BindPostTask(
+    on_error_cb = WTF::BindPostTask(
         main_thread_task_runner_,
-        WTF::BindOnce(&CallbackInterface::OnVideoEncodingError,
-                      WrapPersistent(callback_interface())));
+        WTF::CrossThreadBindOnce(
+            &CallbackInterface::OnVideoEncodingError,
+            MakeUnwrappingCrossThreadHandle(callback_interface())));
   }
 
-  media::GpuVideoAcceleratorFactories* gpu_factories =
-      Platform::Current()->GetGpuFactories();
-  return std::make_unique<MediaRecorderEncoderWrapper>(
-      std::move(encoding_task_runner), *codec_profile.profile, bits_per_second_,
-      is_screencast, create_vea_encoder ? gpu_factories : nullptr,
+  encoder_ = WTF::SequenceBound<MediaRecorderEncoderWrapper>(
+      encoding_task_runner, encoding_task_runner, *codec_profile.profile,
+      bits_per_second_, is_screencast, create_vea_encoder,
       create_vea_encoder
-          ? GetCreateHardwareVideoEncoderCallback(codec_profile.codec_id)
+          ? GetCreateHardwareVideoEncoderCallback(
+                codec_profile.codec_id, Platform::Current()->GetGpuFactories())
           : GetCreateSoftwareVideoEncoderCallback(codec_profile.codec_id),
-      on_encoded_video_cb_, std::move(on_error_cb));
+      WTF::BindPostTask(
+          main_thread_task_runner_,
+          WTF::CrossThreadBindRepeating(
+              &CallbackInterface::OnEncodedVideo,
+              MakeUnwrappingCrossThreadHandle(callback_interface()))),
+      std::move(on_error_cb));
 }
 
 void VideoTrackRecorderImpl::InitializeEncoder(
@@ -1027,8 +991,10 @@
   TRACE_EVENT("media", "VideoTrackRecorderImpl::InitializeEncoder");
   DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
   auto codec_profile = codec_profile_;
-  const bool can_use_vea = CanUseAcceleratedEncoder(
-      codec_profile, input_size.width(), input_size.height());
+  const bool can_use_vea =
+      CanUseAcceleratedEncoder(codec_profile, input_size.width(),
+                               input_size.height()) &&
+      Platform::Current()->GetGpuFactories();
   CHECK(callback_interface());
 
   std::optional<media::VideoCodecProfile> profile =
@@ -1050,18 +1016,17 @@
   auto encoding_task_runner =
       base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
   CHECK(encoding_task_runner);
-  auto encoder = CreateMediaVideoEncoder(encoding_task_runner, codec_profile,
-                                         is_screencast, create_vea_encoder);
-
   UmaHistogramForCodec(create_vea_encoder, codec_profile.codec_id);
-  CHECK(encoder);
+
+  CreateMediaVideoEncoder(encoding_task_runner, codec_profile, is_screencast,
+                          create_vea_encoder);
+  CHECK(encoder_);
 
   auto metrics_provider =
       callback_interface()->Get()
           ? callback_interface()->Get()->CreateVideoEncoderMetricsProvider()
           : nullptr;
-  CHECK(metrics_provider);
-  encoder_.emplace(encoding_task_runner, std::move(encoder));
+
   encoder_.AsyncCall(&Encoder::InitializeEncoder)
       .WithArgs(key_frame_config_, std::move(metrics_provider),
                 frame_buffer_pool_limit_);
@@ -1071,7 +1036,7 @@
 }
 
 void VideoTrackRecorderImpl::OnHardwareEncoderError(
-    const media::EncoderStatus& error_status) {
+    media::EncoderStatus error_status) {
   DVLOG(3) << __func__ << ", error_status: "
            << media::EncoderStatusCodeToString(error_status);
   DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
diff --git a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h
index 55ab9d78..cc24bc8 100644
--- a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h
+++ b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.h
@@ -108,8 +108,7 @@
     CreateVideoEncoderMetricsProvider() = 0;
 
     // Called on encountering encoder errors.
-    virtual void OnVideoEncodingError(
-        const media::EncoderStatus& error_status) = 0;
+    virtual void OnVideoEncodingError(media::EncoderStatus error_status) = 0;
 
     // Called when a track's ready state changes.
     virtual void OnSourceReadyStateChanged() = 0;
@@ -138,12 +137,11 @@
     }
   };
 
-  using OnEncodedVideoCB = base::RepeatingCallback<void(
+  using OnEncodedVideoCB = WTF::CrossThreadFunction<void(
       const media::Muxer::VideoParameters& params,
       scoped_refptr<media::DecoderBuffer> encoded_data,
       std::optional<media::VideoEncoder::CodecDescription> codec_description,
       base::TimeTicks capture_timestamp)>;
-  using OnErrorCB = base::RepeatingCallback<void(const media::EncoderStatus&)>;
 
   // MediaStreamVideoSink implementation
   double GetRequiredMinFramesPerSec() const override { return 1; }
@@ -170,7 +168,7 @@
   class MODULES_EXPORT Encoder {
    public:
     Encoder(scoped_refptr<base::SequencedTaskRunner> encoding_task_runner,
-            const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_cb,
+            VideoTrackRecorder::OnEncodedVideoCB on_encoded_video_cb,
             uint32_t bits_per_second);
     virtual ~Encoder();
 
@@ -280,48 +278,6 @@
     media::VideoFrameConverter frame_converter_;
   };
 
-  // Class to encapsulate the enumeration of CodecIds/VideoCodecProfiles
-  // supported by the VEA underlying platform. Provides methods to query the
-  // preferred CodecId and to check if a given CodecId is supported.
-  class MODULES_EXPORT CodecEnumerator {
-   public:
-    explicit CodecEnumerator(
-        const media::VideoEncodeAccelerator::SupportedProfiles&
-            vea_supported_profiles);
-
-    CodecEnumerator(const CodecEnumerator&) = delete;
-    CodecEnumerator& operator=(const CodecEnumerator&) = delete;
-
-    ~CodecEnumerator();
-
-    // Returns the first CodecId that has an associated VEA VideoCodecProfile,
-    // or VP8 if none available.
-    CodecId GetPreferredCodecId(MediaTrackContainerType type) const;
-
-    // Returns supported VEA VideoCodecProfile which matches |codec| and
-    // |profile| and whether VEA supports VBR encoding for the profile.
-    std::pair<media::VideoCodecProfile, bool> FindSupportedVideoCodecProfile(
-        CodecId codec,
-        media::VideoCodecProfile profile) const;
-
-    // Returns VEA's first supported VideoCodedProfile for a given CodecId and
-    // whether VBR encoding is supported by VEA for the profile, or
-    // VIDEO_CODEC_PROFILE_UNKNOWN otherwise.
-    std::pair<media::VideoCodecProfile, bool>
-    GetFirstSupportedVideoCodecProfile(CodecId codec) const;
-
-    // Returns a list of supported media::VEA::SupportedProfile for a given
-    // CodecId, or empty vector if CodecId is unsupported.
-    media::VideoEncodeAccelerator::SupportedProfiles GetSupportedProfiles(
-        CodecId codec) const;
-
-   private:
-    // VEA-supported profiles grouped by CodecId.
-    HashMap<CodecId, media::VideoEncodeAccelerator::SupportedProfiles>
-        supported_profiles_;
-    CodecId preferred_codec_id_ = CodecId::kLast;
-  };
-
   VideoTrackRecorder(
       scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner,
       WeakCell<CallbackInterface>* callback_interface);
@@ -411,22 +367,12 @@
                          bool allow_vea_encoder,
                          media::VideoFrame::StorageType frame_storage_type,
                          gfx::Size input_size);
-  std::unique_ptr<Encoder> CreateSoftwareVideoEncoder(
-      scoped_refptr<base::SequencedTaskRunner> encoding_task_runner,
-      CodecProfile codec_profile,
-      bool is_screencast);
-  std::unique_ptr<Encoder> CreateHardwareVideoEncoder(
-      scoped_refptr<base::SequencedTaskRunner> encoding_task_runner,
-      CodecProfile codec_profile,
-      const gfx::Size& input_size,
-      bool use_import_mode,
-      bool is_screencast);
-  std::unique_ptr<Encoder> CreateMediaVideoEncoder(
+  void CreateMediaVideoEncoder(
       scoped_refptr<base::SequencedTaskRunner> encoding_task_runner,
       CodecProfile codec_profile,
       bool is_screencast,
       bool create_vea_encoder);
-  void OnHardwareEncoderError(const media::EncoderStatus& error_status);
+  void OnHardwareEncoderError(media::EncoderStatus error_status);
 
   void ConnectToTrack(const VideoCaptureDeliverFrameCB& callback);
   void DisconnectFromTrack();
@@ -438,12 +384,11 @@
   Persistent<MediaStreamComponent> track_;
 
   // Holds inner class to encode using whichever codec is configured.
-  WTF::SequenceBound<std::unique_ptr<Encoder>> encoder_;
+  WTF::SequenceBound<Encoder> encoder_;
 
   const KeyFrameRequestProcessor::Configuration key_frame_config_;
   const CodecProfile codec_profile_;
   const uint32_t bits_per_second_;
-  const OnEncodedVideoCB on_encoded_video_cb_;
   const size_t frame_buffer_pool_limit_;
   bool should_pause_encoder_on_initialization_
       GUARDED_BY_CONTEXT(main_sequence_checker_) = false;
diff --git a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder_unittest.cc b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder_unittest.cc
index fdb95340..b70ea6c 100644
--- a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder_unittest.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder_unittest.cc
@@ -188,7 +188,7 @@
 
   MOCK_METHOD(void,
               OnVideoEncodingError,
-              (const media::EncoderStatus& status),
+              (media::EncoderStatus status),
               (override));
   MOCK_METHOD(void, OnSourceReadyStateChanged, (), (override));
   void Trace(Visitor* v) const override { v->Trace(weak_factory_); }
@@ -1466,274 +1466,4 @@
                          VideoTrackRecorderPassthroughTest,
                          ValuesIn(kTrackRecorderTestCodec));
 
-class CodecEnumeratorTest : public ::testing::Test {
- public:
-  using CodecEnumerator = VideoTrackRecorder::CodecEnumerator;
-  using CodecId = VideoTrackRecorder::CodecId;
-
-  CodecEnumeratorTest() = default;
-
-  CodecEnumeratorTest(const CodecEnumeratorTest&) = delete;
-  CodecEnumeratorTest& operator=(const CodecEnumeratorTest&) = delete;
-
-  ~CodecEnumeratorTest() override = default;
-
-  media::VideoEncodeAccelerator::SupportedProfiles MakeVp8Profiles() {
-    media::VideoEncodeAccelerator::SupportedProfiles profiles;
-    profiles.emplace_back(media::VP8PROFILE_ANY, gfx::Size(1920, 1080), 30, 1);
-    return profiles;
-  }
-
-  media::VideoEncodeAccelerator::SupportedProfiles MakeVp9Profiles(
-      bool vbr_support = false) {
-    media::VideoEncodeAccelerator::SupportedProfiles profiles;
-    auto rc_mode =
-        media::VideoEncodeAccelerator::SupportedRateControlMode::kConstantMode;
-    if (vbr_support) {
-      rc_mode |= media::VideoEncodeAccelerator::SupportedRateControlMode::
-          kVariableMode;
-    }
-
-    profiles.emplace_back(media::VP9PROFILE_PROFILE0, gfx::Size(1920, 1080), 60,
-                          1, rc_mode);
-    profiles.emplace_back(media::VP9PROFILE_PROFILE1, gfx::Size(1920, 1080), 60,
-                          1, rc_mode);
-    profiles.emplace_back(media::VP9PROFILE_PROFILE2, gfx::Size(1920, 1080), 30,
-                          1, rc_mode);
-    profiles.emplace_back(media::VP9PROFILE_PROFILE3, gfx::Size(1920, 1080), 30,
-                          1, rc_mode);
-    return profiles;
-  }
-
-  media::VideoEncodeAccelerator::SupportedProfiles MakeAv1Profiles(
-      bool vbr_support = false) {
-    media::VideoEncodeAccelerator::SupportedProfiles profiles;
-    auto rc_mode =
-        media::VideoEncodeAccelerator::SupportedRateControlMode::kConstantMode;
-    if (vbr_support) {
-      rc_mode |= media::VideoEncodeAccelerator::SupportedRateControlMode::
-          kVariableMode;
-    }
-
-    profiles.emplace_back(media::AV1PROFILE_PROFILE_MAIN, gfx::Size(1920, 1080),
-                          60, 1, rc_mode);
-    profiles.emplace_back(media::AV1PROFILE_PROFILE_HIGH, gfx::Size(1920, 1080),
-                          60, 1, rc_mode);
-    profiles.emplace_back(media::AV1PROFILE_PROFILE_PRO, gfx::Size(1920, 1080),
-                          30, 1, rc_mode);
-    return profiles;
-  }
-
-  media::VideoEncodeAccelerator::SupportedProfiles MakeVp8Vp9Profiles() {
-    media::VideoEncodeAccelerator::SupportedProfiles profiles =
-        MakeVp8Profiles();
-    media::VideoEncodeAccelerator::SupportedProfiles vp9_profiles =
-        MakeVp9Profiles();
-    profiles.insert(profiles.end(), vp9_profiles.begin(), vp9_profiles.end());
-    return profiles;
-  }
-
-  media::VideoEncodeAccelerator::SupportedProfiles MakeH264Profiles(
-      bool vbr_support = false) {
-    media::VideoEncodeAccelerator::SupportedProfiles profiles;
-    auto rc_mode =
-        media::VideoEncodeAccelerator::SupportedRateControlMode::kConstantMode;
-    if (vbr_support) {
-      rc_mode |= media::VideoEncodeAccelerator::SupportedRateControlMode::
-          kVariableMode;
-    }
-
-    profiles.emplace_back(media::H264PROFILE_BASELINE, gfx::Size(1920, 1080),
-                          24, 1, rc_mode);
-    profiles.emplace_back(media::H264PROFILE_MAIN, gfx::Size(1920, 1080), 30, 1,
-                          rc_mode);
-    profiles.emplace_back(media::H264PROFILE_EXTENDED, gfx::Size(1920, 1080),
-                          30, 1, rc_mode);
-    profiles.emplace_back(media::H264PROFILE_HIGH, gfx::Size(1920, 1080), 60, 1,
-                          rc_mode);
-    profiles.emplace_back(media::H264PROFILE_HIGH10PROFILE,
-                          gfx::Size(1920, 1080), 60, 1, rc_mode);
-    profiles.emplace_back(media::H264PROFILE_MULTIVIEWHIGH,
-                          gfx::Size(1920, 1080), 60, 1, rc_mode);
-    return profiles;
-  }
-
-#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
-  media::VideoEncodeAccelerator::SupportedProfiles MakeHEVCProfiles(
-      bool vbr_support = false) {
-    media::VideoEncodeAccelerator::SupportedProfiles profiles;
-    auto rc_mode =
-        media::VideoEncodeAccelerator::SupportedRateControlMode::kConstantMode;
-    if (vbr_support) {
-      rc_mode |= media::VideoEncodeAccelerator::SupportedRateControlMode::
-          kVariableMode;
-    }
-
-    profiles.emplace_back(media::HEVCPROFILE_MAIN, gfx::Size(1920, 1080), 24, 1,
-                          rc_mode);
-    profiles.emplace_back(media::HEVCPROFILE_MAIN10, gfx::Size(1920, 1080), 24,
-                          1, rc_mode);
-    profiles.emplace_back(media::HEVCPROFILE_MAIN_STILL_PICTURE,
-                          gfx::Size(1920, 1080), 24, 1, rc_mode);
-    profiles.emplace_back(media::HEVCPROFILE_REXT, gfx::Size(1920, 1080), 24, 1,
-                          rc_mode);
-    return profiles;
-  }
-#endif
-
-  test::TaskEnvironment task_environment_;
-};
-
-TEST_F(CodecEnumeratorTest, GetPreferredCodecIdDefault) {
-  // Empty supported profiles.
-  MediaTrackContainerType type = GetMediaContainerTypeFromString("");
-  const CodecEnumerator emulator(
-      (media::VideoEncodeAccelerator::SupportedProfiles()));
-  EXPECT_EQ(CodecId::kVp8, emulator.GetPreferredCodecId(type));
-}
-
-TEST_F(CodecEnumeratorTest, GetPreferredCodecIdVp8) {
-  MediaTrackContainerType type = GetMediaContainerTypeFromString("");
-  const CodecEnumerator emulator(MakeVp8Profiles());
-  EXPECT_EQ(CodecId::kVp8, emulator.GetPreferredCodecId(type));
-}
-
-TEST_F(CodecEnumeratorTest, GetPreferredCodecIdVp9) {
-  MediaTrackContainerType type = GetMediaContainerTypeFromString("");
-  const CodecEnumerator emulator(MakeVp9Profiles());
-  EXPECT_EQ(CodecId::kVp9, emulator.GetPreferredCodecId(type));
-}
-
-TEST_F(CodecEnumeratorTest, GetPreferredCodecIdVp8Vp9) {
-  MediaTrackContainerType type = GetMediaContainerTypeFromString("");
-  const CodecEnumerator emulator(MakeVp8Vp9Profiles());
-  EXPECT_EQ(CodecId::kVp8, emulator.GetPreferredCodecId(type));
-}
-
-TEST_F(CodecEnumeratorTest, MakeSupportedProfilesVp9) {
-  const CodecEnumerator emulator(MakeVp9Profiles());
-  media::VideoEncodeAccelerator::SupportedProfiles profiles =
-      emulator.GetSupportedProfiles(CodecId::kVp9);
-  EXPECT_EQ(1u, profiles.size());
-  EXPECT_EQ(media::VP9PROFILE_PROFILE0, profiles[0].profile);
-}
-
-TEST_F(CodecEnumeratorTest, MakeSupportedProfilesNoVp8) {
-  const CodecEnumerator emulator(MakeVp9Profiles());
-  media::VideoEncodeAccelerator::SupportedProfiles profiles =
-      emulator.GetSupportedProfiles(CodecId::kVp8);
-  EXPECT_TRUE(profiles.empty());
-}
-
-TEST_F(CodecEnumeratorTest, GetFirstSupportedVideoCodecProfileVp9) {
-  const CodecEnumerator emulator(MakeVp9Profiles());
-  EXPECT_EQ(std::make_pair(media::VP9PROFILE_PROFILE0, /*vbr_support=*/false),
-            emulator.GetFirstSupportedVideoCodecProfile(CodecId::kVp9));
-}
-
-TEST_F(CodecEnumeratorTest, GetFirstSupportedVideoCodecProfileNoVp8) {
-  const CodecEnumerator emulator(MakeVp9Profiles());
-  EXPECT_EQ(
-      std::make_pair(media::VIDEO_CODEC_PROFILE_UNKNOWN, /*vbr_support=*/false),
-      emulator.GetFirstSupportedVideoCodecProfile(CodecId::kVp8));
-}
-
-TEST_F(CodecEnumeratorTest, GetFirstSupportedVideoCodecProfileVp9VBR) {
-  const CodecEnumerator emulator(MakeVp9Profiles(/*vbr_support=*/true));
-  EXPECT_EQ(std::make_pair(media::VP9PROFILE_PROFILE0, /*vbr_support=*/true),
-            emulator.GetFirstSupportedVideoCodecProfile(CodecId::kVp9));
-}
-
-TEST_F(CodecEnumeratorTest, GetFirstSupportedVideoCodecProfileNoVp8VBR) {
-  const CodecEnumerator emulator(MakeVp9Profiles(/*vbr_support=*/true));
-  EXPECT_EQ(
-      std::make_pair(media::VIDEO_CODEC_PROFILE_UNKNOWN, /*vbr_support=*/false),
-      emulator.GetFirstSupportedVideoCodecProfile(CodecId::kVp8));
-}
-
-TEST_F(CodecEnumeratorTest, GetPreferredCodecIdAv1) {
-  MediaTrackContainerType type = GetMediaContainerTypeFromString("");
-  const CodecEnumerator emulator(MakeAv1Profiles());
-  EXPECT_EQ(CodecId::kAv1, emulator.GetPreferredCodecId(type));
-}
-
-TEST_F(CodecEnumeratorTest, MakeSupportedProfilesAv1) {
-  const CodecEnumerator emulator(MakeAv1Profiles());
-  media::VideoEncodeAccelerator::SupportedProfiles profiles =
-      emulator.GetSupportedProfiles(CodecId::kAv1);
-  EXPECT_EQ(1u, profiles.size());
-  EXPECT_EQ(media::AV1PROFILE_PROFILE_MAIN, profiles[0].profile);
-}
-
-TEST_F(CodecEnumeratorTest, GetFirstSupportedVideoCodecProfileVAv1) {
-  const CodecEnumerator emulator(MakeAv1Profiles());
-  EXPECT_EQ(
-      std::make_pair(media::AV1PROFILE_PROFILE_MAIN, /*vbr_support=*/false),
-      emulator.GetFirstSupportedVideoCodecProfile(CodecId::kAv1));
-}
-
-#if BUILDFLAG(USE_PROPRIETARY_CODECS)
-TEST_F(CodecEnumeratorTest, FindSupportedVideoCodecProfileH264) {
-  const CodecEnumerator emulator(MakeH264Profiles());
-  EXPECT_EQ(std::make_pair(media::H264PROFILE_HIGH, /*vbr_support=*/false),
-            emulator.FindSupportedVideoCodecProfile(CodecId::kH264,
-                                                    media::H264PROFILE_HIGH));
-}
-
-TEST_F(CodecEnumeratorTest, FindSupportedVideoCodecProfileH264VBR) {
-  const CodecEnumerator emulator(MakeH264Profiles(/*vbr_support=*/true));
-  EXPECT_EQ(std::make_pair(media::H264PROFILE_HIGH, /*vbr_support=*/true),
-            emulator.FindSupportedVideoCodecProfile(CodecId::kH264,
-                                                    media::H264PROFILE_HIGH));
-}
-
-TEST_F(CodecEnumeratorTest, FindSupportedVideoCodecProfileNoProfileH264) {
-  const CodecEnumerator emulator(MakeH264Profiles());
-  EXPECT_EQ(
-      std::make_pair(media::VIDEO_CODEC_PROFILE_UNKNOWN, /*vbr_support=*/false),
-      emulator.FindSupportedVideoCodecProfile(
-          CodecId::kH264, media::H264PROFILE_HIGH422PROFILE));
-}
-
-TEST_F(CodecEnumeratorTest, FindSupportedVideoCodecProfileNoProfileH264VBR) {
-  const CodecEnumerator emulator(MakeH264Profiles(/*vbr_support=*/true));
-  EXPECT_EQ(
-      std::make_pair(media::VIDEO_CODEC_PROFILE_UNKNOWN, /*vbr_support=*/false),
-      emulator.FindSupportedVideoCodecProfile(
-          CodecId::kH264, media::H264PROFILE_HIGH422PROFILE));
-}
-#endif
-
-#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
-TEST_F(CodecEnumeratorTest, MakeSupportedProfilesHevc) {
-  const CodecEnumerator emulator(MakeHEVCProfiles());
-  media::VideoEncodeAccelerator::SupportedProfiles profiles =
-      emulator.GetSupportedProfiles(CodecId::kHevc);
-  EXPECT_EQ(1u, profiles.size());
-  EXPECT_EQ(media::HEVCPROFILE_MAIN, profiles[0].profile);
-}
-
-TEST_F(CodecEnumeratorTest, FindSupportedVideoCodecProfileHevc) {
-  const CodecEnumerator emulator(MakeHEVCProfiles());
-  EXPECT_EQ(std::make_pair(media::HEVCPROFILE_MAIN, /*vbr_support=*/false),
-            emulator.FindSupportedVideoCodecProfile(CodecId::kHevc,
-                                                    media::HEVCPROFILE_MAIN));
-}
-
-TEST_F(CodecEnumeratorTest, FindSupportedVideoCodecProfileHevcVBR) {
-  const CodecEnumerator emulator(MakeHEVCProfiles(/*vbr_support=*/true));
-  EXPECT_EQ(std::make_pair(media::HEVCPROFILE_MAIN, /*vbr_support=*/true),
-            emulator.FindSupportedVideoCodecProfile(CodecId::kHevc,
-                                                    media::HEVCPROFILE_MAIN));
-}
-
-TEST_F(CodecEnumeratorTest, FindSupportedVideoCodecProfileNoProfileHevc) {
-  const CodecEnumerator emulator(MakeHEVCProfiles());
-  EXPECT_EQ(
-      std::make_pair(media::VIDEO_CODEC_PROFILE_UNKNOWN, /*vbr_support=*/false),
-      emulator.FindSupportedVideoCodecProfile(CodecId::kHevc,
-                                              media::HEVCPROFILE_MAIN10));
-}
-#endif
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/ml/ml.cc b/third_party/blink/renderer/modules/ml/ml.cc
index dead991..c2f9a55 100644
--- a/third_party/blink/renderer/modules/ml/ml.cc
+++ b/third_party/blink/renderer/modules/ml/ml.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_power_preference.h"
 #include "third_party/blink/renderer/modules/ml/ml_context.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_error.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
@@ -130,10 +131,53 @@
                                            GPUDevice* gpu_device,
                                            ExceptionState& exception_state) {
   webnn::ScopedTrace scoped_trace("ML::createContext(GPUDevice)");
-  exception_state.ThrowDOMException(
-      DOMExceptionCode::kNotSupportedError,
-      "ML.createContext(GPUDevice) is not supported.");
-  return EmptyPromise();
+  if (!script_state->ContextIsValid()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "Invalid script state");
+    return EmptyPromise();
+  }
+
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver<MLContext>>(
+      script_state, exception_state.GetContext());
+  auto promise = resolver->Promise();
+
+  // Ensure `resolver` is rejected if the `CreateWebNNContext()` callback isn't
+  // run due to a WebNN service connection error.
+  pending_resolvers_.insert(resolver);
+
+  EnsureWebNNServiceConnection();
+
+  // TODO(crbug.com/409110243): implement WebNNContextImpl creation from
+  // GPUDevice.
+  webnn_context_provider_->CreateWebNNContext(
+      webnn::mojom::blink::CreateContextOptions::New(
+          ConvertBlinkDeviceTypeToMojo(
+              V8MLDeviceType(V8MLDeviceType::Enum::kGpu)),
+          ConvertBlinkPowerPreferenceToMojo(
+              V8MLPowerPreference(V8MLPowerPreference::Enum::kDefault))),
+      WTF::BindOnce(
+          [](ML* ml, ScriptPromiseResolver<MLContext>* resolver,
+             webnn::ScopedTrace scoped_trace, GPUDevice* gpu_device,
+             webnn::mojom::blink::CreateContextResultPtr result) {
+            ml->pending_resolvers_.erase(resolver);
+
+            if (result->is_error()) {
+              const webnn::mojom::blink::Error& create_context_error =
+                  *result->get_error();
+              resolver->RejectWithDOMException(
+                  WebNNErrorCodeToDOMExceptionCode(create_context_error.code),
+                  create_context_error.message);
+              return;
+            }
+
+            resolver->Resolve(MakeGarbageCollected<MLContext>(
+                resolver->GetExecutionContext(), gpu_device,
+                std::move(result->get_success())));
+          },
+          WrapPersistent(this), WrapPersistent(resolver),
+          std::move(scoped_trace), WrapPersistent(gpu_device)));
+
+  return promise;
 }
 
 void ML::EnsureWebNNServiceConnection() {
diff --git a/third_party/blink/renderer/modules/ml/ml_context.cc b/third_party/blink/renderer/modules/ml/ml_context.cc
index b386bec..89d982e 100644
--- a/third_party/blink/renderer/modules/ml/ml_context.cc
+++ b/third_party/blink/renderer/modules/ml/ml_context.cc
@@ -56,6 +56,7 @@
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph_utils.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_tensor.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 
@@ -63,6 +64,9 @@
 
 namespace {
 
+const char kContextWebGPUInteropUnsupportedError[] =
+    "The context does not support WebGPU interop.";
+
 MLDataTypeLimits* SupportedDataTypesToDataTypeLimits(
     const webnn::SupportedDataTypes& supported_data_types) {
   MLDataTypeLimits* data_type_limits = MLDataTypeLimits::Create();
@@ -123,6 +127,24 @@
       WTF::BindOnce(&MLContext::OnLost, WrapWeakPersistent(this)));
 }
 
+MLContext::MLContext(
+    ExecutionContext* execution_context,
+    GPUDevice* gpu_device,
+    webnn::mojom::blink::CreateContextSuccessPtr create_context_success)
+    : device_type_(V8MLDeviceType::Enum::kGpu),
+      power_preference_(V8MLPowerPreference::Enum::kDefault),
+      lost_property_(MakeGarbageCollected<LostProperty>(execution_context)),
+      context_remote_(execution_context),
+      properties_(std::move(create_context_success->context_properties)),
+      webnn_handle_(std::move(create_context_success->context_handle)),
+      gpu_device_(gpu_device) {
+  context_remote_.Bind(
+      std::move(create_context_success->context_remote),
+      execution_context->GetTaskRunner(TaskType::kMachineLearning));
+  context_remote_.set_disconnect_with_reason_handler(
+      WTF::BindOnce(&MLContext::OnLost, WrapWeakPersistent(this)));
+}
+
 MLContext::~MLContext() = default;
 
 V8MLDeviceType MLContext::GetDeviceType() const {
@@ -140,6 +162,7 @@
   visitor->Trace(graphs_);
   visitor->Trace(graph_builders_);
   visitor->Trace(tensors_);
+  visitor->Trace(gpu_device_);
   ScriptWrappable::Trace(visitor);
 }
 
@@ -1005,6 +1028,11 @@
     return EmptyPromise();
   }
 
+  if (descriptor->exportableToGPU() && !gpu_device_) {
+    exception_state.ThrowTypeError(kContextWebGPUInteropUnsupportedError);
+    return EmptyPromise();
+  }
+
   ASSIGN_OR_RETURN(
       webnn::OperandDescriptor validated_descriptor,
       webnn::OperandDescriptor::Create(
@@ -1275,7 +1303,6 @@
 
 ScriptPromise<GPUBuffer> MLContext::exportToGPU(
     ScriptState* script_state,
-    GPUDevice* device,
     MLTensor* tensor,
     ExceptionState& exception_state) {
   webnn::ScopedTrace scoped_trace("MLContext::exportToGPU");
@@ -1299,6 +1326,10 @@
         "The source tensor cannot be exported to WebGPU.");
     return EmptyPromise();
   }
+  if (!gpu_device_) {
+    exception_state.ThrowTypeError(kContextWebGPUInteropUnsupportedError);
+    return EmptyPromise();
+  }
   // TODO(crbug.com/345352987): Implement MLTensor's exportToGPU.
   exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
                                     "MLContext::exportToGPU is not supported.");
diff --git a/third_party/blink/renderer/modules/ml/ml_context.h b/third_party/blink/renderer/modules/ml/ml_context.h
index 875d1a7..761a4d3 100644
--- a/third_party/blink/renderer/modules/ml/ml_context.h
+++ b/third_party/blink/renderer/modules/ml/ml_context.h
@@ -54,6 +54,12 @@
       const V8MLPowerPreference power_preference,
       webnn::mojom::blink::CreateContextSuccessPtr create_context_success);
 
+  // Constructs for MLContext(GPUDevice).
+  MLContext(
+      ExecutionContext* execution_context,
+      GPUDevice* gpu_device,
+      webnn::mojom::blink::CreateContextSuccessPtr create_context_success);
+
   MLContext(const MLContext&) = delete;
   MLContext& operator=(const MLContext&) = delete;
 
@@ -104,7 +110,6 @@
                 ExceptionState& exception_state);
 
   ScriptPromise<GPUBuffer> exportToGPU(ScriptState* script_state,
-                                       GPUDevice* device,
                                        MLTensor* tensor,
                                        ExceptionState& exception_state);
 
@@ -147,6 +152,10 @@
   HeapHashSet<WeakMember<MLGraph>> graphs_;
   HeapHashSet<WeakMember<MLGraphBuilder>> graph_builders_;
   HeapHashSet<WeakMember<MLTensor>> tensors_;
+
+  // The `WebNNContext` was initialized from a WebGPU device which can be
+  // used for interop.
+  WeakMember<GPUDevice> gpu_device_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/ml/ml_context.idl b/third_party/blink/renderer/modules/ml/ml_context.idl
index 0649d86..d808130 100644
--- a/third_party/blink/renderer/modules/ml/ml_context.idl
+++ b/third_party/blink/renderer/modules/ml/ml_context.idl
@@ -340,12 +340,9 @@
     CallWith=ScriptState
   ] MLOpSupportLimits opSupportLimits();
 
-  // TODO(crbug.com/345352987): remove device once MLContext(gpuDevice) is
-  // implemented.
   [
     RuntimeEnabled=MachineLearningNeuralNetwork,
     CallWith=ScriptState,
     RaisesException
-  ] Promise<GPUBuffer> exportToGPU(
-    GPUDevice device, MLTensor tensor);
+  ] Promise<GPUBuffer> exportToGPU(MLTensor tensor);
 };
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index 74ceeb7..d56de51 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -511,6 +511,9 @@
 
   OriginTrialContext::AddTokens(this, response_origin_trial_tokens);
 
+  // Allows `ContextFeatureSettings` to update before preparing script engine.
+  ReportingProxy().WillPrepareForEvaluation();
+
   // TODO(nhiroki): Clarify mappings between the steps 4.8-4.11 and
   // implementation.
 
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
index 400ad4c3..0bfb7ae01 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
@@ -172,6 +172,14 @@
   Client().FailedToFetchModuleScript();
 }
 
+void ServiceWorkerGlobalScopeProxy::WillPrepareForEvaluation() {
+  DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
+  ScriptState::Scope scope(
+      WorkerGlobalScope()->ScriptController()->GetScriptState());
+  Client().WillPrepareForEvaluation(
+      WorkerGlobalScope()->ScriptController()->GetContext());
+}
+
 void ServiceWorkerGlobalScopeProxy::WillEvaluateScript() {
   DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
   TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h
index f7f3e68..b86e0fa 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h
@@ -129,6 +129,7 @@
   void DidFetchScript() override;
   void DidFailToFetchClassicScript() override;
   void DidFailToFetchModuleScript() override;
+  void WillPrepareForEvaluation() override;
   void WillEvaluateScript() override;
   void DidEvaluateTopLevelScript(bool success) override;
   void DidCloseWorkerGlobalScope() override;
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc b/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc
index 2c3bcc71..78285963 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc
+++ b/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc
@@ -191,9 +191,7 @@
     // Otherwise the lookups below would not correctly deduplicate strings.
     std::unique_ptr<ParkableStringImpl::SecureDigest> expected_digest =
         ParkableStringImpl::HashString(string_impl.get());
-    base::span<const uint8_t> expected_span(*expected_digest);
-    base::span<const uint8_t> provided_span(*digest);
-    CHECK_EQ(expected_span, provided_span);
+    CHECK(*expected_digest == *digest);
 #endif  // DCHECK_IS_ON()
   }
   DCHECK(digest.get());
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc
index 3bc9cdc6..1024b24d 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc
@@ -9,6 +9,7 @@
 #include <algorithm>
 
 #include "base/logging.h"
+#include "base/strings/to_string.h"
 #include "cc/paint/paint_canvas.h"
 #include "third_party/blink/renderer/platform/fonts/font.h"
 #include "third_party/blink/renderer/platform/fonts/plain_text_node.h"
@@ -75,7 +76,7 @@
     cluster_ends_.Shrink(0);
   }
 
-  DVLOG(4) << "   Cluster ends: " << base::span(cluster_ends_);
+  DVLOG(4) << "   Cluster ends: " << base::ToString(base::span(cluster_ends_));
 
   cluster_ends_offset_ = from;
   current_text_ = text;
@@ -97,11 +98,11 @@
   DCHECK(!current_text_.IsNull());
 
   DVLOG(4) << "   CommitText from: " << from << " to: " << to;
-  DVLOG(4)
-      << "   CommitText glyphs: "
-      << base::span(pending_glyphs_).last(current_character_indexes_.size());
+  DVLOG(4) << "   CommitText glyphs: "
+           << base::ToString(base::span(pending_glyphs_)
+                                 .last(current_character_indexes_.size()));
   DVLOG(4) << "   CommitText cluster starts: "
-           << base::span(current_character_indexes_);
+           << base::ToString(base::span(current_character_indexes_));
 
   wtf_size_t pending_utf8_original_size = pending_utf8_.size();
   wtf_size_t pending_utf8_character_indexes_original_size =
@@ -152,8 +153,9 @@
                   UNSAFE_TODO(pending_utf8_.data() + pending_utf8_.size()))
            << "\"";
   DVLOG(4) << "  CommitText UTF-8 indexes: "
-           << base::span(pending_utf8_character_indexes_)
-                  .subspan(pending_utf8_character_indexes_original_size);
+           << base::ToString(
+                  base::span(pending_utf8_character_indexes_)
+                      .subspan(pending_utf8_character_indexes_original_size));
 }
 
 void ShapeResultBloberizer::CommitPendingRun() {
@@ -194,9 +196,10 @@
   if (text_size) {
     DVLOG(4) << "  CommitPendingRun text: \""
              << std::string(pending_utf8_.begin(), pending_utf8_.end()) << "\"";
-    DVLOG(4) << "  CommitPendingRun glyphs: " << base::span(pending_glyphs_);
+    DVLOG(4) << "  CommitPendingRun glyphs: "
+             << base::ToString(base::span(pending_glyphs_));
     DVLOG(4) << "  CommitPendingRun indexes: "
-             << base::span(pending_utf8_character_indexes_);
+             << base::ToString(base::span(pending_utf8_character_indexes_));
     DCHECK_EQ(pending_utf8_character_indexes_.size(), run_size);
     std::ranges::copy(pending_utf8_character_indexes_, buffer.clusters);
     std::ranges::copy(pending_utf8_, buffer.utf8text);
@@ -416,7 +419,8 @@
     std::sort(cluster_starts_.begin(), cluster_starts_.end());
     DCHECK_EQ(std::ranges::adjacent_find(cluster_starts_),
               cluster_starts_.end());
-    DVLOG(4) << "  Cluster starts: " << base::span(cluster_starts_);
+    DVLOG(4) << "  Cluster starts: "
+             << base::ToString(base::span(cluster_starts_));
     if (!cluster_starts_.empty()) {
       // 'from' may point inside a cluster; the least seen index may be larger.
       DCHECK_LE(from, *cluster_starts_.begin());
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 95cbb5b..37c6aab 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -327,6 +327,9 @@
         "Linux": "experimental",
         "default": "",
       },
+      origin_trial_feature_name: "AIPromptAPIMultimodalInput",
+      origin_trial_os: ["win", "mac", "linux"],
+      origin_trial_allows_third_party: true,
       implied_by: ["AIPromptAPIMultimodalInput"],
     },
     {
@@ -352,6 +355,9 @@
         "Linux": "experimental",
         "default": "",
       },
+      origin_trial_feature_name: "AIPromptAPIMultimodalInput",
+      origin_trial_os: ["win", "mac", "linux"],
+      origin_trial_allows_third_party: true,
       base_feature_status: "enabled",
       copied_from_base_feature_if: "overridden",
     },
@@ -2432,10 +2438,6 @@
       name: "ForceReduceMotion",
     },
     {
-      name: "ForceTallerSelectPopup",
-      status: {"ChromeOS": "stable"},
-    },
-    {
       // TODO(crbug.com/1419161): Remove this feature after M113 has been stable
       // for a few weeks or more. This is a kill switch that, when enabled, goes
       // back to the old behavior, which was to restore the state for <input>
@@ -2906,6 +2908,10 @@
       status: "stable",
     },
     {
+      name: "LayoutMinSizeAutoIndefinite",
+      status: "stable",
+    },
+    {
       name: "LayoutMinSizeIndefinite",
       status: "stable",
     },
@@ -4427,13 +4433,6 @@
       status: "stable",
     },
     {
-      // Sets the minimum target size of <option> in <select> to 24x24 CSS
-      // pixels to meet Accessibility standards.
-      // https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html
-      name: "SelectOptionAccessibilityTargetSize",
-      status: "stable",
-    },
-    {
       // Makes the HTML parser allow most tags inside of <select> instead of
       // only <option>, <optgroup>, and <hr>.
       // https://github.com/whatwg/html/issues/10310
@@ -5042,23 +5041,12 @@
       origin_trial_allows_third_party: true,
       base_feature_status: "enabled",
       copied_from_base_feature_if: "overridden",
-      implied_by: ["TranslationAPIV1"],
     },
     {
       name: "TranslationAPIForWorkers",
       public: true,
     },
     {
-      name: "TranslationAPIV1",
-      status: {
-        "Win": "experimental",
-        "Mac": "experimental",
-        "Linux": "experimental",
-        "default": "",
-      },
-      copied_from_base_feature_if: "overridden",
-    },
-    {
       // Ensures symbols are treated as word boundaries during traversal.
       // See https://crbug.com/40252642
       name: "TreatSymbolsAsWordBoundary",
diff --git a/third_party/blink/renderer/platform/wtf/BUILD.gn b/third_party/blink/renderer/platform/wtf/BUILD.gn
index cae2f80..0b952ee 100644
--- a/third_party/blink/renderer/platform/wtf/BUILD.gn
+++ b/third_party/blink/renderer/platform/wtf/BUILD.gn
@@ -49,6 +49,7 @@
     "assertions.h",
     "atomic_operations.cc",
     "atomic_operations.h",
+    "bind_post_task.h",
     "bit_field.h",
     "bloom_filter.h",
     "casting.h",
@@ -297,6 +298,7 @@
     "allocator/partitions_test.cc",
     "assertions_test.cc",
     "atomic_operations_test.cc",
+    "bind_post_task_test.cc",
     "bit_field_test.cc",
     "bloom_filter_test.cc",
     "casting_test.cc",
diff --git a/third_party/blink/renderer/platform/wtf/bind_post_task.h b/third_party/blink/renderer/platform/wtf/bind_post_task.h
new file mode 100644
index 0000000..2275221
--- /dev/null
+++ b/third_party/blink/renderer/platform/wtf/bind_post_task.h
@@ -0,0 +1,116 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BIND_POST_TASK_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BIND_POST_TASK_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/functional/callback.h"
+#include "base/location.h"
+#include "base/task/sequenced_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_std.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+
+// See base::BindPostTask() for docs, this is the WTF cross-thread version.
+
+namespace WTF {
+
+namespace internal {
+
+// Based on base::internal::BindPostTaskTrampoline with modifications for
+// CrossThreadFunction/CrossThreadOnceFunction. CrossThreadOnceFunction works
+// identically to the base version, but CrossThreadFunction is not copyable or
+// convertible to a CrossThreadOnceFunction, so a task must be posted to run it
+// instead of directly passing it to `task_runner`. Safety is guaranteed by
+// ensuring CrossThreadBindPostTaskTrampoline is destructed on `task_runner`.
+template <typename CallbackType>
+class CrossThreadBindPostTaskTrampoline {
+ public:
+  CrossThreadBindPostTaskTrampoline(
+      scoped_refptr<base::SequencedTaskRunner> task_runner,
+      const base::Location& location,
+      CallbackType callback)
+      : task_runner_(std::move(task_runner)),
+        location_(location),
+        callback_(std::move(callback)) {
+    DCHECK(task_runner_);
+    CHECK(callback_);
+  }
+  ~CrossThreadBindPostTaskTrampoline() {
+    DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  }
+
+  CrossThreadBindPostTaskTrampoline(
+      const CrossThreadBindPostTaskTrampoline& other) = delete;
+  CrossThreadBindPostTaskTrampoline& operator=(
+      const CrossThreadBindPostTaskTrampoline& other) = delete;
+
+  template <typename... Args>
+  void RunOnce(Args... args) {
+    task_runner_->PostTask(
+        location_, ConvertToBaseOnceCallback(CrossThreadBindOnce(
+                       std::move(callback_), std::forward<Args>(args)...)));
+  }
+
+  template <typename... Args>
+  void RunRepeating(Args... args) {
+    // Safe since the destruction of `this` is posted to `task_runner_`.
+    task_runner_->PostTask(
+        location_,
+        ConvertToBaseOnceCallback(CrossThreadBindOnce(
+            &RunOnTaskRunner<Args...>, CrossThreadUnretained(&callback_),
+            std::forward<Args>(args)...)));
+  }
+
+ private:
+  template <typename... Args>
+  static void RunOnTaskRunner(CallbackType* callback, Args... args) {
+    callback->Run(std::forward<Args>(args)...);
+  }
+
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  const base::Location location_;
+  CallbackType callback_;
+};
+
+}  // namespace internal
+
+template <typename ReturnType, typename... Args>
+  requires std::is_void_v<ReturnType>
+CrossThreadOnceFunction<void(Args...)> BindPostTask(
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    CrossThreadOnceFunction<ReturnType(Args...)> callback,
+    const base::Location& location = FROM_HERE) {
+  using Helper = internal::CrossThreadBindPostTaskTrampoline<
+      CrossThreadOnceFunction<void(Args...)>>;
+
+  std::unique_ptr<Helper, base::OnTaskRunnerDeleter> helper(
+      new Helper(task_runner, location, std::move(callback)),
+      base::OnTaskRunnerDeleter(task_runner));
+  return CrossThreadBindOnce(&Helper::template RunOnce<Args...>,
+                             std::move(helper));
+}
+
+template <typename ReturnType, typename... Args>
+  requires std::is_void_v<ReturnType>
+CrossThreadFunction<void(Args...)> BindPostTask(
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    CrossThreadFunction<ReturnType(Args...)> callback,
+    const base::Location& location = FROM_HERE) {
+  using Helper = internal::CrossThreadBindPostTaskTrampoline<
+      CrossThreadFunction<void(Args...)>>;
+
+  std::unique_ptr<Helper, base::OnTaskRunnerDeleter> helper(
+      new Helper(task_runner, location, std::move(callback)),
+      base::OnTaskRunnerDeleter(task_runner));
+  return CrossThreadBindRepeating(&Helper::template RunRepeating<Args...>,
+                                  std::move(helper));
+}
+
+}  // namespace WTF
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BIND_POST_TASK_H_
diff --git a/third_party/blink/renderer/platform/wtf/bind_post_task_test.cc b/third_party/blink/renderer/platform/wtf/bind_post_task_test.cc
new file mode 100644
index 0000000..e99ea19
--- /dev/null
+++ b/third_party/blink/renderer/platform/wtf/bind_post_task_test.cc
@@ -0,0 +1,278 @@
+// 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 "third_party/blink/renderer/platform/wtf/bind_post_task.h"
+
+#include "base/functional/bind.h"
+#include "base/functional/callback_helpers.h"
+#include "base/location.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace WTF {
+
+namespace {
+
+void Increment(int* value) {
+  ++(*value);
+}
+
+void Add(int* sum, int a, int b) {
+  *sum = a + b;
+}
+
+void SetIntFromUniquePtr(int* variable, std::unique_ptr<int> value) {
+  *variable = *value;
+}
+
+int Multiply(int value) {
+  return value * 5;
+}
+
+// Helper class from base/task/bind_post_task_unittest.cc, adapted for WTF.
+class WTFSequenceRestrictionChecker {
+ public:
+  WTFSequenceRestrictionChecker(bool* set_on_destroy,
+                                base::OnceClosure quit_closure)
+      : set_on_destroy_(set_on_destroy),
+        quit_closure_(std::move(quit_closure)) {}
+
+  ~WTFSequenceRestrictionChecker() {
+    EXPECT_TRUE(checker_.CalledOnValidSequence());
+    *set_on_destroy_ = true;
+    if (quit_closure_) {
+      std::move(quit_closure_).Run();
+    }
+  }
+
+  void Run() { EXPECT_TRUE(checker_.CalledOnValidSequence()); }
+
+ private:
+  base::SequenceCheckerImpl checker_;
+  bool* set_on_destroy_;
+  base::OnceClosure quit_closure_;
+};
+
+}  // namespace
+
+class BindPostTaskTest : public testing::Test {
+ public:
+  ~BindPostTaskTest() override {
+    // Ensures boundg tasks destruct cleanly before teardown.
+    CycleTasks();
+  }
+
+ protected:
+  void CycleTasks() {
+    task_runner_->PostTask(FROM_HERE, task_environment_.QuitClosure());
+    task_environment_.RunUntilQuit();
+  }
+
+  base::test::TaskEnvironment task_environment_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_ =
+      task_environment_.GetMainThreadTaskRunner();
+};
+
+TEST_F(BindPostTaskTest, OnceFunctionNoArgs) {
+  int count = 0;
+
+  auto bound_task = BindPostTask(
+      task_runner_,
+      CrossThreadBindOnce(&Increment, CrossThreadUnretained(&count)));
+  std::move(bound_task).Run();
+
+  EXPECT_EQ(0, count);
+  CycleTasks();
+  EXPECT_EQ(1, count);
+}
+
+TEST_F(BindPostTaskTest, OnceFunctionWithArgs) {
+  int sum = 0;
+
+  auto bound_task = BindPostTask(
+      task_runner_,
+      CrossThreadBindOnce(&Add, CrossThreadUnretained(&sum), 5, 10));
+  std::move(bound_task).Run();
+
+  EXPECT_EQ(0, sum);
+  CycleTasks();
+  EXPECT_EQ(15, sum);
+}
+
+TEST_F(BindPostTaskTest, OnceWithBoundMoveOnlyArg) {
+  int val = 0;
+
+  auto inner_cb =
+      CrossThreadBindOnce(&SetIntFromUniquePtr, CrossThreadUnretained(&val),
+                          std::make_unique<int>(10));
+  auto post_cb = BindPostTask(task_runner_, std::move(inner_cb));
+  std::move(post_cb).Run();
+
+  EXPECT_EQ(val, 0);
+  CycleTasks();
+  EXPECT_EQ(val, 10);
+}
+
+TEST_F(BindPostTaskTest, OnceWithUnboundMoveOnlyArg) {
+  int val = 0;
+
+  auto inner_cb =
+      CrossThreadBindOnce(&SetIntFromUniquePtr, CrossThreadUnretained(&val));
+  auto post_cb = BindPostTask(task_runner_, std::move(inner_cb));
+  std::move(post_cb).Run(std::make_unique<int>(10));
+
+  EXPECT_EQ(val, 0);
+  CycleTasks();
+  EXPECT_EQ(val, 10);
+}
+
+TEST_F(BindPostTaskTest, OnceWithIgnoreResult) {
+  auto inner_cb = CrossThreadBindOnce(base::IgnoreResult(&Multiply));
+  auto post_cb = BindPostTask(task_runner_, std::move(inner_cb));
+
+  std::move(post_cb).Run(1);
+  CycleTasks();
+}
+
+TEST_F(BindPostTaskTest, OnceRunDestroyedOnBound) {
+  bool destroyed_on_main = false;
+  auto checker = std::make_unique<WTFSequenceRestrictionChecker>(
+      &destroyed_on_main, task_environment_.QuitClosure());
+
+  auto cb_owning_checker = CrossThreadBindOnce(
+      &WTFSequenceRestrictionChecker::Run, std::move(checker));
+  auto post_cb = BindPostTask(task_runner_, std::move(cb_owning_checker));
+
+  base::Thread other_thread("other_thread_once_run");
+  ASSERT_TRUE(other_thread.Start());
+  other_thread.task_runner()->PostTask(
+      FROM_HERE, ConvertToBaseOnceCallback(std::move(post_cb)));
+  other_thread.Stop();  // Flushes tasks and waits for completion.
+
+  EXPECT_FALSE(destroyed_on_main);
+  task_environment_.RunUntilQuit();
+  EXPECT_TRUE(destroyed_on_main);
+}
+
+TEST_F(BindPostTaskTest, OnceNotRunDestroyedOnBound) {
+  bool destroyed_on_main = false;
+  auto checker = std::make_unique<WTFSequenceRestrictionChecker>(
+      &destroyed_on_main, task_environment_.QuitClosure());
+
+  auto cb_owning_checker = CrossThreadBindOnce(
+      &WTFSequenceRestrictionChecker::Run, std::move(checker));
+  auto post_cb = BindPostTask(task_runner_, std::move(cb_owning_checker));
+
+  base::Thread other_thread("other_thread_once_not_run");
+  ASSERT_TRUE(other_thread.Start());
+  // Destroy post_cb on another thread. This should post back to main thread
+  // for destruction of cb_owning_checker.
+  other_thread.task_runner()->PostTask(
+      FROM_HERE, base::DoNothingWithBoundArgs(
+                     ConvertToBaseOnceCallback(std::move(post_cb))));
+  other_thread.Stop();
+
+  EXPECT_FALSE(destroyed_on_main);
+  task_environment_.RunUntilQuit();
+  EXPECT_TRUE(destroyed_on_main);
+}
+
+TEST_F(BindPostTaskTest, RepeatingFunctionNoArgs) {
+  int count = 0;
+
+  auto bound_task = BindPostTask(
+      task_runner_,
+      CrossThreadBindRepeating(&Increment, CrossThreadUnretained(&count)));
+
+  bound_task.Run();
+  EXPECT_EQ(0, count);
+  CycleTasks();
+  EXPECT_EQ(1, count);
+
+  bound_task.Run();
+  EXPECT_EQ(1, count);
+  CycleTasks();
+  EXPECT_EQ(2, count);
+}
+
+TEST_F(BindPostTaskTest, RepeatingFunctionWithArgs) {
+  int sum = 0;
+
+  auto bound_task = BindPostTask(
+      task_runner_,
+      CrossThreadBindRepeating(&Add, CrossThreadUnretained(&sum), 3, 4));
+
+  bound_task.Run();
+  EXPECT_EQ(0, sum);
+  CycleTasks();
+  EXPECT_EQ(7, sum);
+
+  // Re-running a repeating callback will re-evaluate arguments if not owned.
+  // In this case, the values are hardcoded, so the result is the same.
+  sum = 0;
+  bound_task.Run();
+  EXPECT_EQ(0, sum);
+  CycleTasks();
+  EXPECT_EQ(7, sum);
+}
+
+TEST_F(BindPostTaskTest, RepeatingWithIgnoreResult) {
+  auto inner_cb = CrossThreadBindRepeating(base::IgnoreResult(&Multiply));
+  auto post_cb = BindPostTask(task_runner_, std::move(inner_cb));
+
+  post_cb.Run(1);
+  CycleTasks();
+
+  post_cb.Run(2);
+  CycleTasks();
+}
+
+TEST_F(BindPostTaskTest, RepeatingRunDestroyedOnBound) {
+  bool destroyed_on_main = false;
+  auto checker_ptr = std::make_unique<WTFSequenceRestrictionChecker>(
+      &destroyed_on_main, task_environment_.QuitClosure());
+  auto cb_owning_checker = CrossThreadBindRepeating(
+      &WTFSequenceRestrictionChecker::Run, std::move(checker_ptr));
+  auto post_cb = BindPostTask(task_runner_, std::move(cb_owning_checker));
+
+  base::Thread other_thread("other_thread_repeating_run");
+  ASSERT_TRUE(other_thread.Start());
+  other_thread.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(base::BindLambdaForTesting([&]() {
+        post_cb.Run();
+        post_cb.Run();
+        post_cb.Reset();
+      })));
+  other_thread.Stop();
+
+  EXPECT_FALSE(destroyed_on_main);
+  task_environment_.RunUntilQuit();
+  EXPECT_TRUE(destroyed_on_main);
+}
+
+TEST_F(BindPostTaskTest, RepeatingNotRunDestroyedOnBound) {
+  bool destroyed_on_main = false;
+  auto checker_ptr = std::make_unique<WTFSequenceRestrictionChecker>(
+      &destroyed_on_main, task_environment_.QuitClosure());
+  auto cb_owning_checker = CrossThreadBindRepeating(
+      &WTFSequenceRestrictionChecker::Run, std::move(checker_ptr));
+  auto post_cb = BindPostTask(task_runner_, std::move(cb_owning_checker));
+
+  base::Thread other_thread("other_thread_repeating_run");
+  ASSERT_TRUE(other_thread.Start());
+  // Destroy post_cb on another thread. This should post back to main thread
+  // for destruction of cb_owning_checker.
+  other_thread.task_runner()->PostTask(
+      FROM_HERE, base::DoNothingWithBoundArgs(
+                     ConvertToBaseRepeatingCallback(std::move(post_cb))));
+  other_thread.Stop();  // Flushes tasks and waits for completion.
+
+  EXPECT_FALSE(destroyed_on_main);
+  task_environment_.RunUntilQuit();
+  EXPECT_TRUE(destroyed_on_main);
+}
+
+}  // namespace WTF
diff --git a/third_party/blink/renderer/platform/wtf/cross_thread_copier_media.h b/third_party/blink/renderer/platform/wtf/cross_thread_copier_media.h
index e4ef52e..9da311cc 100644
--- a/third_party/blink/renderer/platform/wtf/cross_thread_copier_media.h
+++ b/third_party/blink/renderer/platform/wtf/cross_thread_copier_media.h
@@ -38,6 +38,8 @@
 class AudioBus;
 class AudioParameters;
 struct AudioGlitchInfo;
+template <typename T>
+class TypedStatus;
 class VideoFrame;
 struct VideoCaptureFeedback;
 struct VideoTransformation;
@@ -63,6 +65,12 @@
   STATIC_ONLY(CrossThreadCopier);
 };
 
+template <typename T>
+struct CrossThreadCopier<media::TypedStatus<T>>
+    : public CrossThreadCopierPassThrough<media::TypedStatus<T>> {
+  STATIC_ONLY(CrossThreadCopier);
+};
+
 template <>
 struct CrossThreadCopier<media::VideoCaptureFeedback>
     : public CrossThreadCopierPassThrough<media::VideoCaptureFeedback> {
diff --git a/third_party/blink/renderer/platform/wtf/text/string_impl.cc b/third_party/blink/renderer/platform/wtf/text/string_impl.cc
index 231abba..5afc786 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_impl.cc
+++ b/third_party/blink/renderer/platform/wtf/text/string_impl.cc
@@ -276,7 +276,8 @@
 
   StaticStringsTable::const_iterator it = StaticStrings().find(hash);
   if (it != StaticStrings().end()) {
-    DCHECK_EQ(it->value->Span8(), base::as_bytes(string));
+    DCHECK_EQ(base::as_string_view(it->value->Span8()),
+              base::as_string_view(string));
     return it->value;
   }
   const wtf_size_t narrowed_length = static_cast<wtf_size_t>(string.size());
diff --git a/third_party/blink/tools/OWNERS b/third_party/blink/tools/OWNERS
index 77bd5de..e485616 100644
--- a/third_party/blink/tools/OWNERS
+++ b/third_party/blink/tools/OWNERS
@@ -1,3 +1,2 @@
 tkent@chromium.org
-weizhong@google.com
 jonathanjlee@google.com
diff --git a/third_party/blink/web_tests/HEADLESS_OWNERS b/third_party/blink/web_tests/HEADLESS_OWNERS
index 1aa8b6d..3c73769 100644
--- a/third_party/blink/web_tests/HEADLESS_OWNERS
+++ b/third_party/blink/web_tests/HEADLESS_OWNERS
@@ -31,7 +31,6 @@
 
 
 rbyers@chromium.org
-weizhong@google.com
 masonf@chromium.org
 pdr@chromium.org
 futhark@chromium.org
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index ed0086e..cc34ef2cc 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1688,9 +1688,6 @@
 # crbug.com/40771785 This test fails when SplitCacheByNetworkIsolationKey is enabled.
 crbug.com/40771785 http/tests/devtools/network/network-prefetch.js [ Failure ]
 
-crbug.com/1051044 external/wpt/css/filter-effects/effect-reference-feimage-001.html [ Failure Pass ]
-crbug.com/1051044 external/wpt/css/filter-effects/effect-reference-feimage-003.html [ Failure Pass ]
-
 # On all platforms media tests don't currently use gpu-accelerated (proprietary)
 # codecs, so no benefit to running them again with gpu acceleration enabled.
 crbug.com/555703 virtual/media-gpu-accelerated/* [ Failure Skip ]
@@ -2744,8 +2741,12 @@
 crbug.com/413411328 external/wpt/css/css-values/urls/referrer-policy/unsafe-url/url-image-referrerpolicy-same-origin.sub.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
-crbug.com/422822346 [ Linux ] external/wpt/webdriver/tests/bidi/browser/create_user_context/invalid.py [ Failure ]
-crbug.com/422433462 [ Linux ] external/wpt/webdriver/tests/bidi/browsing_context/history_updated/history_updated.py [ Failure ]
+crbug.com/422832549 [ Win10.20h2 ] external/wpt/css/css-contain/contain-body-dir-004.html [ Timeout ]
+crbug.com/422814959 [ Win10.20h2 ] external/wpt/html/cross-origin-embedder-policy/credentialless/cache.window.html [ Crash ]
+crbug.com/422790249 [ Win10.20h2 ] external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/property-location-set.https.html [ Crash ]
+crbug.com/422809234 [ Win11 ] virtual/close-watcher/external/wpt/close-watcher/iframes/dialog-same-origin-ynn.html [ Skip Timeout ]
+crbug.com/422822346 external/wpt/webdriver/tests/bidi/browser/create_user_context/invalid.py [ Failure ]
+crbug.com/422433462 external/wpt/webdriver/tests/bidi/browsing_context/history_updated/history_updated.py [ Failure ]
 crbug.com/421746161 [ Mac14 ] external/wpt/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html?1-2 [ Crash Pass ]
 crbug.com/421653422 [ Mac12-arm64 ] external/wpt/css/css-pseudo/active-selection-021.html [ Failure ]
 crbug.com/421650040 [ Win10.20h2 ] external/wpt/fetch/metadata/xslt.https.sub.html [ Timeout ]
@@ -2840,6 +2841,7 @@
 [ Linux ] external/wpt/css/css-display/display-contents-inline-002.html [ Failure ]
 [ Win10.20h2 ] external/wpt/fedcm/fedcm-store.https.html [ Timeout ]
 [ Win10.20h2 ] external/wpt/touch-events/single-touch-vertical-rl.html [ Timeout ]
+crbug.com/422822316 [ Win11-arm64 ] external/wpt/touch-events/single-touch-vertical-rl.html [ Timeout ]
 [ Win10.20h2 ] virtual/speculation-rules-prerender-target-hint/external/wpt/speculation-rules/prerender/no-vary-search.https.html?2-2 [ Crash ]
 crbug.com/417031845 [ Win10.20h2 ] virtual/view-transition-mpa-serialization/external/wpt/css/css-view-transitions/dynamic-stylesheet-animations-timing-function.html [ Pass Timeout ]
 crbug.com/416615751 external/wpt/css/css-contain/contain-layout-stacking-context-001.html [ Failure ]
diff --git a/third_party/blink/web_tests/VIRTUAL_OWNERS b/third_party/blink/web_tests/VIRTUAL_OWNERS
index b635d43b..72d9df0 100644
--- a/third_party/blink/web_tests/VIRTUAL_OWNERS
+++ b/third_party/blink/web_tests/VIRTUAL_OWNERS
@@ -47,4 +47,3 @@
 schenney@chromium.org
 tkent@chromium.org
 vmpstr@chromium.org
-weizhong@google.com
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index fd945466..7717e7ce8 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1008,7 +1008,7 @@
       "fast/parser"
     ],
     "args": ["--disable-features=DOMPartsAPI,DOMPartsAPIMinimal"],
-    "expires": "Jun 1, 2025"
+    "expires": "Jun 1, 2026"
   },
   {
     "prefix": "dom-parts-minimal",
@@ -1018,7 +1018,7 @@
       "external/wpt/dom/parts"
     ],
     "args": ["--enable-features=DOMPartsAPIMinimal"],
-    "expires": "Jun 1, 2025"
+    "expires": "Jun 1, 2026"
   },
   {
     "prefix": "backface-visibility-interop",
@@ -1262,7 +1262,7 @@
     "platforms": ["Linux", "Mac", "Win"],
     "bases": ["external/wpt/html/semantics/popovers"],
     "args": ["--disable-blink-features=HTMLAnchorAttribute"],
-    "expires": "Jun 1, 2025"
+    "expires": "Jun 1, 2026"
   },
   {
     "prefix": "interest-target-no-partial-interest",
@@ -1282,7 +1282,7 @@
       "external/wpt/html/semantics/interactive-elements/the-dialog-element"
     ],
     "args": ["--disable-blink-features=DialogCloseWhenOpenRemoved"],
-    "expires": "Jun 1, 2025"
+    "expires": "Jun 1, 2026"
   },
   {
     "prefix": "parakeet",
@@ -1785,7 +1785,7 @@
     "platforms": ["Linux", "Mac", "Win"],
     "bases": ["external/wpt/custom-elements/registries/"],
     "args": ["--disable-blink-features=ScopedCustomElementRegistry"],
-    "expires": "Jun 1, 2025"
+    "expires": "Jun 1, 2026"
   },
 
   "This individual test must be run separately from others, because it makes a",
@@ -4054,50 +4054,6 @@
       "syg@chromium.org",
       "samuelmaddock@electronjs.org"
     ]
-  },{
-    "prefix": "select-option-accessibility-target-size-disabled",
-    "platforms": ["Linux", "Win"],
-    "bases": [
-      "fast/forms/select-popup/popup-menu-accessibility-minimum-target-size.html",
-      "fast/forms/select-popup/popup-menu-appearance.html",
-      "fast/forms/select-popup/popup-menu-appearance-single-option.html",
-      "fast/forms/select-popup/popup-menu-appearance-long.html",
-      "fast/forms/select-popup/popup-menu-appearance-zero-font-size.html",
-      "fast/forms/select-popup/popup-menu-appearance-transform.html"
-    ],
-    "args": ["--disable-features=SelectOptionAccessibilityTargetSize"],
-    "expires": "Jun 1, 2025",
-    "owners": ["masonf@chromium.org", "stephanie.zhang@microsoft.com", "sajos@microsoft.com"]
-  },
-  {
-    "prefix": "force-taller-select-popup-disabled",
-    "platforms": ["Linux", "Win"],
-    "bases": [
-      "fast/forms/select-popup/popup-menu-accessibility-minimum-target-size.html",
-      "fast/forms/select-popup/popup-menu-appearance.html",
-      "fast/forms/select-popup/popup-menu-appearance-single-option.html",
-      "fast/forms/select-popup/popup-menu-appearance-long.html",
-      "fast/forms/select-popup/popup-menu-appearance-zero-font-size.html",
-      "fast/forms/select-popup/popup-menu-appearance-transform.html"
-    ],
-    "args": ["--disable-features=ForceTallerSelectPopup"],
-    "expires": "Jun 1, 2025",
-    "owners": ["tkent@chromium.org", "stephanie.zhang@microsoft.com", "sajos@microsoft.com"]
-  },
-  {
-    "prefix": "both-taller-select-popup-options-disabled",
-    "platforms": ["Linux", "Win"],
-    "bases": [
-      "fast/forms/select-popup/popup-menu-accessibility-minimum-target-size.html",
-      "fast/forms/select-popup/popup-menu-appearance.html",
-      "fast/forms/select-popup/popup-menu-appearance-single-option.html",
-      "fast/forms/select-popup/popup-menu-appearance-long.html",
-      "fast/forms/select-popup/popup-menu-appearance-zero-font-size.html",
-      "fast/forms/select-popup/popup-menu-appearance-transform.html"
-    ],
-    "args": ["--disable-features=SelectOptionAccessibilityTargetSize,ForceTallerSelectPopup"],
-    "expires": "Jun 1, 2025",
-    "owners": ["masonf@chromium.org", "stephanie.zhang@microsoft.com", "sajos@microsoft.com"]
   },
   {
     "prefix": "partitioned-popins-disabled",
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 8af0ed5..d883f83d 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -188473,7 +188473,7 @@
       ]
      ],
      "host-defined.html": [
-      "9e9776754a3f360537e899e064a0d3b36dbe14c3",
+      "563bc52e527927030270658c4c965d51045edcbb",
       [
        null,
        [
@@ -379318,7 +379318,7 @@
       []
      ],
      "idlharness-expected.txt": [
-      "c805a680ffa67ef11734df0a4020b593b29e0aaa",
+      "d61ee6abf857f9b0d4ae3e2f5a748dba93b8c822",
       []
      ],
      "iframe.html": [
@@ -382153,10 +382153,6 @@
       "5fa6248617c90049e42ebbd8c7d7dc77edb056af",
       []
      ],
-     "idlharness.window-expected.txt": [
-      "73b50e7884b31d31be24e60058480c17750b9fbe",
-      []
-     ],
      "initial-about-blank.window-expected.txt": [
       "a715c1e2339881b9cc061246cf1c0b71cdf4d371",
       []
@@ -412963,7 +412959,7 @@
      []
     ],
     "crash-reporting.idl": [
-     "6eaee138a828f7749026458265e7db598822330f",
+     "ba21afcf39718207fe8f56308adc18f9287b9e88",
      []
     ],
     "credential-management.idl": [
@@ -413115,7 +413111,7 @@
      []
     ],
     "cssom-view.idl": [
-     "88abb078485fc2159c15b5401dc9944d47a47940",
+     "160c27ca05046cf543ecb575019826f2e9379a78",
      []
     ],
     "cssom.idl": [
@@ -413167,7 +413163,7 @@
      []
     ],
     "element-timing.idl": [
-     "ef73ca6c0f610ff8cdb14a8bd0859efb8eca743b",
+     "4f42823704a42d9c40beed4cce8ec9824cfcd65e",
      []
     ],
     "encoding.idl": [
@@ -413275,7 +413271,7 @@
      []
     ],
     "html.idl": [
-     "9c84e6a67efa4f7ffe6f9638c582af4424c473db",
+     "3a7dce9693ef3ecaaf1ea274029663b8068c4222",
      []
     ],
     "idle-detection.idl": [
@@ -413539,7 +413535,7 @@
      []
     ],
     "privacy-preserving-attribution.idl": [
-     "0ab5d0fc21e49d2140ee014819debc26998473ef",
+     "02a3579820469e72913220ce4e6086d486c33d28",
      []
     ],
     "private-aggregation-api.idl": [
@@ -413595,7 +413591,7 @@
      []
     ],
     "resource-timing.idl": [
-     "66f2841d744af3a39a55c907c739a9f1bb92aff0",
+     "499d27b6ee69b984ad57c66c929352be1eb17ab7",
      []
     ],
     "saa-non-cookie-storage.idl": [
@@ -413614,12 +413610,8 @@
      "6f93db15a74e052913a277e5130226280a3f9311",
      []
     ],
-    "scoped-custom-elements-registry.idl": [
-     "46ca2d6b9c45805d8aa684af7fe91af6dd5d7919",
-     []
-    ],
     "screen-capture.idl": [
-     "db9282ce0a57bb3b84ea45f5ed2d7e69bc3a8a32",
+     "eb5685eee4117d03d2b6dbad15edf430c1c2cac4",
      []
     ],
     "screen-orientation.idl": [
@@ -413639,7 +413631,7 @@
      []
     ],
     "secure-payment-confirmation.idl": [
-     "5b67ca6267839c4c54ba75c55a9da03231642303",
+     "0a2207684ec71074ffea786bf459b6073799e8d3",
      []
     ],
     "selection-api.idl": [
@@ -413667,7 +413659,7 @@
      []
     ],
     "speech-api.idl": [
-     "94a416f262b361e326c5b3c89fa8d160c1118b48",
+     "9620e60dc5057b5f48100bc4563d01aaf174d303",
      []
     ],
     "sri.idl": [
@@ -413763,7 +413755,7 @@
      []
     ],
     "wai-aria.idl": [
-     "deebc5626e2a925c4b7c7bad92c8492ebb4dcb08",
+     "3364bc9a76936b1eed3873af339e5c11feb10458",
      []
     ],
     "wasm-js-api.idl": [
@@ -413775,7 +413767,7 @@
      []
     ],
     "web-animations-2.idl": [
-     "c4a0c2532d97806eeead38d1e1924c485d775491",
+     "f18cdd4f4583d959119a07e4248e94210fad1036",
      []
     ],
     "web-animations.idl": [
@@ -413871,7 +413863,7 @@
      []
     ],
     "webgpu.idl": [
-     "4fec46a2557033a941b5da7a7481aa8125696ed2",
+     "1fc896c6b1625f13c6ed00ce14a34ce1dfa4db88",
      []
     ],
     "webhid.idl": [
@@ -413987,7 +413979,7 @@
      []
     ],
     "writing-assistance-apis.idl": [
-     "916daee754e3ef992ad8fa282882c88dd9aa0734",
+     "82acfdb48e2d3a051ae0e43d6d34355cf8259ae3",
      []
     ],
     "xhr.idl": [
@@ -428157,7 +428149,7 @@
      []
     ],
     "idlharness.window-expected.txt": [
-     "26510427d7d3e25db33805729267b6919345ed3c",
+     "18443119282ecb0b3055145065963199613ee71b",
      []
     ],
     "webspeech.js": [
@@ -433577,7 +433569,7 @@
      ]
     },
     "idlharness.window-expected.txt": [
-     "25a1596d55e853bc810555d3e2ebadeee959921b",
+     "6ef6b07ba91515289811863993ab1bb1c4a0f832",
      []
     ],
     "interfaces": {
@@ -540575,24 +540567,6 @@
        {}
       ]
      ],
-     "idlharness.window.js": [
-      "b2727e3a8742a791ba51fabf0de02c744ae0d732",
-      [
-       "custom-elements/registries/idlharness.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/WebIDLParser.js"
-         ],
-         [
-          "script",
-          "/resources/idlharness.js"
-         ]
-        ]
-       }
-      ]
-     ],
      "initial-about-blank.window.js": [
       "b3bb7e139b5c543bae2d4cc86d3f5ebe1fe197f1",
       [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/grid/grid-gap-decorations-045-ref.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/grid/grid-gap-decorations-045-ref.html
new file mode 100644
index 0000000..b002cf0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/grid/grid-gap-decorations-045-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/#break">
+<link rel="author" title="Javier Contreras" href="mailto:javiercon@microsoft.com">
+<style>
+    #container,
+    body {
+        overflow: hidden;
+        margin: 0px;
+    }
+
+    #container {
+        display: grid;
+        grid-template-rows: 4rem 2rem auto;
+        width: 80%;
+        gap: 2px;
+        border: solid 2px black;
+        row-rule: 2px solid hotpink, 1px dashed grey;
+        background-color: teal;
+    }
+
+    #one {
+        height: 100px;
+        width: 100px;
+    }
+</style>
+<div id="container">
+    <div id="one"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/grid/grid-gap-decorations-045.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/grid/grid-gap-decorations-045.html
new file mode 100644
index 0000000..a90d45d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/grid/grid-gap-decorations-045.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>
+    CSS Gap Decorations: Make sure decorations resize when container/window resizes.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
+<link rel="match" href="grid-gap-decorations-045-ref.html">
+<link rel="author" title="Javier Contreras" href="mailto:javiercon@microsoft.com">
+<style>
+    #container,
+    body {
+        overflow: hidden;
+        margin: 0px;
+    }
+
+    #container {
+        display: grid;
+        grid-template-rows: 4rem 2rem auto;
+        width: 50%;
+        gap: 2px;
+        border: solid 2px black;
+        row-rule: 2px solid hotpink, 1px dashed grey;
+        background-color: teal;
+    }
+
+    #one {
+        height: 100px;
+        width: 100px;
+    }
+</style>
+<div id="container">
+    <div id="one"></div>
+</div>
+<script>
+    window.addEventListener('load', function () {
+        const container = document.getElementById('container');
+        container.style.width = '80%';
+    });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/idlharness-expected.txt b/third_party/blink/web_tests/external/wpt/css/cssom-view/idlharness-expected.txt
index c805a68..d61ee6a 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom-view/idlharness-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/idlharness-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 58 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 61 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] Screen interface: existence and properties of interface object
   assert_equals: prototype of self's property "Screen" is not Function.prototype expected function "function () { [native code] }" but got function "function EventTarget() { [native code] }"
 [FAIL] Screen interface: existence and properties of interface prototype object
@@ -12,6 +12,10 @@
   assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
 [FAIL] CSSPseudoElement interface: operation convertPointFromNode(DOMPointInit, GeometryNode, optional ConvertCoordinateOptions)
   assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
+[FAIL] HTMLElement interface: attribute scrollParent
+  assert_true: The prototype object must have a property "scrollParent" expected true got false
+[FAIL] HTMLElement interface: document.createElement("div") must inherit property "scrollParent" with the proper type
+  assert_inherits: property "scrollParent" not found in prototype chain
 [FAIL] Element interface: document.createElement("div") must inherit property "getBoxQuads(optional BoxQuadOptions)" with the proper type
   assert_inherits: property "getBoxQuads" not found in prototype chain
 [FAIL] Element interface: calling getBoxQuads(optional BoxQuadOptions) on document.createElement("div") with too few arguments must throw TypeError
@@ -28,6 +32,8 @@
   assert_inherits: property "convertPointFromNode" not found in prototype chain
 [FAIL] Element interface: calling convertPointFromNode(DOMPointInit, GeometryNode, optional ConvertCoordinateOptions) on document.createElement("div") with too few arguments must throw TypeError
   assert_inherits: property "convertPointFromNode" not found in prototype chain
+[FAIL] HTMLElement interface: document.createElement("img") must inherit property "scrollParent" with the proper type
+  assert_inherits: property "scrollParent" not found in prototype chain
 [FAIL] Element interface: document.createElement("img") must inherit property "getBoxQuads(optional BoxQuadOptions)" with the proper type
   assert_inherits: property "getBoxQuads" not found in prototype chain
 [FAIL] Element interface: calling getBoxQuads(optional BoxQuadOptions) on document.createElement("img") with too few arguments must throw TypeError
diff --git a/third_party/blink/web_tests/external/wpt/custom-elements/registries/idlharness.window-expected.txt b/third_party/blink/web_tests/external/wpt/custom-elements/registries/idlharness.window-expected.txt
deleted file mode 100644
index 73b50e78..0000000
--- a/third_party/blink/web_tests/external/wpt/custom-elements/registries/idlharness.window-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] idl_test validation
-  Validation error at line 4 in scoped-custom-elements-registry, inside `partial interface CustomElementRegistry`:\n  undefined initialize(Node root)\n            ^ The operation "initialize" has already been defined for the base interface "CustomElementRegistry" either in itself or in a mixin\n\nValidation error at line 320 in dom, inside `dictionary ImportNodeOptions`:\ndictionary ImportNodeOptions {\n           ^ The name "ImportNodeOptions" of type "dictionary" was already seen
-[FAIL] Partial interface CustomElementRegistry: member names are unique
-  assert_true: member undefined is unique expected true got false
-[FAIL] Partial interface HTMLTemplateElement: member names are unique
-  assert_true: member shadowRootCustomElementRegistry is unique expected true got false
-[FAIL] Partial interface Element: member names are unique
-  assert_true: member customElementRegistry is unique expected true got false
-[FAIL] Partial dictionary ShadowRootInit: member names are unique
-  assert_true: member customElementRegistry is unique expected true got false
-[FAIL] Partial dictionary ElementCreationOptions: member names are unique
-  assert_true: member customElementRegistry is unique expected true got false
-[FAIL] Document includes DocumentOrShadowRoot: member names are unique
-  assert_true: member customElementRegistry is unique expected true got false
-[FAIL] ShadowRoot includes DocumentOrShadowRoot: member names are unique
-  assert_true: member customElementRegistry is unique expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/custom-elements/registries/idlharness.window.js b/third_party/blink/web_tests/external/wpt/custom-elements/registries/idlharness.window.js
deleted file mode 100644
index b2727e3..0000000
--- a/third_party/blink/web_tests/external/wpt/custom-elements/registries/idlharness.window.js
+++ /dev/null
@@ -1,20 +0,0 @@
-// META: script=/resources/WebIDLParser.js
-// META: script=/resources/idlharness.js
-
-idl_test(
-  ["scoped-custom-elements-registry"],
-  ["html", "dom"],
-  (idl_array) => {
-    let element = document.createElement("div");
-    let shadowRoot = element.attachShadow({ mode: "open" });
-    let customElementRegistry = new CustomElementRegistry();
-    let templateElement = document.createElement("template");
-    idl_array.add_objects({
-      document,
-      element,
-      shadowRoot,
-      customElementRegistry,
-      templateElement,
-    });
-  },
-);
diff --git a/third_party/blink/web_tests/external/wpt/dom/nodes/attributes-expected.txt b/third_party/blink/web_tests/external/wpt/dom/nodes/attributes-expected.txt
index 06affc3..b62a4bea 100644
--- a/third_party/blink/web_tests/external/wpt/dom/nodes/attributes-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/dom/nodes/attributes-expected.txt
@@ -1,9 +1,9 @@
 This is a testharness.js-based test.
 Found 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] When qualifiedName does not match the Name production, an INVALID_CHARACTER_ERR exception is to be thrown. (toggleAttribute)
-  assert_throws_dom: function "function() { el.toggleAttribute(invalid_names[i]) }" did not throw
+  assert_throws_dom: function "function() { el.toggleAttribute(invalid_names[i], true) }" did not throw
 [FAIL] When qualifiedName does not match the Name production, an INVALID_CHARACTER_ERR exception is to be thrown, even if the attribute is already present. (toggleAttribute)
-  assert_throws_dom: function "function() {\n      el.children[i].toggleAttribute("~")\n    }" did not throw
+  assert_throws_dom: function "function() {\n      el.children[i].toggleAttribute("~", false)\n    }" did not throw
 [FAIL] When qualifiedName does not match the Name production, an INVALID_CHARACTER_ERR exception is to be thrown. (setAttribute)
   assert_throws_dom: function "function() { el.setAttribute(invalid_names[i], "test") }" did not throw
 [FAIL] When qualifiedName does not match the Name production, an INVALID_CHARACTER_ERR exception is to be thrown, even if the attribute is already present. (setAttribute)
diff --git a/third_party/blink/web_tests/external/wpt/dom/nodes/name-validation.tentative.html b/third_party/blink/web_tests/external/wpt/dom/nodes/name-validation.tentative.html
index a6b12d1ef..ca5e9c1 100644
--- a/third_party/blink/web_tests/external/wpt/dom/nodes/name-validation.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/dom/nodes/name-validation.tentative.html
@@ -203,6 +203,12 @@
         `element.toggleAttribute should not have thrown an error for: ${debugString(validAttributeName)} ${error.toString()}`);
     }
     try {
+      element.toggleAttribute(validAttributeName, false);
+    } catch (error) {
+      assert_unreached(
+        `element.toggleAttribute with false should not have thrown an error for: ${debugString(validAttributeName)} ${error.toString()}`);
+    }
+    try {
       document.createAttribute(validAttributeName);
     } catch (error) {
       assert_unreached(
@@ -221,6 +227,10 @@
       `element.toggleAttribute should throw an error for: ${debugString(invalidAttributeName)}`);
     assert_throws_dom(
       'InvalidCharacterError',
+      () => element.toggleAttribute(invalidAttributeName, false),
+      `element.toggleAttribute with false should throw an error for: ${debugString(invalidAttributeName)}`);
+    assert_throws_dom(
+      'InvalidCharacterError',
       () => document.createAttribute(invalidAttributeName),
       `document.createAttribute should throw an error for: ${debugString(invalidAttributeName)}`);
   }
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-height-in-flex-ref.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-height-in-flex-ref.html
new file mode 100644
index 0000000..04c000a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-height-in-flex-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<style>
+
+select, select::picker(select) {
+  appearance: base-select;
+}
+select::picker-icon {
+  font-size: 60px;
+}
+
+</style>
+
+<select>
+  <option>Option</option>
+</select>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-height-in-flex.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-height-in-flex.html
new file mode 100644
index 0000000..628c7ec5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-height-in-flex.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<link rel="match" href="min-height-in-flex-ref.html">
+<style>
+
+select, select::picker(select) {
+  appearance: base-select;
+}
+select::picker-icon {
+  font-size: 60px;
+}
+
+</style>
+
+<div style="display:flex; flex-direction: column; align-items: flex-start; height: 100px;">
+  <select>
+    <option>Option</option>
+  </select>
+  <div style="color: transparent">
+    This div has a lot of text in it but it should not make the select become smaller.
+    A lot of text.  Really a lot of text.
+    A lot of text.  Really a lot of text.
+    A lot of text.  Really a lot of text.
+    A lot of text.  Really a lot of text.
+    A lot of text.  Really a lot of text.
+    A lot of text.  Really a lot of text.
+    A lot of text.  Really a lot of text.
+    A lot of text.  Really a lot of text.
+    A lot of text.  Really a lot of text.
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-001-notref.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-001-notref.html
new file mode 100644
index 0000000..422f187d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-001-notref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<style>
+
+select, select::picker(select) {
+  appearance: base-select;
+}
+select::picker-icon {
+  display: none;
+}
+
+select {
+  width: 25px;
+  height: calc(max(1lh, 24px) + 1px);
+}
+
+</style>
+
+<select></select>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-001-ref.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-001-ref.html
new file mode 100644
index 0000000..1c37e12b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-001-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<link rel="match" href="min-size-empty-001-ref2.html">
+<style>
+
+select, select::picker(select) {
+  appearance: base-select;
+}
+select::picker-icon {
+  display: none;
+}
+
+select {
+  width: 24px;
+  height: max(1lh, 24px);
+}
+
+</style>
+
+<select></select>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-001-ref2.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-001-ref2.html
new file mode 100644
index 0000000..69170af
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-001-ref2.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<link rel="mismatch" href="min-size-empty-001-notref.html">
+<style>
+
+select, select::picker(select) {
+  appearance: base-select;
+}
+select::picker-icon {
+  display: none;
+}
+
+select {
+  width: 23px;
+  height: calc(max(1lh, 24px) - 1px);
+}
+
+</style>
+
+<select></select>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-001.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-001.html
new file mode 100644
index 0000000..fd5cc4a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-001.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<link rel="match" href="min-size-empty-001-ref.html">
+<style>
+
+select, select::picker(select) {
+  appearance: base-select;
+}
+select::picker-icon {
+  display: none;
+}
+
+</style>
+
+<select></select>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-002-notref.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-002-notref.html
new file mode 100644
index 0000000..0500b54b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-002-notref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<style>
+
+select, select::picker(select) {
+  appearance: base-select;
+}
+select::picker-icon {
+  display: none;
+}
+
+select {
+  width: 25px;
+  height: calc(max(1lh, 24px) + 1px);
+}
+
+</style>
+
+<select><option></option></select>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-002-ref.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-002-ref.html
new file mode 100644
index 0000000..c9e2d23
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-002-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<link rel="match" href="min-size-empty-002-ref2.html">
+<style>
+
+select, select::picker(select) {
+  appearance: base-select;
+}
+select::picker-icon {
+  display: none;
+}
+
+select {
+  width: 24px;
+  height: max(1lh, 24px);
+}
+
+</style>
+
+<select><option></option></select>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-002-ref2.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-002-ref2.html
new file mode 100644
index 0000000..dfbbbca
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-002-ref2.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<link rel="mismatch" href="min-size-empty-002-notref.html">
+<style>
+
+select, select::picker(select) {
+  appearance: base-select;
+}
+select::picker-icon {
+  display: none;
+}
+
+select {
+  width: 23px;
+  height: calc(max(1lh, 24px) - 1px);
+}
+
+</style>
+
+<select><option></option></select>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-002.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-002.html
new file mode 100644
index 0000000..b6256c9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-size-empty-002.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<link rel="match" href="min-size-empty-002-ref.html">
+<style>
+
+select, select::picker(select) {
+  appearance: base-select;
+}
+select::picker-icon {
+  display: none;
+}
+
+</style>
+
+<select><option></option></select>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-width-in-flex-ref.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-width-in-flex-ref.html
new file mode 100644
index 0000000..33df77b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-width-in-flex-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<style>
+
+select, select::picker(select) {
+  appearance: base-select;
+}
+
+</style>
+
+<select>
+  <option>This is an option</option>
+</select>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-width-in-flex.html b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-width-in-flex.html
new file mode 100644
index 0000000..f3510a2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/replaced-elements/the-select-element/customizable-select/min-width-in-flex.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<link rel="match" href="min-width-in-flex-ref.html">
+<style>
+
+select, select::picker(select) {
+  appearance: base-select;
+}
+
+select {
+  white-space: nowrap;
+}
+
+</style>
+
+<div style="display:flex; flex-direction: row; width: 400px">
+  <select style="align-self: flex-start">
+    <option>This is an option</option>
+  </select>
+  <div style="color: transparent">
+    This div has a lot of text in it but it should not make the select become smaller.
+    A lot of text.  Really a lot of text.
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-fill-reftest.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-fill-reftest.html
new file mode 100644
index 0000000..130849e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-fill-reftest.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>The icon of the element should change if the fill color is changed</title>
+<!-- TODO: Update the link to the permission icon spec -->
+<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md">
+<link rel="mismatch" href="standard-location-permission-element-ref.html">
+<style>
+  ::permission-icon {
+    fill: red;
+  }
+</style>
+<permission id="geolocation" type="geolocation"></permission>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-height-reftest.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-height-reftest.html
new file mode 100644
index 0000000..9f52fd75
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-height-reftest.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>The icon of the element should change if the height is changed</title>
+<!-- TODO: Update the link to the permission icon spec -->
+<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md">
+<link rel="mismatch" href="standard-location-permission-element-ref.html">
+<style>
+  ::permission-icon {
+    height: 20px;
+  }
+</style>
+<permission id="geolocation" type="geolocation"></permission>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-margin-inline-end-reftest.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-margin-inline-end-reftest.html
new file mode 100644
index 0000000..b4edffb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-margin-inline-end-reftest.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>The icon of the element should change if the margin-inline-end is changed</title>
+<!-- TODO: Update the link to the permission icon spec -->
+<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md">
+<link rel="mismatch" href="standard-location-permission-element-ref.html">
+<style>
+  ::permission-icon {
+    margin-inline-end: 50px;
+  }
+</style>
+<permission id="geolocation" type="geolocation"/>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-max-height-reftest-ref.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-max-height-reftest-ref.html
new file mode 100644
index 0000000..abda1ad
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-max-height-reftest-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>
+  A standard permission element of type location, without 50px height.
+</title>
+
+<style>
+  ::permission-icon {
+    height: 50px;
+  }
+</style>
+
+<permission id="geolocation" type="geolocation"></permission>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-max-height-reftest.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-max-height-reftest.html
new file mode 100644
index 0000000..45abc0f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-max-height-reftest.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>The icon of the element should be restricted to 50px due to the max-height property.</title>
+<!-- TODO: Update the link to the permission icon spec -->
+<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md">
+<link rel="match" href="icon-css-property-max-height-reftest-ref.html">
+<style>
+  ::permission-icon {
+    height: 60px;
+    max-height: 50px;
+  }
+</style>
+<permission id="geolocation" type="geolocation"></permission>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-min-height-reftest.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-min-height-reftest.html
new file mode 100644
index 0000000..d1fe8d07
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-min-height-reftest.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>The icon of the element should change if the min-height is changed</title>
+<!-- TODO: Update the link to the permission icon spec -->
+<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md">
+<link rel="mismatch" href="standard-location-permission-element-ref.html">
+<style>
+  ::permission-icon {
+    min-height: 50px;
+  }
+</style>
+<permission id="geolocation" type="geolocation"></permission>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-stroke-reftest.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-stroke-reftest.html
new file mode 100644
index 0000000..9d0ff75c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-stroke-reftest.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>The icon of the element should change if the stroke is changed</title>
+<!-- TODO: Update the link to the permission icon spec -->
+<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md">
+<link rel="mismatch" href="standard-location-permission-element-ref.html">
+<style>
+  ::permission-icon {
+    stroke: red;
+  }
+</style>
+<permission id="geolocation" type="geolocation"></permission>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-stroke-width-reftest-ref.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-stroke-width-reftest-ref.html
new file mode 100644
index 0000000..5ed8af5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-stroke-width-reftest-ref.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>
+  A standard permission element of type location, with a red stroke with the
+  default stroke-width of 1px.
+</title>
+
+<style>
+  ::permission-icon {
+    stroke: red;
+  }
+</style>
+<permission id="geolocation" type="geolocation"></permission>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-stroke-width-reftest.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-stroke-width-reftest.html
new file mode 100644
index 0000000..a268e3c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-css-property-stroke-width-reftest.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>The icon of the element should change if the stroke-width is changed</title>
+<!-- TODO: Update the link to the permission icon spec -->
+<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md">
+<link rel="mismatch" href="icon-css-property-stroke-width-reftest-ref.html">
+<style>
+  ::permission-icon {
+    stroke-width: 2px;
+  }
+</style>
+<permission id="geolocation" type="geolocation"></permission>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-different-for-precise-location-reftest.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-different-for-precise-location-reftest.html
new file mode 100644
index 0000000..bf58fdf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-different-for-precise-location-reftest.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<title>The precise and non-precise location icons should be different.</title>
+<!-- TODO: Update the link to the permission icon spec -->
+<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md">
+<link rel="mismatch" href="standard-location-permission-element-ref.html">
+<permission id="geolocation" type="geolocation" preciselocation></permission>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-hidden-reftest.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-hidden-reftest.html
index 79055da..a5b5bb61d 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-hidden-reftest.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-hidden-reftest.html
@@ -8,4 +8,4 @@
     display: none;
   }
 </style>
-<permission id="geolocation" type="geolocation"/>
+<permission id="geolocation" type="geolocation"></permission>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-restricted-css-no-effect-reftest.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-restricted-css-no-effect-reftest.html
new file mode 100644
index 0000000..5c0ea13fd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-restricted-css-no-effect-reftest.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>The icon of the permission element should not change when any of the restricted CSS properties are changed.</title>
+<!-- TODO: Update the link to the permission icon spec -->
+<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md">
+<link rel="match" href="standard-location-permission-element-ref.html">
+<style>
+  ::permission-icon {
+    margin-inline-start: 100px;
+    float: inline-end;
+    padding: 50px;
+    opacity: 0.5;
+    stroke-opacity: 0.5;
+    fill-rule: evenodd;
+  }
+</style>
+<permission id="geolocation" type="geolocation"></permission>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-unique-per-type-reftest.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-unique-per-type-reftest.html
index d51b1c4..162a785 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-unique-per-type-reftest.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/icon-unique-per-type-reftest.html
@@ -3,4 +3,4 @@
 <!-- TODO: Update the link to the permission icon spec -->
 <link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md">
 <link rel="mismatch" href="standard-location-permission-element-ref.html">
-<permission id="camera" type="camera"/>
+<permission id="camera" type="camera"></permission>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/standard-location-permission-element-ref.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/standard-location-permission-element-ref.html
index 15ffe75..d9f55b3 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/standard-location-permission-element-ref.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/permission-icon/standard-location-permission-element-ref.html
@@ -1,4 +1,4 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>A standard permission element of type location, without any non-default styling</title>
-<permission id="geolocation" type="geolocation"/>
+<permission id="geolocation" type="geolocation"></permission>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/crash-reporting.idl b/third_party/blink/web_tests/external/wpt/interfaces/crash-reporting.idl
index 6eaee13..ba21afc 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/crash-reporting.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/crash-reporting.idl
@@ -8,6 +8,6 @@
   [Default] object toJSON();
   readonly attribute DOMString? reason;
   readonly attribute DOMString? stack;
-  readonly attribute DOMString? is_top_level;
+  readonly attribute boolean? is_top_level;
   readonly attribute DocumentVisibilityState? page_visibility;
 };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/cssom-view.idl b/third_party/blink/web_tests/external/wpt/interfaces/cssom-view.idl
index 88abb07..160c27ca 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/cssom-view.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/cssom-view.idl
@@ -141,6 +141,7 @@
 };
 
 partial interface HTMLElement {
+  readonly attribute Element? scrollParent;
   readonly attribute Element? offsetParent;
   readonly attribute long offsetTop;
   readonly attribute long offsetLeft;
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/element-timing.idl b/third_party/blink/web_tests/external/wpt/interfaces/element-timing.idl
index ef73ca6..4f42823 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/element-timing.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/element-timing.idl
@@ -13,7 +13,7 @@
     readonly attribute unsigned long naturalHeight;
     readonly attribute DOMString id;
     readonly attribute Element? element;
-    readonly attribute DOMString url;
+    readonly attribute USVString url;
     [Default] object toJSON();
 };
 
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/html.idl b/third_party/blink/web_tests/external/wpt/interfaces/html.idl
index 9c84e6a..3a7dce96 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/html.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/html.idl
@@ -1235,18 +1235,19 @@
 interface HTMLScriptElement : HTMLElement {
   [HTMLConstructor] constructor();
 
-  [CEReactions] attribute USVString src;
   [CEReactions] attribute DOMString type;
+  [CEReactions] attribute USVString src;
   [CEReactions] attribute boolean noModule;
   [CEReactions] attribute boolean async;
   [CEReactions] attribute boolean defer;
-  [CEReactions] attribute DOMString? crossOrigin;
-  [CEReactions] attribute DOMString text;
-  [CEReactions] attribute DOMString integrity;
-  [CEReactions] attribute DOMString referrerPolicy;
   [SameObject, PutForwards=value] readonly attribute DOMTokenList blocking;
+  [CEReactions] attribute DOMString? crossOrigin;
+  [CEReactions] attribute DOMString referrerPolicy;
+  [CEReactions] attribute DOMString integrity;
   [CEReactions] attribute DOMString fetchPriority;
 
+  [CEReactions] attribute DOMString text;
+
   static boolean supports(DOMString type);
 
   // also has obsolete members
@@ -1694,6 +1695,7 @@
   constructor(DOMString type, optional ToggleEventInit eventInitDict = {});
   readonly attribute DOMString oldState;
   readonly attribute DOMString newState;
+  readonly attribute Element source;
 };
 
 dictionary ToggleEventInit : EventInit {
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/privacy-preserving-attribution.idl b/third_party/blink/web_tests/external/wpt/interfaces/privacy-preserving-attribution.idl
index 0ab5d0f..02a3579 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/privacy-preserving-attribution.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/privacy-preserving-attribution.idl
@@ -30,9 +30,12 @@
   unsigned long lifetimeDays = 30;
 };
 
+dictionary PrivateAttributionImpressionResult {
+};
+
 [SecureContext, Exposed=Window]
 partial interface PrivateAttribution {
-  undefined saveImpression(PrivateAttributionImpressionOptions options);
+  Promise<PrivateAttributionImpressionResult> saveImpression(PrivateAttributionImpressionOptions options);
 };
 
 dictionary PrivateAttributionConversionOptions {
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/resource-timing.idl b/third_party/blink/web_tests/external/wpt/interfaces/resource-timing.idl
index 66f2841..499d27b 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/resource-timing.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/resource-timing.idl
@@ -28,6 +28,7 @@
     readonly attribute unsigned short responseStatus;
     readonly attribute RenderBlockingStatusType renderBlockingStatus;
     readonly attribute DOMString contentType;
+    readonly attribute DOMString contentEncoding;
     [Default] object toJSON();
 };
 
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/scoped-custom-elements-registry.idl b/third_party/blink/web_tests/external/wpt/interfaces/scoped-custom-elements-registry.idl
deleted file mode 100644
index 46ca2d6b..0000000
--- a/third_party/blink/web_tests/external/wpt/interfaces/scoped-custom-elements-registry.idl
+++ /dev/null
@@ -1,38 +0,0 @@
-[Exposed=Window]
-partial interface CustomElementRegistry {
-  constructor();
-  undefined initialize(Node root);
-};
-
-[Exposed=Window]
-partial interface HTMLTemplateElement {
-  [CEReactions] attribute DOMString shadowRootCustomElementRegistry;
-};
-
-[Exposed=Window]
-partial interface Document {
-  readonly attribute CustomElementRegistry? customElementRegistry;
-};
-
-[Exposed=Window]
-partial interface Element {
-  readonly attribute CustomElementRegistry? customElementRegistry;
-};
-
-[Exposed=Window]
-partial interface ShadowRoot {
-  readonly attribute CustomElementRegistry? customElementRegistry;
-};
-
-dictionary ImportNodeOptions {
-  CustomElementRegistry customElementRegistry;
-  boolean selfOnly = false;
-};
-
-partial dictionary ShadowRootInit {
-  CustomElementRegistry customElementRegistry;
-};
-
-partial dictionary ElementCreationOptions {
-  CustomElementRegistry customElementRegistry;
-};
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/screen-capture.idl b/third_party/blink/web_tests/external/wpt/interfaces/screen-capture.idl
index db9282ce..eb5685ee 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/screen-capture.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/screen-capture.idl
@@ -29,6 +29,12 @@
   "exclude"
 };
 
+enum WindowAudioPreferenceEnum {
+  "system",
+  "window",
+  "exclude"
+};
+
 enum SurfaceSwitchingPreferenceEnum {
   "include",
   "exclude"
@@ -45,6 +51,7 @@
   CaptureController controller;
   SelfCapturePreferenceEnum selfBrowserSurface;
   SystemAudioPreferenceEnum systemAudio;
+  WindowAudioPreferenceEnum windowAudio;
   SurfaceSwitchingPreferenceEnum surfaceSwitching;
   MonitorTypeSurfacesEnum monitorTypeSurfaces;
 };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/secure-payment-confirmation.idl b/third_party/blink/web_tests/external/wpt/interfaces/secure-payment-confirmation.idl
index 5b67ca6..0a22076 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/secure-payment-confirmation.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/secure-payment-confirmation.idl
@@ -11,6 +11,7 @@
     unsigned long timeout;
     USVString payeeName;
     USVString payeeOrigin;
+    sequence<PaymentEntityLogo> paymentEntitiesLogos;
     AuthenticationExtensionsClientInputs extensions;
     sequence<USVString> locale;
     boolean showOptOut;
@@ -40,6 +41,7 @@
   USVString topOrigin;
   USVString payeeName;
   USVString payeeOrigin;
+  sequence<PaymentEntityLogo> paymentEntitiesLogos;
   PaymentCurrencyAmount total;
   PaymentCredentialInstrument instrument;
 };
@@ -53,6 +55,7 @@
     required USVString topOrigin;
     USVString payeeName;
     USVString payeeOrigin;
+    sequence<PaymentEntityLogo> paymentEntitiesLogos;
     required PaymentCurrencyAmount total;
     required PaymentCredentialInstrument instrument;
 };
@@ -62,3 +65,8 @@
     required USVString icon;
     boolean iconMustBeShown = true;
 };
+
+dictionary PaymentEntityLogo {
+    required USVString url;
+    required USVString label;
+};
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/speech-api.idl b/third_party/blink/web_tests/external/wpt/interfaces/speech-api.idl
index 94a416f..9620e60 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/speech-api.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/speech-api.idl
@@ -12,7 +12,7 @@
     attribute boolean continuous;
     attribute boolean interimResults;
     attribute unsigned long maxAlternatives;
-    attribute SpeechRecognitionMode mode;
+    attribute boolean processLocally;
     attribute SpeechRecognitionPhraseList phrases;
 
     // methods to drive the speech interaction
@@ -20,8 +20,8 @@
     undefined start(MediaStreamTrack audioTrack);
     undefined stop();
     undefined abort();
-    static Promise<AvailabilityStatus> availableOnDevice(DOMString lang);
-    static Promise<boolean> installOnDevice(DOMString lang);
+    static Promise<AvailabilityStatus> available(SpeechRecognitionOptions options);
+    static Promise<boolean> install(SpeechRecognitionOptions options);
 
     // event methods
     attribute EventHandler onaudiostart;
@@ -37,6 +37,11 @@
     attribute EventHandler onend;
 };
 
+dictionary SpeechRecognitionOptions {
+  required sequence<DOMString> langs;
+  boolean processLocally = false;
+};
+
 enum SpeechRecognitionErrorCode {
     "no-speech",
     "aborted",
@@ -48,12 +53,6 @@
     "phrases-not-supported"
 };
 
-enum SpeechRecognitionMode {
-    "ondevice-preferred", // On-device speech recognition if available, otherwise use Cloud speech recognition as a fallback.
-    "ondevice-only", // On-device speech recognition only. Returns an error if on-device speech recognition is not available.
-    "cloud-only", // Cloud speech recognition only.
-};
-
 enum AvailabilityStatus {
     "unavailable",
     "downloadable",
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl b/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl
index deebc562..3364bc9a 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/wai-aria.idl
@@ -57,4 +57,4 @@
   [CEReactions] attribute DOMString? ariaValueNow;
   [CEReactions] attribute DOMString? ariaValueText;
 };
-Element includes ARIAMixin;
+   Element includes ARIAMixin;
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/web-animations-2.idl b/third_party/blink/web_tests/external/wpt/interfaces/web-animations-2.idl
index c4a0c25..f18cdd4 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/web-animations-2.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/web-animations-2.idl
@@ -118,7 +118,7 @@
 interface AnimationTrigger {
   constructor(optional AnimationTriggerOptions options = {});
   attribute AnimationTimeline timeline;
-  attribute AnimationTriggerType type;
+  attribute AnimationTriggerBehavior behavior;
   attribute any rangeStart;
   attribute any rangeEnd;
   attribute any exitRangeStart;
@@ -127,11 +127,11 @@
 
 dictionary AnimationTriggerOptions {
   AnimationTimeline? timeline;
-  AnimationTriggerType? type = "once";
+  AnimationTriggerBehavior? behavior = "once";
   (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeStart = "normal";
   (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) rangeEnd = "normal";
   (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) exitRangeStart = "auto";
   (TimelineRangeOffset or CSSNumericValue or CSSKeywordValue or DOMString) exitRangeEnd = "auto";
 };
 
-enum AnimationTriggerType { "once", "repeat", "alternate", "state" };
+enum AnimationTriggerBehavior { "once", "repeat", "alternate", "state" };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webgpu.idl b/third_party/blink/web_tests/external/wpt/interfaces/webgpu.idl
index 4fec46a..1fc896c 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webgpu.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webgpu.idl
@@ -127,6 +127,7 @@
     "clip-distances",
     "dual-source-blending",
     "subgroups",
+    "texture-formats-tier1",
 };
 
 [Exposed=(Window, Worker), SecureContext]
@@ -295,6 +296,8 @@
     "r8sint",
 
     // 16-bit formats
+    "r16unorm",
+    "r16snorm",
     "r16uint",
     "r16sint",
     "r16float",
@@ -307,6 +310,8 @@
     "r32uint",
     "r32sint",
     "r32float",
+    "rg16unorm",
+    "rg16snorm",
     "rg16uint",
     "rg16sint",
     "rg16float",
@@ -327,6 +332,8 @@
     "rg32uint",
     "rg32sint",
     "rg32float",
+    "rgba16unorm",
+    "rgba16snorm",
     "rgba16uint",
     "rgba16sint",
     "rgba16float",
@@ -556,7 +563,11 @@
     required sequence<GPUBindGroupEntry> entries;
 };
 
-typedef (GPUSampler or GPUTextureView or GPUBufferBinding or GPUExternalTexture) GPUBindingResource;
+typedef (GPUSampler or
+         GPUTextureView or
+         GPUBuffer or
+         GPUBufferBinding or
+         GPUExternalTexture) GPUBindingResource;
 
 dictionary GPUBindGroupEntry {
     required GPUIndex32 binding;
@@ -990,7 +1001,7 @@
         optional sequence<GPUBufferDynamicOffset> dynamicOffsets = []);
 
     undefined setBindGroup(GPUIndex32 index, GPUBindGroup? bindGroup,
-        Uint32Array dynamicOffsetsData,
+        [AllowShared] Uint32Array dynamicOffsetsData,
         GPUSize64 dynamicOffsetsDataStart,
         GPUSize32 dynamicOffsetsDataLength);
 };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/writing-assistance-apis.idl b/third_party/blink/web_tests/external/wpt/interfaces/writing-assistance-apis.idl
index 916daee..82acfdb 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/writing-assistance-apis.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/writing-assistance-apis.idl
@@ -56,7 +56,7 @@
   DOMString context;
 };
 
-enum SummarizerType { "tl;dr", "teaser", "key-points", "headline" };
+enum SummarizerType { "tldr", "teaser", "key-points", "headline" };
 enum SummarizerFormat { "plain-text", "markdown" };
 enum SummarizerLength { "short", "medium", "long" };
 
diff --git a/third_party/blink/web_tests/external/wpt/lint.ignore b/third_party/blink/web_tests/external/wpt/lint.ignore
index 1577148..cf83e45 100644
--- a/third_party/blink/web_tests/external/wpt/lint.ignore
+++ b/third_party/blink/web_tests/external/wpt/lint.ignore
@@ -820,6 +820,7 @@
 TESTDRIVER-IN-UNSUPPORTED-TYPE: payment-handler/change-shipping-option-manual.https.html
 TESTDRIVER-IN-UNSUPPORTED-TYPE: payment-handler/payment-request-event-manual.https.html
 TESTDRIVER-IN-UNSUPPORTED-TYPE: payment-handler/supports-shipping-contact-delegation-manual.https.html
+TESTDRIVER-IN-UNSUPPORTED-TYPE: speech-api/SpeechRecognition-phrases-manual.https.html
 
 # Tests automatically imported from the WebAssembly/spec repository that should be removed after getting fixed upstream.
 SET TIMEOUT: wasm/core/js/harness/testharness.js
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-form-userInitiated.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-form-userInitiated.html
index 246e028..d237b52a 100644
--- a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-form-userInitiated.html
+++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/navigate-form-userInitiated.html
@@ -5,7 +5,7 @@
 <script src="/resources/testdriver-vendor.js"></script>
 <script src="/resources/testdriver-actions.js"></script>
 <form id="form" method="post" action="">
-<input id="submit" type="submit" value="Submit">
+<input id="submit" name="submitter" type="submit" value="Submit">
 </form>
 <script>
 async_test(t => {
@@ -24,6 +24,7 @@
     assert_equals(e.destination.id, "");
     assert_equals(e.destination.index, -1);
     assert_not_equals(e.formData, null);
+    assert_equals(e.formData.get("submitter"), "Submit");
     assert_equals(e.sourceElement, submit);
   });
   window.onload = t.step_func(() => test_driver.click(submit));
diff --git a/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-phrases-manual.https.html b/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-phrases-manual.https.html
index 2d0b19a..0f596a8 100644
--- a/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-phrases-manual.https.html
+++ b/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-phrases-manual.https.html
@@ -1,7 +1,10 @@
 <!DOCTYPE html>
 <html lang="en">
+<meta name="timeout" content="long">
 <title>SpeechRecognition Phrases</title>
 
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 
@@ -22,6 +25,24 @@
 }
 
 promise_test(async (t) => {
+    window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
+
+    // Install en-US for on-device speech recognition.
+    const installOptions = { langs: ["en-US"], processLocally: true };
+    const installPromise = test_driver.bless(
+        "Install on-device en-US speech recognition",
+        () => SpeechRecognition.install(installOptions)
+    );
+    assert_true(
+        installPromise instanceof Promise,
+        "SpeechRecognition.install() should return a Promise."
+    );
+    const installResult = await installPromise;
+    assert_true(
+        installResult,
+        "SpeechRecognition.install() for en-US should resolve with true."
+    );
+
     // Verify the audio track for recognition context exists.
     const audioTrack = await getAudioTrackFromFile("/media/recognition_context.mp3");
     assert_true(
@@ -31,9 +52,8 @@
 
     // Create the first speech recognition with a mode that does not support contextual biasing.
     // Note that this may vary between browsers in the future.
-    window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
     const recognition1 = new SpeechRecognition();
-    recognition1.mode = "cloud-only";
+    recognition1.processLocally = false;
     recognition1.lang = "en-US";
 
     recognition1.onerror = function(event) {
@@ -50,7 +70,7 @@
 
     // Create the second speech recognition with a mode that supports contextual biasing.
     const recognition2 = new SpeechRecognition();
-    recognition2.mode = "ondevice-only";
+    recognition2.processLocally = true;
     recognition2.lang = "en-US";
 
     recognition2.onerror = function(event) {
@@ -67,7 +87,11 @@
     const recognitionPromise = new Promise((resolve) => {
         recognition2.onresult = (event) => {
             const transcript = event.results[0][0].transcript;
-            resolve(transcript);
+            const words = transcript.toLowerCase().split(' ');
+            // Resolve when the last word is "expectations".
+            if (words.length > 0 && words[words.length - 1] === "expectations") {
+                resolve(transcript);
+            }
         };
     });
     recognition2.start(audioTrack);
diff --git a/third_party/blink/web_tests/external/wpt/speech-api/idlharness.window-expected.txt b/third_party/blink/web_tests/external/wpt/speech-api/idlharness.window-expected.txt
index 2651042..18443119 100644
--- a/third_party/blink/web_tests/external/wpt/speech-api/idlharness.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/speech-api/idlharness.window-expected.txt
@@ -20,7 +20,7 @@
   assert_own_property: self does not have own property "SpeechRecognition" expected property "SpeechRecognition" missing
 [FAIL] SpeechRecognition interface: attribute maxAlternatives
   assert_own_property: self does not have own property "SpeechRecognition" expected property "SpeechRecognition" missing
-[FAIL] SpeechRecognition interface: attribute mode
+[FAIL] SpeechRecognition interface: attribute processLocally
   assert_own_property: self does not have own property "SpeechRecognition" expected property "SpeechRecognition" missing
 [FAIL] SpeechRecognition interface: attribute phrases
   assert_own_property: self does not have own property "SpeechRecognition" expected property "SpeechRecognition" missing
@@ -32,9 +32,9 @@
   assert_own_property: self does not have own property "SpeechRecognition" expected property "SpeechRecognition" missing
 [FAIL] SpeechRecognition interface: operation abort()
   assert_own_property: self does not have own property "SpeechRecognition" expected property "SpeechRecognition" missing
-[FAIL] SpeechRecognition interface: operation availableOnDevice(DOMString)
+[FAIL] SpeechRecognition interface: operation available(SpeechRecognitionOptions)
   assert_own_property: self does not have own property "SpeechRecognition" expected property "SpeechRecognition" missing
-[FAIL] SpeechRecognition interface: operation installOnDevice(DOMString)
+[FAIL] SpeechRecognition interface: operation install(SpeechRecognitionOptions)
   assert_own_property: self does not have own property "SpeechRecognition" expected property "SpeechRecognition" missing
 [FAIL] SpeechRecognition interface: attribute onaudiostart
   assert_own_property: self does not have own property "SpeechRecognition" expected property "SpeechRecognition" missing
@@ -70,7 +70,7 @@
   assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: SpeechRecognition is not defined"
 [FAIL] SpeechRecognition interface: new SpeechRecognition() must inherit property "maxAlternatives" with the proper type
   assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: SpeechRecognition is not defined"
-[FAIL] SpeechRecognition interface: new SpeechRecognition() must inherit property "mode" with the proper type
+[FAIL] SpeechRecognition interface: new SpeechRecognition() must inherit property "processLocally" with the proper type
   assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: SpeechRecognition is not defined"
 [FAIL] SpeechRecognition interface: new SpeechRecognition() must inherit property "phrases" with the proper type
   assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: SpeechRecognition is not defined"
@@ -84,13 +84,13 @@
   assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: SpeechRecognition is not defined"
 [FAIL] SpeechRecognition interface: new SpeechRecognition() must inherit property "abort()" with the proper type
   assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: SpeechRecognition is not defined"
-[FAIL] SpeechRecognition interface: new SpeechRecognition() must inherit property "availableOnDevice(DOMString)" with the proper type
+[FAIL] SpeechRecognition interface: new SpeechRecognition() must inherit property "available(SpeechRecognitionOptions)" with the proper type
   assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: SpeechRecognition is not defined"
-[FAIL] SpeechRecognition interface: calling availableOnDevice(DOMString) on new SpeechRecognition() with too few arguments must throw TypeError
+[FAIL] SpeechRecognition interface: calling available(SpeechRecognitionOptions) on new SpeechRecognition() with too few arguments must throw TypeError
   assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: SpeechRecognition is not defined"
-[FAIL] SpeechRecognition interface: new SpeechRecognition() must inherit property "installOnDevice(DOMString)" with the proper type
+[FAIL] SpeechRecognition interface: new SpeechRecognition() must inherit property "install(SpeechRecognitionOptions)" with the proper type
   assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: SpeechRecognition is not defined"
-[FAIL] SpeechRecognition interface: calling installOnDevice(DOMString) on new SpeechRecognition() with too few arguments must throw TypeError
+[FAIL] SpeechRecognition interface: calling install(SpeechRecognitionOptions) on new SpeechRecognition() with too few arguments must throw TypeError
   assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: SpeechRecognition is not defined"
 [FAIL] SpeechRecognition interface: new SpeechRecognition() must inherit property "onaudiostart" with the proper type
   assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: SpeechRecognition is not defined"
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/idlharness.window-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/idlharness.window-expected.txt
index 25a1596d..6ef6b07 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/idlharness.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/idlharness.window-expected.txt
@@ -106,8 +106,8 @@
   assert_own_property: self does not have own property "SequenceEffect" expected property "SequenceEffect" missing
 [FAIL] AnimationTrigger interface: attribute timeline
   assert_equals: setter must be function for PutForwards, Replaceable, or non-readonly attributes expected "function" but got "undefined"
-[FAIL] AnimationTrigger interface: attribute type
-  assert_equals: setter must be function for PutForwards, Replaceable, or non-readonly attributes expected "function" but got "undefined"
+[FAIL] AnimationTrigger interface: attribute behavior
+  assert_true: The prototype object must have a property "behavior" expected true got false
 [FAIL] AnimationTrigger interface: attribute rangeStart
   assert_equals: setter must be function for PutForwards, Replaceable, or non-readonly attributes expected "function" but got "undefined"
 [FAIL] AnimationTrigger interface: attribute rangeEnd
diff --git a/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-expected.png
deleted file mode 100644
index f92fd05..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png b/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png
deleted file mode 100644
index f57c6b6a..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-single-option-expected.png b/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-single-option-expected.png
deleted file mode 100644
index cf0b78f..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-single-option-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-transform-expected.png b/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-transform-expected.png
deleted file mode 100644
index e0802ee9..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-transform-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-zero-font-size-expected.png b/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-zero-font-size-expected.png
deleted file mode 100644
index ae716e6..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-zero-font-size-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-expected.png
deleted file mode 100644
index f92fd05..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png b/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png
deleted file mode 100644
index f57c6b6a..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-single-option-expected.png b/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-single-option-expected.png
deleted file mode 100644
index cf0b78f..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-single-option-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-transform-expected.png b/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-transform-expected.png
deleted file mode 100644
index e0802ee9..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-transform-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-zero-font-size-expected.png b/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-zero-font-size-expected.png
deleted file mode 100644
index ae716e6..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-zero-font-size-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win10/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png b/third_party/blink/web_tests/platform/win10/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png
deleted file mode 100644
index d7181608..0000000
--- a/third_party/blink/web_tests/platform/win10/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win10/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png b/third_party/blink/web_tests/platform/win10/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png
deleted file mode 100644
index d7181608..0000000
--- a/third_party/blink/web_tests/platform/win10/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/README.md b/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/README.md
deleted file mode 100644
index 847abc04..0000000
--- a/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-This suite tests the target size of <option> within the <select> dropdown with
-`--disable-blink-features=SelectOptionAccessibilityTargetSize,ForceTallerSelectPopup,`.
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-accessibility-minimum-target-size-expected.txt b/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-accessibility-minimum-target-size-expected.txt
deleted file mode 100644
index a272dbe3..0000000
--- a/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-accessibility-minimum-target-size-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] The minimum height of the option element in the popup menu should be 24 CSS pixels.
-  assert_greater_than_equal: The minimum height of the option element in the popup menu should be 24 CSS pixels. expected a number greater than or equal to 24 but got 17
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-expected.png b/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-expected.png
deleted file mode 100644
index 5f8b7ea..0000000
--- a/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png b/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png
deleted file mode 100644
index 3bce59a..0000000
--- a/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-single-option-expected.png b/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-single-option-expected.png
deleted file mode 100644
index 458963d..0000000
--- a/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-single-option-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-transform-expected.png b/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-transform-expected.png
deleted file mode 100644
index f0f667e..0000000
--- a/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-transform-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-zero-font-size-expected.png b/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-zero-font-size-expected.png
deleted file mode 100644
index b67116b..0000000
--- a/third_party/blink/web_tests/virtual/both-taller-select-popup-options-disabled/fast/forms/select-popup/popup-menu-appearance-zero-font-size-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/force-taller-select-popup-disabled/README.md b/third_party/blink/web_tests/virtual/force-taller-select-popup-disabled/README.md
deleted file mode 100644
index ac5bcfb2..0000000
--- a/third_party/blink/web_tests/virtual/force-taller-select-popup-disabled/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-This suite tests the target size of <option> within the <select> dropdown with
-`--disable-blink-features=ForceTallerSelectPopup`.
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/scoped-custom-element-registry-disabled/external/wpt/custom-elements/registries/idlharness.window-expected.txt b/third_party/blink/web_tests/virtual/scoped-custom-element-registry-disabled/external/wpt/custom-elements/registries/idlharness.window-expected.txt
deleted file mode 100644
index bc8bef8a..0000000
--- a/third_party/blink/web_tests/virtual/scoped-custom-element-registry-disabled/external/wpt/custom-elements/registries/idlharness.window-expected.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] idl_test setup
-  promise_test: Unhandled rejection with value: object "TypeError: Failed to construct 'CustomElementRegistry': Illegal constructor"
-[FAIL] idl_test validation
-  Validation error at line 4 in scoped-custom-elements-registry, inside `partial interface CustomElementRegistry`:\n  undefined initialize(Node root)\n            ^ The operation "initialize" has already been defined for the base interface "CustomElementRegistry" either in itself or in a mixin\n\nValidation error at line 320 in dom, inside `dictionary ImportNodeOptions`:\ndictionary ImportNodeOptions {\n           ^ The name "ImportNodeOptions" of type "dictionary" was already seen
-[FAIL] Partial interface CustomElementRegistry: member names are unique
-  assert_true: member undefined is unique expected true got false
-[FAIL] Partial interface HTMLTemplateElement: member names are unique
-  assert_true: member shadowRootCustomElementRegistry is unique expected true got false
-[FAIL] Partial interface Element: member names are unique
-  assert_true: member customElementRegistry is unique expected true got false
-[FAIL] Partial dictionary ShadowRootInit: member names are unique
-  assert_true: member customElementRegistry is unique expected true got false
-[FAIL] Partial dictionary ElementCreationOptions: member names are unique
-  assert_true: member customElementRegistry is unique expected true got false
-[FAIL] Document includes DocumentOrShadowRoot: member names are unique
-  assert_true: member customElementRegistry is unique expected true got false
-[FAIL] ShadowRoot includes DocumentOrShadowRoot: member names are unique
-  assert_true: member customElementRegistry is unique expected true got false
-[FAIL] HTMLTemplateElement interface: attribute shadowRootCustomElementRegistry
-  assert_true: The prototype object must have a property "shadowRootCustomElementRegistry" expected true got false
-[FAIL] CustomElementRegistry interface: operation initialize(Node)
-  assert_own_property: interface prototype object missing non-static operation expected property "initialize" missing
-[FAIL] Document interface: attribute customElementRegistry
-  assert_true: The prototype object must have a property "customElementRegistry" expected true got false
-[FAIL] ShadowRoot interface: attribute customElementRegistry
-  assert_true: The prototype object must have a property "customElementRegistry" expected true got false
-[FAIL] Element interface: attribute customElementRegistry
-  assert_true: The prototype object must have a property "customElementRegistry" expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/README.md b/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/README.md
deleted file mode 100644
index 1d86db49..0000000
--- a/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-This suite tests the target size of <option> within the <select> dropdown with
-`--disable-blink-features=SelectOptionAccessibilityTargetSize`.
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-accessibility-minimum-target-size-expected.txt b/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-accessibility-minimum-target-size-expected.txt
deleted file mode 100644
index a272dbe3..0000000
--- a/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-accessibility-minimum-target-size-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] The minimum height of the option element in the popup menu should be 24 CSS pixels.
-  assert_greater_than_equal: The minimum height of the option element in the popup menu should be 24 CSS pixels. expected a number greater than or equal to 24 but got 17
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-expected.png b/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-expected.png
deleted file mode 100644
index 5f8b7ea..0000000
--- a/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png b/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png
deleted file mode 100644
index 3bce59a..0000000
--- a/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-long-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-single-option-expected.png b/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-single-option-expected.png
deleted file mode 100644
index 458963d..0000000
--- a/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-single-option-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-transform-expected.png b/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-transform-expected.png
deleted file mode 100644
index f0f667e..0000000
--- a/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-transform-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-zero-font-size-expected.png b/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-zero-font-size-expected.png
deleted file mode 100644
index b67116b..0000000
--- a/third_party/blink/web_tests/virtual/select-option-accessibility-target-size-disabled/fast/forms/select-popup/popup-menu-appearance-zero-font-size-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.serviceworker_gpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.serviceworker_gpu-expected.txt
deleted file mode 100644
index 409fb5d..0000000
--- a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.serviceworker_gpu-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Create context with GPUDevice.
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'createContext' on 'ML': ML.createContext(GPUDevice) is not supported."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.sharedworker_gpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.sharedworker_gpu-expected.txt
deleted file mode 100644
index 409fb5d..0000000
--- a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.sharedworker_gpu-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Create context with GPUDevice.
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'createContext' on 'ML': ML.createContext(GPUDevice) is not supported."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.worker_gpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.worker_gpu-expected.txt
deleted file mode 100644
index 409fb5d..0000000
--- a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any.worker_gpu-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Create context with GPUDevice.
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'createContext' on 'ML': ML.createContext(GPUDevice) is not supported."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any_gpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any_gpu-expected.txt
deleted file mode 100644
index 409fb5d..0000000
--- a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/validation_tests/createContext.https.any_gpu-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] Create context with GPUDevice.
-  promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'createContext' on 'ML': ML.createContext(GPUDevice) is not supported."
-Harness: the test ran to completion.
-
diff --git a/third_party/catapult b/third_party/catapult
index aa341ec..304d8b7 160000
--- a/third_party/catapult
+++ b/third_party/catapult
@@ -1 +1 @@
-Subproject commit aa341ec41f6d475102eee85ddec60d403ef575cd
+Subproject commit 304d8b75253e53388a57530f579fbd1049538fa6
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 6ab4487..e9953f0 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 6ab44879f40f97dfb7bb2093300e3263b474198d
+Subproject commit e9953f0394b4f7f1c2a61a0827ed8b0fded4ad60
diff --git a/third_party/ipcz/src/ipcz/node_messages.py b/third_party/ipcz/src/ipcz/node_messages.py
index a82f840..8368cf35 100644
--- a/third_party/ipcz/src/ipcz/node_messages.py
+++ b/third_party/ipcz/src/ipcz/node_messages.py
@@ -502,21 +502,24 @@
 
 
 if __name__ == '__main__':
-    parser = argparse.ArgumentParser(description="""
-Parses ipcz message definition files (*_messages_generator.h) and generates
-.cc and .h files for use by the IPCZ build. Determines the interface type from
-the provided IPCZ_MSG_BEGIN_INTERFACE(name) and supports both Node and Test.
-
-node_messages.py --dir=.\third_party\ipcz\src\ipcz
-
-Writes node_messages.h and node_messages.cc in the same directory `messages`.
-Files are only touched if the contents have changed.
-
-Can be run in a check-only mode which does not write the output files, but
-instead validates that the checked-in file matches the expected output.
-
-node_messages.py --dir=.\third_party\ipcz\src\ipcz --check
-                                     """)
+    parser = argparse.ArgumentParser(description=(
+        "Parses ipcz message definition files (*_messages_generator.h) and\n"
+        "generates .cc and .h files for use by the IPCZ build. Determines the\n"
+        "interface type from the provided IPCZ_MSG_BEGIN_INTERFACE(name) and\n"
+        "supports both Node and Test.\n"
+        "\n"
+        r"  node_messages.py --dir=.\third_party\ipcz\src\ipcz"
+        "\n\n"
+        "Writes node_messages.h and node_messages.cc in the same directory\n"
+        "`messages`. Files are only touched if the contents have changed.\n"
+        "\n"
+        "Can be run in a check-only mode which does not write the output\n"
+        "files, but instead validates that the checked-in file matches the\n"
+        "expected output.\n"
+        "\n"
+        r"  node_messages.py --dir=.\third_party\ipcz\src\ipcz --check"),
+                                     formatter_class=argparse.
+                                     RawDescriptionHelpFormatter)
     parser.add_argument('--template',
                         default='node_messages',
                         help='template prefix')
diff --git a/third_party/rust/anstyle/v1/BUILD.gn b/third_party/rust/anstyle/v1/BUILD.gn
index d933a9c..a0cb345e 100644
--- a/third_party/rust/anstyle/v1/BUILD.gn
+++ b/third_party/rust/anstyle/v1/BUILD.gn
@@ -28,7 +28,7 @@
   edition = "2021"
   cargo_pkg_name = "anstyle"
   cargo_pkg_description = "ANSI text styling"
-  cargo_pkg_version = "1.0.10"
+  cargo_pkg_version = "1.0.11"
 
   allow_unsafe = true
 
diff --git a/third_party/rust/anstyle/v1/README.chromium b/third_party/rust/anstyle/v1/README.chromium
index 6b9b7580..066a9a6f6 100644
--- a/third_party/rust/anstyle/v1/README.chromium
+++ b/third_party/rust/anstyle/v1/README.chromium
@@ -1,7 +1,7 @@
 Name: anstyle
 URL: https://crates.io/crates/anstyle
-Version: 1.0.10
-Revision: 9ce373f0202db52eea0ba3167fd1c6300a0a17b6
+Version: 1.0.11
+Revision: 886539c95318db5de9db49b6d66d19413bd308cc
 License: Apache-2.0
 License File: //third_party/rust/chromium_crates_io/vendor/anstyle-v1/LICENSE-APACHE
 Shipped: no
diff --git a/third_party/rust/chromium_crates_io/Cargo.lock b/third_party/rust/chromium_crates_io/Cargo.lock
index 39128fa..e877e38 100644
--- a/third_party/rust/chromium_crates_io/Cargo.lock
+++ b/third_party/rust/chromium_crates_io/Cargo.lock
@@ -9,7 +9,7 @@
 
 [[package]]
 name = "anstyle"
-version = "1.0.10"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -74,7 +74,7 @@
 
 [[package]]
 name = "cc"
-version = "1.2.24"
+version = "1.2.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "shlex",
@@ -672,7 +672,7 @@
 
 [[package]]
 name = "llguidance"
-version = "0.7.21"
+version = "0.7.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "anyhow",
@@ -1040,7 +1040,7 @@
 
 [[package]]
 name = "toktrie"
-version = "0.7.24"
+version = "0.7.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "anyhow",
diff --git a/third_party/rust/chromium_crates_io/supply-chain/config.toml b/third_party/rust/chromium_crates_io/supply-chain/config.toml
index bac7204..0af3f00 100644
--- a/third_party/rust/chromium_crates_io/supply-chain/config.toml
+++ b/third_party/rust/chromium_crates_io/supply-chain/config.toml
@@ -44,7 +44,7 @@
 [policy."adler2:2.0.0"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
-[policy."anstyle:1.0.10"]
+[policy."anstyle:1.0.11"]
 criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."anyhow:1.0.98"]
@@ -74,7 +74,7 @@
 [policy."calendrical_calculations:0.2.0"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
-[policy."cc:1.2.24"]
+[policy."cc:1.2.26"]
 criteria = []
 
 [policy."cfg-if:1.0.0"]
@@ -260,7 +260,7 @@
 [policy."litemap:0.8.0"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
-[policy."llguidance:0.7.21"]
+[policy."llguidance:0.7.29"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."log:0.4.27"]
@@ -395,7 +395,7 @@
 [policy."tinystr:0.8.1"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
-[policy."toktrie:0.7.24"]
+[policy."toktrie:0.7.29"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."tzif:0.3.0"]
diff --git a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/.cargo_vcs_info.json
index ac6697d5..e96b9ee 100644
--- a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/.cargo_vcs_info.json
+++ b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "9ce373f0202db52eea0ba3167fd1c6300a0a17b6"
+    "sha1": "886539c95318db5de9db49b6d66d19413bd308cc"
   },
   "path_in_vcs": "crates/anstyle"
 }
\ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/Cargo.lock b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/Cargo.lock
index a9c641d..506b822b 100644
--- a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/Cargo.lock
+++ b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/Cargo.lock
@@ -4,7 +4,7 @@
 
 [[package]]
 name = "anstyle"
-version = "1.0.10"
+version = "1.0.11"
 dependencies = [
  "lexopt",
 ]
diff --git a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/Cargo.toml
index b3d81ec..7b6eed4 100644
--- a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/Cargo.toml
+++ b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/Cargo.toml
@@ -13,7 +13,7 @@
 edition = "2021"
 rust-version = "1.66.0"
 name = "anstyle"
-version = "1.0.10"
+version = "1.0.11"
 build = false
 include = [
     "build.rs",
@@ -22,15 +22,14 @@
     "Cargo.lock",
     "LICENSE*",
     "README.md",
-    "benches/**/*",
     "examples/**/*",
 ]
+autolib = false
 autobins = false
 autoexamples = false
 autotests = false
 autobenches = false
 description = "ANSI text styling"
-homepage = "https://github.com/rust-cli/anstyle"
 readme = "README.md"
 keywords = [
     "ansi",
@@ -47,6 +46,7 @@
 rustdoc-args = [
     "--cfg",
     "docsrs",
+    "--generate-link-to-definition",
 ]
 
 [package.metadata.release]
@@ -87,6 +87,10 @@
 [Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD"""
 search = "<!-- next-url -->"
 
+[features]
+default = ["std"]
+std = []
+
 [lib]
 name = "anstyle"
 path = "src/lib.rs"
@@ -100,10 +104,6 @@
 [dev-dependencies.lexopt]
 version = "0.3.0"
 
-[features]
-default = ["std"]
-std = []
-
 [lints.clippy]
 bool_assert_comparison = "allow"
 branches_sharing_code = "allow"
@@ -140,7 +140,7 @@
 macro_use_imports = "warn"
 mem_forget = "warn"
 mutex_integer = "warn"
-needless_continue = "warn"
+needless_continue = "allow"
 needless_for_each = "warn"
 negative_feature_names = "warn"
 path_buf_push_overwrite = "warn"
@@ -149,6 +149,7 @@
 redundant_feature_names = "warn"
 ref_option_ref = "warn"
 rest_pat_in_fully_bound_structs = "warn"
+result_large_err = "allow"
 same_functions_in_if_condition = "warn"
 self_named_module_files = "warn"
 semicolon_if_nothing_returned = "warn"
@@ -165,6 +166,7 @@
 zero_sized_map_values = "warn"
 
 [lints.rust]
+unnameable_types = "warn"
 unreachable_pub = "warn"
 unsafe_op_in_unsafe_fn = "warn"
 unused_lifetimes = "warn"
diff --git a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/Cargo.toml.orig b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/Cargo.toml.orig
index 35b7e9d..5784d3c5 100644
--- a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/Cargo.toml.orig
+++ b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/Cargo.toml.orig
@@ -1,8 +1,7 @@
 [package]
 name = "anstyle"
-version = "1.0.10"
+version = "1.0.11"
 description = "ANSI text styling"
-homepage = "https://github.com/rust-cli/anstyle"
 categories = ["command-line-interface"]
 keywords = ["ansi", "terminal", "color", "no_std"]
 repository.workspace = true
@@ -13,7 +12,7 @@
 
 [package.metadata.docs.rs]
 all-features = true
-rustdoc-args = ["--cfg", "docsrs"]
+rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"]
 
 [package.metadata.release]
 tag-prefix = ""
diff --git a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/LICENSE-MIT b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/LICENSE-MIT
index 9dc20e2..a2d0108 100644
--- a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/LICENSE-MIT
+++ b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/LICENSE-MIT
@@ -1,4 +1,4 @@
-Copyright (c) 2022 The rust-cli Developers
+Copyright (c) Individual contributors
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/README.md b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/README.md
index 706e8a1..3025c18 100644
--- a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/README.md
+++ b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/README.md
@@ -26,16 +26,16 @@
 
 Licensed under either of
 
- * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
+* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
+* MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
 
 at your option.
 
-### Contribution
+## [Contribute](../../CONTRIBUTING.md)
 
 Unless you explicitly state otherwise, any contribution intentionally
 submitted for inclusion in the work by you, as defined in the Apache-2.0
-license, shall be dual licensed as above, without any additional terms or
+license, shall be dual-licensed as above, without any additional terms or
 conditions.
 
 [Crates.io]: https://crates.io/crates/anstyle
diff --git a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/color.rs b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/color.rs
index b930a020..479921d4 100644
--- a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/color.rs
+++ b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/color.rs
@@ -656,7 +656,7 @@
 
     #[test]
     fn print_size_of() {
-        use std::mem::size_of;
+        use core::mem::size_of;
         dbg!(size_of::<Color>());
         dbg!(size_of::<AnsiColor>());
         dbg!(size_of::<Ansi256Color>());
diff --git a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/effect.rs b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/effect.rs
index 507dfc8..4a076d4 100644
--- a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/effect.rs
+++ b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/effect.rs
@@ -385,7 +385,7 @@
 
     #[test]
     fn print_size_of() {
-        use std::mem::size_of;
+        use core::mem::size_of;
         dbg!(size_of::<Effects>());
         dbg!(size_of::<EffectsDisplay>());
     }
diff --git a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/lib.rs b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/lib.rs
index 3ce6a842..a8802224e 100644
--- a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/lib.rs
+++ b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/lib.rs
@@ -47,6 +47,8 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 #![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![warn(missing_docs)]
+#![warn(clippy::std_instead_of_core)]
+#![warn(clippy::std_instead_of_alloc)]
 #![warn(clippy::print_stderr)]
 #![warn(clippy::print_stdout)]
 
@@ -62,3 +64,7 @@
 pub use effect::*;
 pub use reset::*;
 pub use style::*;
+
+#[doc = include_str!("../README.md")]
+#[cfg(doctest)]
+pub struct ReadmeDoctests;
diff --git a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/reset.rs b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/reset.rs
index 5a4a5f7..b581e16d6 100644
--- a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/reset.rs
+++ b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/reset.rs
@@ -28,7 +28,7 @@
 
     #[test]
     fn print_size_of() {
-        use std::mem::size_of;
+        use core::mem::size_of;
         dbg!(size_of::<Reset>());
     }
 
diff --git a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/style.rs b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/style.rs
index cfa8085..7cc2bd5 100644
--- a/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/style.rs
+++ b/third_party/rust/chromium_crates_io/vendor/anstyle-v1/src/style.rs
@@ -431,7 +431,7 @@
 #[test]
 #[cfg(feature = "std")]
 fn print_size_of() {
-    use std::mem::size_of;
+    use core::mem::size_of;
     dbg!(size_of::<Style>());
     dbg!(size_of::<StyleDisplay>());
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/cc-v1/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/cc-v1/Cargo.toml
index 4076815..7330449 100644
--- a/third_party/rust/chromium_crates_io/vendor/cc-v1/Cargo.toml
+++ b/third_party/rust/chromium_crates_io/vendor/cc-v1/Cargo.toml
@@ -6,13 +6,13 @@
 # by `tools/crates/run_gnrt.py vendor`. Do not edit!
 #
 # This is an empty placeholder that has replaced the
-# `cc-1.2.24` crate.
+# `cc-1.2.26` crate.
 #
 # See `//tools/crates/gnrt/removed_crate.md` to learn more.
 
 [package]
 name = "cc"
-version = "1.2.24"
+version = "1.2.26"
 
 [package.metadata.gnrt]
 is_placeholder = true
diff --git a/third_party/rust/chromium_crates_io/vendor/cc-v1/src/lib.rs b/third_party/rust/chromium_crates_io/vendor/cc-v1/src/lib.rs
index b5b46e96..aaccf6cc 100644
--- a/third_party/rust/chromium_crates_io/vendor/cc-v1/src/lib.rs
+++ b/third_party/rust/chromium_crates_io/vendor/cc-v1/src/lib.rs
@@ -6,6 +6,6 @@
 // by `tools/crates/run_gnrt.py vendor`. Do not edit!
 //
 // This is an empty placeholder that has replaced the
-// `cc-1.2.24` crate.
+// `cc-1.2.26` crate.
 //
 // See `//tools/crates/gnrt/removed_crate.md` to learn more.
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/.cargo_vcs_info.json
index ae47b38..3006235 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/.cargo_vcs_info.json
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "5f43908e1cbebdb713a83f309e807f37fc1b4ed4",
+    "sha1": "e43c8a0678eec12712c217b637dadb0f9b867d33",
     "dirty": true
   },
   "path_in_vcs": "parser"
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.lock b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.lock
index aa8cd95f..642b9f3 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.lock
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.lock
@@ -434,7 +434,7 @@
 
 [[package]]
 name = "llguidance"
-version = "0.7.21"
+version = "0.7.29"
 dependencies = [
  "anyhow",
  "derivre",
@@ -816,9 +816,9 @@
 
 [[package]]
 name = "toktrie"
-version = "0.7.21"
+version = "0.7.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "279e477cd582ba1b60765c07e34fea21eefd32f1862c223ff434f6c52498df1e"
+checksum = "24abc046cdf691cae38efcc45a52e68e03ab26954157c6c15e2e7c9f6e46fef4"
 dependencies = [
  "anyhow",
  "bytemuck",
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml
index 95fe81b..9c42412 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml
@@ -12,7 +12,7 @@
 [package]
 edition = "2021"
 name = "llguidance"
-version = "0.7.21"
+version = "0.7.29"
 build = "build.rs"
 autolib = false
 autobins = false
@@ -95,7 +95,7 @@
 features = ["preserve_order"]
 
 [dependencies.toktrie]
-version = "0.7.21"
+version = "0.7.29"
 
 [dev-dependencies.regex]
 version = "1.11.1"
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml.orig b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml.orig
index 839a8bc10..2ba9a07 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml.orig
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml.orig
@@ -1,13 +1,13 @@
 [package]
 name = "llguidance"
-version = "0.7.21"
+version = "0.7.29"
 edition = "2021"
 license = "MIT"
 description = "Super-fast Structured Outputs"
 repository = "https://github.com/guidance-ai/llguidance"
 
 [dependencies]
-toktrie = { version = "0.7.21" }
+toktrie = { version = "0.7.29" }
 derivre = { version = "=0.3.8", default-features = false, features = ["compress"] }
 serde = { version = "1.0.217", features = ["derive"] }
 serde_json = { version = "1.0.138", features = ["preserve_order"] }
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/llguidance.h b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/llguidance.h
index efd0167e..a1d59c23 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/llguidance.h
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/llguidance.h
@@ -434,6 +434,13 @@
                                   bool *is_stopped_p);
 
 /**
+ * Clone the stop-sequence controller.
+ * The cloned controller shares (under mutex) regex caches if any, so that
+ * cloning is cheap.
+ */
+struct LlgStopController *llg_clone_stop_controller(const struct LlgStopController *stop_ctrl);
+
+/**
  * Free the stop-sequence controller
  */
 void llg_free_stop_controller(struct LlgStopController *stop_ctrl);
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/parser.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/parser.rs
index bebf30a..4765f95 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/parser.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/parser.rs
@@ -53,6 +53,14 @@
     }
 }
 
+macro_rules! debug_def {
+    ($s:expr, $($arg:tt)*) => {
+        if cfg!(feature = "logging") && DEBUG && $s.scratch.log_enabled() {
+            eprintln!($($arg)*);
+        }
+    }
+}
+
 macro_rules! item_trace {
     ($($arg:tt)*) => {
         if ITEM_TRACE {
@@ -322,6 +330,8 @@
     // mode, which is used for computing the token mask on the
     // pre-lexemes.
     definitive: bool,
+
+    log_override: bool,
 }
 
 #[derive(Clone)]
@@ -425,6 +435,7 @@
     // history - items are not popped in definitive mode.
     lexer_stack: Vec<LexerState>,
     lexer_stack_top_eos: bool,
+    lexer_stack_flush_position: usize,
     rows: Vec<Row>,
     rows_valid_end: usize,
 
@@ -488,9 +499,14 @@
             items: vec![],
             grammar_stack: vec![],
             definitive: true,
+            log_override: false,
         }
     }
 
+    fn log_enabled(&self) -> bool {
+        self.definitive || self.log_override
+    }
+
     // Set current working Earley to empty set
     // The set backing data is at `pos`
     fn new_row(&mut self, pos: usize) {
@@ -523,7 +539,7 @@
     }
 
     fn push_grammar_stack(&mut self, node: GrammarStackNode) {
-        if self.definitive {
+        if self.log_enabled() {
             debug!("push_grammar_stack: {:?}", node);
         }
         let ptr = GrammarStackPtr::new(self.grammar_stack.len());
@@ -543,7 +559,7 @@
         } else {
             self.items[self.row_end] = item;
         }
-        if self.definitive {
+        if self.log_enabled() {
             debug!(
                 "      addu: {} ({})",
                 self.item_to_string(self.row_end),
@@ -650,6 +666,7 @@
             limits,
             backtrack_byte_count: 0,
             lexer_stack_top_eos: false,
+            lexer_stack_flush_position: 0,
             lexer_stack: vec![LexerState {
                 row_idx: 0,
                 lexer_state,
@@ -778,12 +795,6 @@
             });
         }
 
-        if set.is_zero() {
-            // nothing allowed
-            // we're going to be stopped outside - we better flush the lexer
-            let _ = self.flush_lexer();
-        }
-
         let eos = computer.trie().eos_token();
         if eos != INVALID_TOKEN && start.is_empty() && self.lexer_allows_eos() {
             set.allow_token(eos);
@@ -920,7 +931,7 @@
         self.stats = ParserStats::default();
     }
 
-    fn assert_definitive(&self) {
+    fn assert_definitive_inner(&self) {
         assert!(self.scratch.definitive);
         assert!(self.backtrack_byte_count == 0);
         if self.num_rows() != self.row_infos.len() {
@@ -932,6 +943,14 @@
         }
     }
 
+    fn assert_definitive(&self) {
+        self.assert_definitive_inner();
+
+        if self.lexer_spec().can_rollback() {
+            self.check_lexer_bytes_invariant();
+        }
+    }
+
     pub fn get_bytes(&self) -> &[u8] {
         &self.bytes
     }
@@ -980,7 +999,6 @@
             n_bytes,
             self.byte_to_token_idx.len()
         );
-        self.check_lexer_bytes_invariant();
 
         let new_len = self.byte_to_token_idx.len() - n_bytes;
 
@@ -995,7 +1013,6 @@
         self.rows_valid_end = self.num_rows();
 
         self.assert_definitive();
-        self.check_lexer_bytes_invariant();
 
         Ok(())
     }
@@ -1003,6 +1020,7 @@
     pub fn validate_tokens(&mut self, tokens: &[TokenId]) -> usize {
         self.assert_definitive();
         self.run_speculative("validate_tokens", |state| {
+            state.scratch.log_override = true;
             let mut applied_idx = state.byte_to_token_idx.len();
             let tok_env = state.tok_env.clone();
             let trie = tok_env.tok_trie();
@@ -1081,6 +1099,12 @@
                     .push(self.token_idx.try_into().unwrap());
             }
         }
+        debug_def!(
+            self,
+            "add_numeric_token: idx={:?} bytes={:?}",
+            idx,
+            tok_bytes
+        );
         let ok = self.advance_parser(PreLexeme::just_idx(MatchingLexemesIdx::Single(idx)));
         ensure!(
             ok,
@@ -1140,9 +1164,20 @@
                 let row_idx = self.num_rows() - 1;
                 self.row_infos[row_idx].apply_token_idx(self.token_idx);
 
+                self.lexer_stack_flush_position = 0;
                 let idx = self.flush_and_check_numeric(tok_id).unwrap();
                 self.add_numeric_token(idx, tok_bytes)?;
 
+                // if flush_lexer() added a stack entry
+                if self.lexer_stack_flush_position > 0 {
+                    // we make sure it's not on the top
+                    assert!(self.lexer_stack_flush_position + 1 < self.lexer_stack.len());
+                    // and remove it
+                    self.lexer_stack.remove(self.lexer_stack_flush_position);
+                }
+
+                self.assert_definitive();
+
                 return Ok(0);
             }
         }
@@ -1298,6 +1333,8 @@
             self.print_row(self.num_rows() - 1);
         }
 
+        self.assert_definitive();
+
         Ok(0)
     }
 
@@ -1458,10 +1495,6 @@
         // debug!("trie_started: rows={} lexer={}", self.num_rows(), self.lexer_stack.len());
         self.assert_definitive();
 
-        if self.lexer_spec().can_rollback() {
-            self.check_lexer_bytes_invariant();
-        }
-
         self.trie_lexer_stack = self.lexer_stack.len();
         self.trie_grammar_stack = self.scratch.grammar_stack.len();
         self.scratch.definitive = false;
@@ -1497,6 +1530,8 @@
         self.scratch.definitive = true;
         self.assert_definitive();
         self.rows_valid_end = self.num_rows();
+        self.scratch.log_override = false; // reset
+        self.lexer_stack_flush_position = 0;
     }
 
     fn run_speculative<T>(&mut self, lbl: &str, f: impl FnOnce(&mut Self) -> T) -> T {
@@ -1655,14 +1690,19 @@
         }
         let curr = self.lexer_state();
         let lex_result = self.lexer_mut().try_lexeme_end(curr.lexer_state);
+        let prev_len = self.lexer_stack.len();
         let r = self.advance_lexer_or_parser(lex_result, curr);
+        if self.lexer_stack.len() != prev_len {
+            assert!(self.lexer_stack.len() == prev_len + 1);
+            assert!(prev_len > 0);
+            self.lexer_stack_flush_position = prev_len;
+        }
         assert!(self.backtrack_byte_count == 0);
         r
     }
 
     pub fn scan_eos(&mut self) -> bool {
         self.assert_definitive(); // ???
-        self.check_lexer_bytes_invariant();
 
         let lexer_eos = self.lexer_allows_eos();
 
@@ -1691,7 +1731,7 @@
             self.lexer_stack_top_eos = true;
         }
 
-        self.check_lexer_bytes_invariant();
+        self.assert_definitive(); // ???
 
         false
     }
@@ -1758,14 +1798,13 @@
         self.scratch.new_row(items.end);
         self.scratch.push_lexeme_idx = lexeme.idx;
 
-        if self.scratch.definitive {
-            debug!(
-                "  scan: {} at row={} token={}",
-                self.lexer().dbg_lexeme(lexeme),
-                row_idx,
-                self.token_idx,
-            );
-        }
+        debug_def!(
+            self,
+            "  scan: {} at row={} token={}",
+            self.lexer().dbg_lexeme(lexeme),
+            row_idx,
+            self.token_idx,
+        );
 
         // This loop performs the scan inference rule
         // (slide 21 of Kallmeyer 2018).  It is an
@@ -1886,9 +1925,7 @@
             let item_idx = agenda_ptr;
             let item = self.scratch.items[agenda_ptr];
             agenda_ptr += 1;
-            if self.scratch.definitive {
-                debug!("    agenda: {}", self.item_to_string(item_idx));
-            }
+            debug_def!(self, "    agenda: {}", self.item_to_string(item_idx));
 
             let rule = item.rhs_ptr();
             let after_dot = self.grammar.sym_idx_dot(rule);
@@ -1984,13 +2021,12 @@
                     .start_state(&self.scratch.push_allowed_lexemes)
             };
 
-            if self.scratch.definitive {
-                debug!(
-                    "  push row: {} {:?}",
-                    self.allowed_lexemes_dbg(lex_start),
-                    grammar_id
-                );
-            }
+            debug_def!(
+                self,
+                "  push row: {} {:?}",
+                self.allowed_lexemes_dbg(lex_start),
+                grammar_id
+            );
 
             // Add the working row to the parser state
             let idx = self.num_rows();
@@ -2038,9 +2074,7 @@
     }
 
     fn process_max_tokens(&mut self, ptr: GrammarStackPtr, lexeme: &Lexeme) {
-        if self.scratch.definitive {
-            debug!("  process_max_tokens");
-        }
+        debug_def!(self, "  process_max_tokens");
         let curr_idx = self.num_rows();
         let top = &self.scratch.grammar_stack[ptr.as_usize()];
         self.scratch.push_grm_top = top.back_ptr;
@@ -2114,12 +2148,13 @@
 
         while grm_stack_top.as_usize() > 0 {
             let grm_top = &self.scratch.grammar_stack[grm_stack_top.as_usize()];
-            if self.scratch.definitive {
-                debug!(
-                    "  pop grammar_stack: top={:?}, curr={:?}, #{}",
-                    grm_top.grammar_id, grammar_ids, self.token_idx
-                );
-            }
+            debug_def!(
+                self,
+                "  pop grammar_stack: top={:?}, curr={:?}, #{}",
+                grm_top.grammar_id,
+                grammar_ids,
+                self.token_idx
+            );
             if grammar_ids.contains(&grm_top.grammar_id) {
                 // token_idx is one behind
                 if grm_top.token_horizon <= self.token_idx as u32 {
@@ -2128,12 +2163,12 @@
                     // We only pop one grammar off the stack.
                     // If more grammars have the same token horizon, they will get popped
                     // in the next step - we might overrun a bit.
-                    if self.scratch.definitive {
-                        debug!(
-                            "  hit token limit horizon={} token_idx={}",
-                            grm_top.token_horizon, self.token_idx
-                        );
-                    }
+                    debug_def!(
+                        self,
+                        "  hit token limit horizon={} token_idx={}",
+                        grm_top.token_horizon,
+                        self.token_idx
+                    );
                     max_token_ptr = Some(grm_stack_top);
                 }
                 break;
@@ -2248,13 +2283,14 @@
                     .saturating_sub(1);
                 self.row_infos[added_row].start_byte_idx -= new_start;
             }
-            debug!(
-                "lex: re-start {:?} (via {:?}); allowed: {}",
-                no_hidden.lexer_state,
-                transition_byte.map(|b| b as char),
-                self.allowed_lexemes_dbg(added_row_start_state)
-            );
         }
+        debug_def!(
+            self,
+            "lex: re-start {:?} (via {:?}); allowed: {}",
+            no_hidden.lexer_state,
+            transition_byte.map(|b| b as char),
+            self.allowed_lexemes_dbg(added_row_start_state)
+        );
 
         no_hidden
     }
@@ -2273,7 +2309,7 @@
 
         let hidden_bytes = lexeme.hidden_bytes();
 
-        let trace_here = self.scratch.definitive;
+        let trace_here = self.scratch.log_enabled();
 
         if trace_here {
             trace!(
@@ -2344,7 +2380,7 @@
                 });
             }
             if self.scratch.definitive {
-                self.assert_definitive();
+                self.assert_definitive_inner();
             }
         } else {
             if trace_here {
@@ -2357,7 +2393,7 @@
                     byte: None,
                     ..no_hidden
                 });
-                self.assert_definitive();
+                self.assert_definitive_inner();
                 self.backtrack_byte_count = hidden_bytes.len();
             } else {
                 // prevent any further matches in this branch
@@ -2463,9 +2499,7 @@
                         .lexer_mut()
                         .check_for_single_byte_lexeme(no_hidden.lexer_state, b);
                     if let Some(second_lexeme) = single {
-                        if self.scratch.definitive {
-                            debug!("single byte lexeme: {:?}", second_lexeme);
-                        }
+                        debug_def!(self, "single byte lexeme: {:?}", second_lexeme);
                         no_hidden.byte = None;
                         self.lexer_stack.push(no_hidden);
 
@@ -2484,16 +2518,15 @@
                         }
                     }
                 }
+                debug_def!(self, "  push normal: {no_hidden:?}");
                 self.lexer_stack.push(no_hidden);
             }
             if self.scratch.definitive {
-                self.assert_definitive();
+                self.assert_definitive_inner();
             }
             true
         } else {
-            if self.scratch.definitive {
-                debug!("  scan failed");
-            }
+            debug_def!(self, "  scan failed");
             false
         }
     }
@@ -2827,4 +2860,10 @@
         copy.shared = Arc::new(Mutex::new(shared.clone()));
         copy
     }
+
+    pub fn test_trigger_lexer_error(&mut self) -> Result<()> {
+        self.with_shared(|_state| {
+            panic!("synthetic error");
+        })
+    }
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/ffi.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/ffi.rs
index 7bd5350..89b8ad4 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/ffi.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/ffi.rs
@@ -330,6 +330,7 @@
     last_commit_result: CommitResult,
 }
 
+#[derive(Clone)]
 pub struct LlgStopController {
     stop_controller: StopController,
     last_result: String,
@@ -923,6 +924,16 @@
     stop_ctrl.last_result.as_ptr() as *const c_char
 }
 
+/// Clone the stop-sequence controller.
+/// The cloned controller shares (under mutex) regex caches if any, so that
+/// cloning is cheap.
+#[no_mangle]
+pub extern "C" fn llg_clone_stop_controller(
+    stop_ctrl: &LlgStopController,
+) -> *mut LlgStopController {
+    Box::into_raw(Box::new(stop_ctrl.clone()))
+}
+
 /// Free the stop-sequence controller
 /// # Safety
 /// This function should only be called from C code.
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/grammar_builder.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/grammar_builder.rs
index bdec2279..814e98e 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/grammar_builder.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/grammar_builder.rs
@@ -132,6 +132,9 @@
     }
 
     pub fn and(&mut self, nodes: Vec<RegexId>) -> RegexId {
+        if nodes.len() == 1 {
+            return nodes[0];
+        }
         self.add_ast(RegexAst::And(map_ids(&nodes))).unwrap()
     }
 
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/ast.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/ast.rs
index 7b91195d..c4911d2d 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/ast.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/ast.rs
@@ -79,29 +79,41 @@
 #[derive(Debug, Clone)]
 pub struct TokenParams(pub Vec<String>);
 
-/// Represents a list of expansions.
+/// Represents an alternative (OR) of productions in a grammar.
 #[derive(Debug)]
 pub struct Expansions(pub Location, pub Vec<Alias>);
 
 impl Expansions {
     pub fn single_atom(&self) -> Option<&Atom> {
-        if self.1.len() == 1 && self.1[0].expansion.0.len() == 1 {
-            Some(&self.1[0].expansion.0[0].atom)
+        if self.1.len() == 1
+            && self.1[0].conjuncts.len() == 1
+            && self.1[0].conjuncts[0].0.len() == 1
+        {
+            Some(&self.1[0].conjuncts[0].0[0].atom)
         } else {
             None
         }
     }
+
+    pub fn take_single_atom(&mut self) -> Option<Atom> {
+        if self.single_atom().is_none() {
+            None
+        } else {
+            Some(self.1[0].conjuncts.pop().unwrap().0.pop().unwrap().atom)
+        }
+    }
 }
 
 /// Represents an alias in the grammar.
+/// Each alias consists of possibly multiple conjuncts (AND).
 #[derive(Debug)]
 pub struct Alias {
-    pub expansion: Expansion,
+    pub conjuncts: Vec<Expansion>,
     #[allow(dead_code)]
     pub alias: Option<String>,
 }
 
-/// Represents an expansion consisting of expressions.
+/// Represents a concatenation of expressions in the grammar.
 #[derive(Debug)]
 pub struct Expansion(pub Vec<Expr>);
 
@@ -119,6 +131,7 @@
     Group(Expansions),
     Maybe(Expansions),
     Value(Value),
+    Not(Box<Atom>),
 }
 
 /// Represents different values in the grammar.
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/compiler.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/compiler.rs
index a75e1a8..2f03283 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/compiler.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/compiler.rs
@@ -111,6 +111,10 @@
                 let id = self.do_token_expansions(expansions)?;
                 Ok(self.builder.regex.optional(id))
             }
+            Atom::Not(inner) => {
+                let id = self.do_token_atom(*inner)?;
+                Ok(self.builder.regex.not(id))
+            }
             Atom::Value(value) => match value {
                 Value::LiteralRange(a, b) => {
                     ensure!(
@@ -218,12 +222,18 @@
             .into_iter()
             .map(|alias| {
                 let args = alias
-                    .expansion
-                    .0
+                    .conjuncts
                     .into_iter()
-                    .map(|e| self.do_token_expr(e))
+                    .map(|exp| {
+                        let args = exp
+                            .0
+                            .into_iter()
+                            .map(|e| self.do_token_expr(e))
+                            .collect::<Result<Vec<_>>>()?;
+                        Ok(self.builder.regex.concat(args))
+                    })
                     .collect::<Result<Vec<_>>>()?;
-                Ok(self.builder.regex.concat(args))
+                Ok(self.builder.regex.and(args))
             })
             .collect::<Result<Vec<_>>>()
             .map_err(|e| expansions.0.augment(e))?;
@@ -265,6 +275,11 @@
                 let id = self.do_expansions(expansions)?;
                 Ok(self.builder.optional(id))
             }
+            Atom::Not(_) => {
+                // treat as token
+                let rx = self.do_token_atom(expr)?;
+                Ok(self.lift_regex(rx)?)
+            }
             Atom::Value(value) => {
                 match &value {
                     Value::Name(n) => {
@@ -363,9 +378,15 @@
         let options = expansions
             .1
             .into_iter()
-            .map(|alias| {
+            .map(|mut alias| {
+                ensure!(
+                    alias.conjuncts.len() == 1,
+                    "& is only supported for tokens, not rules; try renaming the rule to UPPERCASE"
+                );
                 let args = alias
-                    .expansion
+                    .conjuncts
+                    .pop()
+                    .unwrap()
                     .0
                     .into_iter()
                     .map(|e| self.do_expr(&loc, e))
@@ -478,8 +499,7 @@
                         return self.gen_grammar(g, rule.temperature, props);
                     }
                     Some(Atom::Value(Value::Json(_) | Value::NestedLark(_))) => {
-                        if let Atom::Value(x) = rule.expansions.1[0].expansion.0.pop().unwrap().atom
-                        {
+                        if let Some(Atom::Value(x)) = rule.expansions.take_single_atom() {
                             return self.do_nested(&rule.expansions.0, x, rule.temperature, props);
                         } else {
                             unreachable!();
@@ -580,11 +600,11 @@
             expansions: Expansions(
                 loc.clone(),
                 vec![Alias {
-                    expansion: Expansion(vec![Expr {
+                    conjuncts: vec![Expansion(vec![Expr {
                         atom: Atom::Value(Value::LiteralRegex(regex.to_string(), "".to_string())),
                         op: None,
                         range: None,
-                    }]),
+                    }])],
                     alias: None,
                 }],
             ),
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/lexer.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/lexer.rs
index 6af12ebd..0b28fab5 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/lexer.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/lexer.rs
@@ -49,6 +49,7 @@
     Number,
     Newline,
     VBar,
+    And,          // &
     SpecialToken, // <something>
     GrammarRef,   // @grammar_id or @7
     // special
@@ -144,6 +145,7 @@
         (Token::RBracket, "]"),
         (Token::Tilde, "~"),
         (Token::VBar, "|"),
+        (Token::And, "&"),
         (Token::Equals, "="),
     ];
 
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/parser.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/parser.rs
index 2386847..c7a6a9e 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/parser.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/parser.rs
@@ -357,13 +357,20 @@
 
     /// Parses an alias.
     fn parse_alias(&mut self) -> Result<Alias> {
-        let expansion = self.parse_expansion()?;
+        let mut conjuncts = Vec::with_capacity(1);
+        loop {
+            let expansion = self.parse_expansion()?;
+            conjuncts.push(expansion);
+            if !self.match_token(Token::And) {
+                break;
+            }
+        }
         let alias = if self.match_token(Token::Arrow) {
             Some(self.expect_token_val(Token::Rule)?)
         } else {
             None
         };
-        Ok(Alias { expansion, alias })
+        Ok(Alias { conjuncts, alias })
     }
 
     /// Parses an expansion.
@@ -376,6 +383,7 @@
                 || self.has_token(Token::RBrace)
                 || self.has_token(Token::RParen)
                 || self.has_token(Token::RBracket)
+                || self.has_token(Token::And)
             {
                 break;
             }
@@ -391,7 +399,8 @@
         let mut range = None;
         if let Some(op_token) = self.match_token_with_value(Token::Op) {
             op = Some(Op(op_token.clone()));
-        } else if self.match_token(Token::Tilde) {
+        } else if self.has_tokens(&[Token::Tilde, Token::Number]) {
+            self.expect_token(Token::Tilde)?;
             let start_num = self.expect_token_val(Token::Number)?.parse::<i32>()?;
             let end_num = if self.match_token(Token::DotDot) {
                 Some(self.expect_token_val(Token::Number)?.parse::<i32>()?)
@@ -426,16 +435,27 @@
 
     /// Parses an atom.
     fn parse_atom(&mut self) -> Result<Atom> {
-        if self.match_token(Token::LParen) {
+        let mut negated = false;
+        if self.match_token(Token::Tilde) {
+            negated = true;
+        }
+
+        let res = if self.match_token(Token::LParen) {
             let expansions = self.parse_expansions()?;
             self.expect_token(Token::RParen)?;
-            Ok(Atom::Group(expansions))
+            Atom::Group(expansions)
         } else if self.match_token(Token::LBracket) {
             let expansions = self.parse_expansions()?;
             self.expect_token(Token::RBracket)?;
-            Ok(Atom::Maybe(expansions))
+            Atom::Maybe(expansions)
         } else {
-            Ok(Atom::Value(self.parse_value()?))
+            Atom::Value(self.parse_value()?)
+        };
+
+        if negated {
+            Ok(Atom::Not(Box::new(res)))
+        } else {
+            Ok(res)
         }
     }
 
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/matcher.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/matcher.rs
index 38df990..0204426b 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/matcher.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/matcher.rs
@@ -86,6 +86,10 @@
         self.consume_tokens(&[token])
     }
 
+    pub fn test_trigger_lexer_error(&mut self) -> Result<()> {
+        self.with_inner(|inner| inner.parser.parser.test_trigger_lexer_error())
+    }
+
     pub fn rollback(&mut self, num_tokens: usize) -> Result<()> {
         self.with_inner(|inner| inner.parser.rollback(num_tokens))
     }
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/stop_controller.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/stop_controller.rs
index d53cc11..737529e 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/stop_controller.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/stop_controller.rs
@@ -1,3 +1,5 @@
+use std::sync::{Arc, Mutex};
+
 use anyhow::Result;
 use derivre::{RegexAst, RegexBuilder, StateID};
 use toktrie::{TokEnv, TokTrie, TokenId};
@@ -10,12 +12,14 @@
     },
 };
 
+#[derive(Clone)]
 struct StopRegex {
-    dfa: RegexVec,
+    dfa: Arc<Mutex<RegexVec>>,
     state: StateID,
     initial_state: StateID,
 }
 
+#[derive(Clone)]
 pub struct StopController {
     tok_env: TokEnv,
     is_stopped: bool,
@@ -73,7 +77,7 @@
             )?;
             let initial_state = dfa.initial_state(&all_regex);
             res.regex = Some(StopRegex {
-                dfa,
+                dfa: Arc::new(Mutex::new(dfa)),
                 state: initial_state,
                 initial_state,
             });
@@ -104,23 +108,24 @@
                 buf.extend_from_slice(format!("<[{}]>", tok_id).as_bytes());
             } else if let Some(rx) = self.regex.as_mut() {
                 let mut state = rx.state;
+                let mut dfa = rx.dfa.lock().unwrap();
                 for &b in bytes {
                     buf.push(b);
-                    let state2 = rx.dfa.transition(state, b);
+                    let state2 = dfa.transition(state, b);
                     // println!("state: {:?} -{:?}-> {:?}", state, b as char, state2);
                     state = state2;
                     assert!(!state.is_dead());
                     if state.has_lowest_match() {
                         self.is_stopped = true;
                         rx.state = state;
-                        let stop_len = rx.dfa.lookahead_len_for_state(state).unwrap_or(0);
+                        let stop_len = dfa.lookahead_len_for_state(state).unwrap_or(0);
                         buf.truncate(buf.len().saturating_sub(stop_len));
                         return buf;
                     }
                 }
 
                 rx.state = state;
-                let chop = rx.dfa.possible_lookahead_len(state);
+                let chop = dfa.possible_lookahead_len(state);
                 let to_return = buf.len().saturating_sub(chop);
                 // println!("chop: {:?} {}", String::from_utf8_lossy(&buf), chop);
                 let valid_len = valid_utf8_len(&buf[..to_return]);
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/tokenparser.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/tokenparser.rs
index 0ef203b..61c74d6f 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/tokenparser.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/tokenparser.rs
@@ -18,6 +18,7 @@
     pub logger: Logger,
     pub limits: ParserLimits,
     pub bias_computer: Arc<dyn BiasComputer>,
+    pub dbg_grammar: String,
     last_step_stats: ParserStats,
     max_step_stats: ParserStats,
     eos_token: TokenId,
@@ -106,6 +107,7 @@
             stop_reason: StopReason::NotStopped,
             error_message: None,
             parser,
+            dbg_grammar: String::new(),
             eos_token,
             llm_tokens: Vec::new(),
             llm_bytes: Vec::new(),
@@ -274,12 +276,19 @@
     }
 
     pub fn augment_err(&self, e: impl Display) -> String {
-        format!("{e}\n<state>\n{}\n</state>", self.dump_state())
+        format!(
+            "{e}\n<state>\n{}\n</state><grammar>\n{}\n</grammar>",
+            self.dump_state(),
+            self.dbg_grammar
+        )
     }
 
     pub fn dump_state(&self) -> String {
+        // make sure not take self.parser.shared lock
+        // for example, self.parser.lexer_stats() takes it
+        // if we take it after panic, it will be poisoned
         format!(
-            "Tokens: {}\n{} tokens, {} bytes; grm_prefix: {:?}\nFlags:{}{}\nLexer: {}\nParser: {}\nStop: {}\nError: {}",
+            "Tokens: {}\n{} tokens, {} bytes; grm_prefix: {:?}\nFlags:{}{}\nParser: {}\nStop: {}\nError: {}",
             self.tok_trie().tokens_dbg(&self.llm_tokens),
             self.llm_tokens.len(),
             self.llm_bytes.len(),
@@ -294,7 +303,6 @@
             } else {
                 ""
             },
-            self.parser.lexer_stats(),
             self.parser.stats(),
             self.stop_reason,
             self.error_message.as_deref().unwrap_or("None"),
diff --git a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/.cargo_vcs_info.json
index 203890f..df9fa7d 100644
--- a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/.cargo_vcs_info.json
+++ b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "cec3037edf97c88afc3d7546b54e9d59fbd3ee7e"
+    "sha1": "e43c8a0678eec12712c217b637dadb0f9b867d33"
   },
   "path_in_vcs": "toktrie"
 }
\ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.lock b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.lock
index cdec84d..b011fb43 100644
--- a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.lock
+++ b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.lock
@@ -106,7 +106,7 @@
 
 [[package]]
 name = "toktrie"
-version = "0.7.24"
+version = "0.7.29"
 dependencies = [
  "anyhow",
  "bytemuck",
diff --git a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml
index 24e07609..0a5f9db 100644
--- a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml
+++ b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml
@@ -12,7 +12,7 @@
 [package]
 edition = "2021"
 name = "toktrie"
-version = "0.7.24"
+version = "0.7.29"
 build = false
 autolib = false
 autobins = false
diff --git a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml.orig b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml.orig
index 201caec..2f7f0a6 100644
--- a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml.orig
+++ b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "toktrie"
-version = "0.7.24"
+version = "0.7.29"
 edition = "2021"
 license = "MIT"
 description = "LLM Token Trie library"
diff --git a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/src/tokenv.rs b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/src/tokenv.rs
index 39857213..aa0f8cf 100644
--- a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/src/tokenv.rs
+++ b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/src/tokenv.rs
@@ -72,7 +72,12 @@
 
     /// Tokenize a string. It will interpret <|special_tokens|> as special.
     fn tokenize_special(&self, s: &str) -> Vec<TokenId> {
-        self.tokenize(s)
+        self.tokenize_bytes_special(s.as_bytes())
+    }
+
+    /// Tokenize a byte slice. It will interpret <|special_tokens|> as special.
+    fn tokenize_bytes_special(&self, s: &[u8]) -> Vec<TokenId> {
+        self.tokenize_bytes(s)
     }
 
     /// End of sentence token
diff --git a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/src/toktree.rs b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/src/toktree.rs
index ccc1f44..1be3619 100644
--- a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/src/toktree.rs
+++ b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/src/toktree.rs
@@ -515,6 +515,68 @@
         tokens
     }
 
+    /// Tokenize a string, interpreting `<name>` as special tokens.
+    pub fn tokenize_with_special<F>(&self, s: &str, str_tokenize: F) -> Vec<TokenId>
+    where
+        F: Fn(&str) -> Vec<TokenId>,
+    {
+        let max_len = 100;
+
+        let bytes = s.as_bytes();
+        let mut out = Vec::new();
+        let mut last = 0; // byte‐offset of the next “raw” segment
+        let mut i = 0; // current byte index
+
+        while i < bytes.len() {
+            if bytes[i] != b'<' {
+                i += 1;
+                continue;
+            }
+            // Potential start of `<...>`
+            let mut valid = true;
+            let mut j = i + 1;
+            let mut len_inside = 0;
+            // scan up to max_len chars or until we hit `>` or `<`
+            while j < bytes.len() && len_inside < max_len {
+                match bytes[j] {
+                    b'<' => {
+                        valid = false;
+                        break;
+                    }
+                    b'>' => break,
+                    _ => {
+                        len_inside += 1;
+                        j += 1;
+                    }
+                }
+            }
+            if !valid || j >= bytes.len() || bytes[j] != b'>' || len_inside == 0 {
+                // treat this `<` as literal
+                i += 1;
+                continue;
+            }
+
+            let name = &s[i..=j];
+            if let Some(special_tok) = self.get_special_token(name) {
+                if last < i {
+                    out.extend(str_tokenize(&s[last..i]));
+                }
+                out.push(special_tok);
+            } else {
+                // fallback: tokenize `<name>` literally
+                out.extend(str_tokenize(&s[last..=j]));
+            }
+            // advance past the `>`
+            i = j + 1;
+            last = i;
+        }
+        // any trailing text:
+        if last < bytes.len() {
+            out.extend(str_tokenize(&s[last..]));
+        }
+        out
+    }
+
     pub fn tokenize_with_greedy_fallback(
         &self,
         bytes: &[u8],
diff --git a/third_party/rust/llguidance/v0_7/BUILD.gn b/third_party/rust/llguidance/v0_7/BUILD.gn
index d0ee53b..6be7fc5 100644
--- a/third_party/rust/llguidance/v0_7/BUILD.gn
+++ b/third_party/rust/llguidance/v0_7/BUILD.gn
@@ -67,7 +67,7 @@
   edition = "2021"
   cargo_pkg_name = "llguidance"
   cargo_pkg_description = "Super-fast Structured Outputs"
-  cargo_pkg_version = "0.7.21"
+  cargo_pkg_version = "0.7.29"
 
   allow_unsafe = true
 
diff --git a/third_party/rust/llguidance/v0_7/README.chromium b/third_party/rust/llguidance/v0_7/README.chromium
index 5bad587..89a8f9f 100644
--- a/third_party/rust/llguidance/v0_7/README.chromium
+++ b/third_party/rust/llguidance/v0_7/README.chromium
@@ -1,7 +1,7 @@
 Name: llguidance
 URL: https://crates.io/crates/llguidance
-Version: 0.7.21
-Revision: 5f43908e1cbebdb713a83f309e807f37fc1b4ed4
+Version: 0.7.29
+Revision: e43c8a0678eec12712c217b637dadb0f9b867d33
 License: MIT
 License File: //third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/LICENSE
 Shipped: yes
diff --git a/third_party/rust/temporal_capi/BUILD.gn b/third_party/rust/temporal_capi/BUILD.gn
index 31d5c74..59463af 100644
--- a/third_party/rust/temporal_capi/BUILD.gn
+++ b/third_party/rust/temporal_capi/BUILD.gn
@@ -1,3 +1,7 @@
+# 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.
+
 # v8 depends on temporal_capi, which may occasionally update in ways that
 # change its API. Since temporal_capi lives in the chromium repo, v8 cannot
 # be atomically upgraded alongside temporal_capi changes. Instead, we have some
diff --git a/third_party/rust/toktrie/v0_7/BUILD.gn b/third_party/rust/toktrie/v0_7/BUILD.gn
index 072325f..2500a46 100644
--- a/third_party/rust/toktrie/v0_7/BUILD.gn
+++ b/third_party/rust/toktrie/v0_7/BUILD.gn
@@ -29,7 +29,7 @@
   edition = "2021"
   cargo_pkg_name = "toktrie"
   cargo_pkg_description = "LLM Token Trie library"
-  cargo_pkg_version = "0.7.24"
+  cargo_pkg_version = "0.7.29"
 
   allow_unsafe = false
 
diff --git a/third_party/rust/toktrie/v0_7/README.chromium b/third_party/rust/toktrie/v0_7/README.chromium
index 9c3d069..f9c3b07 100644
--- a/third_party/rust/toktrie/v0_7/README.chromium
+++ b/third_party/rust/toktrie/v0_7/README.chromium
@@ -1,7 +1,7 @@
 Name: toktrie
 URL: https://crates.io/crates/toktrie
-Version: 0.7.24
-Revision: cec3037edf97c88afc3d7546b54e9d59fbd3ee7e
+Version: 0.7.29
+Revision: e43c8a0678eec12712c217b637dadb0f9b867d33
 License: MIT
 License File: //third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/LICENSE
 Shipped: yes
diff --git a/third_party/skia b/third_party/skia
index 9ba1169..9100700 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 9ba1169cd85206cb6113e43356b639c5d1913b49
+Subproject commit 9100700840668a8a3276c05f114463f9b8c7a264
diff --git a/third_party/swiftshader b/third_party/swiftshader
index 11dc1b1..a857503 160000
--- a/third_party/swiftshader
+++ b/third_party/swiftshader
@@ -1 +1 @@
-Subproject commit 11dc1b167af4369aafa058d77f204700c0ecbb1c
+Subproject commit a857503547cb01a294df9a2c2df6bee18693b05c
diff --git a/third_party/webrtc b/third_party/webrtc
index 4cbaeac..a424cbdc 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 4cbaeac5a15340612960eeaf14842b3faf09f0b5
+Subproject commit a424cbdcd65b2729417eaf8827a812a0d6ad2b00
diff --git a/third_party/wpt_tools/OWNERS b/third_party/wpt_tools/OWNERS
index f890415..352dac07 100644
--- a/third_party/wpt_tools/OWNERS
+++ b/third_party/wpt_tools/OWNERS
@@ -1,2 +1 @@
-weizhong@google.com
 jonathanjlee@google.com
diff --git a/third_party/zxcvbn-cpp/google.patch b/third_party/zxcvbn-cpp/google.patch
index 5c121343..1e188db 100644
--- a/third_party/zxcvbn-cpp/google.patch
+++ b/third_party/zxcvbn-cpp/google.patch
@@ -1,7 +1,7 @@
-diff --git a/native-src/zxcvbn/adjacency_graphs_js_bindings.cpp b/native-src/zxcvbn/adjacency_graphs_js_bindings.cpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/adjacency_graphs_js_bindings.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/adjacency_graphs_js_bindings.cpp
 deleted file mode 100644
-index 7e1ae34..0000000
---- a/native-src/zxcvbn/adjacency_graphs_js_bindings.cpp
+index 7e1ae349a8e0f..0000000000000
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/adjacency_graphs_js_bindings.cpp
 +++ /dev/null
 @@ -1,49 +0,0 @@
 -#include <zxcvbn/common_js.hpp>
@@ -53,10 +53,10 @@
 -}
 -
 -#endif
-diff --git a/native-src/zxcvbn/common.hpp b/native-src/zxcvbn/common.hpp
-index cba756c..1f66367 100644
---- a/native-src/zxcvbn/common.hpp
-+++ b/native-src/zxcvbn/common.hpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/common.hpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/common.hpp
+index cba756c449a63..1f66367112aa8 100644
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/common.hpp
++++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/common.hpp
 @@ -1,7 +1,6 @@
  #ifndef __ZXCVBN__COMMON_HPP
  #define __ZXCVBN__COMMON_HPP
@@ -97,10 +97,10 @@
    std::string matched_word;
    rank_t rank;
    bool l33t;
-diff --git a/native-src/zxcvbn/common_js.hpp b/native-src/zxcvbn/common_js.hpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/common_js.hpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/common_js.hpp
 deleted file mode 100644
-index 729cbad..0000000
---- a/native-src/zxcvbn/common_js.hpp
+index 729cbad18709c..0000000000000
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/common_js.hpp
 +++ /dev/null
 @@ -1,515 +0,0 @@
 -#ifndef __ZXCVBN__COMMON_JS_HPP
@@ -618,10 +618,10 @@
 -}
 -
 -#endif
-diff --git a/native-src/zxcvbn/feedback.cpp b/native-src/zxcvbn/feedback.cpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/feedback.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/feedback.cpp
 deleted file mode 100644
-index d22c296..0000000
---- a/native-src/zxcvbn/feedback.cpp
+index d22c296188973..0000000000000
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/feedback.cpp
 +++ /dev/null
 @@ -1,174 +0,0 @@
 -#include <zxcvbn/feedback.hpp>
@@ -798,10 +798,10 @@
 -
 -}
 -
-diff --git a/native-src/zxcvbn/feedback.hpp b/native-src/zxcvbn/feedback.hpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/feedback.hpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/feedback.hpp
 deleted file mode 100644
-index 6f6b62b..0000000
---- a/native-src/zxcvbn/feedback.hpp
+index 6f6b62b9da054..0000000000000
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/feedback.hpp
 +++ /dev/null
 @@ -1,20 +0,0 @@
 -#ifndef __ZXCVBN__FEEDBACK_HPP
@@ -824,11 +824,11 @@
 -}
 -
 -#endif
-diff --git a/native-src/zxcvbn/frequency_lists.cpp b/native-src/zxcvbn/frequency_lists.cpp
-index 4e4b727..2eb003f 100644
---- a/native-src/zxcvbn/frequency_lists.cpp
-+++ b/native-src/zxcvbn/frequency_lists.cpp
-@@ -1,24 +1,249 @@
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.cpp
+index a4d65612d8a25..67a2aff5542f3 100644
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.cpp
++++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.cpp
+@@ -1,24 +1,245 @@
  #include <zxcvbn/frequency_lists.hpp>
  
 -#include <zxcvbn/_frequency_lists.hpp>
@@ -993,8 +993,10 @@
 +  CHECK_EQ(vec.size(), dict_size);
 +  vec.push_back(MarkedBigEndianU15::MARKER_BIT);
 +  data_ = Datawrapper(std::move(vec));
-+}
-+
+ }
+ 
+-RankedDicts default_ranked_dicts() {
+-  return convert_to_ranked_dicts(_frequency_lists::get_default_ranked_dicts());
 +RankedDicts::RankedDicts(std::unique_ptr<base::MemoryMappedFile> map)
 +    : data_(std::move(map)) {}
 +
@@ -1045,8 +1047,8 @@
 +        range_last--;
 +    }
 +  }
-+}
-+
+ }
+ 
 +// Determine whether an entry starts at the given offset; in other words,
 +// determine whether a MarkedBigEndianU15 starts there.
 +bool RankedDicts::IsRealMarker(size_t offset) const {
@@ -1060,14 +1062,12 @@
 +    }
 +  }
 +  return false;
- }
- 
--RankedDicts default_ranked_dicts() {
--  return convert_to_ranked_dicts(_frequency_lists::get_default_ranked_dicts());
++}
++
 +void SetRankedDictsImplementation(RankedDicts dicts) {
 +  default_ranked_dicts() = std::move(dicts);
- }
- 
++}
++
 +void SetRankedDicts(RankedDicts dicts) {
 +  // Destroying a `RankedDict` may block if it is based on a `MemoryMappedFile`.
 +  // Therefore this helper moves the task of doing it to a thread pool.
@@ -1076,17 +1076,17 @@
 +      base::BindOnce(&DoNothing, std::move(default_ranked_dicts())));
 +  default_ranked_dicts() = std::move(dicts);
 +}
- 
++
 +RankedDicts& default_ranked_dicts() {
 +  static base::NoDestructor<RankedDicts> default_dicts;
 +  return *default_dicts;
  }
 +
 +}  // namespace zxcvbn
-diff --git a/native-src/zxcvbn/frequency_lists.hpp b/native-src/zxcvbn/frequency_lists.hpp
-index c75ea30..fed3aaa 100644
---- a/native-src/zxcvbn/frequency_lists.hpp
-+++ b/native-src/zxcvbn/frequency_lists.hpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.hpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.hpp
+index c75ea30d5a911..fed3aaa2ca6a3 100644
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.hpp
++++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists.hpp
 @@ -1,37 +1,85 @@
  #ifndef __ZXCVBN__FREQUENCY_LISTS_HPP
  #define __ZXCVBN__FREQUENCY_LISTS_HPP
@@ -1192,10 +1192,10 @@
 +} // namespace zxcvbn
  
  #endif
-diff --git a/native-src/zxcvbn/frequency_lists_common.hpp b/native-src/zxcvbn/frequency_lists_common.hpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists_common.hpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists_common.hpp
 deleted file mode 100644
-index 40eee25..0000000
---- a/native-src/zxcvbn/frequency_lists_common.hpp
+index 40eee250f244a..0000000000000
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/frequency_lists_common.hpp
 +++ /dev/null
 @@ -1,28 +0,0 @@
 -#ifndef __ZXCVBN__FREQUENCY_LISTS_COMMON_HPP
@@ -1226,10 +1226,10 @@
 -}
 -
 -#endif
-diff --git a/native-src/zxcvbn/js_frequency_lists.cpp b/native-src/zxcvbn/js_frequency_lists.cpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/js_frequency_lists.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/js_frequency_lists.cpp
 deleted file mode 100644
-index 510ba3f..0000000
---- a/native-src/zxcvbn/js_frequency_lists.cpp
+index 510ba3f4307b8..0000000000000
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/js_frequency_lists.cpp
 +++ /dev/null
 @@ -1,65 +0,0 @@
 -#include <zxcvbn/_frequency_lists.hpp>
@@ -1297,10 +1297,10 @@
 -}
 -
 -}
-diff --git a/native-src/zxcvbn/matching.cpp b/native-src/zxcvbn/matching.cpp
-index 6d53664..e247818 100644
---- a/native-src/zxcvbn/matching.cpp
-+++ b/native-src/zxcvbn/matching.cpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/matching.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/matching.cpp
+index 6d53664f884c0..e247818214d50 100644
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/matching.cpp
++++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/matching.cpp
 @@ -19,28 +19,44 @@
  #include <utility>
  #include <unordered_set>
@@ -1642,10 +1642,10 @@
            sequence_space = 26;
          }
          result.push_back(Match(i, j, token,
-diff --git a/native-src/zxcvbn/matching.hpp b/native-src/zxcvbn/matching.hpp
-index 7ded5d8..4486038 100644
---- a/native-src/zxcvbn/matching.hpp
-+++ b/native-src/zxcvbn/matching.hpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/matching.hpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/matching.hpp
+index 7ded5d89573fc..448603892a5e3 100644
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/matching.hpp
++++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/matching.hpp
 @@ -10,8 +10,8 @@
  
  namespace zxcvbn {
@@ -1667,10 +1667,10 @@
  
  }
  
-diff --git a/native-src/zxcvbn/matching_js_bindings.cpp b/native-src/zxcvbn/matching_js_bindings.cpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/matching_js_bindings.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/matching_js_bindings.cpp
 deleted file mode 100644
-index 1dfcb53..0000000
---- a/native-src/zxcvbn/matching_js_bindings.cpp
+index 1dfcb53bc42d4..0000000000000
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/matching_js_bindings.cpp
 +++ /dev/null
 @@ -1,262 +0,0 @@
 -#include <zxcvbn/matching.hpp>
@@ -1935,10 +1935,10 @@
 -  emscripten::function("omnimatch", &zxcvbn_js::omnimatch);
 -}
 -
-diff --git a/native-src/zxcvbn/optional.hpp b/native-src/zxcvbn/optional.hpp
-index 68b77e0..b3fb257 100644
---- a/native-src/zxcvbn/optional.hpp
-+++ b/native-src/zxcvbn/optional.hpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/optional.hpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/optional.hpp
+index 68b77e0e9b487..b3fb2577e169f 100644
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/optional.hpp
++++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/optional.hpp
 @@ -7,6 +7,7 @@
  #include <new>
  #include <stdexcept>
@@ -1947,10 +1947,10 @@
  
  #include <cstdint>
  #include <cassert>
-diff --git a/native-src/zxcvbn/scoring.cpp b/native-src/zxcvbn/scoring.cpp
-index c652e2d..e5c120a 100644
---- a/native-src/zxcvbn/scoring.cpp
-+++ b/native-src/zxcvbn/scoring.cpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/scoring.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/scoring.cpp
+index c652e2d2d9dea..e5c120a86a5c4 100644
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/scoring.cpp
++++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/scoring.cpp
 @@ -9,6 +9,8 @@
  
  #include <cmath>
@@ -2033,10 +2033,10 @@
      if (std::regex_match(word, regex)) return 2;
    }
    // otherwise calculate the number of ways to capitalize U+L uppercase+lowercase letters
-diff --git a/native-src/zxcvbn/scoring.hpp b/native-src/zxcvbn/scoring.hpp
-index 901c71f..1038ee3 100644
---- a/native-src/zxcvbn/scoring.hpp
-+++ b/native-src/zxcvbn/scoring.hpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/scoring.hpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/scoring.hpp
+index 901c71f15e3bc..1038ee34587cc 100644
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/scoring.hpp
++++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/scoring.hpp
 @@ -11,10 +11,10 @@
  
  namespace zxcvbn {
@@ -2061,10 +2061,10 @@
  #undef MATCH_FN
  
  guesses_t uppercase_variations(const Match & match);
-diff --git a/native-src/zxcvbn/scoring_js_bindings.cpp b/native-src/zxcvbn/scoring_js_bindings.cpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/scoring_js_bindings.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/scoring_js_bindings.cpp
 deleted file mode 100644
-index ceb43f4..0000000
---- a/native-src/zxcvbn/scoring_js_bindings.cpp
+index ceb43f440de62..0000000000000
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/scoring_js_bindings.cpp
 +++ /dev/null
 @@ -1,181 +0,0 @@
 -#include <zxcvbn/common_js.hpp>
@@ -2248,10 +2248,10 @@
 -}
 -
 -#endif
-diff --git a/native-src/zxcvbn/util.cpp b/native-src/zxcvbn/util.cpp
-index 084d3cc..e7478d5 100644
---- a/native-src/zxcvbn/util.cpp
-+++ b/native-src/zxcvbn/util.cpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/util.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/util.cpp
+index 084d3cc0cd7c8..e7478d5bc9d95 100644
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/util.cpp
++++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/util.cpp
 @@ -1,74 +1,57 @@
  #include <zxcvbn/util.hpp>
  
@@ -2395,10 +2395,10 @@
  char32_t utf8_decode(const std::string & start,
                       std::string::size_type & idx) {
    auto ret = _utf8_decode(start.begin() + idx, start.end());
-diff --git a/native-src/zxcvbn/util.hpp b/native-src/zxcvbn/util.hpp
-index b784c1f..90fa998 100644
---- a/native-src/zxcvbn/util.hpp
-+++ b/native-src/zxcvbn/util.hpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/util.hpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/util.hpp
+index b784c1f6c1d28..90fa99816c92f 100644
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/util.hpp
++++ b/third_party/zxcvbn-cpp/native-src/zxcvbn/util.hpp
 @@ -29,8 +29,6 @@ std::string::size_type character_len(const std::string &,
                                       std::string::size_type end) __attribute__((pure));
  std::string::size_type character_len(const std::string &)  __attribute__((pure));
@@ -2408,10 +2408,10 @@
  char32_t utf8_decode(const std::string & start,
                       std::string::size_type & idx);
  
-diff --git a/native-src/zxcvbn/zxcvbn.cpp b/native-src/zxcvbn/zxcvbn.cpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/zxcvbn.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/zxcvbn.cpp
 deleted file mode 100644
-index d9b5291..0000000
---- a/native-src/zxcvbn/zxcvbn.cpp
+index d9b52916680f1..0000000000000
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/zxcvbn.cpp
 +++ /dev/null
 @@ -1,50 +0,0 @@
 -#include <zxcvbn/zxcvbn.h>
@@ -2464,10 +2464,10 @@
 -}
 -
 -}
-diff --git a/native-src/zxcvbn/zxcvbn.h b/native-src/zxcvbn/zxcvbn.h
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/zxcvbn.h b/third_party/zxcvbn-cpp/native-src/zxcvbn/zxcvbn.h
 deleted file mode 100644
-index 67a6d03..0000000
---- a/native-src/zxcvbn/zxcvbn.h
+index 67a6d03798a56..0000000000000
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/zxcvbn.h
 +++ /dev/null
 @@ -1,26 +0,0 @@
 -#ifndef __ZXCVBN_H
@@ -2496,10 +2496,10 @@
 -#endif
 -
 -#endif
-diff --git a/native-src/zxcvbn/zxcvbn.hpp b/native-src/zxcvbn/zxcvbn.hpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/zxcvbn.hpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/zxcvbn.hpp
 deleted file mode 100644
-index a48bcd8..0000000
---- a/native-src/zxcvbn/zxcvbn.hpp
+index a48bcd8c11961..0000000000000
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/zxcvbn.hpp
 +++ /dev/null
 @@ -1,23 +0,0 @@
 -#ifndef __ZXCVBN__ZXCVBN_HPP
@@ -2525,10 +2525,10 @@
 -}
 -
 -#endif
-diff --git a/native-src/zxcvbn/zxcvbn_js_bindings.cpp b/native-src/zxcvbn/zxcvbn_js_bindings.cpp
+diff --git a/third_party/zxcvbn-cpp/native-src/zxcvbn/zxcvbn_js_bindings.cpp b/third_party/zxcvbn-cpp/native-src/zxcvbn/zxcvbn_js_bindings.cpp
 deleted file mode 100644
-index f0ba44c..0000000
---- a/native-src/zxcvbn/zxcvbn_js_bindings.cpp
+index f0ba44c3539d8..0000000000000
+--- a/third_party/zxcvbn-cpp/native-src/zxcvbn/zxcvbn_js_bindings.cpp
 +++ /dev/null
 @@ -1,104 +0,0 @@
 -#include <zxcvbn/common_js.hpp>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 9f64bad..de443310 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -12604,6 +12604,7 @@
   <int value="-491131428" label="OmniboxNewAnswerLayout:disabled"/>
   <int value="-490652941" label="WebViewZeroCopyVideo:enabled"/>
   <int value="-489672760" label="ImeFstDecoderParamsUpdate:disabled"/>
+  <int value="-489122628" label="ShowTabListAnimations:disabled"/>
   <int value="-488779992" label="blink-settings"/>
   <int value="-488744544" label="PromoBrowserCommands:disabled"/>
   <int value="-488228498"
@@ -13482,6 +13483,7 @@
   <int value="-157983843" label="CrostiniUseLxd5:disabled"/>
   <int value="-157036790" label="CrosPrivacyHubAppPermissions:disabled"/>
   <int value="-156983642" label="ByDateHistoryInSidePanel:disabled"/>
+  <int value="-154842647" label="ShowTabListAnimations:enabled"/>
   <int value="-154632776" label="HelpAppWelcomeTips:disabled"/>
   <int value="-154071189"
       label="AdaptiveButtonInTopToolbarPageSummary:disabled"/>
diff --git a/tools/metrics/histograms/metadata/ai/enums.xml b/tools/metrics/histograms/metadata/ai/enums.xml
index 4f2d520a9..b227ab9 100644
--- a/tools/metrics/histograms/metadata/ai/enums.xml
+++ b/tools/metrics/histograms/metadata/ai/enums.xml
@@ -116,6 +116,26 @@
 
 <!-- LINT.ThenChange(//third_party/blink/renderer/modules/ai/availability.h:Availability) -->
 
+<!-- LINT.IfChange(LanguageModelInputRole) -->
+
+<enum name="LanguageModelInputRole">
+  <int value="0" label="System"/>
+  <int value="1" label="User"/>
+  <int value="2" label="Assistant"/>
+</enum>
+
+<!-- LINT.ThenChange(//third_party/blink/renderer/modules/ai/ai_metrics.h:LanguageModelInputRole) -->
+
+<!-- LINT.IfChange(LanguageModelInputType) -->
+
+<enum name="LanguageModelInputType">
+  <int value="0" label="Text"/>
+  <int value="1" label="Image"/>
+  <int value="2" label="Audio"/>
+</enum>
+
+<!-- LINT.ThenChange(//third_party/blink/renderer/modules/ai/ai_metrics.h:LanguageModelInputType) -->
+
 <!-- LINT.IfChange(LoadTranslateKitResult) -->
 
 <enum name="LoadTranslateKitResult">
diff --git a/tools/metrics/histograms/metadata/ai/histograms.xml b/tools/metrics/histograms/metadata/ai/histograms.xml
index 03acb52..7ebe3e9f 100644
--- a/tools/metrics/histograms/metadata/ai/histograms.xml
+++ b/tools/metrics/histograms/metadata/ai/histograms.xml
@@ -22,6 +22,20 @@
 
 <histograms>
 
+<!-- LINT.IfChange(LanguageModelExpectedInputOrOutput) -->
+
+<variants name="LanguageModelExpectedInputOrOutput">
+  <variant name="ExpectedInputs" summary="Expected inputs."/>
+  <variant name="ExpectedOutputs" summary="Expected outputs."/>
+</variants>
+
+<!-- LINT.ThenChange(//third_party/blink/renderer/modules/ai/language_model.cc:LanguageModelExpectedInputOrOutput) -->
+
+<variants name="LanguageModelFunctionName">
+  <variant name="availability" summary="Check availability of the model."/>
+  <variant name="create" summary="Create a session."/>
+</variants>
+
 <!-- LINT.IfChange(SessionType) -->
 
 <variants name="SessionType">
@@ -47,6 +61,104 @@
   <variant name="Writer" summary="AI writer session"/>
 </variants>
 
+<histogram name="AI.LanguageModel.APIUsage.Messages.Role"
+    enum="LanguageModelInputRole" expires_after="2025-10-27">
+  <owner>btriebw@chromium.org</owner>
+  <owner>ayui@chromium.org</owner>
+  <owner>src/chrome/browser/ai/OWNERS</owner>
+  <summary>
+    This is recorded for each message provided to a LanguageModel `prompt` or
+    `append` invocation. It can be used to measure the usage of the `role` of
+    individual messages.
+  </summary>
+</histogram>
+
+<histogram name="AI.LanguageModel.APIUsage.Messages.Text.Length"
+    units="characters" expires_after="2025-10-27">
+  <owner>btriebw@chromium.org</owner>
+  <owner>ayui@chromium.org</owner>
+  <owner>src/chrome/browser/ai/OWNERS</owner>
+  <summary>
+    This is recorded for each message content provided to a LanguageModel
+    `prompt` or `append` invocation. It can be used to measure the usage of the
+    text length of individual messages.
+  </summary>
+</histogram>
+
+<histogram name="AI.LanguageModel.APIUsage.Messages.Type"
+    enum="LanguageModelInputType" expires_after="2025-10-27">
+  <owner>btriebw@chromium.org</owner>
+  <owner>ayui@chromium.org</owner>
+  <owner>src/chrome/browser/ai/OWNERS</owner>
+  <summary>
+    This is recorded for each message content provided to a LanguageModel
+    `prompt` or `append` invocation. It can be used to measure the usage of the
+    `type` of individual messages.
+  </summary>
+</histogram>
+
+<histogram
+    name="AI.LanguageModel.APIUsage.{LanguageModelFunctionName}.TemperatureX1000"
+    units="tempX1000" expires_after="2025-10-27">
+  <owner>btriebw@chromium.org</owner>
+  <owner>ayui@chromium.org</owner>
+  <owner>src/chrome/browser/ai/OWNERS</owner>
+  <summary>
+    This is recorded each time when the LanguageModel `create` or `availability`
+    function is invoked. It can be used to measure the usage of the
+    `temperature` option. It is only recorded when the option is set. The value
+    is multiplied by 1000 to support floating point metrics. The value is in the
+    range 0 to ~2000.
+  </summary>
+  <token key="LanguageModelFunctionName" variants="LanguageModelFunctionName"/>
+</histogram>
+
+<histogram name="AI.LanguageModel.APIUsage.{LanguageModelFunctionName}.TopK"
+    units="tokens" expires_after="2025-10-27">
+  <owner>btriebw@chromium.org</owner>
+  <owner>ayui@chromium.org</owner>
+  <owner>src/chrome/browser/ai/OWNERS</owner>
+  <summary>
+    This is recorded each time when the LanguageModel `create` or `availability`
+    function is invoked. It can be used to measure the usage of the `topK`
+    option. It is only recorded when the option is set.
+  </summary>
+  <token key="LanguageModelFunctionName" variants="LanguageModelFunctionName"/>
+</histogram>
+
+<histogram
+    name="AI.LanguageModel.APIUsage.{LanguageModelFunctionName}.{LanguageModelExpectedInputOrOutput}.Language"
+    enum="LocaleCodeBCP47" expires_after="2025-10-27">
+  <owner>btriebw@chromium.org</owner>
+  <owner>ayui@chromium.org</owner>
+  <owner>src/chrome/browser/ai/OWNERS</owner>
+  <summary>
+    This is recorded each time when the LanguageModel `create` or `availability`
+    function is invoked. It can be used to measure the usage of specified
+    expected input or output languages. option. It is only recorded when the
+    language(s) are specified.
+  </summary>
+  <token key="LanguageModelFunctionName" variants="LanguageModelFunctionName"/>
+  <token key="LanguageModelExpectedInputOrOutput"
+      variants="LanguageModelExpectedInputOrOutput"/>
+</histogram>
+
+<histogram
+    name="AI.LanguageModel.APIUsage.{LanguageModelFunctionName}.{LanguageModelExpectedInputOrOutput}.Type"
+    enum="LanguageModelInputType" expires_after="2025-10-27">
+  <owner>btriebw@chromium.org</owner>
+  <owner>ayui@chromium.org</owner>
+  <owner>src/chrome/browser/ai/OWNERS</owner>
+  <summary>
+    This is recorded each time when the LanguageModel `create` or `availability`
+    function is invoked. It can be used to measure the usage of the
+    `temperature` option. It is only recorded when the option is set.
+  </summary>
+  <token key="LanguageModelFunctionName" variants="LanguageModelFunctionName"/>
+  <token key="LanguageModelExpectedInputOrOutput"
+      variants="LanguageModelExpectedInputOrOutput"/>
+</histogram>
+
 <histogram name="AI.Session.{SessionType}.ContextTime" units="ms"
     expires_after="2026-05-19">
   <owner>cduvall@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 85e7472..2d1caa52 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -7450,6 +7450,19 @@
   </token>
 </histogram>
 
+<histogram name="Autofill.VirtualCardEnrollBubble.LatencySinceDownstream"
+    units="ms" expires_after="2026-06-01">
+  <owner>siyua@chromium.org</owner>
+  <owner>payments-autofill-team@google.com</owner>
+  <summary>
+    Logs the latency between card extraction of the server retrieved masked
+    server card and the timestamp for when the Virtual Card Enroll Bubble is
+    shown. This metric is logged right before the Virtual Card Enroll Bubble is
+    shown, but if web contents are unavailable it will be logged before waiting
+    for web contents to become available.
+  </summary>
+</histogram>
+
 <histogram name="Autofill.VirtualCardEnrollBubble.LatencySinceUpstream"
     units="ms" expires_after="2026-06-01">
   <owner>vinnypersky@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/compositing/enums.xml b/tools/metrics/histograms/metadata/compositing/enums.xml
index 3974826..a2448ff 100644
--- a/tools/metrics/histograms/metadata/compositing/enums.xml
+++ b/tools/metrics/histograms/metadata/compositing/enums.xml
@@ -156,13 +156,6 @@
   <int value="25" label="SCRGB_LINEAR_80_NITS"/>
 </enum>
 
-<enum name="FrameReportType">
-  <int value="0" label="Presented Frame"/>
-  <int value="1" label="Missed Deadline Frame"/>
-  <int value="2" label="Dropped Frame"/>
-  <int value="3" label="Compositor Only Frame"/>
-</enum>
-
 <enum name="LCDTextDisallowedReason">
   <int value="0" label="None"/>
   <int value="1" label="Setting"/>
diff --git a/tools/metrics/histograms/metadata/compositing/histograms.xml b/tools/metrics/histograms/metadata/compositing/histograms.xml
index dc725f3..4be1a755c 100644
--- a/tools/metrics/histograms/metadata/compositing/histograms.xml
+++ b/tools/metrics/histograms/metadata/compositing/histograms.xml
@@ -881,64 +881,6 @@
   </token>
 </histogram>
 
-<histogram
-    name="CompositorLatency.CompositorOnlyFrame{InteractionType}.{StageType}"
-    units="microseconds" expires_after="2025-10-12">
-  <owner>jonross@chromium.org</owner>
-  <owner>chrome-gpu-metric-alerts@chromium.org</owner>
-  <summary>
-    Tracks the duration of various stages in the pipeline as a single frame goes
-    through the various stages in the compositor in the cases that there is no
-    damage from main thread.
-
-    This metric uses base::Histogram::FactoryMicrosecondsTimeGet so only users
-    with high resolution clocks will report this metric.
-
-    Note: This metric was expired from 2023-03-01 to 2023-05-05.
-  </summary>
-  <token key="InteractionType">
-    <variant name=""/>
-    <variant name=".CanvasAnimation"/>
-    <variant name=".CompositorAnimation"/>
-    <variant name=".JSAnimation"/>
-    <variant name=".MainThreadAnimation"/>
-    <variant name=".NativePropertyAnimation"/>
-    <variant name=".PinchZoom"/>
-    <variant name=".RAF"/>
-    <variant name=".RasterAnimation"/>
-    <variant name=".ScrollbarScroll"/>
-    <variant name=".SETCompositorAnimation"/>
-    <variant name=".SETMainThreadAnimation"/>
-    <variant name=".TouchScroll"/>
-    <variant name=".WheelScroll"/>
-  </token>
-  <token key="StageType">
-    <variant name="BeginImplFrameToFinishImpl"/>
-    <variant name="ImplFrameDoneToSubmitCompositorFrame"/>
-    <variant name="SendBeginMainFrameToBeginMainAbort"/>
-    <variant name="SubmitCompositorFrameToPresentationCompositorFrame"/>
-    <variant
-        name="SubmitCompositorFrameToPresentationCompositorFrame.BufferAvailableToBufferReady"/>
-    <variant
-        name="SubmitCompositorFrameToPresentationCompositorFrame.BufferReadyToLatch"/>
-    <variant
-        name="SubmitCompositorFrameToPresentationCompositorFrame.LatchToSwapEnd"/>
-    <variant
-        name="SubmitCompositorFrameToPresentationCompositorFrame.ReceivedCompositorFrameToStartDraw"/>
-    <variant
-        name="SubmitCompositorFrameToPresentationCompositorFrame.StartDrawToSwapStart"/>
-    <variant
-        name="SubmitCompositorFrameToPresentationCompositorFrame.SubmitToReceiveCompositorFrame"/>
-    <variant
-        name="SubmitCompositorFrameToPresentationCompositorFrame.SwapEndToPresentationCompositorFrame"/>
-    <variant
-        name="SubmitCompositorFrameToPresentationCompositorFrame.SwapStartToBufferAvailable"/>
-    <variant
-        name="SubmitCompositorFrameToPresentationCompositorFrame.SwapStartToSwapEnd"/>
-    <variant name="TotalLatency"/>
-  </token>
-</histogram>
-
 <histogram name="CompositorLatency.IpcThread.{LatencyType}"
     units="microseconds" expires_after="2025-10-26">
   <owner>jonross@chromium.org</owner>
@@ -972,57 +914,17 @@
   </token>
 </histogram>
 
-<histogram name="CompositorLatency.Type{Sequence}" enum="FrameReportType"
-    expires_after="2025-10-26">
-  <owner>jonross@chromium.org</owner>
-  <owner>chrome-gpu-metric-alerts@chromium.org</owner>
-  <summary>
-    Whether frame is dropped or presented meeting or missing the deadline or is
-    a compositor only frame.
-  </summary>
-  <token key="Sequence">
-    <variant name="" summary="All Sequences"/>
-    <variant name=".AnyInteraction" summary="At least one active interaction"/>
-    <variant name=".CanvasAnimation" summary="Main-thread canvas animation"/>
-    <variant name=".CompositorAnimation" summary="Compositor-driven animation"/>
-    <variant name=".JSAnimation" summary="JS-driven animation"/>
-    <variant name=".MainThreadAnimation"
-        summary="Main-thread driven animation"/>
-    <variant name=".NoInteraction" summary="No active interaction"/>
-    <variant name=".PinchZoom" summary="Pinch-to-zoom interaction"/>
-    <variant name=".RAF" summary="rAF callback driven animation"/>
-    <variant name=".ScrollbarScroll" summary="Scrollbar driven scrolls"/>
-    <variant name=".TouchScroll" summary="Touchscreen driven interaction"/>
-    <variant name=".Video" summary="Video playback"/>
-    <variant name=".WheelScroll" summary="Mousewheel driven interaction"/>
-  </token>
-</histogram>
-
-<histogram name="CompositorLatency{ReportType}{InteractionType}.{StageType}"
+<histogram name="CompositorLatency2{InteractionType}.{StageType}"
     units="microseconds" expires_after="2025-10-05">
   <owner>jonross@chromium.org</owner>
   <owner>chrome-gpu-metric-alerts@chromium.org</owner>
   <summary>
     Tracks the duration of various stages in the pipeline as a single frame goes
-    through the various stages in the compositor {ReportType}.
+    through the various stages in the compositor.
 
     This metric uses base::Histogram::FactoryMicrosecondsTimeGet so only users
     with high resolution clocks will report this metric.
   </summary>
-  <token key="ReportType">
-    <variant name=""
-        summary="reported for frames that went through all the compositor
-                 stages in chromium, and was presented to the user"/>
-    <variant name=".DroppedFrame"
-        summary="reported for frames that the user did not see: this includes
-                 frames that are dropped in various stages inside chromium,
-                 or even frames that went through all stages in chromium, but
-                 ultimately was not presented to the user"/>
-    <variant name=".MissedDeadlineFrame"
-        summary="reported for frames that went through all the compositor
-                 stages in chromium, and was presented to the user, but were
-                 delayed and missed their deadline"/>
-  </token>
   <token key="InteractionType">
     <variant name=""/>
     <variant name=".CanvasAnimation"/>
@@ -1046,6 +948,7 @@
     <variant name="EndActivateToSubmitCompositorFrame"/>
     <variant name="EndCommitToActivation"/>
     <variant name="SendBeginMainFrameToCommit"/>
+    <variant name="SendBeginMainFrameToCommit.AccessibiltyUpdate"/>
     <variant name="SendBeginMainFrameToCommit.Animate"/>
     <variant name="SendBeginMainFrameToCommit.BeginMainSentToStarted"/>
     <variant name="SendBeginMainFrameToCommit.CompositeCommit"/>
diff --git a/tools/metrics/histograms/metadata/download/histograms.xml b/tools/metrics/histograms/metadata/download/histograms.xml
index fa0c1dd..be7bbcce 100644
--- a/tools/metrics/histograms/metadata/download/histograms.xml
+++ b/tools/metrics/histograms/metadata/download/histograms.xml
@@ -229,7 +229,7 @@
 
 <histogram
     name="Download.Bubble.Subpage.{DownloadDangerTypeString}.{ButtonType}ButtonActionTime"
-    units="ms" expires_after="2025-06-08">
+    units="ms" expires_after="2025-09-08">
   <owner>chlily@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -249,7 +249,7 @@
 </histogram>
 
 <histogram name="Download.Bubble.SubpageAction"
-    enum="DownloadBubbleSubpageAction" expires_after="2025-06-08">
+    enum="DownloadBubbleSubpageAction" expires_after="2025-09-08">
   <owner>chlily@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -1145,7 +1145,7 @@
 </histogram>
 
 <histogram name="Download.ShowedDownloadWarning.DownloadsPage"
-    enum="DownloadItem.DangerType" expires_after="2025-06-08">
+    enum="DownloadItem.DangerType" expires_after="2025-09-08">
   <owner>chlily@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/glic/enums.xml b/tools/metrics/histograms/metadata/glic/enums.xml
index b074423..1674b69f 100644
--- a/tools/metrics/histograms/metadata/glic/enums.xml
+++ b/tools/metrics/histograms/metadata/glic/enums.xml
@@ -33,6 +33,7 @@
   <int value="1" label="Get context failed"/>
   <int value="2" label="Invalid action proto"/>
   <int value="3" label="Target not found"/>
+  <int value="4" label="Failed to start task"/>
 </enum>
 
 <!-- LINT.ThenChange(//chrome/browser/glic/host/glic.mojom:ActInFocusedTabErrorReason) -->
@@ -309,6 +310,28 @@
   <int value="3" label="Non-PDF main doc; PDF not found"/>
 </enum>
 
+<!-- LINT.IfChange(WebClientUnresponsiveState) -->
+
+<enum name="WebClientUnresponsiveState">
+  <int value="0" label="Entered; webview event">
+    Entered unresponsive state, detected by a webview unresponsive event&quot;
+  </int>
+  <int value="1" label="Entered; heartbeat">
+    Entered unresponsive state, detected by Glic's custom heartbeat check&quot;
+  </int>
+  <int value="2" label="Already unresponsive; webview event">
+    Already on unresponsive state, detected again by a webview unresponsive
+    event&quot;
+  </int>
+  <int value="3" label="Already unresponsive; heartbeat">
+    Already on unresponsive state, detected again by Glic's custom heartbeat
+    check&quot;
+  </int>
+  <int value="4" label="Exited">Exited unresponsive state&quot;</int>
+</enum>
+
+<!-- LINT.ThenChange(//chrome/browser/resources/glic/glic_app_controller.ts:WebClientUnresponsiveState) -->
+
 <!-- LINT.IfChange(WebUiState) -->
 
 <enum name="WebUiState">
diff --git a/tools/metrics/histograms/metadata/glic/histograms.xml b/tools/metrics/histograms/metadata/glic/histograms.xml
index 3a1888a..ae57dc0 100644
--- a/tools/metrics/histograms/metadata/glic/histograms.xml
+++ b/tools/metrics/histograms/metadata/glic/histograms.xml
@@ -200,6 +200,28 @@
   </token>
 </histogram>
 
+<histogram name="Glic.Host.WebClientUnresponsiveState"
+    enum="WebClientUnresponsiveState" expires_after="2026-05-30">
+  <owner>carlosk@chromium.org</owner>
+  <owner>harringtond@chromium.org</owner>
+  <summary>
+    Tracks the detection of the web client being unresponsive and the respective
+    state transitions into and out of the unresponsive state. Reported upon
+    detection of unresponsiveness and unresposive state transitions.
+  </summary>
+</histogram>
+
+<histogram name="Glic.Host.WebClientUnresponsiveState.Duration" units="ms"
+    expires_after="2026-05-30">
+  <owner>carlosk@chromium.org</owner>
+  <owner>harringtond@chromium.org</owner>
+  <summary>
+    Records for how long the web client was kept in an unresponsive state.
+    Recorded when the state is swtiching from unresponsive to any other
+    supported state.
+  </summary>
+</histogram>
+
 <histogram name="Glic.Metrics.Error" enum="GlicMetricsError"
     expires_after="2026-01-15">
   <owner>dewittj@google.com</owner>
@@ -445,16 +467,6 @@
   <summary>Record the panel webui state any time it changes.</summary>
 </histogram>
 
-<histogram name="Glic.PanelWebUiState.FinishState" enum="WebUiState"
-    expires_after="2025-01-15">
-  <owner>dewittj@google.com</owner>
-  <owner>iwells@google.com</owner>
-  <summary>
-    Obsolete metric, replaced by FinishState2. Record the state when the panel
-    display is finished.
-  </summary>
-</histogram>
-
 <histogram name="Glic.PanelWebUiState.FinishState2" enum="WebUiState"
     expires_after="2026-01-15">
   <owner>dewittj@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/omnibox/histograms.xml b/tools/metrics/histograms/metadata/omnibox/histograms.xml
index bba1f6a..22f932e 100644
--- a/tools/metrics/histograms/metadata/omnibox/histograms.xml
+++ b/tools/metrics/histograms/metadata/omnibox/histograms.xml
@@ -1825,6 +1825,136 @@
   </summary>
 </histogram>
 
+<histogram name="Omnibox.NavigationToPopupShown.DomContentLoaded" units="ms"
+    expires_after="2025-11-16">
+  <owner>mercerd@google.com</owner>
+  <owner>chrome-desktop-search@google.com</owner>
+  <summary>
+    The length of time between a DOMContentLoaded event and the suggest popup
+    being shown to the user.
+
+    Recorded the first time the the omnibox popup is opened after a
+    DOMContentLoaded loaded. Only recorded on the first omnibox popup shown
+    after a navigation. Subsequent omnibox openings are ignored. If the user
+    focused the omnibox before the main document element was available, this
+    metric will not be recorded. Each tab keeps track of its own page change
+    events and omnibox popup open events, meaning if the user opens multiple
+    tabs and opens the omnibox popup on each one, this metric will be recorded
+    multiple times.
+  </summary>
+</histogram>
+
+<histogram
+    name="Omnibox.NavigationToPopupShown.DomContentLoaded.ByPageContext.{OmniboxPageContext}"
+    units="ms" expires_after="2025-11-16">
+  <owner>mercerd@google.com</owner>
+  <owner>chrome-desktop-search@google.com</owner>
+  <summary>
+    The length of time between a DOMContentLoaded event and the suggest popup
+    being shown to the user.
+
+    Recorded the first time the the omnibox popup is opened after a
+    DOMContentLoaded event. Only recorded on the first omnibox popup shown after
+    a navigation. Subsequent omnibox openings are ignored. If the user focused
+    the omnibox before the main document element was available, this metric will
+    not be recorded. Each tab keeps track of its own DOMContentLoaded events and
+    omnibox popup open events, meaning if the user opens multiple tabs and opens
+    the omnibox popup on each one, this metric will be recorded multiple times.
+
+    Sliced by page context to help better understand the latency budget for
+    different page types.
+  </summary>
+  <token key="OmniboxPageContext" variants="OmniboxPageContext"/>
+</histogram>
+
+<histogram name="Omnibox.NavigationToPopupShown.MainDocumentElementAvailable"
+    units="ms" expires_after="2025-11-16">
+  <owner>mercerd@google.com</owner>
+  <owner>chrome-desktop-search@google.com</owner>
+  <summary>
+    The length of time between a the main document element being available and
+    the suggest popup being shown to the user. The main document element is
+    available when the HTML has finished parsing and window.document is
+    available.
+
+    Recorded the first time the the omnibox popup is opened after a main
+    document element available event. Only recorded on the first omnibox popup
+    shown after a navigation. Subsequent omnibox openings are ignored. If the
+    user focused the omnibox before the main document element was available,
+    this metric will not be recorded. Each tab keeps track of its own main
+    document element available events and omnibox popup open events, meaning if
+    the user opens multiple tabs and opens the omnibox popup on each one, this
+    metric will be recorded multiple times.
+  </summary>
+</histogram>
+
+<histogram
+    name="Omnibox.NavigationToPopupShown.MainDocumentElementAvailable.ByPageContext.{OmniboxPageContext}"
+    units="ms" expires_after="2025-11-16">
+  <owner>mercerd@google.com</owner>
+  <owner>chrome-desktop-search@google.com</owner>
+  <summary>
+    The length of time between a the main document element being available and
+    the suggest popup being shown to the user. The main document element is
+    available when the HTML has finished parsing and window.document is
+    available.
+
+    Recorded the first time the the omnibox popup is opened after a main
+    document element available event. Only recorded on the first omnibox popup
+    shown after a navigation. Subsequent omnibox openings are ignored. If the
+    user focused the omnibox before the main document element was available,
+    this metric will not be recorded. Each tab keeps track of its own main
+    document element available events and omnibox popup open events, meaning if
+    the user opens multiple tabs and opens the omnibox popup on each one, this
+    metric will be recorded multiple times.
+
+    Sliced by page context to help better understand the latency budget for
+    different page types.
+  </summary>
+  <token key="OmniboxPageContext" variants="OmniboxPageContext"/>
+</histogram>
+
+<histogram name="Omnibox.NavigationToPopupShown.PrimaryPageChanged" units="ms"
+    expires_after="2025-11-16">
+  <owner>mercerd@google.com</owner>
+  <owner>chrome-desktop-search@google.com</owner>
+  <summary>
+    The length of time between a page change event and the suggest popup being
+    shown to the user.
+
+    Recorded the first time the the omnibox popup is opened after a page change
+    event. Only recorded on the first omnibox popup shown after a navigation.
+    Subsequent omnibox openings are ignored. If the user focused the omnibox
+    before the main document element was available, this metric will not be
+    recorded. Each tab keeps track of its own page change events and omnibox
+    popup open events, meaning if the user opens multiple tabs and opens the
+    omnibox popup on each one, this metric will be recorded multiple times.
+  </summary>
+</histogram>
+
+<histogram
+    name="Omnibox.NavigationToPopupShown.PrimaryPageChanged.ByPageContext.{OmniboxPageContext}"
+    units="ms" expires_after="2025-11-16">
+  <owner>mercerd@google.com</owner>
+  <owner>chrome-desktop-search@google.com</owner>
+  <summary>
+    The length of time between a page change event and the suggest popup being
+    shown to the user.
+
+    Recorded the first time the the omnibox popup is opened after a page change
+    event. Only recorded on the first omnibox popup shown after a navigation.
+    Subsequent omnibox openings are ignored. If the user focused the omnibox
+    before the main document element was available, this metric will not be
+    recorded. Each tab keeps track of its own page change events and omnibox
+    popup open events, meaning if the user opens multiple tabs and opens the
+    omnibox popup on each one, this metric will be recorded multiple times.
+
+    Sliced by page context to help better understand the latency budget for
+    different page types.
+  </summary>
+  <token key="OmniboxPageContext" variants="OmniboxPageContext"/>
+</histogram>
+
 <histogram name="Omnibox.NumberOfVisibleCharacters" units="characters"
     expires_after="2025-09-07">
   <owner>peilinwang@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/password/enums.xml b/tools/metrics/histograms/metadata/password/enums.xml
index 194b603f..20ae81d 100644
--- a/tools/metrics/histograms/metadata/password/enums.xml
+++ b/tools/metrics/histograms/metadata/password/enums.xml
@@ -49,6 +49,7 @@
   <int value="14" label="'Select plus address' link in the password sheet"/>
   <int value="15" label="'Manage plus addresses' link in the password sheet"/>
   <int value="16" label="'Manage loyalty cards' link"/>
+  <int value="17" label="'Verify it's you' link"/>
 </enum>
 
 <enum name="AccessorySheetTrigger">
diff --git a/tools/metrics/histograms/metadata/stability/histograms.xml b/tools/metrics/histograms/metadata/stability/histograms.xml
index a916694..485c674 100644
--- a/tools/metrics/histograms/metadata/stability/histograms.xml
+++ b/tools/metrics/histograms/metadata/stability/histograms.xml
@@ -24,6 +24,7 @@
 
 <variants name="CdmUtilityProcessType">
   <variant name="CdmServiceBroker"/>
+  <variant name="MediaDrmSupport"/>
   <variant name="MediaFoundationServiceBroker"/>
 </variants>
 
diff --git a/tools/metrics/histograms/metadata/sync/enums.xml b/tools/metrics/histograms/metadata/sync/enums.xml
index 0b75476..18896fe 100644
--- a/tools/metrics/histograms/metadata/sync/enums.xml
+++ b/tools/metrics/histograms/metadata/sync/enums.xml
@@ -1339,6 +1339,7 @@
   <int value="4" label="Password Manager Error Message"/>
   <int value="5" label="Account menu"/>
   <int value="6" label="Password Manager Settings"/>
+  <int value="7" label="Password Manager keyboard accessory"/>
 </enum>
 
 <!-- LINT.ThenChange(/components/sync/service/sync_service_utils.h:TrustedVaultUserActionTrigger) -->
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index bff778ea..b44855c5 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v50.1/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "5f960ad892ce2fa1a1839943315c7dc31b74293a",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/d578361dd62d748ad35d275cfea84f7508589a94/trace_processor_shell.exe"
+            "hash": "3600f47aa8d89c58afe46dba15f4c39237ebd968",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/44b4b71be5ea096ed37e44c5b33c92c9f224dbee/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "99f971ca131f6d11c73f4b918099d434bdd8093c",
diff --git a/ui/android/java/res/values/one_off_colors.xml b/ui/android/java/res/values/one_off_colors.xml
index 90aec8b..3041b65 100644
--- a/ui/android/java/res/values/one_off_colors.xml
+++ b/ui/android/java/res/values/one_off_colors.xml
@@ -73,6 +73,17 @@
     <color name="tab_group_card_text_color_red_gm3">#3A0907</color>
     <color name="tab_group_card_text_color_yellow_gm3">#2F1400</color>
 
+    <!-- Tab Group Card Placeholder related colors. These should not be used elsewhere. -->
+    <!-- The color for grey exists in color pallet -->
+    <color name="tab_group_card_placeholder_color_blue_gm3">#D0E4FF</color>
+    <color name="tab_group_card_placeholder_color_cyan_gm3">#ACEDFF</color>
+    <color name="tab_group_card_placeholder_color_green_gm3">#BEEFBB</color>
+    <color name="tab_group_card_placeholder_color_orange_gm3">#FFDCC3</color>
+    <color name="tab_group_card_placeholder_color_pink_gm3">#FFD8EF</color>
+    <color name="tab_group_card_placeholder_color_purple_gm3">#EEDCFE</color>
+    <color name="tab_group_card_placeholder_color_red_gm3">#FFDADC</color>
+    <color name="tab_group_card_placeholder_color_yellow_gm3">#FFE07C</color>
+
     <!-- Colors used in Google Pay icon -->
     <color name="google_pay_icon_grey_dark">#3C4043</color>
 
diff --git a/ui/ozone/platform/wayland/host/zwp_text_input_v3.cc b/ui/ozone/platform/wayland/host/zwp_text_input_v3.cc
index 9db2534..59864ef 100644
--- a/ui/ozone/platform/wayland/host/zwp_text_input_v3.cc
+++ b/ui/ozone/platform/wayland/host/zwp_text_input_v3.cc
@@ -340,9 +340,9 @@
 }
 
 void ZwpTextInputV3Impl::ResetInputEventsState() {
-  pending_input_events_.preedit = std::nullopt;
-  pending_input_events_.commit = std::nullopt;
-  pending_input_events_.delete_surrounding_text = std::nullopt;
+  pending_input_events_.preedit = {};
+  pending_input_events_.commit = {};
+  pending_input_events_.delete_surrounding_text = {};
 }
 
 void ZwpTextInputV3Impl::Commit() {
@@ -417,21 +417,21 @@
     // preedit range. So deletion of surrounding text implicitly clears
     // preedit in that case.
     int32_t index = surrounding_text->delete_around_range.start() -
-                    delete_surrounding_text->before_length;
+                    delete_surrounding_text.before_length;
     DVLOG_IF(1, index < 0)
-        << "got before_length=" << delete_surrounding_text->before_length
+        << "got before_length=" << delete_surrounding_text.before_length
         << " which results in negative index for deletion around range="
         << surrounding_text->delete_around_range;
     uint32_t length =
         (index < 0 ? surrounding_text->delete_around_range.end()
-                   : delete_surrounding_text->before_length +
+                   : delete_surrounding_text.before_length +
                          surrounding_text->delete_around_range.length()) +
-        delete_surrounding_text->after_length;
+        delete_surrounding_text.after_length;
     // Force minimum index of 0.
     index = std::max(0, index);
     if (index + length > surrounding_text->full_length) {
-      DVLOG(1) << "got before_length=" << delete_surrounding_text->before_length
-               << " after_length=" << delete_surrounding_text->after_length
+      DVLOG(1) << "got before_length=" << delete_surrounding_text.before_length
+               << " after_length=" << delete_surrounding_text.after_length
                << " which makes the deletion around range="
                << surrounding_text->delete_around_range
                << " extend beyond text length="
@@ -447,33 +447,31 @@
     size_t delete_start = base::checked_cast<size_t>(index);
     size_t delete_end = delete_start;
     surrounding_text->full_length -= length;
-    if (commit_string) {
-      // Update full length in case SetSurroundingText is not called before the
-      // next delete_surrounding_text + done.
-      surrounding_text->full_length += commit_string->length();
-    }
-    if (preedit_data) {
-      // Add incoming preedit to deletion range and update full length in case
-      // SetSurroundingText is not called before the next
-      // delete_surrounding_text + done.
-      delete_end += preedit_data->text.length();
-      surrounding_text->full_length += preedit_data->text.length();
-    }
+
+    // Update full length in case SetSurroundingText is not called before the
+    // next delete_surrounding_text + done.
+    surrounding_text->full_length += commit_string.length();
+
+    // Add incoming preedit to deletion range and update full length in case
+    // SetSurroundingText is not called before the next
+    // delete_surrounding_text + done.
+    delete_end += preedit_data.text.length();
+    surrounding_text->full_length += preedit_data.text.length();
     surrounding_text->delete_around_range =
         gfx::Range(delete_start, delete_end);
   }
-  if (commit_string) {
+
+  if (!commit_string.empty()) {
     // Replace the existing preedit with the commit string.
-    self->client_->OnCommitString(commit_string->c_str());
+    self->client_->OnCommitString(commit_string);
   }
-  if (preedit_data) {
-    // Finally process any new preedit string.
-    gfx::Range preedit_cursor =
-        (preedit_data->cursor_begin < 0 || preedit_data->cursor_end < 0)
-            ? gfx::Range::InvalidRange()
-            : gfx::Range(preedit_data->cursor_begin, preedit_data->cursor_end);
-    self->client_->OnPreeditString(preedit_data->text, {}, preedit_cursor);
-  }
+
+  // Finally process any new preedit string.
+  gfx::Range preedit_cursor =
+      (preedit_data.cursor_begin < 0 || preedit_data.cursor_end < 0)
+          ? gfx::Range::InvalidRange()
+          : gfx::Range(preedit_data.cursor_begin, preedit_data.cursor_end);
+  self->client_->OnPreeditString(preedit_data.text, {}, preedit_cursor);
 
   self->ResetInputEventsState();
   self->SendPendingImeData();
diff --git a/ui/ozone/platform/wayland/host/zwp_text_input_v3.h b/ui/ozone/platform/wayland/host/zwp_text_input_v3.h
index d5ab0e9..6727ef4 100644
--- a/ui/ozone/platform/wayland/host/zwp_text_input_v3.h
+++ b/ui/ozone/platform/wayland/host/zwp_text_input_v3.h
@@ -121,6 +121,7 @@
     int32_t cursor_end = 0;
   };
   struct DeleteSurroundingText {
+    explicit operator bool() const { return before_length || after_length; }
     uint32_t before_length = 0;
     uint32_t after_length = 0;
   };
@@ -129,9 +130,9 @@
   struct InputEvents {
     InputEvents();
     ~InputEvents();
-    std::optional<PreeditData> preedit;
-    std::optional<std::string> commit;
-    std::optional<DeleteSurroundingText> delete_surrounding_text;
+    PreeditData preedit;
+    std::string commit;
+    DeleteSurroundingText delete_surrounding_text;
     uint32_t last_done_serial = 0;
   };
 
diff --git a/ui/ozone/platform/wayland/host/zwp_text_input_v3_unittest.cc b/ui/ozone/platform/wayland/host/zwp_text_input_v3_unittest.cc
index 65400a1..cd47066 100644
--- a/ui/ozone/platform/wayland/host/zwp_text_input_v3_unittest.cc
+++ b/ui/ozone/platform/wayland/host/zwp_text_input_v3_unittest.cc
@@ -745,7 +745,8 @@
   InSequence s;
   EXPECT_CALL(test_client_, OnDeleteSurroundingText(10, 29));
   EXPECT_CALL(test_client_, OnCommitString(kCommitString));
-  EXPECT_CALL(test_client_, OnPreeditString(_, _, _)).Times(0);
+  EXPECT_CALL(test_client_,
+              OnPreeditString("", std::vector<SpanStyle>{}, gfx::Range()));
   PostToServerAndWait([kCommitString](wl::TestWaylandServerThread* server) {
     auto* text_input = server->text_input_manager_v3()->text_input();
     zwp_text_input_v3_send_delete_surrounding_text(text_input->resource(), 1,
@@ -814,7 +815,7 @@
   VerifyAndClearExpectations();
 }
 
-TEST_F(ZwpTextInputV3Test, PendingInputEventsClearedOnEnable) {
+TEST_F(ZwpTextInputV3Test, PendingInputEventsEmptiedOnEnable) {
   constexpr std::string kCommitString("CommitString");
   constexpr std::string_view kPreeditString("PreeditString");
   constexpr gfx::Range kPreeditCursor{0, 13};
@@ -840,10 +841,11 @@
   text_input_v3_->Enable();
   VerifyAndClearExpectations();
 
-  // Sending done should have no effect.
+  // Sending done should only report empty preedit.
   EXPECT_CALL(test_client_, OnDeleteSurroundingText(_, _)).Times(0);
   EXPECT_CALL(test_client_, OnCommitString(_)).Times(0);
-  EXPECT_CALL(test_client_, OnPreeditString(_, _, _)).Times(0);
+  EXPECT_CALL(test_client_,
+              OnPreeditString("", std::vector<SpanStyle>{}, gfx::Range()));
   PostToServerAndWait([](wl::TestWaylandServerThread* server) {
     auto* text_input = server->text_input_manager_v3()->text_input();
     zwp_text_input_v3_send_done(text_input->resource(), 1);
@@ -851,7 +853,7 @@
   VerifyAndClearExpectations();
 }
 
-TEST_F(ZwpTextInputV3Test, PendingInputEventsClearedOnDisable) {
+TEST_F(ZwpTextInputV3Test, PendingInputEventsEmptiedOnDisable) {
   constexpr std::string kCommitString("CommitString");
   constexpr std::string_view kPreeditString("PreeditString");
   constexpr gfx::Range kPreeditCursor{0, 13};
@@ -867,7 +869,7 @@
                                                    1);
   });
 
-  // Disable should clear pending requests.
+  // Disable should reset input events state to default.
   PostToServerAndWait([](wl::TestWaylandServerThread* server) {
     auto* zwp_text_input = server->text_input_manager_v3()->text_input();
     InSequence s;
@@ -877,10 +879,11 @@
   text_input_v3_->Disable();
   VerifyAndClearExpectations();
 
-  // Sending done should have no effect.
+  // Sending done should report empty preedit.
   EXPECT_CALL(test_client_, OnDeleteSurroundingText(_, _)).Times(0);
   EXPECT_CALL(test_client_, OnCommitString(_)).Times(0);
-  EXPECT_CALL(test_client_, OnPreeditString(_, _, _)).Times(0);
+  EXPECT_CALL(test_client_,
+              OnPreeditString("", std::vector<SpanStyle>{}, gfx::Range()));
   PostToServerAndWait([](wl::TestWaylandServerThread* server) {
     auto* text_input = server->text_input_manager_v3()->text_input();
     zwp_text_input_v3_send_done(text_input->resource(), 1);
@@ -888,7 +891,7 @@
   VerifyAndClearExpectations();
 }
 
-TEST_F(ZwpTextInputV3Test, PendingInputEventsClearedOnReset) {
+TEST_F(ZwpTextInputV3Test, PendingInputEventsEmptiedOnReset) {
   constexpr std::string kCommitString("CommitString");
   constexpr std::string_view kPreeditString("PreeditString");
   constexpr gfx::Range kPreeditCursor{0, 13};
@@ -915,10 +918,12 @@
   text_input_v3_->Reset();
   VerifyAndClearExpectations();
 
-  // Sending done should have no effect.
+  // Sending done should only report empty preedit.
   EXPECT_CALL(test_client_, OnDeleteSurroundingText(_, _)).Times(0);
   EXPECT_CALL(test_client_, OnCommitString(_)).Times(0);
-  EXPECT_CALL(test_client_, OnPreeditString(_, _, _)).Times(0);
+  EXPECT_CALL(test_client_,
+              OnPreeditString("", std::vector<SpanStyle>{}, gfx::Range()))
+      .Times(2);
   PostToServerAndWait([](wl::TestWaylandServerThread* server) {
     auto* text_input = server->text_input_manager_v3()->text_input();
     zwp_text_input_v3_send_done(text_input->resource(), 1);
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index ee65ebfb..56183d6 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -1049,6 +1049,8 @@
     "test/combobox_test_api.h",
     "test/configurable_test_frame_view.cc",
     "test/configurable_test_frame_view.h",
+    "test/configurable_test_non_client_frame_view.cc",
+    "test/configurable_test_non_client_frame_view.h",
     "test/desktop_test_views_delegate.h",
     "test/dialog_test.cc",
     "test/dialog_test.h",
diff --git a/ui/views/test/configurable_test_non_client_frame_view.cc b/ui/views/test/configurable_test_non_client_frame_view.cc
new file mode 100644
index 0000000..65b2a5f4
--- /dev/null
+++ b/ui/views/test/configurable_test_non_client_frame_view.cc
@@ -0,0 +1,50 @@
+// 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 "ui/views/test/configurable_test_non_client_frame_view.h"
+
+#include "ui/base/hit_test.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+
+namespace views::test {
+
+ConfigurableTestNonClientFrameView::ConfigurableTestNonClientFrameView() =
+    default;
+ConfigurableTestNonClientFrameView::~ConfigurableTestNonClientFrameView() =
+    default;
+
+gfx::Rect ConfigurableTestNonClientFrameView::GetBoundsForClientView() const {
+  return bounds();
+}
+
+gfx::Rect ConfigurableTestNonClientFrameView::GetWindowBoundsForClientBounds(
+    const gfx::Rect& client_bounds) const {
+  return client_bounds;
+}
+
+int ConfigurableTestNonClientFrameView::NonClientHitTest(
+    const gfx::Point& point) {
+  if (hit_test_callback_) {
+    return hit_test_callback_.Run(point);
+  }
+
+  return HTNOWHERE;
+}
+
+void ConfigurableTestNonClientFrameView::GetWindowMask(const gfx::Size& size,
+                                                       SkPath* window_mask) {
+  if (window_mask_callback_) {
+    window_mask_callback_.Run(size, window_mask);
+  }
+}
+
+void ConfigurableTestNonClientFrameView::ResetWindowControls() {}
+void ConfigurableTestNonClientFrameView::UpdateWindowIcon() {}
+void ConfigurableTestNonClientFrameView::UpdateWindowTitle() {}
+void ConfigurableTestNonClientFrameView::SizeConstraintsChanged() {}
+
+BEGIN_METADATA(ConfigurableTestNonClientFrameView)
+END_METADATA
+
+}  // namespace views::test
diff --git a/ui/views/test/configurable_test_non_client_frame_view.h b/ui/views/test/configurable_test_non_client_frame_view.h
new file mode 100644
index 0000000..c9f2953
--- /dev/null
+++ b/ui/views/test/configurable_test_non_client_frame_view.h
@@ -0,0 +1,64 @@
+// 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 UI_VIEWS_TEST_CONFIGURABLE_TEST_NON_CLIENT_FRAME_VIEW_H_
+#define UI_VIEWS_TEST_CONFIGURABLE_TEST_NON_CLIENT_FRAME_VIEW_H_
+
+#include <utility>
+
+#include "base/functional/callback_forward.h"
+#include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/views/window/non_client_view.h"
+
+namespace views::test {
+
+// A test-only NonClientFrameView for custom frames (not based on
+// NativeFrameView) that allows for configuring behaviors like window shapring
+// and hit-testing via callbacks.
+class ConfigurableTestNonClientFrameView : public NonClientFrameView {
+  METADATA_HEADER(ConfigurableTestNonClientFrameView, NonClientFrameView)
+
+ public:
+  using WindowMaskCallback =
+      base::RepeatingCallback<void(const gfx::Size&, SkPath*)>;
+  using HitTestCallback = base::RepeatingCallback<int(const gfx::Point&)>;
+
+  ConfigurableTestNonClientFrameView();
+  ConfigurableTestNonClientFrameView(
+      const ConfigurableTestNonClientFrameView&) = delete;
+  ConfigurableTestNonClientFrameView& operator=(
+      const ConfigurableTestNonClientFrameView&) = delete;
+  ~ConfigurableTestNonClientFrameView() override;
+
+  // Configuration
+  void SetWindowMaskCallback(WindowMaskCallback callback) {
+    window_mask_callback_ = std::move(callback);
+  }
+
+  void SetHitTestCallback(HitTestCallback callback) {
+    hit_test_callback_ = std::move(callback);
+  }
+
+ private:
+  // NonClientFrameView
+  gfx::Rect GetBoundsForClientView() const override;
+  gfx::Rect GetWindowBoundsForClientBounds(
+      const gfx::Rect& client_bounds) const override;
+  int NonClientHitTest(const gfx::Point& point) override;
+  void GetWindowMask(const gfx::Size& size, SkPath* window_mask) override;
+  void ResetWindowControls() override;
+  void UpdateWindowIcon() override;
+  void UpdateWindowTitle() override;
+  void SizeConstraintsChanged() override;
+
+  // When run this callback should set a custom window mask on the frame.
+  WindowMaskCallback window_mask_callback_;
+
+  // When run this callback should perform a custom hit test on the frame.
+  HitTestCallback hit_test_callback_;
+};
+
+}  // namespace views::test
+
+#endif  // UI_VIEWS_TEST_CONFIGURABLE_TEST_NON_CLIENT_FRAME_VIEW_H_
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_impl_unittest.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_impl_unittest.cc
index 78ddb3cb..063569a 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_impl_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_impl_unittest.cc
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
 #include <utility>
 
 #include "base/command_line.h"
+#include "base/functional/bind.h"
 #include "ui/aura/native_window_occlusion_tracker.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/hit_test.h"
@@ -12,6 +14,7 @@
 #include "ui/compositor/compositor.h"
 #include "ui/display/display_switches.h"
 #include "ui/platform_window/platform_window.h"
+#include "ui/views/test/configurable_test_non_client_frame_view.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h"
@@ -23,57 +26,6 @@
 // DesktopWindowTreeHostPlatform.
 
 namespace {
-// A NonClientFrameView with a window mask with the bottom right corner cut out.
-class ShapedNonClientFrameView : public NonClientFrameView {
- public:
-  ShapedNonClientFrameView() = default;
-
-  ShapedNonClientFrameView(const ShapedNonClientFrameView&) = delete;
-  ShapedNonClientFrameView& operator=(const ShapedNonClientFrameView&) = delete;
-
-  ~ShapedNonClientFrameView() override = default;
-
-  // NonClientFrameView:
-  gfx::Rect GetBoundsForClientView() const override { return bounds(); }
-  gfx::Rect GetWindowBoundsForClientBounds(
-      const gfx::Rect& client_bounds) const override {
-    return client_bounds;
-  }
-  int NonClientHitTest(const gfx::Point& point) override {
-    // Fake bottom for non client event test.
-    if (point == gfx::Point(500, 500)) {
-      return HTBOTTOM;
-    }
-    return HTNOWHERE;
-  }
-  void GetWindowMask(const gfx::Size& size, SkPath* window_mask) override {
-    int right = size.width();
-    int bottom = size.height();
-
-    window_mask->moveTo(0, 0);
-    window_mask->lineTo(0, bottom);
-    window_mask->lineTo(right, bottom);
-    window_mask->lineTo(right, 10);
-    window_mask->lineTo(right - 10, 10);
-    window_mask->lineTo(right - 10, 0);
-    window_mask->close();
-  }
-  void ResetWindowControls() override {}
-  void UpdateWindowIcon() override {}
-  void UpdateWindowTitle() override {}
-  void SizeConstraintsChanged() override {}
-
-  bool GetAndResetLayoutRequest() {
-    bool layout_requested = layout_requested_;
-    layout_requested_ = false;
-    return layout_requested;
-  }
-
- private:
-  void Layout(PassKey) override { layout_requested_ = true; }
-
-  bool layout_requested_ = false;
-};
 
 class MouseEventRecorder : public ui::EventHandler {
  public:
@@ -113,7 +65,34 @@
   // WidgetDelegateView:
   std::unique_ptr<NonClientFrameView> CreateNonClientFrameView(
       Widget* widget) override {
-    return std::make_unique<ShapedNonClientFrameView>();
+    // Create a NonClientFrameView with a window mask with the bottom right
+    // corner cut out.
+    auto frame_view =
+        std::make_unique<test::ConfigurableTestNonClientFrameView>();
+    frame_view->SetWindowMaskCallback(
+        base::BindRepeating([](const gfx::Size& size, SkPath* window_mask) {
+          int right = size.width();
+          int bottom = size.height();
+
+          window_mask->moveTo(0, 0);
+          window_mask->lineTo(0, bottom);
+          window_mask->lineTo(right, bottom);
+          window_mask->lineTo(right, 10);
+          window_mask->lineTo(right - 10, 10);
+          window_mask->lineTo(right - 10, 0);
+          window_mask->close();
+        }));
+
+    frame_view->SetHitTestCallback(
+        base::BindRepeating([](const gfx::Point& point) -> int {
+          if (point == gfx::Point(500, 500)) {
+            return HTBOTTOM;
+          }
+
+          return HTNOWHERE;
+        }));
+
+    return std::move(frame_view);
   }
 };
 
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox.ts b/ui/webui/resources/cr_components/searchbox/searchbox.ts
index 1421ea2..960294f 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox.ts
+++ b/ui/webui/resources/cr_components/searchbox/searchbox.ts
@@ -358,7 +358,7 @@
     performance.measure('realbox-creation', 'realbox-creation-start');
   }
 
-  getSuggestionsElement(): HTMLElement {
+  getSuggestionsElement(): SearchboxDropdownElement {
     return this.$.matches;
   }