diff --git a/DEPS b/DEPS
index adcd837..2199c751 100644
--- a/DEPS
+++ b/DEPS
@@ -294,15 +294,15 @@
   # 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': '53cfcdd684fe8e27721245e047abc0a6e5f66ef6',
+  'skia_revision': '891566fc6c86826a2999d040b3cfca46bd48967a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '8c396d356a3034fa58739e764fba006d8b1e6eae',
+  'v8_revision': '370446a348a888bf83d4324c2668fe174fb7cf74',
   # 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': '619e8bf8a8b656be5e2c786584b2a7910d1e1ebc',
+  'angle_revision': 'a035c7b6c3db68994cffeff5a2f39acb8fb460c9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -378,7 +378,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'e1e514dbf063209c9e2c71852ff41dad7c94b919',
+  'devtools_frontend_revision': '72872b9f96f826156f50b8416decbf0a437e4427',
   # 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.
@@ -1563,7 +1563,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '9ce226d8f9370de9fddcec8d604728541108e8dd',
+    '60b638f88d1ae3076b23e34c18e0be8c61726f93',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1717,7 +1717,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'VdXM1S8GiDxaJAssEcBneM6U0nC70nnBcYy-9ogj1fMC',
+          'version': 'hj7M2_z9EXhgQSKNgje7pkAFanZwMetHxul8NVrXqyIC',
       },
     ],
     'condition': 'checkout_android and non_git_source',
@@ -2593,7 +2593,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '6e2232a6e0465a68bb8610a2b0d45e3fd509993d',
+    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + 'b771e98cb55a18bb45cc5ca6feb62483d863f1c3',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2770,7 +2770,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': 'gIU2zCUZXpDKF6pIPqNpKQFCb0v8wTlzWkgi9oWiRGgC',
+              'version': 'JPUb5oFXJVCZnmHps_x4jsuJNZzT4zTUYPDo0AsI1K0C',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -2912,16 +2912,16 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@08147a1cf942d635a8324ad45d8ac8991d90994c',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@61576f7bb0ed2d1560bf03ea018207eda497544a',
   'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@a45e175a45fe2792bf62e3acebcc817fecfc8422',
   'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3',
   'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@c8ad050fcb29e42a2f57d9f59e97488f465c436d',
   'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@44c93ad924b647b0d803ef4c924251c4341b838b',
   'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@2cd90f9d20df57eac214c148f3aed885372ddcfe',
-  'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@8beef6cb63ffadb02300bf6321b4d3af85ea7417',
+  'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@da8d2caad9341ca8c5a7c3deba217d7da50a7c24',
   'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@f0f308ad2cdc2e8fd58985d6230df4a29cc44eb6',
-  'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@f3cfb7fa8994e37c7c0568e33a785591af2ca696',
-  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@5f2be4c2611e6b1dfdc224c06b322068ab52e09d',
+  'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@ec329e2721921f79743b90307ee047d08e057788',
+  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@42094612240193e0748bb7c19294ca144da4db97',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '56300b29fbfcc693ee6609ddad3fdd5b7a449a21',
@@ -3097,7 +3097,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': '9sbaE4WE_ul2P2n_JqfekOJLh0wLgn0STUmG9oY-t88C',
+        'version': 'PhJgwXAFaqym_NpaN8tGKFGd0c3sLqJno_AWVUlrWXkC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3108,7 +3108,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'iA7-imMwQLdKeayvbHJRZl9ePsh7tpuz86px_d7dXUMC',
+        'version': 'K41QC9aoVTnf6znHvc2A_z9Wq-1_t-y_SwzQYwxwNisC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4739,7 +4739,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        'c0e9707df578b2b9906f957fe6e9d64722db23f2',
+        '373e3df530fae297dc1f4663e6284052172e34a0',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
index 17c923fd..50487a0 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
@@ -655,7 +655,7 @@
                         longestUiBlockingTaskTimeMs,
                         Math.max(
                                 browserController.getContentStartDuration(),
-                                browserController.getFlushStartupTasksDuration()));
+                                browserController.getStartupTasksLongestBlockingDuration()));
 
         // Record histograms
         String startupModeString =
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 0c9757ce..0891940 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -212,6 +212,7 @@
     "bit_cast.h",
     "bits.h",
     "build_time.h",
+    "byte_count.h",
     "callback_list.cc",
     "callback_list.h",
     "cancelable_callback.h",
@@ -3326,6 +3327,7 @@
     "bit_cast_unittest.cc",
     "bits_unittest.cc",
     "build_time_unittest.cc",
+    "byte_count_unittest.cc",
     "callback_list_unittest.cc",
     "cancelable_callback_unittest.cc",
     "check_is_test_unittest.cc",
diff --git a/base/byte_count.h b/base/byte_count.h
new file mode 100644
index 0000000..081094d
--- /dev/null
+++ b/base/byte_count.h
@@ -0,0 +1,102 @@
+// 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 BASE_BYTE_COUNT_H_
+#define BASE_BYTE_COUNT_H_
+
+#include <cstdint>
+
+#include "base/numerics/checked_math.h"
+#include "base/numerics/safe_conversions.h"
+
+namespace base {
+
+// Represents an integral number of bytes. Supports arithmetic operations and
+// conversions to/from KiB, MiB and GiB. Any operation that overflows will
+// result in a crash and thus this should only be used for trusted inputs.
+//
+// Sample usage:
+//
+//   // Share unit conversion code.
+//   constexpr ByteCount kBufferSize = MiB(1);
+//   std::vector<char> buffer(kBufferSize.InBytesUnsigned());
+//
+//   // Enforce that correct units are used across APIs at compile time.
+//   ByteCount quota = GetQuota();
+//   SetMetadataSize(base::KiB(10));
+//   SetDatabaseSize(quota - base::KiB(10));
+class ByteCount {
+ public:
+  constexpr ByteCount() = default;
+
+  constexpr explicit ByteCount(int64_t bytes) : bytes_(bytes) {}
+  ~ByteCount() = default;
+
+  ByteCount(const ByteCount&) = default;
+  ByteCount& operator=(const ByteCount&) = default;
+
+  static constexpr ByteCount FromUnsigned(uint64_t bytes) {
+    return ByteCount(checked_cast<int64_t>(bytes));
+  }
+
+  constexpr bool is_zero() const { return bytes_ == 0; }
+
+  // Conversion to integral values.
+  constexpr int64_t InBytes() const { return bytes_; }
+  constexpr int64_t InKiB() const { return bytes_ / 1024; }
+  constexpr int64_t InMiB() const { return bytes_ / 1024 / 1024; }
+  constexpr int64_t InGiB() const { return bytes_ / 1024 / 1024 / 1024; }
+
+  // Conversion to floating point values.
+  constexpr double InKiBF() const { return bytes_ / 1024.0; }
+  constexpr double InMiBF() const { return bytes_ / 1024.0 / 1024.0; }
+  constexpr double InGiBF() const { return bytes_ / 1024.0 / 1024.0 / 1024.0; }
+
+  // Conversion to an unsigned amount of bytes. Only use when it is guaranteed
+  // that the value is positive. Fails if the value is negative.
+  constexpr uint64_t InBytesUnsigned() const {
+    return checked_cast<uint64_t>(bytes_);
+  }
+
+  constexpr ByteCount operator+(ByteCount other) const {
+    return ByteCount(
+        (CheckedNumeric<int64_t>(bytes_) + other.bytes_).ValueOrDie());
+  }
+  constexpr ByteCount operator-(ByteCount other) const {
+    return ByteCount(
+        (CheckedNumeric<int64_t>(bytes_) - other.bytes_).ValueOrDie());
+  }
+
+  template <typename T>
+  constexpr ByteCount operator*(T value) const {
+    return ByteCount((CheckedNumeric<int64_t>(bytes_) * value).ValueOrDie());
+  }
+
+  template <typename T>
+  constexpr ByteCount operator/(T value) const {
+    return ByteCount((CheckedNumeric<int64_t>(bytes_) / value).ValueOrDie());
+  }
+
+  constexpr auto operator<=>(const ByteCount& other) const = default;
+
+ private:
+  int64_t bytes_ = 0;
+};
+
+constexpr ByteCount KiB(int64_t kib) {
+  return ByteCount((CheckedNumeric<int64_t>(kib) * 1024).ValueOrDie());
+}
+
+constexpr ByteCount MiB(int64_t mib) {
+  return ByteCount((CheckedNumeric<int64_t>(mib) * 1024 * 1024).ValueOrDie());
+}
+
+constexpr ByteCount GiB(int64_t gib) {
+  return ByteCount(
+      (CheckedNumeric<int64_t>(gib) * 1024 * 1024 * 1024).ValueOrDie());
+}
+
+}  // namespace base
+
+#endif  // BASE_BYTE_COUNT_H_
diff --git a/base/byte_count_unittest.cc b/base/byte_count_unittest.cc
new file mode 100644
index 0000000..ded1357
--- /dev/null
+++ b/base/byte_count_unittest.cc
@@ -0,0 +1,134 @@
+// 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/byte_count.h"
+
+#include "base/test/gtest_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(ByteCount, ConstructionDefault) {
+  constexpr ByteCount bytes;
+  EXPECT_EQ(0, bytes.InBytes());
+  EXPECT_EQ(0, bytes.InKiB());
+  EXPECT_EQ(0, bytes.InMiB());
+  EXPECT_EQ(0, bytes.InGiB());
+  EXPECT_EQ(0, bytes.InBytesUnsigned());
+}
+
+TEST(ByteCount, ConstructionByteCount) {
+  constexpr ByteCount bytes(1024 * 1024 * 1024);
+  EXPECT_EQ(1024 * 1024 * 1024, bytes.InBytes());
+  EXPECT_EQ(1024 * 1024, bytes.InKiB());
+  EXPECT_EQ(1024, bytes.InMiB());
+  EXPECT_EQ(1, bytes.InGiB());
+  EXPECT_EQ(1024u * 1024 * 1024, bytes.InBytesUnsigned());
+}
+
+TEST(ByteCount, ConstructionUnsigned) {
+  auto bytes = ByteCount::FromUnsigned(5u);
+  EXPECT_EQ(5, bytes.InBytes());
+}
+
+TEST(ByteCount, ConstructionUnsignedInvalid) {
+  BASE_EXPECT_DEATH(
+      { ByteCount::FromUnsigned(std::numeric_limits<uint64_t>::max()); }, "");
+}
+
+TEST(ByteCount, ConstructionOtherUnit) {
+  auto kib5 = KiB(5);
+  EXPECT_EQ(5 * 1024, kib5.InBytes());
+
+  auto mib5 = MiB(5);
+  EXPECT_EQ(5 * 1024 * 1024, mib5.InBytes());
+
+  auto gib5 = GiB(5);
+  EXPECT_EQ(5ll * 1024 * 1024 * 1024, gib5.InBytes());
+}
+
+TEST(ByteCount, ConstructionOtherUnitInvalid) {
+  BASE_EXPECT_DEATH({ KiB(std::numeric_limits<int64_t>::max()); }, "");
+
+  BASE_EXPECT_DEATH({ MiB(std::numeric_limits<int64_t>::max()); }, "");
+
+  BASE_EXPECT_DEATH({ GiB(std::numeric_limits<int64_t>::max()); }, "");
+}
+
+TEST(ByteCount, IsZero) {
+  EXPECT_TRUE(ByteCount(0).is_zero());
+  EXPECT_FALSE(ByteCount(-2).is_zero());
+  EXPECT_FALSE(ByteCount(2).is_zero());
+}
+
+TEST(ByteCount, InFloating) {
+  constexpr ByteCount bytes(3435973836);
+  EXPECT_THAT(bytes.InKiBF(), testing::DoubleEq(3355443.19921875));
+  EXPECT_THAT(bytes.InMiBF(), testing::DoubleEq(3276.7999992370605));
+  EXPECT_THAT(bytes.InGiBF(), testing::DoubleEq(3.1999999992549419));
+}
+
+TEST(ByteCount, InUnsignedInvalid) {
+  ByteCount bytes(-2);
+  BASE_EXPECT_DEATH({ bytes.InBytesUnsigned(); }, "");
+}
+
+TEST(ByteCount, Arithmetic) {
+  ByteCount bytes(42);
+
+  ByteCount add = bytes + ByteCount(10);
+  EXPECT_EQ(52, add.InBytes());
+
+  ByteCount sub = bytes - ByteCount(10);
+  EXPECT_EQ(32, sub.InBytes());
+
+  ByteCount mul = bytes * 10;
+  EXPECT_EQ(420, mul.InBytes());
+
+  ByteCount div = bytes / 2;
+  EXPECT_EQ(21, div.InBytes());
+}
+
+TEST(ByteCount, ArithmeticInvalid) {
+  ByteCount max_bytes(std::numeric_limits<int64_t>::max());
+
+  BASE_EXPECT_DEATH({ max_bytes + max_bytes; }, "");
+
+  BASE_EXPECT_DEATH({ ByteCount() - max_bytes - max_bytes; }, "");
+
+  BASE_EXPECT_DEATH({ max_bytes * 2; }, "");
+
+  BASE_EXPECT_DEATH({ max_bytes / 0; }, "");
+}
+
+TEST(ByteCount, Comparison) {
+  ByteCount a(1);
+  ByteCount b(2);
+  ByteCount c(2);
+
+  EXPECT_TRUE(a < b);
+  EXPECT_FALSE(b < a);
+  EXPECT_FALSE(b < c);
+
+  EXPECT_TRUE(a <= b);
+  EXPECT_FALSE(b <= a);
+  EXPECT_TRUE(b <= c);
+
+  EXPECT_FALSE(a > b);
+  EXPECT_TRUE(b > a);
+  EXPECT_FALSE(b > c);
+
+  EXPECT_FALSE(a >= b);
+  EXPECT_TRUE(b >= a);
+  EXPECT_TRUE(b >= c);
+
+  EXPECT_FALSE(a == b);
+  EXPECT_TRUE(b == c);
+
+  EXPECT_TRUE(a != b);
+  EXPECT_FALSE(b != c);
+}
+
+}  // namespace base
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 4a854877..3263bbe 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
@@ -4,7 +4,6 @@
 
 package org.chromium.base.test.transit;
 
-import org.chromium.base.test.transit.Transition.Trigger;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 
@@ -22,8 +21,12 @@
  * 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
- * Station#exitFacilitySync(Facility, Trigger)}.
+ * with:
+ *
+ * <ul>
+ *   <li>{@link TripBuilder#enterFacility(Facility)}
+ *   <li>{@link TripBuilder#exitFacility(Facility)}}
+ * </ul>
  *
  * @param <HostStationT> the type of host {@link Station} this is scoped to.
  */
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/ScrollableFacility.java b/base/test/android/javatests/src/org/chromium/base/test/transit/ScrollableFacility.java
index 54c528a..dc38e927 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/ScrollableFacility.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/ScrollableFacility.java
@@ -319,7 +319,7 @@
          * @return the return value of the |selectHandler|. e.g. a Facility or Station.
          */
         public SelectReturnT scrollToAndSelect() {
-            return scrollTo().select();
+            return scrollToItemIfNeeded().select();
         }
 
         /**
@@ -328,7 +328,7 @@
          * @return a ItemScrolledTo facility representing the item on the screen, which runs the
          *     |selectHandler| when selected.
          */
-        public ItemOnScreenFacility<SelectReturnT> scrollTo() {
+        public ItemOnScreenFacility<SelectReturnT> scrollToItemIfNeeded() {
             assert mPresence != Presence.ABSENT;
 
             // Could in theory try to scroll to a stub, but not supporting this prevents the
@@ -342,9 +342,9 @@
                 onView(mViewSpec.getViewMatcher())
                         .withFailureHandler(RawFailureHandler.getInstance())
                         .check(matches(isCompletelyDisplayed()));
-                return mHostStation.enterFacilitySync(focusedItem, /* trigger= */ null);
+                return noopTo().enterFacility(focusedItem);
             } catch (AssertionError | NoMatchingViewException e) {
-                return mHostStation.enterFacilitySync(focusedItem, this::triggerScrollTo);
+                return scrollToItemTo().enterFacility(focusedItem);
             }
         }
 
@@ -369,12 +369,14 @@
             return mSelectHandler;
         }
 
-        private void triggerScrollTo() {
+        /** Scroll to the item to start a Transition. */
+        public TripBuilder scrollToItemTo() {
             if (mOffScreenDataMatcher != null) {
                 // If there is a data matcher, use it to scroll as the item might be in a
                 // RecyclerView.
                 try {
-                    onData(mOffScreenDataMatcher).perform(ViewActions.scrollTo());
+                    return runTo(
+                            () -> onData(mOffScreenDataMatcher).perform(ViewActions.scrollTo()));
                 } catch (PerformException performException) {
                     throw TravelException.newTravelException(
                             String.format(
@@ -387,7 +389,10 @@
                 // created but not displayed.
                 assumeNonNull(mViewSpec);
                 try {
-                    onView(mViewSpec.getViewMatcher()).perform(ViewActions.scrollTo());
+                    return runTo(
+                            () ->
+                                    onView(mViewSpec.getViewMatcher())
+                                            .perform(ViewActions.scrollTo()));
                 } catch (PerformException performException) {
                     throw TravelException.newTravelException(
                             String.format(
@@ -484,7 +489,7 @@
         for (ScrollableFacility<?>.Item<?> item : getItems()) {
             if (item.getPresence() == Presence.PRESENT_AND_ENABLED
                     || item.getPresence() == Presence.PRESENT_AND_DISABLED) {
-                item.scrollTo();
+                item.scrollToItemIfNeeded();
             }
         }
     }
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index e0514b4..30b0215 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -78,6 +78,9 @@
     # androidx/test/espresso/web/internal/deps/guava/collect/Maps$1.class:"
     # Also happens in espresso core.
     r'Warning in .*:androidx/test/espresso/.*/guava/collect/.*',
+    # This class is used by androidx.test.uiautomator.GestureController but is
+    # for optional XrExtensions usage.
+    r'Missing class com.android.extensions.xr.*Xr.*',
 )
 
 _BLOCKLISTED_EXPECTATION_PATHS = [
diff --git a/chrome/VERSION b/chrome/VERSION
index 248ba3af..6b0ee53 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=140
 MINOR=0
-BUILD=7276
+BUILD=7277
 PATCH=0
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPanePublicTransitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPanePublicTransitTest.java
index 1505b1f..d270897 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPanePublicTransitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPanePublicTransitTest.java
@@ -84,7 +84,7 @@
         TabSwitcherListEditorFacility listEditor =
                 regularTabSwitcher.openAppMenu().clickSelectTabs();
 
-        listEditor.pressBackToExit();
+        listEditor.pressBackTo().exitFacility();
 
         // Go back to a tab to cleanup tab state
         regularTabSwitcher.selectTabAtIndex(0, WebPageStation.newBuilder());
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherListEditorPTTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherListEditorPTTest.java
index f755388..dbccb8e 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherListEditorPTTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherListEditorPTTest.java
@@ -91,7 +91,7 @@
         RegularTabSwitcherStation tabSwitcher = firstPage.openRegularTabSwitcher();
         TabSwitcherListEditorFacility<RegularTabSwitcherStation> editor =
                 tabSwitcher.openAppMenu().clickSelectTabs();
-        editor.pressBackToExit();
+        editor.pressBackTo().exitFacility();
 
         // Go back to PageStation for InitialStateRule to reset
         firstPage = tabSwitcher.leaveHubToPreviousTabViaBack(WebPageStation.newBuilder());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
index 982ad85..3a1c571 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
@@ -422,9 +422,6 @@
                 getPreferenceScreen().removePreference(passwordParent);
             } else {
                 displayPasswordNoResultScreenMessage();
-                getView()
-                        .announceForAccessibility(
-                                getString(R.string.accessible_find_in_page_no_results));
             }
         }
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/edge_to_edge/EdgeToEdgePTTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/edge_to_edge/EdgeToEdgePTTest.java
index 8ce8b19..d096899 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/edge_to_edge/EdgeToEdgePTTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/edge_to_edge/EdgeToEdgePTTest.java
@@ -6,6 +6,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import static org.chromium.base.test.transit.Triggers.noopTo;
 import static org.chromium.chrome.test.transit.edge_to_edge.ViewportFitCoverPageStation.loadViewportFitCoverPage;
 
 import android.os.Build.VERSION_CODES;
@@ -94,9 +95,7 @@
     public void fromNtpToRegularPage() {
         // Start the page on NTP, chin is not visible.
         var newTabPage = mCtaTestRule.startOnNtp();
-        var chinOnNtp =
-                newTabPage.enterFacilitySync(
-                        new EdgeToEdgeBottomChinFacility<>(true), /* trigger= */ null);
+        var chinOnNtp = noopTo().enterFacility(new EdgeToEdgeBottomChinFacility<>(true));
         assertEquals(
                 "On ntp the bottom chin should be VISIBLE_IF_OTHERS_VISIBLE.",
                 LayerVisibility.VISIBLE_IF_OTHERS_VISIBLE,
@@ -106,9 +105,7 @@
         var pair =
                 TopBottomLinksPageStation.loadPage(mCtaTestRule.getActivityTestRule(), newTabPage);
         TopBottomLinksPageStation regularPage = pair.first;
-        var chinOnWebPage =
-                regularPage.enterFacilitySync(
-                        new EdgeToEdgeBottomChinFacility<>(false), /* trigger= */ null);
+        var chinOnWebPage = noopTo().enterFacility(new EdgeToEdgeBottomChinFacility<>(false));
         assertEquals(
                 "On a regular page the bottom chin should be always VISIBLE.",
                 LayerVisibility.VISIBLE,
@@ -123,9 +120,7 @@
     public void fromNtpToTabSwitcher() {
         // Start the page on NTP, chin is not visible.
         var newTabPage = mCtaTestRule.startOnNtp();
-        var chinOnNtp =
-                newTabPage.enterFacilitySync(
-                        new EdgeToEdgeBottomChinFacility<>(true), /* trigger= */ null);
+        var chinOnNtp = noopTo().enterFacility(new EdgeToEdgeBottomChinFacility<>(true));
         assertEquals(
                 "On ntp the bottom chin should be VISIBLE_IF_OTHERS_VISIBLE.",
                 LayerVisibility.VISIBLE_IF_OTHERS_VISIBLE,
@@ -134,9 +129,7 @@
         // Navigate the page to another web page, ensure the page has the chin visible.
         // On the hub, there's no "page", so don't set an expectation whether the page is opt-in.
         var tabSwitcher = newTabPage.openRegularTabSwitcher();
-        var chinOnHub =
-                tabSwitcher.enterFacilitySync(
-                        new EdgeToEdgeBottomChinFacility<>(null), /* trigger= */ null);
+        var chinOnHub = noopTo().enterFacility(new EdgeToEdgeBottomChinFacility<>(null));
         assertEquals(
                 "On the hub the bottom chin should be always VISIBLE_IF_OTHERS_VISIBLE.",
                 LayerVisibility.VISIBLE_IF_OTHERS_VISIBLE,
@@ -154,9 +147,7 @@
     public void fromNtpToOptInPage() {
         // Start the page on NTP, chin is not visible.
         var newTabPage = mCtaTestRule.startOnNtp();
-        var chinOnNtp =
-                newTabPage.enterFacilitySync(
-                        new EdgeToEdgeBottomChinFacility<>(true), /* trigger= */ null);
+        var chinOnNtp = noopTo().enterFacility(new EdgeToEdgeBottomChinFacility<>(true));
         assertEquals(
                 "On ntp the bottom chin should be VISIBLE_IF_OTHERS_VISIBLE.",
                 LayerVisibility.VISIBLE_IF_OTHERS_VISIBLE,
@@ -166,9 +157,7 @@
         var optInPage =
                 ViewportFitCoverPageStation.loadViewportFitCoverPage(
                         mCtaTestRule.getActivityTestRule(), newTabPage);
-        var chinOnOptInPage =
-                optInPage.enterFacilitySync(
-                        new EdgeToEdgeBottomChinFacility<>(true), /* trigger= */ null);
+        var chinOnOptInPage = noopTo().enterFacility(new EdgeToEdgeBottomChinFacility<>(true));
         assertEquals(
                 "On a opt-in page the bottom chin should be VISIBLE_IF_OTHERS_VISIBLE.",
                 LayerVisibility.VISIBLE_IF_OTHERS_VISIBLE,
@@ -183,9 +172,7 @@
     public void fromBlankPageToOptInPage() {
         // Start the page on NTP, chin is not visible.
         var blankPage = mCtaTestRule.startOnBlankPage();
-        var chinOnBlankPage =
-                blankPage.enterFacilitySync(
-                        new EdgeToEdgeBottomChinFacility<>(false), /* trigger= */ null);
+        var chinOnBlankPage = noopTo().enterFacility(new EdgeToEdgeBottomChinFacility<>(false));
         assertEquals(
                 "On the blank page (not opt-in e2e), chin should be visible.",
                 LayerVisibility.VISIBLE,
@@ -195,9 +182,7 @@
         var optInPage =
                 ViewportFitCoverPageStation.loadViewportFitCoverPage(
                         mCtaTestRule.getActivityTestRule(), blankPage);
-        var chinOnOptInPage =
-                optInPage.enterFacilitySync(
-                        new EdgeToEdgeBottomChinFacility<>(true), /* trigger= */ null);
+        var chinOnOptInPage = noopTo().enterFacility(new EdgeToEdgeBottomChinFacility<>(true));
         assertEquals(
                 "On a opt-in page the bottom chin should be VISIBLE_IF_OTHERS_VISIBLE.",
                 LayerVisibility.VISIBLE_IF_OTHERS_VISIBLE,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/edge_to_edge/EdgeToEdgeStartupTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/edge_to_edge/EdgeToEdgeStartupTest.java
index 3a9b687..375a6320 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/edge_to_edge/EdgeToEdgeStartupTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/edge_to_edge/EdgeToEdgeStartupTest.java
@@ -6,6 +6,8 @@
 
 import static org.junit.Assert.assertNull;
 
+import static org.chromium.base.test.transit.Triggers.noopTo;
+
 import android.os.Build.VERSION_CODES;
 
 import androidx.test.filters.LargeTest;
@@ -59,9 +61,8 @@
     @Test
     @LargeTest
     public void testStartOnNewPage() {
-        mActivityTestRule
-                .startOnBlankPage()
-                .enterFacilitySync(new EdgeToEdgeBottomChinFacility<>(false), null);
+        mActivityTestRule.startOnBlankPage();
+        noopTo().enterFacility(new EdgeToEdgeBottomChinFacility<>(false));
 
         // Hop off, and assume invalid insets came in.
         EdgeToEdgeUtils.setObservedTappableNavigationBarForTesting(true);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxPTTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxPTTest.java
index 5a9c2bc..50fdbbf 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxPTTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxPTTest.java
@@ -110,6 +110,6 @@
         enteredText.clickDelete();
 
         keyboard.close();
-        omniboxAndKeyboard.first.pressBackToClose();
+        omniboxAndKeyboard.first.pressBackTo().exitFacility();
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java
index 6ccd3e80..0b140046 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java
@@ -56,6 +56,7 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.DisabledTest;
+import org.chromium.base.test.util.Features.DisableFeatures;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.blink.mojom.AuthenticatorAttachment;
@@ -1598,6 +1599,7 @@
 
     @Test
     @SmallTest
+    @DisableFeatures(BlinkFeatures.SECURE_PAYMENT_CONFIRMATION_BROWSER_BOUND_KEYS)
     public void testMakeCredential_isPaymentCredentialCreationPassedToFrameHost() {
         mIntentSender.setNextResultIntent(
                 Fido2ApiTestHelper.createErrorIntent(
@@ -1620,6 +1622,30 @@
 
     @Test
     @SmallTest
+    @EnableFeatures(BlinkFeatures.SECURE_PAYMENT_CONFIRMATION_BROWSER_BOUND_KEYS)
+    public void
+            testMakeCredential_isPaymentCredentialCreationPassedToFrameHostWithPaymentOptions() {
+        mIntentSender.setNextResultIntent(
+                Fido2ApiTestHelper.createErrorIntent(
+                        Fido2Api.INVALID_STATE_ERR,
+                        "One of the excluded credentials exists on the local device"));
+
+        mCreationOptions.isPaymentCredentialCreation = true;
+        Assert.assertFalse(mFrameHost.mIsPaymentCredentialCreation);
+        mRequest.handleMakeCredentialRequest(
+                mCreationOptions,
+                mBrowserOptions,
+                mOrigin,
+                mOrigin,
+                Fido2ApiTestHelper.createPaymentOptions(),
+                mCallback::onRegisterResponse,
+                mCallback::onError,
+                mCallback::onRequestOutcome);
+        Assert.assertTrue(mFrameHost.mIsPaymentCredentialCreation);
+    }
+
+    @Test
+    @SmallTest
     @DisableIf.Build(
             sdk_is_greater_than = Build.VERSION_CODES.TIRAMISU,
             message = "crbug.com/347310677")
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTaskTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTaskTest.java
index dbd679c..e76f5d5f 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTaskTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTaskTest.java
@@ -125,7 +125,7 @@
         }
 
         @Override
-        public long getFlushStartupTasksDuration() {
+        public long getStartupTasksLongestBlockingDuration() {
             return 0L;
         }
 
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 25a40dc..9b60bf7 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -18384,12 +18384,6 @@
     <message name="IDS_USED_ON_THIS_SITE" desc="A string indicating to the user that they have used the identity providers' account for federation in the RP in the past. This is based primarily on the knowledge indicated by the identity provider, so no timestamp is known or displayed to the user in this case.">
       You used on this site
     </message>
-    <message name="IDS_MULTI_IDP_ACCOUNT_LAST_USED_ON_THIS_SITE" desc="Lets the user know that this is the account which was last used in FedCM on this site. It may be used as the second parameter to IDS_MULTI_IDP_ACCOUNT_ORIGIN_AND_LAST_USED. Only used for the account which was most recently used.">
-      last used on this site
-    </message>
-    <message name="IDS_MULTI_IDP_ACCOUNT_USED_TIME_AGO" desc="Lets the user know that this account was used some specific time ago. This may be used as used as the second parameter to IDS_MULTI_IDP_ACCOUNT_ORIGIN_AND_LAST_USED. Used for accounts which are not the most recently used but for which we know the time when they were last used.">
-      used <ph name="TIME_AGO">$1<ex>1 month ago</ex></ph>
-    </message>
     <message name="IDS_VERIFY_SHEET_TITLE" desc="Header shown to the user while signing in happens.">
       Verifying…
     </message>
@@ -18909,6 +18903,19 @@
         </message>
     </if>
 
+<!-- Isolated Web Apps Window Management notification. -->
+    <message name="IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_TITLE" desc="Title for the notification displayed when an Isolated Web App opens multiple wtabs/windows.">
+            <ph name="APP_NAME">$1<ex>Example App</ex></ph> has opened multiple windows.
+          </message>
+
+    <message name="IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_MESSAGE" desc="Message for the notification displaying the count of open windows/tabs for an Isolated Web App.">
+      <ph name="OPENINGS_COUNT">$1</ph> new Chrome windows or tabs have been opened by this app. You can manage this behavior under "Pop-ups and Redirects" permission.
+    </message>
+
+    <message name="IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_BUTTON_SETTINGS" desc="Text for the button on the multiple web contents opened notification that opens the app's content settings.">
+      Change permissions
+    </message>
+
     <!-- Common WebUI elements. -->
     <message name="IDS_THUMBS_DOWN" desc="Accessibility label for a thumbs down icon that a user can click to provide negative feedback about a feature.">
       Thumbs down submits feedback that you dislike this.
diff --git a/chrome/app/generated_resources_grd/IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_BUTTON_SETTINGS.png.sha1 b/chrome/app/generated_resources_grd/IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_BUTTON_SETTINGS.png.sha1
new file mode 100644
index 0000000..5c5fce1
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_BUTTON_SETTINGS.png.sha1
@@ -0,0 +1 @@
+d580797eb91127701caaf5e76fd97a1d56eebd83
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_MESSAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_MESSAGE.png.sha1
new file mode 100644
index 0000000..5c5fce1
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_MESSAGE.png.sha1
@@ -0,0 +1 @@
+d580797eb91127701caaf5e76fd97a1d56eebd83
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_TITLE.png.sha1
new file mode 100644
index 0000000..5c5fce1
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_TITLE.png.sha1
@@ -0,0 +1 @@
+d580797eb91127701caaf5e76fd97a1d56eebd83
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_MULTI_IDP_ACCOUNT_LAST_USED_ON_THIS_SITE.png.sha1 b/chrome/app/generated_resources_grd/IDS_MULTI_IDP_ACCOUNT_LAST_USED_ON_THIS_SITE.png.sha1
deleted file mode 100644
index 17e1da5..0000000
--- a/chrome/app/generated_resources_grd/IDS_MULTI_IDP_ACCOUNT_LAST_USED_ON_THIS_SITE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b1a2fd8ced44a189a936d9e116b4728ea2dc9ccd
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_MULTI_IDP_ACCOUNT_ORIGIN_AND_LAST_USED.png.sha1 b/chrome/app/generated_resources_grd/IDS_MULTI_IDP_ACCOUNT_ORIGIN_AND_LAST_USED.png.sha1
deleted file mode 100644
index 17e1da5..0000000
--- a/chrome/app/generated_resources_grd/IDS_MULTI_IDP_ACCOUNT_ORIGIN_AND_LAST_USED.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b1a2fd8ced44a189a936d9e116b4728ea2dc9ccd
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_MULTI_IDP_ACCOUNT_USED_TIME_AGO.png.sha1 b/chrome/app/generated_resources_grd/IDS_MULTI_IDP_ACCOUNT_USED_TIME_AGO.png.sha1
deleted file mode 100644
index 730779f..0000000
--- a/chrome/app/generated_resources_grd/IDS_MULTI_IDP_ACCOUNT_USED_TIME_AGO.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-5f1a2c6f1a9cafa4caff61eb27bad571173735e1
\ No newline at end of file
diff --git a/chrome/app/sharesheet_strings_grdp/DIR_METADATA b/chrome/app/sharesheet_strings_grdp/DIR_METADATA
index 3d15d120..bda997ea 100644
--- a/chrome/app/sharesheet_strings_grdp/DIR_METADATA
+++ b/chrome/app/sharesheet_strings_grdp/DIR_METADATA
@@ -1,4 +1,4 @@
-team_email: "cros-web-apps-team@google.com"
+team_email: "lt-web-apps-team@google.com"
 os: CHROME_OS
 buganizer: {
   component_id: 1389907
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 6e71828..30c7fa8a 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4366,6 +4366,8 @@
       sources += [
         "web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service.cc",
         "web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service.h",
+        "web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_delegate.cc",
+        "web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_delegate.h",
         "web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_factory.cc",
         "web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_factory.h",
       ]
diff --git a/chrome/browser/actor/tools/tab_management_tool.cc b/chrome/browser/actor/tools/tab_management_tool.cc
index 63b1ea6..572f5ef 100644
--- a/chrome/browser/actor/tools/tab_management_tool.cc
+++ b/chrome/browser/actor/tools/tab_management_tool.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/actor/tools/tab_management_tool.h"
 
+#include "base/functional/callback_forward.h"
 #include "base/notimplemented.h"
 #include "chrome/browser/actor/tools/observation_delay_controller.h"
 #include "chrome/browser/actor/tools/tool_callbacks.h"
@@ -45,6 +46,8 @@
 }
 
 void TabManagementTool::Invoke(InvokeCallback callback) {
+  callback_ = std::move(callback);
+
   // TODO(crbug.com/411462297): Only the create action is hooked up and
   // implemented.
   switch (action_) {
@@ -55,11 +58,17 @@
           BrowserWindowInterface::FromSessionID(
               SessionID::FromSerializedValue(window_id_.value()));
       if (!browser_window_interface) {
-        PostResponseTask(std::move(callback),
+        PostResponseTask(std::move(callback_),
                          MakeResult(mojom::ActionResultCode::kWindowWentAway));
         return;
       }
 
+      // The observer is removed in the TabStripModelObserver's destructor.
+      browser_window_interface->GetTabStripModel()->AddObserver(this);
+
+      // Watch for the window going away as well so we don't wait indefinitely.
+      browser_list_observation_.Observe(BrowserList::GetInstance());
+
       // Open a blank tab.
       browser_window_interface->OpenGURL(GURL(url::kAboutBlankURL),
                                          create_disposition_.value());
@@ -69,12 +78,10 @@
     case kClose:
       CHECK(target_tab_.has_value());
       NOTIMPLEMENTED() << "ActivateTab and CloseTab not yet implemented";
-      PostResponseTask(std::move(callback),
+      PostResponseTask(std::move(callback_),
                        MakeResult(mojom::ActionResultCode::kError));
       return;
   }
-
-  PostResponseTask(std::move(callback), MakeOkResult());
 }
 
 std::string TabManagementTool::DebugString() const {
@@ -97,4 +104,28 @@
   return nullptr;
 }
 
+void TabManagementTool::OnTabStripModelChanged(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange& change,
+    const TabStripSelectionChange& selection) {
+  if (change.type() == TabStripModelChange::kInserted) {
+    if (callback_) {
+      PostResponseTask(std::move(callback_), MakeOkResult());
+    }
+  }
+}
+
+void TabManagementTool::OnBrowserRemoved(Browser* browser) {
+  // If the window is destroyed in the interval after a create tab has been
+  // invoked but before the tab's been added, this ensures we don't hang waiting
+  // for the new tab.
+  if (action_ == kCreate) {
+    CHECK(window_id_);
+    if (callback_ && browser->GetSessionID().id() == window_id_.value()) {
+      PostResponseTask(std::move(callback_),
+                       MakeResult(mojom::ActionResultCode::kWindowWentAway));
+    }
+  }
+}
+
 }  // namespace actor
diff --git a/chrome/browser/actor/tools/tab_management_tool.h b/chrome/browser/actor/tools/tab_management_tool.h
index 4ea8fce..64ede27 100644
--- a/chrome/browser/actor/tools/tab_management_tool.h
+++ b/chrome/browser/actor/tools/tab_management_tool.h
@@ -6,8 +6,12 @@
 #define CHROME_BROWSER_ACTOR_TOOLS_TAB_MANAGEMENT_TOOL_H_
 
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/actor/tools/tool.h"
 #include "chrome/browser/actor/tools/tool_request.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_list_observer.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "ui/base/window_open_disposition.h"
 
 namespace actor {
@@ -17,7 +21,9 @@
 // A tool to manage the tabs in a browser window, e.g. create, close,
 // activate, etc.
 // TODO(crbug.com/411462297): Implement actions other than create.
-class TabManagementTool : public Tool {
+class TabManagementTool : public Tool,
+                          public TabStripModelObserver,
+                          public BrowserListObserver {
  public:
   enum Action { kCreate, kActivate, kClose };
 
@@ -42,9 +48,19 @@
   std::unique_ptr<ObservationDelayController> GetObservationDelayer()
       const override;
 
+  // TabStripModelObserver
+  void OnTabStripModelChanged(TabStripModel* tab_strip_model,
+                              const TabStripModelChange& change,
+                              const TabStripSelectionChange& selection) final;
+
+  // BrowserListObserver
+  void OnBrowserRemoved(Browser* browser) final;
+
  private:
   Action action_;
 
+  InvokeCallback callback_;
+
   // Used for activate or close action.
   std::optional<tabs::TabHandle> target_tab_;
 
@@ -54,6 +70,9 @@
   // If creating a tab, the window in which to create the tab.
   std::optional<int32_t> window_id_;
 
+  base::ScopedObservation<BrowserList, BrowserListObserver>
+      browser_list_observation_{this};
+
   base::WeakPtrFactory<TabManagementTool> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/apps/DIR_METADATA b/chrome/browser/apps/DIR_METADATA
index 23334c7..3fc32c7d 100644
--- a/chrome/browser/apps/DIR_METADATA
+++ b/chrome/browser/apps/DIR_METADATA
@@ -1,4 +1,4 @@
-team_email: "cros-web-apps-team@google.com"
+team_email: "lt-web-apps-team@google.com"
 buganizer: {
   component_id: 1389907
 }
diff --git a/chrome/browser/apps/app_service/webapk/webapk_install_task.cc b/chrome/browser/apps/app_service/webapk/webapk_install_task.cc
index 2f17f605..4e980b59 100644
--- a/chrome/browser/apps/app_service/webapk/webapk_install_task.cc
+++ b/chrome/browser/apps/app_service/webapk/webapk_install_task.cc
@@ -83,7 +83,7 @@
           destination: GOOGLE_OWNED_SERVICE
           internal {
             contacts {
-              email: "cros-web-apps-team@google.com"
+              email: "lt-web-apps-team@google.com"
             }
           }
           user_data {
diff --git a/chrome/browser/ash/app_mode/test/kiosk_iwa_version_management_browsertest.cc b/chrome/browser/ash/app_mode/test/kiosk_iwa_version_management_browsertest.cc
index 97b9c95..3e4db4b 100644
--- a/chrome/browser/ash/app_mode/test/kiosk_iwa_version_management_browsertest.cc
+++ b/chrome/browser/ash/app_mode/test/kiosk_iwa_version_management_browsertest.cc
@@ -40,6 +40,7 @@
 #include "components/web_package/test_support/signed_web_bundles/key_pair.h"
 #include "components/webapps/common/web_app_id.h"
 #include "components/webapps/isolated_web_apps/update_channel.h"
+#include "content/public/browser/network_service_util.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_launcher.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -231,7 +232,11 @@
       : feature_list_(ash::features::kIsolatedWebAppKiosk),
         iwa_server_mixin_(&mixin_host_),
         kiosk_mixin_(&mixin_host_,
-                     std::move(config_creator).Run(GetUpdateManifestUrl())) {}
+                     std::move(config_creator).Run(GetUpdateManifestUrl())) {
+    // Prevents a race condition (often in debug builds) where a network service
+    // process is not yet started when kiosk downloads the IWA update manifest.
+    content::ForceInProcessNetworkService();
+  }
 
   ~KioskIwaVersionManagementBaseTest() override = default;
   KioskIwaVersionManagementBaseTest(const KioskIwaVersionManagementBaseTest&) =
diff --git a/chrome/browser/ash/system_web_apps/apps/DIR_METADATA b/chrome/browser/ash/system_web_apps/apps/DIR_METADATA
index 23334c7..3fc32c7d 100644
--- a/chrome/browser/ash/system_web_apps/apps/DIR_METADATA
+++ b/chrome/browser/ash/system_web_apps/apps/DIR_METADATA
@@ -1,4 +1,4 @@
-team_email: "cros-web-apps-team@google.com"
+team_email: "lt-web-apps-team@google.com"
 buganizer: {
   component_id: 1389907
 }
diff --git a/chrome/browser/complex_tasks/task_tab_helper_unittest.cc b/chrome/browser/complex_tasks/task_tab_helper_unittest.cc
index 10572b3..b8a316ad1f 100644
--- a/chrome/browser/complex_tasks/task_tab_helper_unittest.cc
+++ b/chrome/browser/complex_tasks/task_tab_helper_unittest.cc
@@ -49,6 +49,11 @@
     NavigateAndCommit(kSearchURL);
   }
 
+  void TearDown() override {
+    task_tab_helper_ = nullptr;
+    ChromeRenderViewHostTestHarness::TearDown();
+  }
+
   void GoBack() { content::NavigationSimulator::GoBack(web_contents()); }
 
   void GoBackNTimes(int times) {
@@ -72,7 +77,7 @@
     return web_contents()->GetController().GetLastCommittedEntry();
   }
 
-  raw_ptr<MockTaskTabHelper, DanglingUntriaged> task_tab_helper_;
+  raw_ptr<MockTaskTabHelper> task_tab_helper_;
 };
 
 TEST_F(TaskTabHelperUnitTest, TestGetCurrentTaskId) {
diff --git a/chrome/browser/content_settings/sound_content_setting_observer_unittest.cc b/chrome/browser/content_settings/sound_content_setting_observer_unittest.cc
index f3992b5e..ec7074c 100644
--- a/chrome/browser/content_settings/sound_content_setting_observer_unittest.cc
+++ b/chrome/browser/content_settings/sound_content_setting_observer_unittest.cc
@@ -65,6 +65,11 @@
     NavigateAndCommit(GURL(kURL1));
   }
 
+  void TearDown() override {
+    host_content_settings_map_ = nullptr;
+    ChromeRenderViewHostTestHarness::TearDown();
+  }
+
  protected:
   void ChangeSoundContentSettingTo(ContentSetting setting) {
     GURL url = web_contents()->GetLastCommittedURL();
@@ -109,7 +114,7 @@
 #endif
 
  private:
-  raw_ptr<HostContentSettingsMap, DanglingUntriaged> host_content_settings_map_;
+  raw_ptr<HostContentSettingsMap> host_content_settings_map_;
   std::unique_ptr<ukm::TestUkmRecorder> test_ukm_recorder_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
diff --git a/chrome/browser/controlled_frame/controlled_frame_contextmenus_browsertest.cc b/chrome/browser/controlled_frame/controlled_frame_contextmenus_browsertest.cc
index a869d60..5b80acb2 100644
--- a/chrome/browser/controlled_frame/controlled_frame_contextmenus_browsertest.cc
+++ b/chrome/browser/controlled_frame/controlled_frame_contextmenus_browsertest.cc
@@ -295,11 +295,11 @@
 
 // TODO(crbug.com/392208013): Fix and enable on Mac.
 #if BUILDFLAG(IS_MAC)
-#define MAYBE_OnShow DISABLED_OnShow
+#define MAYBE_ShowEvent DISABLED_ShowEvent
 #else
-#define MAYBE_OnShow OnShow
+#define MAYBE_ShowEvent ShowEvent
 #endif  // BUILDFLAG(IS_MAC)
-IN_PROC_BROWSER_TEST_F(ControlledFrameContextMenusTest, MAYBE_OnShow) {
+IN_PROC_BROWSER_TEST_F(ControlledFrameContextMenusTest, MAYBE_ShowEvent) {
   web_app::IsolatedWebAppUrlInfo url_info =
       CreateAndInstallEmptyApp(web_app::ManifestBuilder());
   content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
@@ -319,29 +319,13 @@
     return ('FAIL: frame or frame.contextMenus is undefined');
   }
 
-  if (frame.contextMenus.onShow.hasListeners()) {
-    return 'FAIL: frame.contextMenus.onShow.hasListeners() \
-    returns true before addListener().';
-  }
-
-  frame.contextMenus.onShow.addListener(document.onShowHandler);
-
-  if (!frame.contextMenus.onShow.hasListeners()) {
-    return 'FAIL: frame.contextMenus.hasListeners() \
-    returns false after addListener().';
-  }
-
-  if (!frame.contextMenus.onShow.hasListener(document.onShowHandler)) {
-    return 'FAIL: frame.contextMenus.onShow.hasListener() \
-    returns false after addListener().';
-  }
-
+  frame.contextMenus.addEventListener('show', document.onShowHandler);
   return $1;
 })();
 )",
       kEvalSuccessStr);
 
-  // Add a listener for 'onShow' then simulate right click and expect the
+  // Add a listener for 'show' then simulate right click and expect the
   // listener to be triggered.
   ASSERT_EQ(content::EvalJs(app_frame, add_handler_script), kEvalSuccessStr);
 
@@ -361,23 +345,7 @@
     return ('FAIL: frame or frame.contextMenus is undefined');
   }
 
-  if (!frame.contextMenus.onShow.hasListeners()) {
-    return 'FAIL: frame.contextMenus.onShow.hasListeners() \
-    returns false before removeListener().';
-  }
-
-  frame.contextMenus.onShow.removeListener(document.onShowHandler);
-
-  if (frame.contextMenus.onShow.hasListeners()) {
-    return 'FAIL: frame.contextMenus.hasListeners() \
-    returns true after removeListener().';
-  }
-
-  if (frame.contextMenus.onShow.hasListener(document.onShowHandler)) {
-    return 'FAIL: frame.contextMenus.onShow.hasListener() \
-    returns true after removeListener().';
-  }
-
+  frame.contextMenus.removeEventListener('show', document.onShowHandler);
   return $1;
 })();
 )",
@@ -391,7 +359,35 @@
   ASSERT_EQ(content::EvalJs(app_frame, "document.onShowCount"), 1);
 }
 
-IN_PROC_BROWSER_TEST_F(ControlledFrameContextMenusTest, OnClicked) {
+IN_PROC_BROWSER_TEST_F(ControlledFrameContextMenusTest, NoLegacyOnShowEvent) {
+  web_app::IsolatedWebAppUrlInfo url_info =
+      CreateAndInstallEmptyApp(web_app::ManifestBuilder());
+  content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
+
+  ASSERT_TRUE(CreateControlledFrame(
+      app_frame, embedded_https_test_server().GetURL("/index.html")));
+
+  const auto* check_legacy_event_script = R"(
+new Promise(async (resolve, reject) => {
+  const frame = document.getElementsByTagName('controlledframe')[0];
+  if (!frame || !frame.contextMenus) {
+    reject('FAIL: frame or frame.contextMenus is undefined');
+    return;
+  }
+
+  if (frame.contextMenus.onShow) {
+    reject('FAIL: contextMenus object contains an onShow attribute');
+    return;
+  }
+  resolve('SUCCESS');
+});
+    )";
+
+  ASSERT_EQ(content::EvalJs(app_frame, check_legacy_event_script),
+            kEvalSuccessStr);
+}
+
+IN_PROC_BROWSER_TEST_F(ControlledFrameContextMenusTest, ClickEvent) {
   constexpr std::string kItemID = "107";
 
   web_app::IsolatedWebAppUrlInfo url_info =
@@ -442,29 +438,14 @@
     return ('FAIL: frame or frame.contextMenus is undefined');
   }
 
-  if (frame.contextMenus.onClicked.hasListeners()) {
-    return 'FAIL: frame.contextMenus.onClicked.hasListeners() \
-    returns true before addListener().';
-  }
-
-  frame.contextMenus.onClicked.addListener(document.onClickedHandler);
-
-  if (!frame.contextMenus.onClicked.hasListeners()) {
-    return 'FAIL: frame.contextMenus.hasListeners() \
-    returns false after addListener().';
-  }
-
-  if (!frame.contextMenus.onClicked.hasListener(document.onClickedHandler)) {
-    return 'FAIL: frame.contextMenus.onClicked.hasListener() \
-    returns false after addListener().';
-  }
-
+  frame.contextMenus.addEventListener('click', document.onClickedHandler);
   return $1;
 })();
 )",
       kEvalSuccessStr);
 
-  // Add a global listener for 'onClicked' then simulate clicking on menu item.
+  // Add a global listener for the 'clicked' event, then simulate clicking on
+  // menu item.
   ASSERT_EQ(content::EvalJs(app_frame, add_handler_script), kEvalSuccessStr);
 
   extensions::WebViewGuest* web_view_guest = GetWebViewGuest(app_frame);
@@ -488,29 +469,13 @@
     return ('FAIL: frame or frame.contextMenus is undefined');
   }
 
-  if (!frame.contextMenus.onClicked.hasListeners()) {
-    return 'FAIL: frame.contextMenus.onClicked.hasListeners() \
-    returns false before removeListener().';
-  }
-
-  frame.contextMenus.onClicked.removeListener(document.onClickedHandler);
-
-  if (frame.contextMenus.onClicked.hasListeners()) {
-    return 'FAIL: frame.contextMenus.hasListeners() \
-    returns true after removeListener().';
-  }
-
-  if (frame.contextMenus.onClicked.hasListener(document.onClickedHandler)) {
-    return 'FAIL: frame.contextMenus.onClicked.hasListener() \
-    returns true after removeListener().';
-  }
-
+  frame.contextMenus.removeEventListener('click', document.onClickedHandler);
   return $1;
 })();
 )",
       kEvalSuccessStr);
 
-  // Remove the global listener for 'onClicked' then simulate clicking on menu
+  // Remove the global listener for 'click' then simulate clicking on menu
   // item.
   ASSERT_EQ(content::EvalJs(app_frame, remove_handler_script), kEvalSuccessStr);
 
@@ -522,6 +487,34 @@
       Each(Eq(kItemID)));
 }
 
+IN_PROC_BROWSER_TEST_F(ControlledFrameContextMenusTest, NoLegacyOnClickEvent) {
+  web_app::IsolatedWebAppUrlInfo url_info =
+      CreateAndInstallEmptyApp(web_app::ManifestBuilder());
+  content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
+
+  ASSERT_TRUE(CreateControlledFrame(
+      app_frame, embedded_https_test_server().GetURL("/index.html")));
+
+  const auto* check_legacy_event_script = R"(
+new Promise(async (resolve, reject) => {
+  const frame = document.getElementsByTagName('controlledframe')[0];
+  if (!frame || !frame.contextMenus) {
+    reject('FAIL: frame or frame.contextMenus is undefined');
+    return;
+  }
+
+  if (frame.contextMenus.onClick) {
+    reject('FAIL: contextMenus object contains an onClick attribute');
+    return;
+  }
+  resolve('SUCCESS');
+});
+    )";
+
+  ASSERT_EQ(content::EvalJs(app_frame, check_legacy_event_script),
+            kEvalSuccessStr);
+}
+
 IN_PROC_BROWSER_TEST_F(ControlledFrameContextMenusTest, UpdatedOnClickHandler) {
   constexpr char kItemID[] = "221B";
   constexpr char kFirstOnClickValue[] = "first handler";
diff --git a/chrome/browser/devtools/protocol/audits_protocol_test.cc b/chrome/browser/devtools/protocol/audits_protocol_test.cc
new file mode 100644
index 0000000..0935f85f
--- /dev/null
+++ b/chrome/browser/devtools/protocol/audits_protocol_test.cc
@@ -0,0 +1,40 @@
+// 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/devtools/protocol/devtools_protocol_test_support.h"
+#include "content/public/test/browser_test.h"
+
+namespace content {
+// Test fixture for DevTools Audits protocol tests that need an embedded server.
+class AuditsProtocolWithServerTest : public DevToolsProtocolTestBase {
+
+public:
+  void SetUpOnMainThread() override {
+    DevToolsProtocolTestBase::SetUpOnMainThread();
+    embedded_test_server()->ServeFilesFromSourceDirectory(
+                                                          "chrome/test/data/devtools/protocol");
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(AuditsProtocolWithServerTest, OverrideFlashEmbedwithHTML) {
+    // Attach to the DevTools agent host for the web contents.
+    Attach();
+
+    // Enable the Audits domain to start receiving issue events.
+    SendCommandSync("Audits.enable");
+    SendCommandSync("DOM.enable");
+
+    GURL url = embedded_test_server()->GetURL("/flash_embed_test.html");
+    SendCommandSync("Page.navigate", base::Value::Dict().Set("url", url.spec()));
+
+    base::Value::Dict notification =
+      WaitForNotification("Audits.issueAdded", true);
+    EXPECT_EQ(*notification.FindStringByDottedPath("issue.code"),
+              "DeprecationIssue");
+    EXPECT_EQ(*notification.FindStringByDottedPath("issue.details.deprecationIssueDetails.type"),
+              "OverrideFlashEmbedwithHTML");
+}
+
+}  // namespace content
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index af284bf0..13a6167 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -30,7 +30,7 @@
 [
   {
     "name": "aaudio-per-stream-device-selection",
-    "owners": [ "mjel@google.com", "cros-web-apps-team@google.com" ],
+    "owners": [ "mjel@google.com", "lt-web-apps-team@google.com" ],
     "expiry_milestone": 150
   },
   {
@@ -354,20 +354,20 @@
     "name": "android-use-correct-display-work-area",
     "owners": [
       "renkens@google.com",
-      "cros-web-apps-team@google.com"
+      "lt-web-apps-team@google.com"
     ],
     "expiry_milestone": 145
   },
   {
     "name": "android-web-app-launch-handler",
-    "owners": [ "sselyvon@google.com", "tkachenkoo@google.com", "cros-web-apps-team@google.com" ],
+    "owners": [ "sselyvon@google.com", "tkachenkoo@google.com", "lt-web-apps-team@google.com" ],
     "expiry_milestone": 145
   },
   {
     "name": "android-window-management-web-api",
     "owners": [
       "renkens@google.com",
-      "cros-web-apps-team@google.com"
+      "lt-web-apps-team@google.com"
     ],
     "expiry_milestone": 145
   },
@@ -985,7 +985,7 @@
   },
   {
     "name": "automatic-usb-detach",
-    "owners": ["ovn@google.com", "cros-web-apps-team@google.com"],
+    "owners": ["ovn@google.com", "lt-web-apps-team@google.com"],
     "expiry_milestone": 160
   },
   {
@@ -1746,7 +1746,7 @@
   },
   {
     "name": "content-settings-partitioning",
-    "owners": [ "brettw@chromium.org", "cros-web-apps-team@google.com"],
+    "owners": [ "brettw@chromium.org", "lt-web-apps-team@google.com"],
     "expiry_milestone": 132
   },
   {
@@ -2329,7 +2329,7 @@
   },
   {
     "name": "display-edge-to-edge-fullscreen",
-    "owners": [ "wzwonarz@google.com", "cros-web-apps-krk@google.com" ],
+    "owners": [ "wzwonarz@google.com", "lt-web-apps-krk@google.com" ],
     "expiry_milestone": 150
   },
   {
@@ -2702,7 +2702,7 @@
     "name": "enable-android-document-picture-in-picture",
     "owners": [
       "kkaplon@google.com",
-      "cros-web-apps-team@google.com"
+      "lt-web-apps-team@google.com"
     ],
     "expiry_milestone": 145
   },
@@ -2715,7 +2715,7 @@
     "name": "enable-android-mininal-ui-large-screen",
     "owners": [
       "vkorotkevich@google.com",
-      "cros-web-apps-team@google.com"
+      "lt-web-apps-team@google.com"
     ],
     "expiry_milestone": 145
   },
@@ -2723,7 +2723,7 @@
     "name": "enable-android-window-occlusion",
     "owners": [
       "edcourtney@chromium.org",
-      "cros-web-apps-team@google.com"
+      "lt-web-apps-team@google.com"
     ],
     "expiry_milestone": 142
   },
@@ -2731,7 +2731,7 @@
     "name": "enable-android-window-popup-large-screen",
     "owners": [
       "chominskib@google.com",
-      "cros-web-apps-team@google.com"
+      "lt-web-apps-team@google.com"
     ],
     "expiry_milestone": 142
   },
@@ -2799,7 +2799,7 @@
     "name": "enable-auxiliary-navigation-stays-in-browser",
     "owners": [
       "muratori@google.org",
-      "cros-web-apps-team@google.com"
+      "lt-web-apps-team@google.com"
     ],
     "expiry_milestone": 150
   },
@@ -3157,7 +3157,7 @@
   },
   {
     "name": "enable-desktop-pwas-tab-strip-customizations",
-    "owners": [ "cros-web-apps-core@google.com", "pwa-team@google.com" ],
+    "owners": [ "lt-web-apps-core@google.com", "pwa-team@google.com" ],
     "expiry_milestone": 150
   },
   {
@@ -3258,7 +3258,7 @@
   },
   {
     "name": "enable-exclusive-access-manager-on-android",
-    "owners": [ "abdoeed@google.org", "wzwonarz@chromium.org", "cros-web-apps-team@google.com" ],
+    "owners": [ "abdoeed@google.org", "wzwonarz@chromium.org", "lt-web-apps-team@google.com" ],
     "expiry_milestone": 140
   },
   {
@@ -3889,7 +3889,7 @@
   },
   {
     "name": "enable-navigation-capture-refactor-android",
-    "owners": [ "muratori@google.org", "cros-web-apps-team@google.com" ],
+    "owners": [ "muratori@google.org", "lt-web-apps-team@google.com" ],
     "expiry_milestone": 150
   },
   {
@@ -4104,7 +4104,7 @@
     "name": "enable-reparent-auxiliary-navigation-from-pwa",
     "owners": [
       "muratori@google.org",
-      "cros-web-apps-team@google.com"
+      "lt-web-apps-team@google.com"
     ],
     "expiry_milestone": 150
   },
@@ -4112,7 +4112,7 @@
     "name": "enable-reparent-top-level-navigation-from-pwa",
     "owners": [
       "muratori@google.org",
-      "cros-web-apps-team@google.com"
+      "lt-web-apps-team@google.com"
     ],
     "expiry_milestone": 150
   },
@@ -4195,7 +4195,7 @@
   },
   {
     "name": "enable-service-workers-for-chrome-untrusted",
-    "owners": [ "ortuno@chromium.org", "cros-web-apps-team@google.com" ],
+    "owners": [ "ortuno@chromium.org", "lt-web-apps-team@google.com" ],
     "expiry_milestone": 110
   },
   {
@@ -10177,7 +10177,7 @@
   },
   {
     "name": "web-serial-over-bluetooth",
-    "owners": ["xutan@chromium.org", "ovn@google.com", "cros-web-apps-team@google.com"],
+    "owners": ["xutan@chromium.org", "ovn@google.com", "lt-web-apps-team@google.com"],
     "expiry_milestone": 146
   },
   {
diff --git a/chrome/browser/ntp_customization/java/res/layout/ntp_customization_theme_list_chrome_colors_item_layout.xml b/chrome/browser/ntp_customization/java/res/layout/ntp_customization_theme_list_chrome_colors_item_layout.xml
index de2ebe9ef..f68337f9 100644
--- a/chrome/browser/ntp_customization/java/res/layout/ntp_customization_theme_list_chrome_colors_item_layout.xml
+++ b/chrome/browser/ntp_customization/java/res/layout/ntp_customization_theme_list_chrome_colors_item_layout.xml
@@ -40,6 +40,6 @@
       android:importantForAccessibility="no"
       app:tint="@color/default_icon_color_tint_list"
       android:src="@drawable/ic_check_googblue_24dp"
-      android:visibility="gone"/>
+      android:visibility="invisible"/>
 
 </org.chromium.chrome.browser.ntp_customization.theme.NtpThemeListItemView>
\ No newline at end of file
diff --git a/chrome/browser/ntp_customization/java/res/layout/ntp_customization_theme_list_chrome_default_item_layout.xml b/chrome/browser/ntp_customization/java/res/layout/ntp_customization_theme_list_chrome_default_item_layout.xml
index 87e5071..cfc0a9b 100644
--- a/chrome/browser/ntp_customization/java/res/layout/ntp_customization_theme_list_chrome_default_item_layout.xml
+++ b/chrome/browser/ntp_customization/java/res/layout/ntp_customization_theme_list_chrome_default_item_layout.xml
@@ -40,6 +40,6 @@
       android:importantForAccessibility="no"
       app:tint="@color/default_icon_color_tint_list"
       android:src="@drawable/ic_check_googblue_24dp"
-      android:visibility="gone"/>
+      android:visibility="invisible"/>
 
 </org.chromium.chrome.browser.ntp_customization.theme.NtpThemeListItemView>
diff --git a/chrome/browser/ntp_customization/java/res/layout/ntp_customization_theme_list_upload_an_image_item_layout.xml b/chrome/browser/ntp_customization/java/res/layout/ntp_customization_theme_list_upload_an_image_item_layout.xml
index 361c91ce..b065483 100644
--- a/chrome/browser/ntp_customization/java/res/layout/ntp_customization_theme_list_upload_an_image_item_layout.xml
+++ b/chrome/browser/ntp_customization/java/res/layout/ntp_customization_theme_list_upload_an_image_item_layout.xml
@@ -40,6 +40,6 @@
       android:importantForAccessibility="no"
       app:tint="@color/default_icon_color_tint_list"
       android:src="@drawable/ic_check_googblue_24dp"
-      android:visibility="gone"/>
+      android:visibility="invisible"/>
 
 </org.chromium.chrome.browser.ntp_customization.theme.NtpThemeListItemView>
diff --git a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeListItemView.java b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeListItemView.java
index 5ecf397..c698c72f4 100644
--- a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeListItemView.java
+++ b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeListItemView.java
@@ -36,7 +36,7 @@
         if (visible) {
             ntpThemeListItemTrailingIcon.setVisibility(View.VISIBLE);
         } else {
-            ntpThemeListItemTrailingIcon.setVisibility(View.GONE);
+            ntpThemeListItemTrailingIcon.setVisibility(View.INVISIBLE);
         }
     }
 }
diff --git a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeListItemViewUnitTest.java b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeListItemViewUnitTest.java
index a32b9bd..3aa7bcd 100644
--- a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeListItemViewUnitTest.java
+++ b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/theme/NtpThemeListItemViewUnitTest.java
@@ -57,6 +57,6 @@
 
         mTrailingIcon.setVisibility(View.VISIBLE);
         mNtpThemeListItemView.setTrailingIconVisibility(false);
-        assertEquals(View.GONE, mTrailingIcon.getVisibility());
+        assertEquals(View.INVISIBLE, mTrailingIcon.getVisibility());
     }
 }
diff --git a/chrome/browser/password_manager/password_change/change_password_form_filling_submission_helper.cc b/chrome/browser/password_manager/password_change/change_password_form_filling_submission_helper.cc
index fb21be87..016e2d5 100644
--- a/chrome/browser/password_manager/password_change/change_password_form_filling_submission_helper.cc
+++ b/chrome/browser/password_manager/password_change/change_password_form_filling_submission_helper.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/password_manager/password_change/button_click_helper.h"
 #include "chrome/browser/password_manager/password_change/change_password_form_waiter.h"
+#include "chrome/browser/password_manager/password_change/model_quality_logs_uploader.h"
 #include "chrome/browser/password_manager/password_change/password_change_submission_verifier.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/optimization_guide/core/model_quality/model_execution_logging_wrappers.h"
@@ -62,8 +63,8 @@
         ModelQualityLogsUploader* logs_uploader,
         base::OnceCallback<void(bool)> callback)
     : web_contents_(web_contents),
-      callback_(std::move(callback)),
-      logs_uploader_(logs_uploader) {
+      logs_uploader_(logs_uploader),
+      callback_(std::move(callback)) {
   capture_annotated_page_content_ =
       base::BindOnce(&optimization_guide::GetAIPageContent, web_contents,
                      GetAIPageContentOptions());
@@ -78,8 +79,8 @@
             capture_annotated_page_content,
         base::OnceCallback<void(bool)> result_callback)
     : web_contents_(web_contents),
-      callback_(std::move(result_callback)),
       logs_uploader_(logs_uploader),
+      callback_(std::move(result_callback)),
       capture_annotated_page_content_(
           std::move(capture_annotated_page_content)) {}
 
@@ -244,10 +245,10 @@
     std::optional<optimization_guide::AIPageContentResult> content) {
   if (!content) {
     // Fail immediately as submit element can't be identified without `content`.
+    logs_uploader_->SetOpenFormUnexpectedFailure();
     std::move(callback_).Run(false);
     return;
   }
-
   optimization_guide::proto::PasswordChangeRequest request;
   request.set_step(optimization_guide::proto::PasswordChangeRequest::FlowStep::
                        PasswordChangeRequest_FlowStep_SUBMIT_FORM_STEP);
@@ -259,7 +260,7 @@
       request, /*execution_timeout=*/std::nullopt,
       base::BindOnce(&ChangePasswordFormFillingSubmissionHelper::
                          OnExecutionResponseCallback,
-                     weak_ptr_factory_.GetWeakPtr()));
+                     weak_ptr_factory_.GetWeakPtr(), base::Time::Now()));
 }
 
 OptimizationGuideKeyedService*
@@ -269,19 +270,22 @@
 }
 
 void ChangePasswordFormFillingSubmissionHelper::OnExecutionResponseCallback(
+    base::Time request_time,
     optimization_guide::OptimizationGuideModelExecutionResult execution_result,
     std::unique_ptr<
         optimization_guide::proto::PasswordChangeSubmissionLoggingData>
         logging_data) {
   CHECK(web_contents_);
-  if (!execution_result.response.has_value()) {
-    std::move(callback_).Run(false);
-    return;
-  }
   std::optional<optimization_guide::proto::PasswordChangeResponse> response =
-      optimization_guide::ParsedAnyMetadata<
-          optimization_guide::proto::PasswordChangeResponse>(
-          execution_result.response.value());
+      std::nullopt;
+  if (execution_result.response.has_value()) {
+    response = optimization_guide::ParsedAnyMetadata<
+        optimization_guide::proto::PasswordChangeResponse>(
+        execution_result.response.value());
+  }
+  logs_uploader_->SetSubmitFormQuality(response, std::move(logging_data),
+                                       request_time);
+
   if (!response) {
     std::move(callback_).Run(false);
     return;
@@ -318,6 +322,7 @@
 
   if (!result) {
     // Fail immediately as click failed.
+    logs_uploader_->SubmitFormTargetElementNotFound();
     std::move(callback_).Run(false);
     return;
   }
diff --git a/chrome/browser/password_manager/password_change/change_password_form_filling_submission_helper.h b/chrome/browser/password_manager/password_change/change_password_form_filling_submission_helper.h
index 201663a..d835bc4d 100644
--- a/chrome/browser/password_manager/password_change/change_password_form_filling_submission_helper.h
+++ b/chrome/browser/password_manager/password_change/change_password_form_filling_submission_helper.h
@@ -105,6 +105,7 @@
   OptimizationGuideKeyedService* GetOptimizationService();
 
   void OnExecutionResponseCallback(
+      base::Time request_time,
       optimization_guide::OptimizationGuideModelExecutionResult
           execution_result,
       std::unique_ptr<
@@ -120,13 +121,11 @@
   void OnChangePasswordFormFound(
       password_manager::PasswordFormManager* form_manager);
 
-  const raw_ptr<content::WebContents> web_contents_;
+  const raw_ptr<content::WebContents> web_contents_ = nullptr;
+  raw_ptr<ModelQualityLogsUploader> logs_uploader_ = nullptr;
 
   base::OnceCallback<void(bool)> callback_;
 
-  // Helper for uploading model logs.
-  raw_ptr<ModelQualityLogsUploader> logs_uploader_;
-
   // PasswordFormManager associated with current change password form.
   std::unique_ptr<password_manager::PasswordFormManager> form_manager_;
 
diff --git a/chrome/browser/password_manager/password_change/change_password_form_filling_submission_helper_unittest.cc b/chrome/browser/password_manager/password_change/change_password_form_filling_submission_helper_unittest.cc
index 6afd7f5..c7e0fa8 100644
--- a/chrome/browser/password_manager/password_change/change_password_form_filling_submission_helper_unittest.cc
+++ b/chrome/browser/password_manager/password_change/change_password_form_filling_submission_helper_unittest.cc
@@ -57,6 +57,8 @@
 using ::testing::WithArg;
 using PasswordChangeOutcome = ::optimization_guide::proto::
     PasswordChangeSubmissionData_PasswordChangeOutcome;
+using QualityStatus = optimization_guide::proto::
+    PasswordChangeQuality_StepQuality_SubmissionStatus;
 
 const std::u16string kUsername = u"user";
 const std::u16string kOldPassword = u"qwerty123";
@@ -170,6 +172,13 @@
                                 /*log_entry=*/nullptr));
 }
 
+void CheckSubmitFormStatus(
+    const optimization_guide::proto::LogAiDataRequest& log,
+    const QualityStatus& expected_status) {
+  EXPECT_EQ(log.password_change_submission().quality().submit_form().status(),
+            expected_status);
+}
+
 }  // namespace
 
 class ChangePasswordFormFillingSubmissionHelperTest
@@ -262,6 +271,9 @@
 
   MockStubPasswordManagerDriver& driver() { return driver_; }
   password_manager::FakeFormFetcher& form_fetcher() { return form_fetcher_; }
+  const std::unique_ptr<ModelQualityLogsUploader>& logs_uploader() const {
+    return logs_uploader_;
+  }
 
   password_manager::MockPasswordStoreInterface* profile_password_store() {
     return static_cast<password_manager::MockPasswordStoreInterface*>(
@@ -667,6 +679,11 @@
   verifier->OnPasswordFormSubmission(web_contents());
 
   EXPECT_FALSE(completion_future.Get());
+
+  CheckSubmitFormStatus(
+      logs_uploader()->GetFinalLog(),
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_ELEMENT_NOT_FOUND);
 }
 
 TEST_F(ChangePasswordFormFillingSubmissionHelperTest,
@@ -718,6 +735,11 @@
 
   // Expects that form submission succeeded.
   EXPECT_TRUE(completion_future.Get());
+
+  CheckSubmitFormStatus(
+      logs_uploader()->GetFinalLog(),
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_ACTION_SUCCESS);
 }
 
 TEST_F(ChangePasswordFormFillingSubmissionHelperTest,
@@ -754,6 +776,11 @@
   EXPECT_FALSE(verifier->submission_verifier());
 
   EXPECT_FALSE(completion_future.Get());
+
+  CheckSubmitFormStatus(
+      logs_uploader()->GetFinalLog(),
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_ELEMENT_NOT_FOUND);
 }
 
 TEST_F(ChangePasswordFormFillingSubmissionHelperTest,
diff --git a/chrome/browser/password_manager/password_change/change_password_form_finder.cc b/chrome/browser/password_manager/password_change/change_password_form_finder.cc
index 9fc218f..d747122 100644
--- a/chrome/browser/password_manager/password_change/change_password_form_finder.cc
+++ b/chrome/browser/password_manager/password_change/change_password_form_finder.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/password_manager/password_change/button_click_helper.h"
 #include "chrome/browser/password_manager/password_change/change_password_form_waiter.h"
+#include "chrome/browser/password_manager/password_change/model_quality_logs_uploader.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/optimization_guide/content/browser/page_content_proto_provider.h"
 #include "components/optimization_guide/core/model_quality/model_execution_logging_wrappers.h"
@@ -23,7 +24,7 @@
 using Logger = password_manager::BrowserSavePasswordProgressLogger;
 
 blink::mojom::AIPageContentOptionsPtr GetAIPageContentOptions() {
-  auto options = optimization_guide::ActionableAIPageContentOptions();
+  auto options = optimization_guide::DefaultAIPageContentOptions();
   // WebContents where password change is happening is hidden, and renderer
   // won't capture a snapshot unless it becomes visible again or
   // on_critical_path is set to true.
@@ -54,9 +55,11 @@
 
 ChangePasswordFormFinder::ChangePasswordFormFinder(
     content::WebContents* web_contents,
+    ModelQualityLogsUploader* logs_uploader,
     const GURL& change_password_url,
     ChangePasswordFormWaiter::PasswordFormFoundCallback callback)
     : web_contents_(web_contents),
+      logs_uploader_(logs_uploader),
       change_password_url_(change_password_url),
       callback_(std::move(callback)) {
   capture_annotated_page_content_ =
@@ -77,11 +80,13 @@
 ChangePasswordFormFinder::ChangePasswordFormFinder(
     base::PassKey<class ChangePasswordFormFinderTest>,
     content::WebContents* web_contents,
+    ModelQualityLogsUploader* logs_uploader,
     const GURL& change_password_url,
     ChangePasswordFormWaiter::PasswordFormFoundCallback callback,
     base::OnceCallback<void(optimization_guide::OnAIPageContentDone)>
         capture_annotated_page_content)
     : ChangePasswordFormFinder(web_contents,
+                               logs_uploader,
                                change_password_url,
                                std::move(callback)) {
   capture_annotated_page_content_ = std::move(capture_annotated_page_content);
@@ -121,6 +126,7 @@
     std::move(callback_).Run(nullptr);
     return;
   }
+
   optimization_guide::proto::PasswordChangeRequest request;
   request.set_step(optimization_guide::proto::PasswordChangeRequest::FlowStep::
                        PasswordChangeRequest_FlowStep_OPEN_FORM_STEP);
@@ -135,7 +141,7 @@
       optimization_guide::ModelBasedCapabilityKey::kPasswordChangeSubmission,
       request, /*execution_timeout=*/std::nullopt,
       base::BindOnce(&ChangePasswordFormFinder::OnExecutionResponseCallback,
-                     weak_ptr_factory_.GetWeakPtr()));
+                     weak_ptr_factory_.GetWeakPtr(), base::Time::Now()));
 }
 
 OptimizationGuideKeyedService*
@@ -145,6 +151,7 @@
 }
 
 void ChangePasswordFormFinder::OnExecutionResponseCallback(
+    base::Time request_time,
     optimization_guide::OptimizationGuideModelExecutionResult execution_result,
     std::unique_ptr<
         optimization_guide::proto::PasswordChangeSubmissionLoggingData>
@@ -152,17 +159,18 @@
   CHECK(web_contents_);
   CHECK(callback_);
 
-  if (!execution_result.response.has_value()) {
-    // TODO(crbug.com/407503334): Record metrics here.
-    std::move(callback_).Run(nullptr);
-    return;
-  }
   std::optional<optimization_guide::proto::PasswordChangeResponse> response =
-      optimization_guide::ParsedAnyMetadata<
-          optimization_guide::proto::PasswordChangeResponse>(
-          execution_result.response.value());
+      std::nullopt;
+  if (execution_result.response.has_value()) {
+    response = optimization_guide::ParsedAnyMetadata<
+        optimization_guide::proto::PasswordChangeResponse>(
+        execution_result.response.value());
+  }
+
+  logs_uploader_->SetOpenFormQuality(response, std::move(logging_data),
+                                     request_time);
+
   if (!response) {
-    // TODO(crbug.com/407503334): Record metrics here.
     std::move(callback_).Run(nullptr);
     return;
   }
@@ -181,7 +189,6 @@
     } else {
       std::move(callback_).Run(nullptr);
     }
-    // TODO(crbug.com/407503334): Record metrics here.
     return;
   }
 
@@ -198,7 +205,7 @@
   click_helper_.reset();
 
   if (!result) {
-    // TODO(crbug.com/407503334): Record metrics here.
+    logs_uploader_->OpenFormTargetElementNotFound();
     std::move(callback_).Run(nullptr);
     return;
   }
@@ -216,7 +223,9 @@
         Logger::STRING_PASSWORD_CHANGE_SUBSEQUENT_FORM_WAITING_RESULT,
         form_manager);
   }
-  // TODO(crbug.com/407503334): Record metrics here.
+  if (!form_manager) {
+    logs_uploader_->FormNotDetectedAfterOpening();
+  }
   CHECK(callback_);
   std::move(callback_).Run(form_manager);
 }
diff --git a/chrome/browser/password_manager/password_change/change_password_form_finder.h b/chrome/browser/password_manager/password_change/change_password_form_finder.h
index 5dbe192..70bda70 100644
--- a/chrome/browser/password_manager/password_change/change_password_form_finder.h
+++ b/chrome/browser/password_manager/password_change/change_password_form_finder.h
@@ -21,6 +21,7 @@
 }
 
 class ButtonClickHelper;
+class ModelQualityLogsUploader;
 
 // Helper class which searches for a change password form, performs actuation
 // when necessary. Invokes a callback with a form when it's found, or nullptr
@@ -32,12 +33,14 @@
 
   ChangePasswordFormFinder(
       content::WebContents* web_contents,
+      ModelQualityLogsUploader* logs_uploader,
       const GURL& change_password_url,
       ChangePasswordFormWaiter::PasswordFormFoundCallback callback);
 
   ChangePasswordFormFinder(
       base::PassKey<class ChangePasswordFormFinderTest>,
       content::WebContents* web_contents,
+      ModelQualityLogsUploader* logs_uploader,
       const GURL& change_password_url,
       ChangePasswordFormWaiter::PasswordFormFoundCallback callback,
       base::OnceCallback<void(optimization_guide::OnAIPageContentDone)>
@@ -62,6 +65,7 @@
   OptimizationGuideKeyedService* GetOptimizationService();
 
   void OnExecutionResponseCallback(
+      base::Time request_time,
       optimization_guide::OptimizationGuideModelExecutionResult
           execution_result,
       std::unique_ptr<
@@ -79,10 +83,12 @@
       password_manager::PasswordFormManager* form_manager);
   void OnFormNotFound();
 
-  const raw_ptr<content::WebContents> web_contents_;
+  const raw_ptr<content::WebContents> web_contents_ = nullptr;
+  raw_ptr<ModelQualityLogsUploader> logs_uploader_ = nullptr;
   const GURL change_password_url_;
 
   ChangePasswordFormWaiter::PasswordFormFoundCallback callback_;
+
   base::OnceCallback<void(optimization_guide::OnAIPageContentDone)>
       capture_annotated_page_content_;
 
diff --git a/chrome/browser/password_manager/password_change/change_password_form_finder_unittest.cc b/chrome/browser/password_manager/password_change/change_password_form_finder_unittest.cc
index b18a41c..47fca67 100644
--- a/chrome/browser/password_manager/password_change/change_password_form_finder_unittest.cc
+++ b/chrome/browser/password_manager/password_change/change_password_form_finder_unittest.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/password_manager/account_password_store_factory.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/password_manager/password_change/button_click_helper.h"
+#include "chrome/browser/password_manager/password_change/model_quality_logs_uploader.h"
 #include "chrome/browser/password_manager/profile_password_store_factory.h"
 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
@@ -42,6 +43,9 @@
 using testing::Return;
 using testing::WithArg;
 
+using QualityStatus = optimization_guide::proto::
+    PasswordChangeQuality_StepQuality_SubmissionStatus;
+
 const char kUrlString[] = "https://www.foo.com/";
 
 class FakeChromePasswordManagerClient : public ChromePasswordManagerClient {
@@ -85,6 +89,12 @@
                                 /*log_entry=*/nullptr));
 }
 
+void CheckOpenFormStatus(const optimization_guide::proto::LogAiDataRequest& log,
+                         const QualityStatus& expected_status) {
+  EXPECT_EQ(log.password_change_submission().quality().open_form().status(),
+            expected_status);
+}
+
 }  // namespace
 
 class ChangePasswordFormFinderTest : public ChromeRenderViewHostTestHarness {
@@ -167,15 +177,15 @@
 
 TEST_F(ChangePasswordFormFinderTest, PasswordChangeFormFound) {
   auto form_manager = CreateFormManager();
-
+  ModelQualityLogsUploader logs_uploader(web_contents());
   base::MockOnceCallback<void(password_manager::PasswordFormManager*)>
       completion_callback;
   base::MockCallback<
       base::OnceCallback<void(optimization_guide::OnAIPageContentDone)>>
       capture_annotated_page_content;
   ChangePasswordFormFinder form_waiter(
-      pass_key(), web_contents(), GURL(kUrlString), completion_callback.Get(),
-      capture_annotated_page_content.Get());
+      pass_key(), web_contents(), &logs_uploader, GURL(kUrlString),
+      completion_callback.Get(), capture_annotated_page_content.Get());
 
   ASSERT_TRUE(form_waiter.form_waiter());
   EXPECT_CALL(capture_annotated_page_content, Run).Times(0);
@@ -191,9 +201,10 @@
   base::MockCallback<
       base::OnceCallback<void(optimization_guide::OnAIPageContentDone)>>
       capture_annotated_page_content;
+  ModelQualityLogsUploader logs_uploader(web_contents());
   auto form_finder = std::make_unique<ChangePasswordFormFinder>(
-      pass_key(), web_contents(), GURL(kUrlString), completion_callback.Get(),
-      capture_annotated_page_content.Get());
+      pass_key(), web_contents(), &logs_uploader, GURL(kUrlString),
+      completion_callback.Get(), capture_annotated_page_content.Get());
 
   ASSERT_TRUE(form_finder->form_waiter());
   static_cast<content::WebContentsObserver*>(form_finder->form_waiter())
@@ -209,6 +220,11 @@
   EXPECT_CALL(completion_callback, Run(nullptr));
   task_environment()->FastForwardBy(
       ChangePasswordFormWaiter::kChangePasswordFormWaitingTimeout);
+
+  CheckOpenFormStatus(
+      logs_uploader.GetFinalLog(),
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_ELEMENT_NOT_FOUND);
 }
 
 TEST_F(ChangePasswordFormFinderTest, ExecuteModelOpenFormRequestHasArgs) {
@@ -217,9 +233,10 @@
   base::MockCallback<
       base::OnceCallback<void(optimization_guide::OnAIPageContentDone)>>
       capture_annotated_page_content;
+  ModelQualityLogsUploader logs_uploader(web_contents());
   auto form_finder = std::make_unique<ChangePasswordFormFinder>(
-      pass_key(), web_contents(), GURL(kUrlString), completion_callback.Get(),
-      capture_annotated_page_content.Get());
+      pass_key(), web_contents(), &logs_uploader, GURL(kUrlString),
+      completion_callback.Get(), capture_annotated_page_content.Get());
 
   GURL test_url("https://example.com/change-password");
   std::u16string test_title = u"Change Your Password";
@@ -256,6 +273,11 @@
           optimization_guide::AIPageContentResult()));
   task_environment()->FastForwardBy(
       ChangePasswordFormWaiter::kChangePasswordFormWaitingTimeout);
+
+  CheckOpenFormStatus(
+      logs_uploader.GetFinalLog(),
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_ACTION_SUCCESS);
 }
 
 TEST_F(ChangePasswordFormFinderTest, ButtonClickRequestedButFailed) {
@@ -264,9 +286,10 @@
   base::MockCallback<
       base::OnceCallback<void(optimization_guide::OnAIPageContentDone)>>
       capture_annotated_page_content;
+  ModelQualityLogsUploader logs_uploader(web_contents());
   auto form_finder = std::make_unique<ChangePasswordFormFinder>(
-      pass_key(), web_contents(), GURL(kUrlString), completion_callback.Get(),
-      capture_annotated_page_content.Get());
+      pass_key(), web_contents(), &logs_uploader, GURL(kUrlString),
+      completion_callback.Get(), capture_annotated_page_content.Get());
 
   ASSERT_TRUE(form_finder->form_waiter());
   static_cast<content::WebContentsObserver*>(form_finder->form_waiter())
@@ -288,6 +311,11 @@
 
   EXPECT_CALL(completion_callback, Run(nullptr));
   form_finder->click_helper()->SimulateClickResult(/*result=*/false);
+
+  CheckOpenFormStatus(
+      logs_uploader.GetFinalLog(),
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_ELEMENT_NOT_FOUND);
 }
 
 TEST_F(ChangePasswordFormFinderTest, ButtonClickRequestedAndSucceeded) {
@@ -296,9 +324,10 @@
   base::MockCallback<
       base::OnceCallback<void(optimization_guide::OnAIPageContentDone)>>
       capture_annotated_page_content;
+  ModelQualityLogsUploader logs_uploader(web_contents());
   auto form_finder = std::make_unique<ChangePasswordFormFinder>(
-      pass_key(), web_contents(), GURL(kUrlString), completion_callback.Get(),
-      capture_annotated_page_content.Get());
+      pass_key(), web_contents(), &logs_uploader, GURL(kUrlString),
+      completion_callback.Get(), capture_annotated_page_content.Get());
 
   ASSERT_TRUE(form_finder->form_waiter());
   static_cast<content::WebContentsObserver*>(form_finder->form_waiter())
@@ -329,6 +358,11 @@
   static_cast<password_manager::PasswordFormManagerObserver*>(
       form_finder->form_waiter())
       ->OnPasswordFormParsed(form_manager.get());
+
+  CheckOpenFormStatus(
+      logs_uploader.GetFinalLog(),
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_ACTION_SUCCESS);
 }
 
 TEST_F(ChangePasswordFormFinderTest, ExecuteModelPredictsLoginPage) {
@@ -337,9 +371,10 @@
   base::MockCallback<
       base::OnceCallback<void(optimization_guide::OnAIPageContentDone)>>
       capture_annotated_page_content;
+  ModelQualityLogsUploader logs_uploader(web_contents());
   auto form_finder = std::make_unique<ChangePasswordFormFinder>(
-      pass_key(), web_contents(), GURL(kUrlString), completion_callback.Get(),
-      capture_annotated_page_content.Get());
+      pass_key(), web_contents(), &logs_uploader, GURL(kUrlString),
+      completion_callback.Get(), capture_annotated_page_content.Get());
 
   ASSERT_TRUE(form_finder->form_waiter());
   static_cast<content::WebContentsObserver*>(form_finder->form_waiter())
@@ -373,4 +408,9 @@
 
   EXPECT_EQ(GURL(kUrlString), web_contents()->GetURL());
   EXPECT_TRUE(form_finder->form_waiter());
+
+  CheckOpenFormStatus(
+      logs_uploader.GetFinalLog(),
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_UNEXPECTED_STATE);
 }
diff --git a/chrome/browser/password_manager/password_change/model_quality_logs_uploader.cc b/chrome/browser/password_manager/password_change/model_quality_logs_uploader.cc
index 0750776f..6f78ff02 100644
--- a/chrome/browser/password_manager/password_change/model_quality_logs_uploader.cc
+++ b/chrome/browser/password_manager/password_change/model_quality_logs_uploader.cc
@@ -120,26 +120,33 @@
 }
 
 void ModelQualityLogsUploader::SetOpenFormQuality(
-    const optimization_guide::proto::PasswordChangeResponse& response,
+    const std::optional<optimization_guide::proto::PasswordChangeResponse>&
+        response,
     std::unique_ptr<LoggingData> logging_data,
     base::Time server_request_start_time) {
-  PageType open_form = response.open_form_data().page_type();
-  QualityStatus quality_status;
+  if (!logging_data) {
+    return;
+  }
 
-  if (open_form == PageType::OpenFormResponseData_PageType_SETTINGS_PAGE) {
-    if (response.open_form_data().dom_node_id_to_click()) {
-      // Assume success at this point, if fails to actuate on it the state
-      // will be changed to ELEMENT_NOT_FOUND if the element does not exist
-      // or FORM_NOT_FOUND if after clicking a form was not seen.
-      quality_status = QualityStatus::
-          PasswordChangeQuality_StepQuality_SubmissionStatus_ACTION_SUCCESS;
+  QualityStatus quality_status = QualityStatus::
+      PasswordChangeQuality_StepQuality_SubmissionStatus_UNKNOWN_STATUS;
+  if (response.has_value()) {
+    PageType open_form = response.value().open_form_data().page_type();
+    if (open_form == PageType::OpenFormResponseData_PageType_SETTINGS_PAGE) {
+      if (response.value().open_form_data().dom_node_id_to_click()) {
+        // Assume success at this point. If it fails to actuate on it the state
+        // will be changed to ELEMENT_NOT_FOUND if the element does not exist
+        // or FORM_NOT_FOUND if after clicking a form was not seen.
+        quality_status = QualityStatus::
+            PasswordChangeQuality_StepQuality_SubmissionStatus_ACTION_SUCCESS;
+      } else {
+        quality_status = QualityStatus::
+            PasswordChangeQuality_StepQuality_SubmissionStatus_ELEMENT_NOT_FOUND;
+      }
     } else {
       quality_status = QualityStatus::
-          PasswordChangeQuality_StepQuality_SubmissionStatus_ELEMENT_NOT_FOUND;
+          PasswordChangeQuality_StepQuality_SubmissionStatus_UNEXPECTED_STATE;
     }
-  } else {
-    quality_status = QualityStatus::
-        PasswordChangeQuality_StepQuality_SubmissionStatus_UNEXPECTED_STATE;
   }
 
   final_log_data_.mutable_password_change_submission()->MergeFrom(
@@ -165,6 +172,15 @@
               PasswordChangeQuality_StepQuality_SubmissionStatus_FORM_NOT_FOUND);
 }
 
+void ModelQualityLogsUploader::SetOpenFormUnexpectedFailure() {
+  final_log_data_.mutable_password_change_submission()
+      ->mutable_quality()
+      ->mutable_open_form()
+      ->set_status(
+          QualityStatus::
+              PasswordChangeQuality_StepQuality_SubmissionStatus_UNEXPECTED_STATE);
+}
+
 void ModelQualityLogsUploader::OpenFormTargetElementNotFound() {
   final_log_data_.mutable_password_change_submission()
       ->mutable_quality()
@@ -174,17 +190,33 @@
               PasswordChangeQuality_StepQuality_SubmissionStatus_ELEMENT_NOT_FOUND);
 }
 
+void ModelQualityLogsUploader::SubmitFormTargetElementNotFound() {
+  final_log_data_.mutable_password_change_submission()
+      ->mutable_quality()
+      ->mutable_submit_form()
+      ->set_status(
+          QualityStatus::
+              PasswordChangeQuality_StepQuality_SubmissionStatus_ELEMENT_NOT_FOUND);
+}
+
 void ModelQualityLogsUploader::SetSubmitFormQuality(
-    const optimization_guide::proto::PasswordChangeResponse& response,
+    const std::optional<optimization_guide::proto::PasswordChangeResponse>&
+        response,
     std::unique_ptr<LoggingData> logging_data,
     base::Time server_request_start_time) {
-  QualityStatus quality_status;
-  if (response.submit_form_data().dom_node_id_to_click()) {
-    quality_status = QualityStatus::
-        PasswordChangeQuality_StepQuality_SubmissionStatus_ACTION_SUCCESS;
-  } else {
-    quality_status = QualityStatus::
-        PasswordChangeQuality_StepQuality_SubmissionStatus_ELEMENT_NOT_FOUND;
+  if (!logging_data) {
+    return;
+  }
+  QualityStatus quality_status = QualityStatus::
+      PasswordChangeQuality_StepQuality_SubmissionStatus_UNKNOWN_STATUS;
+  if (response.has_value()) {
+    if (response.value().submit_form_data().dom_node_id_to_click()) {
+      quality_status = QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_ACTION_SUCCESS;
+    } else {
+      quality_status = QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_ELEMENT_NOT_FOUND;
+    }
   }
 
   final_log_data_.mutable_password_change_submission()->MergeFrom(
@@ -206,6 +238,9 @@
         response,
     std::unique_ptr<LoggingData> logging_data,
     base::Time server_request_start_time) {
+  if (!logging_data) {
+    return;
+  }
   FinalModelStatus final_model_status = GetFinalModelStatus(response);
   QualityStatus quality_status = GetVerifySubmissionQualityStatus(response);
 
diff --git a/chrome/browser/password_manager/password_change/model_quality_logs_uploader.h b/chrome/browser/password_manager/password_change/model_quality_logs_uploader.h
index 7b7e7ce..780c730 100644
--- a/chrome/browser/password_manager/password_change/model_quality_logs_uploader.h
+++ b/chrome/browser/password_manager/password_change/model_quality_logs_uploader.h
@@ -34,7 +34,8 @@
 
   // Sets quality data for Step=OPEN_FORM_STEP.
   void SetOpenFormQuality(
-      const optimization_guide::proto::PasswordChangeResponse& response,
+      const std::optional<optimization_guide::proto::PasswordChangeResponse>&
+          response,
       std::unique_ptr<LoggingData> logging_data,
       base::Time server_request_start_time);
 
@@ -42,13 +43,22 @@
   // Step=OPEN_FORM_STEP.
   void FormNotDetectedAfterOpening();
 
+  // To be called if there is an expected failure
+  // in Step=OPEN_FORM_STEP (e.g. Page Content is unavailable).
+  void SetOpenFormUnexpectedFailure();
+
   // To be called if element to click was not found
   // in Step=OPEN_FORM_STEP.
   void OpenFormTargetElementNotFound();
 
+  // To be called if element to click was not found
+  // in Step=OPEN_FORM_STEP.
+  void SubmitFormTargetElementNotFound();
+
   // Sets quality data for Step=SUBMIT_FORM_STEP.
   void SetSubmitFormQuality(
-      const optimization_guide::proto::PasswordChangeResponse& response,
+      const std::optional<optimization_guide::proto::PasswordChangeResponse>&
+          response,
       std::unique_ptr<LoggingData> logging_data,
       base::Time server_request_start_time);
 
diff --git a/chrome/browser/password_manager/password_change/model_quality_logs_uploader_unittest.cc b/chrome/browser/password_manager/password_change/model_quality_logs_uploader_unittest.cc
index 313ec29..807fa07 100644
--- a/chrome/browser/password_manager/password_change/model_quality_logs_uploader_unittest.cc
+++ b/chrome/browser/password_manager/password_change/model_quality_logs_uploader_unittest.cc
@@ -144,7 +144,7 @@
   response.mutable_open_form_data()->set_page_type(
       PageType::OpenFormResponseData_PageType_SETTINGS_PAGE);
   response.mutable_open_form_data()->set_dom_node_id_to_click(123);
-  logs_uploader.SetOpenFormQuality(response, CreateLoggingData(),
+  logs_uploader.SetOpenFormQuality(std::optional(response), CreateLoggingData(),
                                    fake_start_time);
 
   CheckOpenFormStatus(
@@ -159,7 +159,7 @@
   optimization_guide::proto::PasswordChangeResponse response;
   response.mutable_open_form_data()->set_page_type(
       PageType::OpenFormResponseData_PageType_SETTINGS_PAGE);
-  logs_uploader.SetOpenFormQuality(response, CreateLoggingData(),
+  logs_uploader.SetOpenFormQuality(std::optional(response), CreateLoggingData(),
                                    fake_start_time);
   CheckOpenFormStatus(
       logs_uploader.GetFinalLog(),
@@ -173,7 +173,7 @@
   optimization_guide::proto::PasswordChangeResponse response;
   response.mutable_open_form_data()->set_page_type(
       PageType::OpenFormResponseData_PageType_LOG_IN_PAGE);
-  logs_uploader.SetOpenFormQuality(response, CreateLoggingData(),
+  logs_uploader.SetOpenFormQuality(std::optional(response), CreateLoggingData(),
                                    fake_start_time);
   CheckOpenFormStatus(
       logs_uploader.GetFinalLog(),
@@ -186,8 +186,8 @@
   ModelQualityLogsUploader logs_uploader(web_contents());
   optimization_guide::proto::PasswordChangeResponse response;
   response.mutable_submit_form_data()->set_dom_node_id_to_click(123);
-  logs_uploader.SetSubmitFormQuality(response, CreateLoggingData(),
-                                     fake_start_time);
+  logs_uploader.SetSubmitFormQuality(std::optional(response),
+                                     CreateLoggingData(), fake_start_time);
   CheckSubmitFormStatus(
       logs_uploader.GetFinalLog(),
       QualityStatus::
@@ -198,8 +198,8 @@
   const base::Time fake_start_time = base::Time::Now();
   ModelQualityLogsUploader logs_uploader(web_contents());
   optimization_guide::proto::PasswordChangeResponse response;
-  logs_uploader.SetSubmitFormQuality(response, CreateLoggingData(),
-                                     fake_start_time);
+  logs_uploader.SetSubmitFormQuality(std::optional(response),
+                                     CreateLoggingData(), fake_start_time);
   CheckSubmitFormStatus(
       logs_uploader.GetFinalLog(),
       QualityStatus::
@@ -317,6 +317,31 @@
           PasswordChangeQuality_StepQuality_SubmissionStatus_ELEMENT_NOT_FOUND);
 }
 
+TEST_F(ModelQualityLogsUploaderTest, SubmitFormTargetElementNotFound) {
+  const base::Time fake_start_time = base::Time::Now();
+  ModelQualityLogsUploader logs_uploader(web_contents());
+  // Set initial submit form data for ACTION_SUCCESS status.
+  optimization_guide::proto::PasswordChangeResponse submit_form_response;
+  submit_form_response.mutable_submit_form_data()->set_dom_node_id_to_click(-5);
+  logs_uploader.SetSubmitFormQuality(submit_form_response, CreateLoggingData(),
+                                     fake_start_time);
+  const optimization_guide::proto::LogAiDataRequest intial_log =
+      logs_uploader.GetFinalLog();
+  CheckSubmitFormStatus(
+      intial_log,
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_ACTION_SUCCESS);
+
+  // Call function that overwrites the status to ELEMENT_NOT_FOUND status.
+  logs_uploader.SubmitFormTargetElementNotFound();
+  const optimization_guide::proto::LogAiDataRequest final_log =
+      logs_uploader.GetFinalLog();
+  CheckSubmitFormStatus(
+      final_log,
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_ELEMENT_NOT_FOUND);
+}
+
 TEST_F(ModelQualityLogsUploaderTest, FormNotDetectedAfterOpening) {
   const base::Time fake_start_time = base::Time::Now();
   ModelQualityLogsUploader logs_uploader(web_contents());
@@ -344,6 +369,33 @@
           PasswordChangeQuality_StepQuality_SubmissionStatus_FORM_NOT_FOUND);
 }
 
+TEST_F(ModelQualityLogsUploaderTest, OpenFormUnexpectedFailure) {
+  const base::Time fake_start_time = base::Time::Now();
+  ModelQualityLogsUploader logs_uploader(web_contents());
+  // Set initial open form data for ACTION_SUCCESS status.
+  optimization_guide::proto::PasswordChangeResponse open_form_response;
+  open_form_response.mutable_open_form_data()->set_page_type(
+      PageType::OpenFormResponseData_PageType_SETTINGS_PAGE);
+  open_form_response.mutable_open_form_data()->set_dom_node_id_to_click(123);
+  logs_uploader.SetOpenFormQuality(open_form_response, CreateLoggingData(),
+                                   fake_start_time);
+  const optimization_guide::proto::LogAiDataRequest intial_log =
+      logs_uploader.GetFinalLog();
+  CheckOpenFormStatus(
+      intial_log,
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_ACTION_SUCCESS);
+
+  // Call function that overwrites the status to UNEXPECTED_STATE status.
+  logs_uploader.SetOpenFormUnexpectedFailure();
+  const optimization_guide::proto::LogAiDataRequest final_log =
+      logs_uploader.GetFinalLog();
+  CheckOpenFormStatus(
+      final_log,
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_UNEXPECTED_STATE);
+}
+
 TEST_F(ModelQualityLogsUploaderTest, LogGeneralInformationSetOnCreation) {
   const GURL url("http://www.url.com");
   NavigateAndCommit(url);
diff --git a/chrome/browser/password_manager/password_change/password_change_submission_verifier.cc b/chrome/browser/password_manager/password_change/password_change_submission_verifier.cc
index ab9371c..3e409a05 100644
--- a/chrome/browser/password_manager/password_change/password_change_submission_verifier.cc
+++ b/chrome/browser/password_manager/password_change/password_change_submission_verifier.cc
@@ -195,11 +195,8 @@
     }
   }
 
-  if (logging_data) {
-    logs_uploader_->SetVerifySubmissionQuality(
-        response, std::move(logging_data), request_time);
-  }
-
+  logs_uploader_->SetVerifySubmissionQuality(response, std::move(logging_data),
+                                             request_time);
   if (!response) {
     // Password change failed as the response was empty or
     // unable to be parsed.
diff --git a/chrome/browser/password_manager/password_change_browsertest.cc b/chrome/browser/password_manager/password_change_browsertest.cc
index 8a65965..ac82141e 100644
--- a/chrome/browser/password_manager/password_change_browsertest.cc
+++ b/chrome/browser/password_manager/password_change_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/test/gmock_callback_support.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/run_until.h"
+#include "base/test/test_future.h"
 #include "chrome/browser/affiliations/affiliation_service_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h"
@@ -152,8 +153,10 @@
     ASSERT_TRUE(observer.Wait());
   }
 
-  void VerifyUniqueQualityLog(FinalModelStatus final_status,
-                              QualityStatus quality_status) {
+  void VerifyUniqueQualityLog(QualityStatus open_form_status,
+                              QualityStatus submit_form_status,
+                              QualityStatus verify_submission_status,
+                              FinalModelStatus final_status) {
     const std::vector<
         std::unique_ptr<optimization_guide::proto::LogAiDataRequest>>& logs =
         logs_uploader().uploaded_logs();
@@ -168,7 +171,19 @@
                   ->mutable_quality()
                   ->verify_submission()
                   .status(),
-              quality_status);
+              verify_submission_status);
+    EXPECT_EQ(logs[0]
+                  ->mutable_password_change_submission()
+                  ->mutable_quality()
+                  ->open_form()
+                  .status(),
+              open_form_status);
+    EXPECT_EQ(logs[0]
+                  ->mutable_password_change_submission()
+                  ->mutable_quality()
+                  ->submit_form()
+                  .status(),
+              submit_form_status);
   }
 
   void SetPrivacyNoticeAcceptedPref() {
@@ -393,8 +408,8 @@
   delegate->StartPasswordChangeFlow();
 
   // Start observing web_contents where password change happens.
-  SetWebContents(
-      static_cast<PasswordChangeDelegateImpl*>(delegate)->executor());
+  auto* delegate_impl = static_cast<PasswordChangeDelegateImpl*>(delegate);
+  SetWebContents(delegate_impl->executor());
   PasswordsNavigationObserver observer(WebContents());
   EXPECT_TRUE(observer.Wait());
   WaitForElementValue("password", "pa$$word");
@@ -402,7 +417,7 @@
   // Verify generated password is pre-saved.
   WaitForPasswordStore();
   std::string generated_password =
-      base::UTF16ToUTF8(delegate->GetGeneratedPassword());
+      base::UTF16ToUTF8(delegate_impl->generated_password());
   EXPECT_EQ(generated_password,
             GetElementValue(/*iframe_id=*/"null", "new_password_1"));
   CheckThatCredentialsStored(
@@ -439,30 +454,30 @@
 
   password_change_service()->OfferPasswordChangeUi(main_url, u"test",
                                                    u"pa$$word", WebContents());
-  password_change_service()
-      ->GetPasswordChangeDelegate(WebContents())
-      ->StartPasswordChangeFlow();
+  PasswordChangeDelegate* delegate =
+      password_change_service()->GetPasswordChangeDelegate(WebContents());
+  delegate->StartPasswordChangeFlow();
   MockPasswordChangeOutcome(
       PasswordChangeOutcome::
           PasswordChangeSubmissionData_PasswordChangeOutcome_SUCCESSFUL_OUTCOME);
 
-  base::WeakPtr<PasswordChangeDelegate> delegate =
-      password_change_service()
-          ->GetPasswordChangeDelegate(WebContents())
-          ->AsWeakPtr();
   EXPECT_TRUE(base::test::RunUntil([delegate]() {
     return delegate->GetCurrentState() ==
            PasswordChangeDelegate::State::kPasswordSuccessfullyChanged;
   }));
   CheckThatCredentialsStored(
-      /*username=*/"test", base::UTF16ToUTF8(delegate->GetGeneratedPassword()),
+      /*username=*/"test",
+      base::UTF16ToUTF8(static_cast<PasswordChangeDelegateImpl*>(delegate)
+                            ->generated_password()),
       "pa$$word", password_manager::PasswordForm::Type::kChangeSubmission);
 
-  delegate->Stop();
-  EXPECT_TRUE(base::test::RunUntil([&delegate]() {
+  base::WeakPtr<PasswordChangeDelegate> delegate_weak_ptr =
+      delegate->AsWeakPtr();
+  delegate_weak_ptr->Stop();
+  EXPECT_TRUE(base::test::RunUntil([&delegate_weak_ptr]() {
     // Delegate's destructor is called async, so this is needed before checking
     // the metrics report.
-    return delegate == nullptr;
+    return delegate_weak_ptr == nullptr;
   }));
   histogram_tester.ExpectUniqueSample(
       PasswordChangeDelegateImpl::kFinalPasswordChangeStatusHistogram,
@@ -490,9 +505,17 @@
           kPasswordChangeSubmissionOutcomeName,
       static_cast<int>(SubmissionOutcome::kSuccess));
   VerifyUniqueQualityLog(
-      FinalModelStatus::FINAL_MODEL_STATUS_SUCCESS,
+      /*open_form_status=*/
       QualityStatus::
-          PasswordChangeQuality_StepQuality_SubmissionStatus_ACTION_SUCCESS);
+          PasswordChangeQuality_StepQuality_SubmissionStatus_UNKNOWN_STATUS,
+      /* submit_form_status=*/
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_UNKNOWN_STATUS,
+      /*verify_submission_status=*/
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_ACTION_SUCCESS,
+      /*final_status=*/
+      FinalModelStatus::FINAL_MODEL_STATUS_SUCCESS);
 }
 
 IN_PROC_BROWSER_TEST_F(PasswordChangeBrowserTest, OldPasswordIsUpdated) {
@@ -533,7 +556,8 @@
   WaitForPasswordStore();
   CheckThatCredentialsStored(
       base::UTF16ToUTF8(form.username_value),
-      base::UTF16ToUTF8(delegate->GetGeneratedPassword()),
+      base::UTF16ToUTF8(static_cast<PasswordChangeDelegateImpl*>(delegate)
+                            ->generated_password()),
       base::UTF16ToUTF8(form.password_value),
       password_manager::PasswordForm::Type::kChangeSubmission);
 }
@@ -647,7 +671,11 @@
   WaitForPasswordStore();
   CheckThatCredentialsStored(
       /*username=*/"test", "pa$$word",
-      base::UTF16ToUTF8(delegate->GetGeneratedPassword()));
+      base::UTF16ToUTF8(
+          static_cast<PasswordChangeDelegateImpl*>(
+              password_change_service()->GetPasswordChangeDelegate(
+                  WebContents()))
+              ->generated_password()));
 
   delegate->Stop();
   EXPECT_TRUE(base::test::RunUntil([&delegate]() {
@@ -670,9 +698,17 @@
           kPasswordChangeSubmissionOutcomeName,
       static_cast<int>(SubmissionOutcome::kPageError));
   VerifyUniqueQualityLog(
-      FinalModelStatus::FINAL_MODEL_STATUS_FAILURE,
+      /*open_form_status=*/
       QualityStatus::
-          PasswordChangeQuality_StepQuality_SubmissionStatus_FAILURE_STATUS);
+          PasswordChangeQuality_StepQuality_SubmissionStatus_UNKNOWN_STATUS,
+      /* submit_form_status=*/
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_UNKNOWN_STATUS,
+      /*verify_submission_status=*/
+      QualityStatus::
+          PasswordChangeQuality_StepQuality_SubmissionStatus_FAILURE_STATUS,
+      /*final_status=*/
+      FinalModelStatus::FINAL_MODEL_STATUS_FAILURE);
 }
 
 IN_PROC_BROWSER_TEST_F(PasswordChangeBrowserTest, OpenTabWithPasswordChange) {
diff --git a/chrome/browser/password_manager/password_change_delegate.h b/chrome/browser/password_manager/password_change_delegate.h
index f9700a1..7c31b0d 100644
--- a/chrome/browser/password_manager/password_change_delegate.h
+++ b/chrome/browser/password_manager/password_change_delegate.h
@@ -5,8 +5,6 @@
 #ifndef CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_CHANGE_DELEGATE_H_
 #define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_CHANGE_DELEGATE_H_
 
-#include <string>
-
 #include "base/observer_list_types.h"
 
 namespace content {
@@ -107,12 +105,6 @@
   virtual void AddObserver(Observer* observer) = 0;
   virtual void RemoveObserver(Observer* observer) = 0;
 
-  // Getters for current domain where password change is ongoing, username and a
-  // newly generated password. Password exists only after it was generated.
-  virtual std::u16string GetDisplayOrigin() const = 0;
-  virtual const std::u16string& GetUsername() const = 0;
-  virtual const std::u16string& GetGeneratedPassword() const = 0;
-
   virtual base::WeakPtr<PasswordChangeDelegate> AsWeakPtr() = 0;
 };
 
diff --git a/chrome/browser/password_manager/password_change_delegate_impl.cc b/chrome/browser/password_manager/password_change_delegate_impl.cc
index a07ab3d..ec7cc31 100644
--- a/chrome/browser/password_manager/password_change_delegate_impl.cc
+++ b/chrome/browser/password_manager/password_change_delegate_impl.cc
@@ -218,6 +218,9 @@
 }
 
 PasswordChangeDelegateImpl::~PasswordChangeDelegateImpl() {
+  if (logs_uploader_) {
+    logs_uploader_->UploadFinalLog();
+  }
   base::UmaHistogramEnumeration(kFinalPasswordChangeStatusHistogram,
                                 current_state_);
   if (auto logger = GetLoggerIfAvailable(executor_.get())) {
@@ -237,7 +240,7 @@
   CHECK(executor_);
   logs_uploader_ = std::make_unique<ModelQualityLogsUploader>(executor_.get());
   form_finder_ = std::make_unique<ChangePasswordFormFinder>(
-      executor_.get(), change_password_url_,
+      executor_.get(), logs_uploader_.get(), change_password_url_,
       base::BindOnce(&PasswordChangeDelegateImpl::OnPasswordChangeFormFound,
                      weak_ptr_factory_.GetWeakPtr()));
 }
@@ -361,7 +364,7 @@
 
   if (last_committed_url_ == originator_->GetLastCommittedURL()) {
     ManagePasswordsUIController::FromWebContents(originator_)
-        ->ShowChangePasswordBubble();
+        ->ShowChangePasswordBubble(username_, generated_password_);
   } else {
     NavigateToPasswordDetailsPage(
         chrome::FindBrowserWithTab(originator_),
@@ -378,21 +381,6 @@
   observers_.RemoveObserver(observer);
 }
 
-std::u16string PasswordChangeDelegateImpl::GetDisplayOrigin() const {
-  GURL url = submission_verifier_ ? submission_verifier_->GetURL()
-                                  : change_password_url_;
-  return url_formatter::FormatUrlForSecurityDisplay(
-      url, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
-}
-
-const std::u16string& PasswordChangeDelegateImpl::GetUsername() const {
-  return username_;
-}
-
-const std::u16string& PasswordChangeDelegateImpl::GetGeneratedPassword() const {
-  return generated_password_;
-}
-
 void PasswordChangeDelegateImpl::OnPrivacyNoticeAccepted() {
   // Enable via the Optimization Guide's pref.
   profile_->GetPrefs()->SetInteger(
@@ -453,8 +441,6 @@
     MaybeLaunchSurvey(kHatsSurveyTriggerPasswordChangeSuccess,
                       password_change_duration_overall, profile_, originator_);
   }
-  // TODO(crbug.com/407503334): Upload final log on destructor.
-  logs_uploader_->UploadFinalLog();
   submission_verifier_.reset();
 }
 
@@ -467,6 +453,13 @@
                  kPasswordChangeSubmission);
 }
 
+std::u16string PasswordChangeDelegateImpl::GetDisplayOrigin() const {
+  GURL url = submission_verifier_ ? submission_verifier_->GetURL()
+                                  : change_password_url_;
+  return url_formatter::FormatUrlForSecurityDisplay(
+      url, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
+}
+
 base::WeakPtr<PasswordChangeDelegate> PasswordChangeDelegateImpl::AsWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
 }
diff --git a/chrome/browser/password_manager/password_change_delegate_impl.h b/chrome/browser/password_manager/password_change_delegate_impl.h
index 065ab9c2..ab8b6dd 100644
--- a/chrome/browser/password_manager/password_change_delegate_impl.h
+++ b/chrome/browser/password_manager/password_change_delegate_impl.h
@@ -56,6 +56,7 @@
   ChangePasswordFormFinder* form_finder() { return form_finder_.get(); }
   content::WebContents* executor() { return executor_.get(); }
   PasswordChangeUIController* ui_controller() { return ui_controller_.get(); }
+  std::u16string generated_password() { return generated_password_; }
   void SetCustomUIController(
       std::unique_ptr<PasswordChangeUIController> controller) {
     ui_controller_ = std::move(controller);
@@ -77,9 +78,6 @@
   void OnPasswordChangeDeclined() override;
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
-  std::u16string GetDisplayOrigin() const override;
-  const std::u16string& GetUsername() const override;
-  const std::u16string& GetGeneratedPassword() const override;
 
   void OnTabWillDetach(tabs::TabInterface* tab_interface,
                        tabs::TabInterface::DetachReason reason);
@@ -94,6 +92,8 @@
 
   bool IsPrivacyNoticeAcknowledged() const;
 
+  std::u16string GetDisplayOrigin() const;
+
   const GURL change_password_url_;
   const std::u16string username_;
   const std::u16string original_password_;
diff --git a/chrome/browser/password_manager/password_change_delegate_mock.h b/chrome/browser/password_manager/password_change_delegate_mock.h
index 17993a1..2e673be 100644
--- a/chrome/browser/password_manager/password_change_delegate_mock.h
+++ b/chrome/browser/password_manager/password_change_delegate_mock.h
@@ -42,12 +42,6 @@
   MOCK_METHOD(void, OnPasswordChangeDeclined, (), (override));
   MOCK_METHOD(void, AddObserver, (Observer*), (override));
   MOCK_METHOD(void, RemoveObserver, (Observer*), (override));
-  MOCK_METHOD(std::u16string, GetDisplayOrigin, (), (const override));
-  MOCK_METHOD(const std::u16string&, GetUsername, (), (const override));
-  MOCK_METHOD(const std::u16string&,
-              GetGeneratedPassword,
-              (),
-              (const override));
 
   base::WeakPtr<PasswordChangeDelegate> AsWeakPtr() override;
 
diff --git a/chrome/browser/permissions/permission_request_manager_browsertest.cc b/chrome/browser/permissions/permission_request_manager_browsertest.cc
index 21dabf2..ee0573d 100644
--- a/chrome/browser/permissions/permission_request_manager_browsertest.cc
+++ b/chrome/browser/permissions/permission_request_manager_browsertest.cc
@@ -62,7 +62,7 @@
 
 namespace {
 using PredictionGrantLikelihood =
-    permissions::PermissionUmaUtil::PredictionGrantLikelihood;
+    permissions::PermissionUiSelector::PredictionGrantLikelihood;
 using ::permissions::PermissionRequestRelevance;
 using ::testing::Optional;
 
diff --git a/chrome/browser/permissions/prediction_service/prediction_based_permission_ui_selector.cc b/chrome/browser/permissions/prediction_service/prediction_based_permission_ui_selector.cc
index 26c5a35..0984108 100644
--- a/chrome/browser/permissions/prediction_service/prediction_based_permission_ui_selector.cc
+++ b/chrome/browser/permissions/prediction_service/prediction_based_permission_ui_selector.cc
@@ -103,7 +103,7 @@
 }
 
 bool ShouldPredictionTriggerQuietUi(
-    PermissionUmaUtil::PredictionGrantLikelihood likelihood) {
+    permissions::PermissionUiSelector::PredictionGrantLikelihood likelihood) {
   return likelihood == VeryUnlikely;
 }
 
@@ -439,7 +439,7 @@
          request_type == permissions::RequestType::kGeolocation;
 }
 
-std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+std::optional<permissions::PermissionUiSelector::PredictionGrantLikelihood>
 PredictionBasedPermissionUiSelector::PredictedGrantLikelihoodForUKM() {
   return last_request_grant_likelihood_;
 }
diff --git a/chrome/browser/permissions/prediction_service/prediction_based_permission_ui_selector.h b/chrome/browser/permissions/prediction_service/prediction_based_permission_ui_selector.h
index c9d9cd3..a4608891 100644
--- a/chrome/browser/permissions/prediction_service/prediction_based_permission_ui_selector.h
+++ b/chrome/browser/permissions/prediction_service/prediction_based_permission_ui_selector.h
@@ -58,7 +58,7 @@
   };
 
   using PredictionGrantLikelihood =
-      permissions::PermissionUmaUtil::PredictionGrantLikelihood;
+      permissions::PermissionUiSelector::PredictionGrantLikelihood;
   // Constructs an instance in the context of the given |profile|.
   explicit PredictionBasedPermissionUiSelector(Profile* profile);
   ~PredictionBasedPermissionUiSelector() override;
diff --git a/chrome/browser/permissions/prediction_service_browsertest.cc b/chrome/browser/permissions/prediction_service_browsertest.cc
index 9efc498..c9802a0a 100644
--- a/chrome/browser/permissions/prediction_service_browsertest.cc
+++ b/chrome/browser/permissions/prediction_service_browsertest.cc
@@ -89,12 +89,12 @@
     OPTIMIZATION_TARGET_GEOLOCATION_IMAGE_PERMISSION_RELEVANCE;
 
 constexpr auto kLikelihoodUnspecified =
-    PermissionUmaUtil::PredictionGrantLikelihood::
+    PermissionUiSelector::PredictionGrantLikelihood::
         PermissionPrediction_Likelihood_DiscretizedLikelihood_DISCRETIZED_LIKELIHOOD_UNSPECIFIED;
 
 // This is the only server side reply that will tirgger quiet UI at the moment.
 constexpr auto kLikelihoodVeryUnlikely =
-    PermissionUmaUtil::PredictionGrantLikelihood::
+    PermissionUiSelector::PredictionGrantLikelihood::
         PermissionPrediction_Likelihood_DiscretizedLikelihood_VERY_UNLIKELY;
 
 constexpr std::string_view kNotificationsModelExecutionSuccessHistogram =
@@ -329,7 +329,7 @@
       RequestType request_type,
       bool should_expect_quiet_ui,
       std::optional<PermissionRequestRelevance> expected_relevance,
-      std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+      std::optional<PermissionUiSelector::PredictionGrantLikelihood>
           expected_prediction_likelihood) {
     auto* manager = GetPermissionRequestManager();
     GURL url = embedded_test_server()->GetURL(test_url, "/title1.html");
@@ -383,7 +383,7 @@
   std::string test_name;
   std::string holdback_probability;
   bool should_expect_quiet_ui;
-  PermissionUmaUtil::PredictionGrantLikelihood prediction_service_likelihood;
+  PermissionUiSelector::PredictionGrantLikelihood prediction_service_likelihood;
 };
 
 class PredictionServiceHoldbackBrowserTest
@@ -497,7 +497,7 @@
   // will return unspecified.
   float max_likely_threshold;
   bool should_expect_quiet_ui;
-  std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+  std::optional<PermissionUiSelector::PredictionGrantLikelihood>
       expected_prediction_likelihood;
 };
 
@@ -526,7 +526,7 @@
       PermissionAction permission_action,
       bool should_expect_quiet_ui,
       std::optional<PermissionRequestRelevance> expected_relevance,
-      std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+      std::optional<PermissionUiSelector::PredictionGrantLikelihood>
           expected_prediction_likelihood) {
     // We need 4 prompts for the CPSS to kick in on the next prompt.
     // This behaviour is defined by kRequestedPermissionMinimumHistoricalActions
@@ -641,7 +641,7 @@
   // test).
   // It should define the decision shared with the permission request
   // manager.
-  PermissionUmaUtil::PredictionGrantLikelihood prediction_service_likelihood;
+  PermissionUiSelector::PredictionGrantLikelihood prediction_service_likelihood;
   bool should_expect_quiet_ui;
   int success_count_model_execution;
 };
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 006a3b7..8eba010 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -1021,6 +1021,12 @@
 inline constexpr char kSyncedDefaultSearchProviderGUID[] =
     "default_search_provider.synced_guid";
 
+// Deprecated 07/2025.
+inline constexpr char kFirstSyncCompletedInFullSyncMode[] =
+    "sync.first_full_sync_completed";
+inline constexpr char kGoogleServicesSecondLastSyncingGaiaId[] =
+    "google.services.second_last_gaia_id";
+
 // Register local state used only for migration (clearing or moving to a new
 // key).
 void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
@@ -1414,12 +1420,17 @@
   registry->RegisterDictionaryPref(kSharingVapidKey);
   registry->RegisterBooleanPref(kHasSeenWelcomePage, false);
 
-  // Deprecated 06/2025
+  // Deprecated 06/2025.
   registry->RegisterBooleanPref(kStorageGarbageCollect, false);
   registry->RegisterDoublePref(kGaiaCookiePeriodicReportTimeDeprecated, 0);
   registry->RegisterListPref(kWebAuthnCablePairingsPrefName);
   registry->RegisterStringPref(kLastUsedPairingFromSyncPublicKey, "");
   registry->RegisterStringPref(kSyncedDefaultSearchProviderGUID, std::string());
+
+  // Deprecated 07/2025.
+  registry->RegisterBooleanPref(kFirstSyncCompletedInFullSyncMode, false);
+  registry->RegisterStringPref(kGoogleServicesSecondLastSyncingGaiaId,
+                               std::string());
 }
 
 }  // namespace
@@ -2661,6 +2672,10 @@
   profile_prefs->ClearPref(kLastUsedPairingFromSyncPublicKey);
   profile_prefs->ClearPref(kSyncedDefaultSearchProviderGUID);
 
+  // Added 07/2025.
+  profile_prefs->ClearPref(kFirstSyncCompletedInFullSyncMode);
+  profile_prefs->ClearPref(kGoogleServicesSecondLastSyncingGaiaId);
+
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_PROFILE_PREFS
 
diff --git a/chrome/browser/resources/ash/settings/os_apps_page/COMMON_METADATA b/chrome/browser/resources/ash/settings/os_apps_page/COMMON_METADATA
index 3d15d120..bda997ea 100644
--- a/chrome/browser/resources/ash/settings/os_apps_page/COMMON_METADATA
+++ b/chrome/browser/resources/ash/settings/os_apps_page/COMMON_METADATA
@@ -1,4 +1,4 @@
-team_email: "cros-web-apps-team@google.com"
+team_email: "lt-web-apps-team@google.com"
 os: CHROME_OS
 buganizer: {
   component_id: 1389907
diff --git a/chrome/browser/resources/password_manager/dialogs/move_single_password_dialog.ts b/chrome/browser/resources/password_manager/dialogs/move_single_password_dialog.ts
index f4059de..5f1a810 100644
--- a/chrome/browser/resources/password_manager/dialogs/move_single_password_dialog.ts
+++ b/chrome/browser/resources/password_manager/dialogs/move_single_password_dialog.ts
@@ -90,7 +90,7 @@
   }
 
   private onMoveButtonClick_() {
-    assert(this.isAccountStorageEnabled);
+    assert(this.isAccountStoreUser);
     PasswordManagerImpl.getInstance().movePasswordsToAccount(
         [this.password.id]);
     this.dispatchEvent(new CustomEvent('passwords-moved', {
diff --git a/chrome/browser/resources/password_manager/settings_section.ts b/chrome/browser/resources/password_manager/settings_section.ts
index 7cdc8b21..06a0cd7 100644
--- a/chrome/browser/resources/password_manager/settings_section.ts
+++ b/chrome/browser/resources/password_manager/settings_section.ts
@@ -376,7 +376,7 @@
   }
 
   private changeAccountStorageEnabled_() {
-    if (this.isAccountStorageEnabled) {
+    if (this.isAccountStoreUser) {
       this.disableAccountStorage();
     } else {
       this.enableAccountStorage();
diff --git a/chrome/browser/resources/password_manager/user_utils_mixin.ts b/chrome/browser/resources/password_manager/user_utils_mixin.ts
index 1e5bbfe4..4e627a11 100644
--- a/chrome/browser/resources/password_manager/user_utils_mixin.ts
+++ b/chrome/browser/resources/password_manager/user_utils_mixin.ts
@@ -24,14 +24,6 @@
       (superClass) implements UserUtilMixinInterface {
         static get properties() {
           return {
-            /**
-             * Indicates whether the account-scoped password storage is enabled.
-             */
-            isAccountStorageEnabled: {
-              type: Boolean,
-              value: false,
-            },
-
             /* Account storage eligibility. */
             isEligibleForAccountStorage: {
               type: Boolean,
@@ -45,8 +37,7 @@
              */
             isAccountStoreUser: {
               type: Boolean,
-              computed: 'computeIsAccountStoreUser_(' +
-                  'isAccountStorageEnabled, isEligibleForAccountStorage)',
+              value: false,
             },
 
             isSyncingPasswords: {
@@ -74,10 +65,7 @@
           };
         }
 
-        declare isAccountStorageEnabled: boolean;
         declare isEligibleForAccountStorage: boolean;
-        // Whether account storage is enabled and the default storage is
-        // account.
         declare isAccountStoreUser: boolean;
         declare isSyncingPasswords: boolean;
         declare accountEmail: string;
@@ -93,7 +81,7 @@
 
           // Create listener functions.
           this.setIsAccountStorageEnabledListener_ = (enabled) =>
-              this.isAccountStorageEnabled = enabled;
+              this.isAccountStoreUser = enabled;
           const syncInfoChanged = (syncInfo: SyncInfo) => this.syncInfo_ =
               syncInfo;
           const accountInfoChanged = (accountInfo: AccountInfo) =>
@@ -148,11 +136,6 @@
         private computeAvatarImage_(): string {
           return this.accountInfo_?.avatarImage || '';
         }
-
-        private computeIsAccountStoreUser_(): boolean {
-          return this.isEligibleForAccountStorage &&
-              this.isAccountStorageEnabled;
-        }
       }
 
       return UserUtilMixin;
@@ -160,7 +143,6 @@
 
 
 export interface UserUtilMixinInterface {
-  isAccountStorageEnabled: boolean;
   isEligibleForAccountStorage: boolean;
   isAccountStoreUser: boolean;
   isSyncingPasswords: boolean;
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_v2.ts b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_v2.ts
index 2d20150..fdd4f358 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_v2.ts
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog_v2.ts
@@ -31,6 +31,11 @@
 
 import type {SettingsCheckboxElement} from '../controls/settings_checkbox.js';
 import {loadTimeData} from '../i18n_setup.js';
+import type {MetricsBrowserProxy} from '../metrics_browser_proxy.js';
+import {MetricsBrowserProxyImpl} from '../metrics_browser_proxy.js';
+import {routes} from '../route.js';
+import type {Route} from '../router.js';
+import {RouteObserverMixin} from '../router.js';
 
 import type {ClearBrowsingDataBrowserProxy, UpdateSyncStateEvent} from './clear_browsing_data_browser_proxy.js';
 import {BrowsingDataType, ClearBrowsingDataBrowserProxyImpl, TimePeriod} from './clear_browsing_data_browser_proxy.js';
@@ -134,7 +139,7 @@
 }
 
 const SettingsClearBrowsingDataDialogV2ElementBase =
-    WebUiListenerMixin(PrefsMixin(PolymerElement));
+    RouteObserverMixin(WebUiListenerMixin(PrefsMixin(PolymerElement)));
 
 export class SettingsClearBrowsingDataDialogV2Element extends
     SettingsClearBrowsingDataDialogV2ElementBase {
@@ -229,6 +234,8 @@
       ClearBrowsingDataBrowserProxyImpl.getInstance();
   private syncBrowserProxy_: SyncBrowserProxy =
       SyncBrowserProxyImpl.getInstance();
+  private metricsBrowserProxy_: MetricsBrowserProxy =
+      MetricsBrowserProxyImpl.getInstance();
 
   override ready() {
     super.ready();
@@ -282,6 +289,12 @@
     this.setFocusOutlineToVisible_();
   }
 
+  override currentRouteChanged(currentRoute: Route) {
+    if (currentRoute === routes.CLEAR_BROWSER_DATA) {
+      this.metricsBrowserProxy_.recordAction('ClearBrowsingData_DialogCreated');
+    }
+  }
+
   private setUpDataTypeOptionLists_() {
     const expandedOptionsList: BrowsingDataTypeOption[] = [];
     const moreOptionsList: BrowsingDataTypeOption[] = [];
@@ -379,6 +392,8 @@
 
     const dataTypes = this.getSelectedDataTypes_();
     const timePeriod = this.$.timePicker.getSelectedTimePeriod();
+    this.clearBrowsingDataBrowserProxy_
+        .recordSettingsClearBrowsingDataAdvancedTimePeriodHistogram(timePeriod);
 
     // Update the DataType and TimePeriod prefs with the latest selection.
     this.$.deleteBrowsingDataDialog
@@ -436,6 +451,8 @@
 
   private onShowMoreClick_() {
     this.dataTypesExpanded_ = true;
+    this.metricsBrowserProxy_.recordAction(
+        'Settings.DeleteBrowsingData.CheckboxesShowMoreClick');
 
     // Set the focus to the first checkbox in the 'more' options list.
     afterNextRender(this, () => {
@@ -460,6 +477,8 @@
 
   private onManageOtherGoogleDataRowClick_() {
     this.showOtherGoogleDataDialog_ = true;
+    this.metricsBrowserProxy_.recordAction(
+        'Settings.DeleteBrowsingData.OtherDataEntryPointClick');
   }
 
   private setFocusOutlineToVisible_() {
@@ -488,6 +507,8 @@
     // <if expr="not is_chromeos">
     if (e.detail.id === 'signOutLink') {
       this.syncBrowserProxy_.signOut(/*delete_profile=*/ false);
+      this.metricsBrowserProxy_.recordAction(
+          'Settings.DeleteBrowsingData.CookiesSignOutLinkClick');
       return;
     }
     // </if>
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_time_picker.ts b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_time_picker.ts
index c4264876..71d83df 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_time_picker.ts
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_time_picker.ts
@@ -20,6 +20,7 @@
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {loadTimeData} from '../i18n_setup.js';
+import {MetricsBrowserProxyImpl} from '../metrics_browser_proxy.js';
 
 import {TimePeriod} from './clear_browsing_data_browser_proxy.js';
 import {getTemplate} from './clear_browsing_data_time_picker.html.js';
@@ -240,6 +241,9 @@
       anchorAlignmentX: AnchorAlignment.BEFORE_END,
       top: target.getBoundingClientRect().bottom + MENU_VERTICAL_OFFSET_PX,
     });
+
+    MetricsBrowserProxyImpl.getInstance().recordAction(
+        'Settings.DeleteBrowsingData.TimePickerMoreClick');
   }
 
   private onMoreOptionsMenuClose_(e: Event) {
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/other_google_data_dialog.ts b/chrome/browser/resources/settings/clear_browsing_data_dialog/other_google_data_dialog.ts
index 9b3899b..9f7bc04 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/other_google_data_dialog.ts
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/other_google_data_dialog.ts
@@ -25,6 +25,8 @@
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {PasswordManagerImpl, PasswordManagerPage} from '../autofill_page/password_manager_proxy.js';
+import type {MetricsBrowserProxy} from '../metrics_browser_proxy.js';
+import {MetricsBrowserProxyImpl} from '../metrics_browser_proxy.js';
 
 import type {ClearBrowsingDataBrowserProxy, UpdateSyncStateEvent} from './clear_browsing_data_browser_proxy.js';
 import {ClearBrowsingDataBrowserProxyImpl} from './clear_browsing_data_browser_proxy.js';
@@ -80,6 +82,8 @@
       ClearBrowsingDataBrowserProxyImpl.getInstance();
   private syncBrowserProxy_: SyncBrowserProxy =
       SyncBrowserProxyImpl.getInstance();
+  private metricsBrowserProxy_: MetricsBrowserProxy =
+      MetricsBrowserProxyImpl.getInstance();
 
   override ready() {
     super.ready();
@@ -117,16 +121,25 @@
   private onPasswordManagerClick_() {
     PasswordManagerImpl.getInstance().showPasswordManager(
         PasswordManagerPage.PASSWORDS);
+
+    this.metricsBrowserProxy_.recordAction(
+        'Settings.DeleteBrowsingData.PasswordManagerLinkClick');
   }
 
   private onMyActivityLinkClick_() {
     OpenWindowProxyImpl.getInstance().openUrl(
         loadTimeData.getString('deleteBrowsingDataMyActivityUrl'));
+
+    this.metricsBrowserProxy_.recordAction(
+        'Settings.DeleteBrowsingData.MyActivityLinkClick');
   }
 
   private onGoogleSearchHistoryLinkClick_() {
     OpenWindowProxyImpl.getInstance().openUrl(
         loadTimeData.getString('deleteBrowsingDataSearchHistoryUrl'));
+
+    this.metricsBrowserProxy_.recordAction(
+        'Settings.DeleteBrowsingData.GoogleSearchHistoryLinkClick');
   }
 
   private shouldShowMyActivityLink_() {
diff --git a/chrome/browser/sharesheet/COMMON_METADATA b/chrome/browser/sharesheet/COMMON_METADATA
index 3d15d120..bda997ea 100644
--- a/chrome/browser/sharesheet/COMMON_METADATA
+++ b/chrome/browser/sharesheet/COMMON_METADATA
@@ -1,4 +1,4 @@
-team_email: "cros-web-apps-team@google.com"
+team_email: "lt-web-apps-team@google.com"
 os: CHROME_OS
 buganizer: {
   component_id: 1389907
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
index da99c9b..cd0677a 100644
--- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -133,11 +133,6 @@
 constexpr char16_t kBookmarkTitle[] = u"Title";
 constexpr char kBookmarkPageUrl[] = "http://www.foo.com/";
 
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
-constexpr char kPreviouslySyncingGaiaIdMetricName[] =
-    "Sync.BookmarkModelMerger.PreviouslySyncingGaiaId";
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
-
 MATCHER(HasUniquePosition, "") {
   return arg.specifics().bookmark().has_unique_position();
 }
@@ -3132,124 +3127,4 @@
   }
 }
 
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest, PRE_ComparisonMetrics) {
-  base::HistogramTester histogram_tester;
-
-  ASSERT_TRUE(SetupClients());
-
-  // Create one URL bookmark under Bookmarks Bar to deal with non-empty
-  // datasets.
-  ASSERT_TRUE(AddURL(kSingleProfileIndex,
-                     GetBookmarkBarNode(kSingleProfileIndex), 0, u"Url1",
-                     GURL("http://www.url1.com")));
-
-  ASSERT_TRUE(SetupSync(SyncTestAccount::kConsumerAccount1));
-  histogram_tester.ExpectUniqueSample(
-      kPreviouslySyncingGaiaIdMetricName,
-      /*sample=*/2 /*kSyncFeatureNeverPreviouslyTurnedOn*/,
-      /*expected_bucket_count=*/1);
-
-  // Turn Sync off by removing the primary account.
-  GetClient(kSingleProfileIndex)->SignOutPrimaryAccount();
-
-  // Create a second bookmark, this time under Other Bookmarks.
-  ASSERT_TRUE(AddURL(kSingleProfileIndex, GetOtherNode(kSingleProfileIndex), 0,
-                     u"Url2", GURL("http://www.url2.com")));
-
-  // Turn Sync on with the same account.
-  ASSERT_TRUE(SetupSync(SyncTestAccount::kConsumerAccount1));
-  histogram_tester.ExpectBucketCount(
-      kPreviouslySyncingGaiaIdMetricName,
-      /*sample=*/3 /*kCurrentGaiaIdMatchesPreviousWithSyncFeatureOn*/,
-      /*expected_bucket_count=*/1);
-
-  // Sanity-check the existence of a few comparison metrics.
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrl",
-      /*sample=*/4 /*kAccountDataIsStrictSubsetOfLocalData*/,
-      /*expected_bucket_count=*/1);
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndUuid",
-      /*sample=*/4 /*kAccountDataIsStrictSubsetOfLocalData*/,
-      /*expected_bucket_count=*/1);
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "UnderBookmarksBar.ByUrlAndUuid",
-      /*sample=*/3 /*kExactMatchNonEmpty*/,
-      /*expected_bucket_count=*/1);
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndUuid.Between1And19LocalUrlBookmarks",
-      /*sample=*/4 /*kAccountDataIsStrictSubsetOfLocalData*/,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrl",
-      /*sample=*/8 /*kIntersectionBetween50And90Percent*/,
-      /*expected_bucket_count=*/1);
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndUuid",
-      /*sample=*/8 /*kIntersectionBetween50And90Percent*/,
-      /*expected_bucket_count=*/1);
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "UnderBookmarksBar.ByUrlAndUuid",
-      /*sample=*/3 /*kExactMatchNonEmpty*/,
-      /*expected_bucket_count=*/1);
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndUuid.Between1And19LocalUrlBookmarks",
-      /*sample=*/8 /*kIntersectionBetween50And90Percent*/,
-      /*expected_bucket_count=*/1);
-
-  // Enable Sync with a different account.
-  GetClient(kSingleProfileIndex)->SignOutPrimaryAccount();
-  ASSERT_TRUE(GetClient(kSingleProfileIndex)
-                  ->SetupSync(SyncTestAccount::kConsumerAccount2));
-  histogram_tester.ExpectBucketCount(
-      kPreviouslySyncingGaiaIdMetricName,
-      /*sample=*/4 /*kCurrentGaiaIdDiffersPreviousWithSyncFeatureOn*/,
-      /*expected_bucket_count=*/1);
-}
-
-IN_PROC_BROWSER_TEST_F(SingleClientBookmarksSyncTest, ComparisonMetrics) {
-  base::HistogramTester histogram_tester;
-  ASSERT_TRUE(SetupClients());
-  ASSERT_TRUE(GetClient(kSingleProfileIndex)->AwaitSyncSetupCompletion());
-  histogram_tester.ExpectTotalCount(kPreviouslySyncingGaiaIdMetricName, 0);
-
-  // Turn off bookmark sync specifically and turn it back on.
-  GetSyncService(kSingleProfileIndex)
-      ->GetUserSettings()
-      ->SetSelectedTypes(/*sync_everything=*/false, /*types=*/{});
-  ASSERT_TRUE(GetClient(kSingleProfileIndex)->AwaitSyncTransportActive());
-  GetSyncService(kSingleProfileIndex)
-      ->GetUserSettings()
-      ->SetSelectedTypes(/*sync_everything=*/false,
-                         /*types=*/{syncer::UserSelectableType::kBookmarks});
-  ASSERT_TRUE(GetClient(kSingleProfileIndex)->AwaitSyncTransportActive());
-
-  // No metric should be recorded, because this sync the feature was already on
-  // and toggling bookmark sync off and on shouldn't pollute metrics.
-  histogram_tester.ExpectTotalCount(kPreviouslySyncingGaiaIdMetricName, 0);
-
-  // Turn Sync off and on with the same account. Note that `kConsumerAccount2`
-  // needs to be specified again because it doesn't automatically carry over
-  // from the PRE_ test.
-  GetClient(kSingleProfileIndex)->SignOutPrimaryAccount();
-  ASSERT_TRUE(SetupSync(SyncTestAccount::kConsumerAccount2));
-
-  // The histogram should be recorded once again.
-  histogram_tester.ExpectBucketCount(
-      kPreviouslySyncingGaiaIdMetricName,
-      /*sample=*/3 /*kCurrentGaiaIdMatchesPreviousWithSyncFeatureOn*/,
-      /*expected_bucket_count=*/1);
-}
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
-
 }  // namespace
diff --git a/chrome/browser/ui/browser_window/internal/browser_window_features.cc b/chrome/browser/ui/browser_window/internal/browser_window_features.cc
index d1099fe..3132655 100644
--- a/chrome/browser/ui/browser_window/internal/browser_window_features.cc
+++ b/chrome/browser/ui/browser_window/internal/browser_window_features.cc
@@ -509,8 +509,6 @@
   windows_taskbar_icon_updater_.reset();
 #endif
 
-  exclusive_access_manager_.reset();
-
   if (user_education_) {
     user_education_->TearDown();
   }
diff --git a/chrome/browser/ui/passwords/bubble_controllers/password_change/successful_password_change_bubble_controller.cc b/chrome/browser/ui/passwords/bubble_controllers/password_change/successful_password_change_bubble_controller.cc
index d1f2128a..045f0109 100644
--- a/chrome/browser/ui/passwords/bubble_controllers/password_change/successful_password_change_bubble_controller.cc
+++ b/chrome/browser/ui/passwords/bubble_controllers/password_change/successful_password_change_bubble_controller.cc
@@ -6,7 +6,6 @@
 
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
-#include "chrome/browser/password_manager/password_change_delegate.h"
 #include "chrome/browser/ui/passwords/passwords_model_delegate.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -25,9 +24,7 @@
     : PasswordBubbleControllerBase(
           delegate,
           password_manager::metrics_util::UIDisplayDisposition::
-              PASSWORD_CHANGE_BUBBLE),
-      password_change_delegate_(
-          delegate_->GetPasswordChangeDelegate()->AsWeakPtr()) {}
+              PASSWORD_CHANGE_BUBBLE) {}
 
 SuccessfulPasswordChangeBubbleController::
     ~SuccessfulPasswordChangeBubbleController() {
@@ -48,9 +45,6 @@
 void SuccessfulPasswordChangeBubbleController::OpenPasswordManager() {
   dismissal_reason_ = metrics_util::CLICKED_MANAGE_PASSWORD;
   if (delegate_) {
-    // Stop password change flow for this tab.
-    password_change_delegate_->Stop();
-
     delegate_->NavigateToPasswordManagerSettingsPage(
         password_manager::ManagePasswordsReferrer::kPasswordChangeInfoBubble);
   }
@@ -58,9 +52,6 @@
 
 void SuccessfulPasswordChangeBubbleController::FinishPasswordChange() {
   dismissal_reason_ = metrics_util::CLICKED_ACCEPT;
-  if (password_change_delegate_) {
-    password_change_delegate_->Stop();
-  }
 }
 
 void SuccessfulPasswordChangeBubbleController::AuthenticateUser(
@@ -78,12 +69,12 @@
 }
 
 std::u16string SuccessfulPasswordChangeBubbleController::GetUsername() const {
-  return password_change_delegate_->GetUsername();
+  return delegate_->PasswordChangeUsername();
 }
 
 std::u16string SuccessfulPasswordChangeBubbleController::GetNewPassword()
     const {
-  return password_change_delegate_->GetGeneratedPassword();
+  return delegate_->PasswordChangeNewPassword();
 }
 
 void SuccessfulPasswordChangeBubbleController::RequestFavicon(
diff --git a/chrome/browser/ui/passwords/bubble_controllers/password_change/successful_password_change_bubble_controller.h b/chrome/browser/ui/passwords/bubble_controllers/password_change/successful_password_change_bubble_controller.h
index c4c3306..5769f38 100644
--- a/chrome/browser/ui/passwords/bubble_controllers/password_change/successful_password_change_bubble_controller.h
+++ b/chrome/browser/ui/passwords/bubble_controllers/password_change/successful_password_change_bubble_controller.h
@@ -9,8 +9,6 @@
 #include "base/task/cancelable_task_tracker.h"
 #include "chrome/browser/ui/passwords/bubble_controllers/password_bubble_controller_base.h"
 
-class PasswordChangeDelegate;
-
 // Controller for SuccessfulPasswordChangeView which is displayed after
 // successful password change.
 class SuccessfulPasswordChangeBubbleController
@@ -40,9 +38,6 @@
   base::WeakPtr<SuccessfulPasswordChangeBubbleController> GetWeakPtr();
 
  private:
-  // Controls password change process.
-  base::WeakPtr<PasswordChangeDelegate> password_change_delegate_;
-
   // Dismissal reason for a password bubble.
   password_manager::metrics_util::UIDismissalReason dismissal_reason_ =
       password_manager::metrics_util::NO_DIRECT_INTERACTION;
diff --git a/chrome/browser/ui/passwords/bubble_controllers/password_change/successful_password_change_bubble_controller_unittest.cc b/chrome/browser/ui/passwords/bubble_controllers/password_change/successful_password_change_bubble_controller_unittest.cc
index daa182d..85305a9 100644
--- a/chrome/browser/ui/passwords/bubble_controllers/password_change/successful_password_change_bubble_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/bubble_controllers/password_change/successful_password_change_bubble_controller_unittest.cc
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include "base/test/metrics/histogram_tester.h"
-#include "chrome/browser/password_manager/password_change_delegate_mock.h"
 #include "chrome/browser/ui/passwords/passwords_model_delegate_mock.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -19,8 +18,6 @@
  public:
   void CreateController() {
     EXPECT_CALL(mock_delegate_, OnBubbleShown());
-    ON_CALL(mock_delegate_, GetPasswordChangeDelegate)
-        .WillByDefault(Return(&password_change_delegate_));
     controller_ = std::make_unique<SuccessfulPasswordChangeBubbleController>(
         mock_delegate_.AsWeakPtr());
   }
@@ -28,7 +25,6 @@
  protected:
   PasswordsModelDelegateMock mock_delegate_;
   std::unique_ptr<SuccessfulPasswordChangeBubbleController> controller_;
-  PasswordChangeDelegateMock password_change_delegate_;
 };
 
 TEST_F(SuccessfulPasswordChangeBubbleControllerTest,
diff --git a/chrome/browser/ui/passwords/manage_passwords_state.cc b/chrome/browser/ui/passwords/manage_passwords_state.cc
index c89a40d..cf9c692 100644
--- a/chrome/browser/ui/passwords/manage_passwords_state.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_state.cc
@@ -335,6 +335,14 @@
   SetState(password_manager::ui::State::MANAGE_STATE);
 }
 
+void ManagePasswordsState::OpenPasswordChangedBubble(
+    const std::u16string& username,
+    const std::u16string& new_password) {
+  password_change_username_ = username;
+  password_change_new_password_ = new_password;
+  SetState(password_manager::ui::State::PASSWORD_CHANGE_STATE);
+}
+
 void ManagePasswordsState::ClearData() {
   form_manager_.reset();
   clear_selected_password();
@@ -344,6 +352,8 @@
   single_credential_mode_credential_.reset();
   gpm_pin_created_during_recent_passkey_creation_ = false;
   passkey_rp_id_.clear();
+  password_change_username_.clear();
+  password_change_new_password_.clear();
 }
 
 bool ManagePasswordsState::AddForm(const PasswordForm& form) {
diff --git a/chrome/browser/ui/passwords/manage_passwords_state.h b/chrome/browser/ui/passwords/manage_passwords_state.h
index 199886b..c6316b7 100644
--- a/chrome/browser/ui/passwords/manage_passwords_state.h
+++ b/chrome/browser/ui/passwords/manage_passwords_state.h
@@ -134,6 +134,11 @@
   // Move to MANAGE_STATE with initial credential to show its details.
   void OpenPasswordDetailsBubble(const password_manager::PasswordForm& form);
 
+  // Move to PASSWORD_CHANGE_STATE with `username` and `new_password` to
+  // display.
+  void OpenPasswordChangedBubble(const std::u16string& username,
+                                 const std::u16string& new_password);
+
   password_manager::ui::State state() const { return state_; }
   const std::vector<password_manager::PasswordForm>& unsynced_credentials()
       const {
@@ -180,6 +185,14 @@
     single_credential_mode_credential_ = std::nullopt;
   }
 
+  const std::u16string& password_change_username() const {
+    return password_change_username_;
+  }
+
+  const std::u16string& password_change_new_password() const {
+    return password_change_new_password_;
+  }
+
  private:
   // Removes all the PasswordForms and resets passkey state stored in this
   // object.
@@ -226,6 +239,11 @@
 
   // The passkey relying party identifier used during a recent passkey flow.
   std::string passkey_rp_id_;
+
+  // Username and password of a credential that has been updated in a recent
+  // password change flow.
+  std::u16string password_change_username_;
+  std::u16string password_change_new_password_;
 };
 
 #endif  // CHROME_BROWSER_UI_PASSWORDS_MANAGE_PASSWORDS_STATE_H_
diff --git a/chrome/browser/ui/passwords/manage_passwords_state_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_state_unittest.cc
index 911218cc..29d4f1d 100644
--- a/chrome/browser/ui/passwords/manage_passwords_state_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_state_unittest.cc
@@ -675,4 +675,13 @@
   EXPECT_EQ(passwords_data().single_credential_mode_credential(), form);
   EXPECT_TRUE(passwords_data().origin().GetURL().is_empty());
 }
+
+TEST_F(ManagePasswordsStateTest, OpenPasswordChangeBubble) {
+  passwords_data().OpenPasswordChangedBubble(u"username", u"password");
+  EXPECT_EQ(passwords_data().state(),
+            password_manager::ui::PASSWORD_CHANGE_STATE);
+  EXPECT_EQ(passwords_data().password_change_username(), u"username");
+  EXPECT_EQ(passwords_data().password_change_new_password(), u"password");
+}
+
 }  // namespace
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
index 1f18935..0dbd8ec 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
@@ -723,10 +723,14 @@
 
 password_manager::ui::State ManagePasswordsUIController::GetState() const {
   PasswordChangeDelegate* delegate = GetPasswordChangeDelegate();
-  if (delegate &&
-      delegate->GetCurrentState() ==
-          PasswordChangeDelegate::State::kPasswordSuccessfullyChanged) {
-    return password_manager::ui::State::PASSWORD_CHANGE_STATE;
+  if (delegate) {
+    if (delegate->GetCurrentState() ==
+        PasswordChangeDelegate::State::kPasswordSuccessfullyChanged) {
+      // Prevent any UI being displayed instead of the password change bubble.
+      return password_manager::ui::State::PASSWORD_CHANGE_STATE;
+    }
+    // Prevent any UI being displayed during ongoing password change flow.
+    return password_manager::ui::State::INACTIVE_STATE;
   }
   return passwords_data_.state();
 }
@@ -811,6 +815,16 @@
   return passwords_data_.passkey_rp_id();
 }
 
+const std::u16string& ManagePasswordsUIController::PasswordChangeUsername()
+    const {
+  return passwords_data_.password_change_username();
+}
+
+const std::u16string& ManagePasswordsUIController::PasswordChangeNewPassword()
+    const {
+  return passwords_data_.password_change_new_password();
+}
+
 void ManagePasswordsUIController::OnBubbleShown() {
   bubble_status_ = BubbleStatus::SHOWN;
 }
@@ -1152,8 +1166,10 @@
   }
 }
 
-void ManagePasswordsUIController::ShowChangePasswordBubble() {
-  CHECK_EQ(password_manager::ui::PASSWORD_CHANGE_STATE, GetState());
+void ManagePasswordsUIController::ShowChangePasswordBubble(
+    const std::u16string& username,
+    const std::u16string& new_password) {
+  passwords_data_.OpenPasswordChangedBubble(username, new_password);
   bubble_status_ = BubbleStatus::SHOULD_POP_UP;
   UpdateBubbleAndIconVisibility();
 }
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
index c3ec147..661cf08 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
@@ -179,6 +179,8 @@
   bool BubbleIsManualFallbackForSaving() const override;
   bool GpmPinCreatedDuringRecentPasskeyCreation() const override;
   const std::string& PasskeyRpId() const override;
+  const std::u16string& PasswordChangeUsername() const override;
+  const std::u16string& PasswordChangeNewPassword() const override;
   void OnBubbleShown() override;
   void OnBubbleHidden() override;
   void OnNoInteraction() override;
@@ -223,9 +225,13 @@
   }
 #endif  // defined(UNIT_TEST)
 
-  // Hides/Shows the bubble if opened. Mocked in the tests.
+  // Hides the bubble if opened. Mocked in the tests.
   virtual void HidePasswordBubble();
-  virtual void ShowChangePasswordBubble();
+
+  // Opens change password bubble and passes `username` and `new_password` that
+  // should be displayed on it.
+  void ShowChangePasswordBubble(const std::u16string& username,
+                                const std::u16string& new_password);
 
   bool IsShowingBubble() const {
     return bubble_status_ == BubbleStatus::SHOWN ||
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
index d0961b0..b91a8fb 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
@@ -1963,8 +1963,8 @@
   auto test_form_manager =
       CreateFormManagerWithBestMatches(best_matches, &submitted_form());
   controller()->OnPasswordSubmitted(std::move(test_form_manager));
-  ASSERT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
-            controller()->GetState());
+  ASSERT_EQ(controller()->GetState(),
+            password_manager::ui::PENDING_PASSWORD_STATE);
 
   // Emulate password change flow has started.
   PasswordChangeDelegateMock mock_delegate;
@@ -1973,16 +1973,14 @@
           Return(PasswordChangeDelegate::State::kWaitingForChangePasswordForm));
   EXPECT_CALL(*password_change_service, GetPasswordChangeDelegate)
       .WillOnce(Return(&mock_delegate));
-
-  ASSERT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
-            controller()->GetState());
+  ASSERT_EQ(controller()->GetState(), password_manager::ui::INACTIVE_STATE);
 
   // Password change flow has finished successfully. The state should change to
   // `MANAGE_STATE`.
   controller()->OnPasswordChangeFinishedSuccessfully();
   EXPECT_CALL(*password_change_service, GetPasswordChangeDelegate)
       .WillOnce(Return(nullptr));
-  ASSERT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
+  ASSERT_EQ(controller()->GetState(), password_manager::ui::MANAGE_STATE);
 }
 
 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
@@ -2354,3 +2352,12 @@
   EXPECT_EQ(controller()->GetState(), password_manager::ui::MANAGE_STATE);
   EXPECT_FALSE(controller()->IsAutomaticallyOpeningBubble());
 }
+
+TEST_F(ManagePasswordsUIControllerTest, ShowChangePasswordBubble) {
+  EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
+  controller()->ShowChangePasswordBubble(kExampleUsername, kExamplePassword);
+  EXPECT_EQ(controller()->PasswordChangeUsername(), kExampleUsername);
+  EXPECT_EQ(controller()->PasswordChangeNewPassword(), kExamplePassword);
+  EXPECT_TRUE(controller()->opened_automatic_bubble());
+  ExpectIconAndControllerStateIs(password_manager::ui::PASSWORD_CHANGE_STATE);
+}
diff --git a/chrome/browser/ui/passwords/passwords_model_delegate.h b/chrome/browser/ui/passwords/passwords_model_delegate.h
index d0e91c1..a1935b0 100644
--- a/chrome/browser/ui/passwords/passwords_model_delegate.h
+++ b/chrome/browser/ui/passwords/passwords_model_delegate.h
@@ -101,6 +101,13 @@
   // the empty string if there isn't one.
   virtual const std::string& PasskeyRpId() const = 0;
 
+  // Returns username of a password that was updated during a recent password
+  // change flow.
+  virtual const std::u16string& PasswordChangeUsername() const = 0;
+
+  // Returns password that was generated during a recent password change flow.
+  virtual const std::u16string& PasswordChangeNewPassword() const = 0;
+
   // Called from the model when the bubble is displayed.
   virtual void OnBubbleShown() = 0;
 
diff --git a/chrome/browser/ui/passwords/passwords_model_delegate_mock.h b/chrome/browser/ui/passwords/passwords_model_delegate_mock.h
index 99e84fd..af52b2b9 100644
--- a/chrome/browser/ui/passwords/passwords_model_delegate_mock.h
+++ b/chrome/browser/ui/passwords/passwords_model_delegate_mock.h
@@ -67,6 +67,14 @@
               (),
               (const override));
   MOCK_METHOD(const std::string&, PasskeyRpId, (), (const override));
+  MOCK_METHOD(const std::u16string&,
+              PasswordChangeUsername,
+              (),
+              (const override));
+  MOCK_METHOD(const std::u16string&,
+              PasswordChangeNewPassword,
+              (),
+              (const override));
   MOCK_METHOD(void, OnBubbleShown, (), (override));
   MOCK_METHOD(void, OnBubbleHidden, (), (override));
   MOCK_METHOD(void, OnNoInteraction, (), (override));
diff --git a/chrome/browser/ui/signin/signin_view_controller_browsertest.cc b/chrome/browser/ui/signin/signin_view_controller_browsertest.cc
index f075735..cfe5ea5 100644
--- a/chrome/browser/ui/signin/signin_view_controller_browsertest.cc
+++ b/chrome/browser/ui/signin/signin_view_controller_browsertest.cc
@@ -349,8 +349,9 @@
   EXPECT_TRUE(IsSignoutTab(tab));
 }
 
+// https://crbug.com/429624627: Test is flakily crashing.
 IN_PROC_BROWSER_TEST_F(SigninViewControllerBrowserTest,
-                       SignoutOrReauthWithPrompt_Cancel) {
+                       DISABLED_SignoutOrReauthWithPrompt_Cancel) {
   // Setup a primary account.
   AccountInfo primary_account_info = SetPrimaryAccount();
   ASSERT_TRUE(
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_icon_views.cc b/chrome/browser/ui/views/passwords/manage_passwords_icon_views.cc
index 760e5fa9..d72caf45 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_icon_views.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_icon_views.cc
@@ -93,7 +93,6 @@
 
   // Hides the page action icon if the associated toolbar icon is pinned.
   if (state_ == password_manager::ui::INACTIVE_STATE ||
-      state_ == password_manager::ui::PASSWORD_CHANGE_STATE ||
       delegate()->ShouldHidePageActionIcon(this)) {
     SetVisible(false);
     return;
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_page_action_controller.cc b/chrome/browser/ui/views/passwords/manage_passwords_page_action_controller.cc
index 14ade1d6..5ef27d6 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_page_action_controller.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_page_action_controller.cc
@@ -104,9 +104,7 @@
     bool is_blocklisted,
     ManagePasswordsUIController& passwords_ui_controller,
     actions::ActionItem& passwords_action_item) {
-  bool should_be_visible =
-      !(state == password_manager::ui::INACTIVE_STATE ||
-        state == password_manager::ui::PASSWORD_CHANGE_STATE);
+  bool should_be_visible = state != password_manager::ui::INACTIVE_STATE;
   if (should_be_visible) {
     page_action_controller_->Show(kActionShowPasswordsBubbleOrPage);
     const gfx::VectorIcon& icon = GetVectorIconForState(state, is_blocklisted);
diff --git a/chrome/browser/ui/views/passwords/password_change/successful_password_change_view_unittest.cc b/chrome/browser/ui/views/passwords/password_change/successful_password_change_view_unittest.cc
index f9f4a159..6aa5526 100644
--- a/chrome/browser/ui/views/passwords/password_change/successful_password_change_view_unittest.cc
+++ b/chrome/browser/ui/views/passwords/password_change/successful_password_change_view_unittest.cc
@@ -9,8 +9,6 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/gmock_callback_support.h"
-#include "chrome/browser/password_manager/password_change_delegate.h"
-#include "chrome/browser/password_manager/password_change_delegate_mock.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/passwords/bubble_controllers/password_change/successful_password_change_bubble_controller.h"
 #include "chrome/browser/ui/views/passwords/manage_passwords_view_ids.h"
@@ -42,12 +40,9 @@
 
   void SetUp() override {
     PasswordBubbleViewTestBase::SetUp();
-    password_change_delegate_ = std::make_unique<PasswordChangeDelegateMock>();
-    ON_CALL(*model_delegate_mock(), GetPasswordChangeDelegate())
-        .WillByDefault(Return(password_change_delegate_.get()));
-    ON_CALL(*password_change_delegate_, GetUsername())
+    ON_CALL(*model_delegate_mock(), PasswordChangeUsername())
         .WillByDefault(ReturnRef(kTestEmail));
-    ON_CALL(*password_change_delegate_, GetGeneratedPassword())
+    ON_CALL(*model_delegate_mock(), PasswordChangeNewPassword())
         .WillByDefault(ReturnRef(kPassword));
   }
 
@@ -65,10 +60,6 @@
     views::BubbleDialogDelegateView::CreateBubble(view_)->Show();
   }
 
-  PasswordChangeDelegateMock* password_change_delegate() {
-    return password_change_delegate_.get();
-  }
-
   SuccessfulPasswordChangeView* view() { return view_; }
 
   views::Label* GetLabelById(int id) {
@@ -76,7 +67,6 @@
   }
 
  private:
-  std::unique_ptr<PasswordChangeDelegateMock> password_change_delegate_;
   raw_ptr<SuccessfulPasswordChangeView> view_;
 };
 
@@ -107,7 +97,6 @@
 TEST_F(SuccessfulPasswordChangeViewTest, ManagePasswordButtonClick) {
   CreateAndShowView();
 
-  EXPECT_CALL(*password_change_delegate(), Stop);
   EXPECT_CALL(*model_delegate_mock(), NavigateToPasswordManagerSettingsPage);
   auto* manage_passwords_button =
       static_cast<views::Button*>(view()->GetViewByID(static_cast<int>(
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view.cc b/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
index 73a9d26..553969a 100644
--- a/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
+++ b/chrome/browser/ui/views/webid/account_selection_bubble_view.cc
@@ -696,7 +696,7 @@
     // We notify the user that the account has been used in the past based on
     // the IdP's knowledge, e.g. `approved_clients` (or the browser knowledge if
     // that one is not present).
-    std::optional<std::u16string> last_used_string =
+    std::optional<std::u16string> used_string =
         account->idp_claimed_login_state.value_or(
             account->browser_trusted_login_state) ==
                 Account::LoginState::kSignIn
@@ -706,7 +706,7 @@
     accounts_content->AddChildView(
         CreateAccountRow(account, /*clickable_position=*/out_position++,
                          /*should_include_idp=*/true, /*is_modal_dialog=*/false,
-                         /*additional_vertical_padding=*/0, last_used_string));
+                         /*additional_vertical_padding=*/0, used_string));
   }
 }
 
diff --git a/chrome/browser/ui/views/webid/account_selection_view_base.cc b/chrome/browser/ui/views/webid/account_selection_view_base.cc
index e76ad35..a39a422 100644
--- a/chrome/browser/ui/views/webid/account_selection_view_base.cc
+++ b/chrome/browser/ui/views/webid/account_selection_view_base.cc
@@ -347,7 +347,7 @@
     bool should_include_idp,
     bool is_modal_dialog,
     int additional_vertical_padding,
-    std::optional<std::u16string> last_used_string) {
+    std::optional<std::u16string> used_string) {
   int avatar_size =
       is_modal_dialog ? webid::kModalAvatarSize : webid::kDesiredAvatarSize;
   views::style::TextStyle account_display_name_style =
@@ -384,10 +384,10 @@
 
     std::u16string footer = u"";
     if (should_include_idp) {
-      if (last_used_string) {
+      if (used_string) {
         footer = l10n_util::GetStringFUTF16(
             IDS_MULTI_IDP_ACCOUNT_ORIGIN_AND_LAST_USED,
-            base::UTF8ToUTF16(idp_data.idp_for_display), *last_used_string);
+            base::UTF8ToUTF16(idp_data.idp_for_display), *used_string);
       } else {
         footer = base::UTF8ToUTF16(idp_data.idp_for_display);
       }
diff --git a/chrome/browser/ui/views/webid/account_selection_view_base.h b/chrome/browser/ui/views/webid/account_selection_view_base.h
index ee0bc917..d97e57e6 100644
--- a/chrome/browser/ui/views/webid/account_selection_view_base.h
+++ b/chrome/browser/ui/views/webid/account_selection_view_base.h
@@ -212,14 +212,15 @@
   // the account on the left, and information about the account on the right.
   // |clickable_position| contains an int if and only if the account is a
   // HoverButton, and in that case the number is the 0-based position of that
-  // account in the overall dialog.
+  // account in the overall dialog. |used_string| is set if this is a returning
+  // account in a multi IDP dialog.
   std::unique_ptr<views::View> CreateAccountRow(
       const IdentityRequestAccountPtr& account,
       std::optional<int> clickable_position,
       bool should_include_idp,
       bool is_modal_dialog = false,
       int additional_vertical_padding = 0,
-      std::optional<std::u16string> last_used_string = std::nullopt);
+      std::optional<std::u16string> used_string = std::nullopt);
 
   // Returns a StyledLabel containing a disclosure label. The label links to
   // privacy policy and terms of service URLs, if available.
diff --git a/chrome/browser/ui/webui/ash/settings/app_management/DIR_METADATA b/chrome/browser/ui/webui/ash/settings/app_management/DIR_METADATA
index 3d15d120..bda997ea 100644
--- a/chrome/browser/ui/webui/ash/settings/app_management/DIR_METADATA
+++ b/chrome/browser/ui/webui/ash/settings/app_management/DIR_METADATA
@@ -1,4 +1,4 @@
-team_email: "cros-web-apps-team@google.com"
+team_email: "lt-web-apps-team@google.com"
 os: CHROME_OS
 buganizer: {
   component_id: 1389907
diff --git a/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service.cc b/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service.cc
index c48535f..0b00442 100644
--- a/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service.cc
@@ -4,9 +4,232 @@
 
 #include "chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service.h"
 
+#include "base/containers/contains.h"
+#include "base/i18n/message_formatter.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/types/expected_macros.h"
+#include "chrome/browser/extensions/api/tabs/tabs_api.h"
+#include "chrome/browser/notifications/notification_display_service.h"
+#include "chrome/browser/notifications/notification_display_service_factory.h"
+#include "chrome/browser/notifications/notification_handler.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
+#include "chrome/browser/ui/web_applications/app_browser_controller.h"
+#include "chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_delegate.h"
+#include "chrome/browser/web_applications/web_app_filter.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chrome/browser/web_applications/web_app_utils.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/webapps/common/web_app_id.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/message_center/public/cpp/notification.h"
+
+namespace {
+
+constexpr std::string_view
+    kIsolatedWebAppsOpenedTabsCounterNotificationPattern =
+        "isolated_web_apps_opened_tabs_counter_notification_%s";
+
+std::string GetAppName(Profile* profile, const webapps::AppId& app_id) {
+  return web_app::WebAppProvider::GetForWebApps(profile)
+      ->registrar_unsafe()
+      .GetAppShortName(app_id);
+}
+
+std::string GetNotificationIdForApp(const webapps::AppId& app_id) {
+  return base::StringPrintf(
+      kIsolatedWebAppsOpenedTabsCounterNotificationPattern, app_id);
+}
+
+}  // namespace
+
 IsolatedWebAppsOpenedTabsCounterService::
     IsolatedWebAppsOpenedTabsCounterService(Profile* profile)
-    : profile_(*profile) {}
+    : profile_(*profile) {
+  for (Browser* browser : *BrowserList::GetInstance()) {
+    if (browser->profile() == profile) {
+      browser->tab_strip_model()->AddObserver(this);
+    }
+  }
+  browser_list_observation_.Observe(BrowserList::GetInstance());
+}
 
 IsolatedWebAppsOpenedTabsCounterService::
     ~IsolatedWebAppsOpenedTabsCounterService() = default;
+
+void IsolatedWebAppsOpenedTabsCounterService::Shutdown() {
+  for (const auto& [app_id, _] : app_window_counts_) {
+    NotificationDisplayServiceFactory::GetForProfile(profile())->Close(
+        NotificationHandler::Type::TRANSIENT, GetNotificationIdForApp(app_id));
+  }
+
+  app_window_counts_.clear();
+  opened_by_app_map_.clear();
+}
+
+void IsolatedWebAppsOpenedTabsCounterService::OnBrowserAdded(Browser* browser) {
+  if (browser->profile() == profile()) {
+    browser->tab_strip_model()->AddObserver(this);
+  }
+}
+
+void IsolatedWebAppsOpenedTabsCounterService::OnBrowserRemoved(
+    Browser* browser) {
+  if (browser->profile() == profile()) {
+    browser->tab_strip_model()->RemoveObserver(this);
+  }
+}
+
+void IsolatedWebAppsOpenedTabsCounterService::OnTabStripModelChanged(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange& change,
+    const TabStripSelectionChange& selection) {
+  switch (change.type()) {
+    case TabStripModelChange::Type::kInserted:
+      for (const auto& content_with_id : change.GetInsert()->contents) {
+        HandleOpenerCountIfTracked(content_with_id.contents);
+      }
+      break;
+    case TabStripModelChange::Type::kRemoved:
+      for (const auto& content_with_id : change.GetRemove()->contents) {
+        // We only want to decrease the count if the tab was deleted, but not
+        // when moved to another tab group.
+        if (content_with_id.remove_reason ==
+            TabStripModelChange::RemoveReason::kDeleted) {
+          HandleTabOrWindowClosure(content_with_id.contents);
+        }
+      }
+      break;
+    default:
+      break;
+  }
+}
+
+void IsolatedWebAppsOpenedTabsCounterService::HandleOpenerCountIfTracked(
+    content::WebContents* contents) {
+  ASSIGN_OR_RETURN(webapps::AppId opener_app_id,
+                   MaybeGetOpenerIsolatedWebAppId(contents), [] {});
+
+  if (base::Contains(opened_by_app_map_, contents)) {
+    return;
+  }
+
+  IncrementWindowCountForApp(opener_app_id);
+  opened_by_app_map_[contents] = opener_app_id;
+  UpdateOrRemoveNotificationForOpener(opener_app_id);
+}
+
+void IsolatedWebAppsOpenedTabsCounterService::HandleTabOrWindowClosure(
+    content::WebContents* contents) {
+  // If WebContents were not opened by an IWA then there is nothing to do.
+  if (!base::Contains(opened_by_app_map_, contents)) {
+    return;
+  }
+  // Stop tracking closed WebContents and update the count of opened child
+  // WebContents for its opener.
+  webapps::AppId opener_app_id = opened_by_app_map_[contents];
+  opened_by_app_map_.erase(contents);
+  DecrementWindowCountForApp(opener_app_id);
+  UpdateOrRemoveNotificationForOpener(opener_app_id);
+}
+
+std::optional<webapps::AppId>
+IsolatedWebAppsOpenedTabsCounterService::MaybeGetOpenerIsolatedWebAppId(
+    content::WebContents* contents) {
+  content::RenderFrameHost* opener_rfh = contents->GetOpener();
+  if (!opener_rfh) {
+    return std::nullopt;
+  }
+
+  content::WebContents* opener_web_contents =
+      content::WebContents::FromRenderFrameHost(opener_rfh);
+
+  if (!opener_web_contents) {
+    return std::nullopt;
+  }
+
+  // Check if the opener is an IWA that is not policy-installed.
+  web_app::WebAppProvider* provider =
+      web_app::WebAppProvider::GetForWebApps(profile());
+
+  const webapps::AppId* app_id =
+      web_app::WebAppTabHelper::GetAppId(opener_web_contents);
+  if (app_id && provider->registrar_unsafe().IsIsolated(*app_id) &&
+      !provider->registrar_unsafe().AppMatches(
+          *app_id, web_app::WebAppFilter::PolicyInstalledIsolatedWebApp())) {
+    return *app_id;
+  }
+
+  return std::nullopt;
+}
+
+void IsolatedWebAppsOpenedTabsCounterService::IncrementWindowCountForApp(
+    const webapps::AppId& app_id) {
+  app_window_counts_[app_id]++;
+}
+
+void IsolatedWebAppsOpenedTabsCounterService::DecrementWindowCountForApp(
+    const webapps::AppId& app_id) {
+  if (!base::Contains(app_window_counts_, app_id)) {
+    return;
+  }
+  app_window_counts_[app_id]--;
+  if (app_window_counts_[app_id] == 0) {
+    app_window_counts_.erase(app_id);
+  }
+}
+
+void IsolatedWebAppsOpenedTabsCounterService::
+    UpdateOrRemoveNotificationForOpener(const webapps::AppId& app_id) {
+  NotificationDisplayService* display_service =
+      NotificationDisplayServiceFactory::GetForProfile(profile());
+
+  auto it = app_window_counts_.find(app_id);
+
+  // Close the notification if the app isn't found or has only one window.
+  if (it == app_window_counts_.end() || it->second <= 1) {
+    display_service->Close(NotificationHandler::Type::TRANSIENT,
+                           GetNotificationIdForApp(app_id));
+  } else {
+    // Otherwise, update/create a notification for the app.
+    CreateAndDisplayNotification(app_id, it->second);
+  }
+}
+void IsolatedWebAppsOpenedTabsCounterService::CreateAndDisplayNotification(
+    const webapps::AppId& app_id,
+    int current_window_count) {
+  std::string app_name = GetAppName(profile(), app_id);
+  std::u16string title = l10n_util::GetStringFUTF16(
+      IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_TITLE,
+      base::UTF8ToUTF16(app_name));
+
+  std::u16string message = l10n_util::GetStringFUTF16Int(
+      IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_MESSAGE,
+      current_window_count);
+
+  message_center::RichNotificationData rich_data;
+  message_center::ButtonInfo content_settings_button_info;
+  content_settings_button_info.title = l10n_util::GetStringUTF16(
+      IDS_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_NOTIFICATION_BUTTON_SETTINGS);
+  rich_data.buttons.push_back(content_settings_button_info);
+
+  scoped_refptr<message_center::NotificationDelegate> delegate =
+      base::MakeRefCounted<IsolatedWebAppsOpenedTabsCounterServiceDelegate>(
+          profile(), app_id);
+
+  message_center::Notification notification(
+      message_center::NOTIFICATION_TYPE_SIMPLE, GetNotificationIdForApp(app_id),
+      title, message, /*icon=*/ui::ImageModel(),
+      /*display_source=*/std::u16string(), /*origin_url=*/GURL(),
+      /*notifier_id=*/
+      message_center::NotifierId(message_center::NotifierType::APPLICATION,
+                                 app_id),
+      /*optional_fields=*/rich_data, delegate);
+
+  NotificationDisplayServiceFactory::GetForProfile(profile())->Display(
+      NotificationHandler::Type::TRANSIENT, notification, /*metadata=*/nullptr);
+}
diff --git a/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service.h b/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service.h
index cceb09f..569ef12 100644
--- a/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service.h
+++ b/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service.h
@@ -1,3 +1,4 @@
+
 // 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.
@@ -5,10 +6,19 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_WINDOW_MANAGEMENT_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_SERVICE_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_WINDOW_MANAGEMENT_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_SERVICE_H_
 
-#include "base/memory/raw_ref.h"
-#include "components/keyed_service/core/keyed_service.h"
+#include <optional>
 
-class Profile;
+#include "base/containers/flat_map.h"
+#include "base/memory/raw_ref.h"
+#include "base/scoped_multi_source_observation.h"
+#include "base/scoped_observation.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_list_observer.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/webapps/common/web_app_id.h"
 
 // Isolated Web Apps (IWAs) are granted the "Pop-ups and Redirects"
 // content setting permission by default upon installation. As a result,
@@ -33,13 +43,46 @@
 //    notification is created and displayed.
 // 6. As tabs are closed, the count is decremented. The notification is updated
 //    if the count changes or removed if the count drops below 2.
-class IsolatedWebAppsOpenedTabsCounterService : public KeyedService {
+class IsolatedWebAppsOpenedTabsCounterService : public KeyedService,
+                                                public BrowserListObserver,
+                                                public TabStripModelObserver {
  public:
   explicit IsolatedWebAppsOpenedTabsCounterService(Profile* profile);
   ~IsolatedWebAppsOpenedTabsCounterService() override;
 
+  // KeyedService:
+  void Shutdown() override;
+
  private:
+  // BrowserListObserver:
+  void OnBrowserAdded(Browser* browser) override;
+  void OnBrowserRemoved(Browser* browser) override;
+
+  // TabStripModelObserver:
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override;
+
+  void HandleOpenerCountIfTracked(content::WebContents* contents);
+  void HandleTabOrWindowClosure(content::WebContents* contents);
+  std::optional<webapps::AppId> MaybeGetOpenerIsolatedWebAppId(
+      content::WebContents* contents);
+
+  void IncrementWindowCountForApp(const webapps::AppId& app_id);
+  void DecrementWindowCountForApp(const webapps::AppId& app_id);
+
+  void UpdateOrRemoveNotificationForOpener(const webapps::AppId& app_id);
+  void CreateAndDisplayNotification(const webapps::AppId& app_id,
+                                    int current_window_count);
+
+  Profile* profile() { return &profile_.get(); }
+
   const raw_ref<Profile> profile_;
+  base::flat_map<webapps::AppId, int> app_window_counts_;
+  base::flat_map<content::WebContents*, webapps::AppId> opened_by_app_map_;
+  base::ScopedObservation<BrowserList, BrowserListObserver>
+      browser_list_observation_{this};
 };
 
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_WINDOW_MANAGEMENT_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_SERVICE_H_
diff --git a/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_browsertest.cc
new file mode 100644
index 0000000..4f9cf68fc
--- /dev/null
+++ b/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_browsertest.cc
@@ -0,0 +1,315 @@
+// 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/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service.h"
+
+#include <string>
+#include <vector>
+
+#include "base/i18n/message_formatter.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/test_future.h"
+#include "chrome/browser/notifications/notification_display_service_tester.h"
+#include "chrome/browser/notifications/stub_notification_display_service.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.h"
+#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
+#include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h"
+#include "chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_factory.h"
+#include "chrome/browser/web_applications/test/web_app_test_utils.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/webapps/common/web_app_id.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "ui/base/l10n/l10n_util.h"
+
+class Browser;
+
+namespace {
+
+constexpr std::string kIsolatedApp1DefaultName = "IWA 1";
+constexpr std::string kIsolatedApp2DefaultName = "IWA 2";
+constexpr std::string_view kIsolatedAppVersion = "1.0.0";
+
+}  // namespace
+
+class IsolatedWebAppsOpenedTabsCounterServiceBrowserTest
+    : public web_app::IsolatedWebAppBrowserTestHarness {
+ public:
+  IsolatedWebAppsOpenedTabsCounterServiceBrowserTest() = default;
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+
+    display_service_tester_ =
+        std::make_unique<NotificationDisplayServiceTester>(profile());
+
+    isolated_web_apps_opened_tabs_counter_service_ =
+        IsolatedWebAppsOpenedTabsCounterServiceFactory::GetForProfile(
+            profile());
+  }
+
+  void TearDownOnMainThread() override {
+    isolated_web_apps_opened_tabs_counter_service_ = nullptr;
+    InProcessBrowserTest::TearDownOnMainThread();
+  }
+
+  Profile* profile() { return browser()->profile(); }
+
+  webapps::AppId InstallIsolatedWebApp(
+      std::string_view name = kIsolatedApp1DefaultName) {
+    auto app = web_app::IsolatedWebAppBuilder(
+                   web_app::ManifestBuilder().SetName(name).SetVersion(
+                       kIsolatedAppVersion))
+                   .BuildBundle();
+    return app->InstallChecked(profile()).app_id();
+  }
+
+  Browser* OpenIwaWindow(const webapps::AppId& app_id) {
+    Browser* app_browser =
+        web_app::LaunchWebAppBrowserAndWait(profile(), app_id);
+    EXPECT_TRUE(app_browser);
+    return app_browser;
+  }
+
+  content::WebContents* OpenChildWindowFromIwaBrowser(
+      content::WebContents* opener_contents,
+      const GURL& url) {
+    EXPECT_TRUE(opener_contents);
+
+    content::WebContentsAddedObserver windowed_observer;
+    EXPECT_TRUE(ExecJs(opener_contents->GetPrimaryMainFrame(),
+                       "window.open('" + url.spec() + "');"));
+
+    content::WebContents* new_contents = windowed_observer.GetWebContents();
+    WaitForLoadStopWithoutSuccessCheck(new_contents);
+
+    return new_contents;
+  }
+
+  content::WebContents* OpenChildWindowAndExpectNotificationContents(
+      content::WebContents* opener_contents,
+      const GURL& child_url,
+      const webapps::AppId& app_id,
+      int expected_window_count_in_notification,
+      base::test::TestFuture<void>& notification_added_future,
+      std::string app_display_name = kIsolatedApp1DefaultName) {
+    content::WebContents* child_contents =
+        OpenChildWindowFromIwaBrowser(opener_contents, child_url);
+
+    EXPECT_TRUE(notification_added_future.Wait());
+    CheckNotificationContents(app_id, expected_window_count_in_notification,
+                              app_display_name);
+
+    notification_added_future.Clear();
+    return child_contents;
+  }
+
+  std::vector<message_center::Notification> GetDisplayedNotifications() {
+    return display_service_tester_->GetDisplayedNotificationsForType(
+        NotificationHandler::Type::TRANSIENT);
+  }
+
+  size_t GetNotificationCount() { return GetDisplayedNotifications().size(); }
+
+  void CheckNotificationContents(
+      const webapps::AppId& app_id,
+      int opened_window_count,
+      std::string app_display_name = kIsolatedApp1DefaultName) {
+    std::optional<message_center::Notification> notification =
+        display_service_tester_->GetNotification(
+            GetNotificationIdForApp(app_id));
+    ASSERT_TRUE(notification.has_value());
+
+    std::u16string expected_title =
+        base::UTF8ToUTF16(app_display_name + " has opened multiple windows.");
+    EXPECT_EQ(notification->title(), expected_title);
+
+    std::u16string expected_message =
+        base::NumberToString16(opened_window_count) +
+        u" new Chrome windows or tabs have been opened by this app. You "
+        u"can manage this behavior under \"Pop-ups and Redirects\" permission.";
+    EXPECT_EQ(notification->message(), expected_message);
+
+    ASSERT_EQ(notification->buttons().size(), 1u);
+    EXPECT_EQ(notification->buttons()[0].title, u"Change permissions");
+  }
+
+ protected:
+  std::string GetNotificationIdForApp(const webapps::AppId& app_id) {
+    return "isolated_web_apps_opened_tabs_counter_notification_" + app_id;
+  }
+
+  raw_ptr<IsolatedWebAppsOpenedTabsCounterService>
+      isolated_web_apps_opened_tabs_counter_service_;
+  std::unique_ptr<NotificationDisplayServiceTester> display_service_tester_;
+};
+
+IN_PROC_BROWSER_TEST_F(IsolatedWebAppsOpenedTabsCounterServiceBrowserTest,
+                       SingleIwaMultipleWebContentsOpenedServiceNotification) {
+  webapps::AppId app_id = InstallIsolatedWebApp();
+  content::WebContents* iwa_opener_web_contents =
+      OpenIwaWindow(app_id)->tab_strip_model()->GetActiveWebContents();
+
+  base::test::TestFuture<void> notification_added_future;
+  display_service_tester_->SetNotificationAddedClosure(
+      notification_added_future.GetRepeatingCallback());
+
+  content::WebContents* iwa_child_browser1_contents =
+      OpenChildWindowFromIwaBrowser(iwa_opener_web_contents,
+                                    GURL("https://example.com/app1/child1"));
+  ASSERT_FALSE(notification_added_future.IsReady());
+
+  content::WebContents* iwa_child_browser2_contents =
+      OpenChildWindowAndExpectNotificationContents(
+          iwa_opener_web_contents, GURL("https://example.com/app1/child2"),
+          app_id,
+          /*expected_window_count_in_notification=*/2,
+          notification_added_future, kIsolatedApp1DefaultName);
+
+  OpenChildWindowAndExpectNotificationContents(
+      iwa_opener_web_contents, GURL("https://example.com/app1/child3"), app_id,
+      /*expected_window_count_in_notification=*/3, notification_added_future,
+      kIsolatedApp1DefaultName);
+
+  base::test::TestFuture<void> notification_closed_future;
+  display_service_tester_->SetNotificationClosedClosure(
+      notification_closed_future.GetRepeatingCallback());
+
+  iwa_child_browser1_contents->Close();
+  CheckNotificationContents(app_id, /*opened_window_count=*/2);
+  iwa_child_browser2_contents->Close();
+  ASSERT_TRUE(notification_closed_future.Wait());
+  notification_closed_future.Clear();
+  EXPECT_EQ(0u, GetNotificationCount());
+}
+
+IN_PROC_BROWSER_TEST_F(IsolatedWebAppsOpenedTabsCounterServiceBrowserTest,
+                       MultipleOpenerMultipleNotifiations) {
+  webapps::AppId app1_id = InstallIsolatedWebApp();
+  webapps::AppId app2_id = InstallIsolatedWebApp(kIsolatedApp2DefaultName);
+
+  Browser* iwa1_browser = OpenIwaWindow(app1_id);
+  Browser* iwa2_browser = OpenIwaWindow(app2_id);
+
+  // Child WebContents of the first app.
+  base::test::TestFuture<void> notification_added_future;
+  display_service_tester_->SetNotificationAddedClosure(
+      notification_added_future.GetRepeatingCallback());
+
+  OpenChildWindowFromIwaBrowser(
+      iwa1_browser->tab_strip_model()->GetActiveWebContents(),
+      GURL("https://example.com/app1/child1"));
+  ASSERT_FALSE(notification_added_future.IsReady());
+
+  OpenChildWindowAndExpectNotificationContents(
+      iwa1_browser->tab_strip_model()->GetActiveWebContents(),
+      GURL("https://example.com/app1/child2"), app1_id,
+      /*expected_window_count_in_notification=*/2, notification_added_future,
+      kIsolatedApp1DefaultName);
+
+  // Child WebContents of the second app.
+  OpenChildWindowFromIwaBrowser(
+      iwa2_browser->tab_strip_model()->GetActiveWebContents(),
+      GURL("https://example.com/app2/child1"));
+  ASSERT_FALSE(notification_added_future.IsReady());
+
+  OpenChildWindowAndExpectNotificationContents(
+      iwa2_browser->tab_strip_model()->GetActiveWebContents(),
+      GURL("https://example.com/app2/child2"), app2_id,
+      /*expected_window_count_in_notification=*/2, notification_added_future,
+      kIsolatedApp2DefaultName);
+  EXPECT_EQ(2u, GetNotificationCount());
+
+  web_app::CloseAndWait(iwa1_browser);
+  web_app::CloseAndWait(iwa2_browser);
+
+  // Notifications should remain even after parent window closures.
+  EXPECT_EQ(2u, GetNotificationCount());
+}
+
+IN_PROC_BROWSER_TEST_F(IsolatedWebAppsOpenedTabsCounterServiceBrowserTest,
+                       MoveOpenedTabToAnotherBrowserDoesNotAffectCounters) {
+  webapps::AppId app1_id = InstallIsolatedWebApp(kIsolatedApp1DefaultName);
+  content::WebContents* iwa1_opener_contents =
+      OpenIwaWindow(app1_id)->tab_strip_model()->GetActiveWebContents();
+
+  webapps::AppId app2_id = InstallIsolatedWebApp(kIsolatedApp2DefaultName);
+  content::WebContents* iwa2_opener_contents =
+      OpenIwaWindow(app2_id)->tab_strip_model()->GetActiveWebContents();
+
+  base::test::TestFuture<void> notification_added_future;
+  display_service_tester_->SetNotificationAddedClosure(
+      notification_added_future.GetRepeatingCallback());
+
+  // Open two child windows from IWA 1.
+  // This should trigger a notification for IWA 1.
+  content::WebContents* app1_child1_contents = OpenChildWindowFromIwaBrowser(
+      iwa1_opener_contents, GURL("https://example.com/app1/child1"));
+  ASSERT_FALSE(notification_added_future.IsReady());
+
+  OpenChildWindowAndExpectNotificationContents(
+      iwa1_opener_contents, GURL("https://example.com/app1/child2"), app1_id,
+      /*expected_window_count_in_notification=*/2, notification_added_future,
+      kIsolatedApp1DefaultName);
+  EXPECT_EQ(1u, GetNotificationCount());
+
+  // Open two child windows from IWA 2.
+  // This should trigger a notification for IWA 2.
+  OpenChildWindowFromIwaBrowser(iwa2_opener_contents,
+                                GURL("https://example.com/app2/child1"));
+  ASSERT_FALSE(notification_added_future.IsReady());
+
+  OpenChildWindowAndExpectNotificationContents(
+      iwa2_opener_contents, GURL("https://example.com/app2/child2"), app2_id,
+      /*expected_window_count_in_notification=*/2, notification_added_future,
+      kIsolatedApp2DefaultName);
+  EXPECT_EQ(2u, GetNotificationCount());
+
+  ASSERT_TRUE(display_service_tester_->GetNotification(
+      GetNotificationIdForApp(app1_id)));
+  ASSERT_TRUE(display_service_tester_->GetNotification(
+      GetNotificationIdForApp(app2_id)));
+
+  Browser* another_browser = CreateBrowser(profile());
+
+  // Move one of IWA 1's child windows (app1_child1_contents) to the
+  // regular_browser.
+  tabs::TabInterface* tab =
+      tabs::TabInterface::GetFromContents(app1_child1_contents);
+  Browser* original_app1_child1_browser =
+      tab->GetBrowserWindowInterface()->GetBrowserForMigrationOnly();
+
+  int tab_index =
+      original_app1_child1_browser->tab_strip_model()->GetIndexOfWebContents(
+          app1_child1_contents);
+  std::unique_ptr<content::WebContents> extracted_contents =
+      original_app1_child1_browser->tab_strip_model()
+          ->DetachWebContentsAtForInsertion(tab_index);
+
+  another_browser->tab_strip_model()->AppendWebContents(
+      std::move(extracted_contents), true);
+
+  // Verify app1_child1_contents is now in another_browser.
+  ASSERT_EQ(
+      another_browser->tab_strip_model()->GetActiveWebContents()->GetURL(),
+      GURL("https://example.com/app1/child1"));
+
+  // No WebContents were destroyed, so no notification change events should
+  // fire.
+  EXPECT_EQ(2u, GetNotificationCount());
+  CheckNotificationContents(app1_id, /*opened_window_count=*/2,
+                            kIsolatedApp1DefaultName);
+  CheckNotificationContents(app2_id, /*opened_window_count=*/2,
+                            kIsolatedApp2DefaultName);
+}
diff --git a/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_delegate.cc b/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_delegate.cc
new file mode 100644
index 0000000..a23a4430
--- /dev/null
+++ b/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_delegate.cc
@@ -0,0 +1,42 @@
+// 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/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_delegate.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/web_applications/app_browser_controller.h"
+#include "chrome/browser/ui/web_applications/web_app_ui_manager_impl.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
+
+namespace {
+
+constexpr int kSettingsButtonIndex = 0;
+
+void NavigateToAppSettings(Profile* profile, const webapps::AppId& app_id) {
+  web_app::WebAppProvider::GetForWebApps(profile)
+      ->ui_manager()
+      .ShowWebAppSettings(app_id);
+}
+
+}  // namespace
+
+IsolatedWebAppsOpenedTabsCounterServiceDelegate::
+    IsolatedWebAppsOpenedTabsCounterServiceDelegate(
+        Profile* profile,
+        const webapps::AppId& app_id)
+    : profile_(*profile), app_id_(app_id) {}
+
+void IsolatedWebAppsOpenedTabsCounterServiceDelegate::Click(
+    const std::optional<int>& button_index,
+    const std::optional<std::u16string>& reply) {
+  if (button_index == kSettingsButtonIndex) {
+    NavigateToAppSettings(&profile_.get(), app_id_);
+  }
+}
diff --git a/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_delegate.h b/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_delegate.h
new file mode 100644
index 0000000..73d3bc2
--- /dev/null
+++ b/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_delegate.h
@@ -0,0 +1,37 @@
+// 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_WEB_APPLICATIONS_ISOLATED_WEB_APPS_WINDOW_MANAGEMENT_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_SERVICE_DELEGATE_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_WINDOW_MANAGEMENT_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_SERVICE_DELEGATE_H_
+
+#include "base/memory/raw_ptr.h"
+#include "components/webapps/common/web_app_id.h"
+#include "ui/message_center/public/cpp/notification_delegate.h"
+
+class Profile;
+
+class IsolatedWebAppsOpenedTabsCounterServiceDelegate
+    : public message_center::NotificationDelegate {
+ public:
+  IsolatedWebAppsOpenedTabsCounterServiceDelegate(Profile* profile,
+                                                  const webapps::AppId& app_id);
+
+  IsolatedWebAppsOpenedTabsCounterServiceDelegate(
+      const IsolatedWebAppsOpenedTabsCounterServiceDelegate&) = delete;
+  IsolatedWebAppsOpenedTabsCounterServiceDelegate& operator=(
+      const IsolatedWebAppsOpenedTabsCounterServiceDelegate&) = delete;
+
+ protected:
+  ~IsolatedWebAppsOpenedTabsCounterServiceDelegate() override = default;
+
+  // message_center::NotificationDelegate:
+  void Click(const std::optional<int>& button_index,
+             const std::optional<std::u16string>& reply) override;
+
+ private:
+  const raw_ref<Profile> profile_;
+  const webapps::AppId app_id_;
+};
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_WINDOW_MANAGEMENT_ISOLATED_WEB_APPS_OPENED_TABS_COUNTER_SERVICE_DELEGATE_H_
diff --git a/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_factory.cc b/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_factory.cc
index 06b911f2..74780d3 100644
--- a/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_factory.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_factory.cc
@@ -21,7 +21,7 @@
 namespace {
 BASE_FEATURE(kIsolatedWebAppsOpenedTabsCounterServiceNotification,
              "IsolatedWebAppsOpenedTabsCounterServiceNotification",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 }
 
 IsolatedWebAppsOpenedTabsCounterServiceFactory*
diff --git a/chrome/browser/webauthn/enclave_authenticator_browsertest.cc b/chrome/browser/webauthn/enclave_authenticator_browsertest.cc
index 6179f21..8da8f7c0 100644
--- a/chrome/browser/webauthn/enclave_authenticator_browsertest.cc
+++ b/chrome/browser/webauthn/enclave_authenticator_browsertest.cc
@@ -86,6 +86,7 @@
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -950,6 +951,12 @@
 }
 
 IN_PROC_BROWSER_TEST_F(EnclaveAuthenticatorBrowserTest, NonWebauthnRequest) {
+  if (base::FeatureList::IsEnabled(
+          blink::features::kSecurePaymentConfirmationBrowserBoundKeys)) {
+    GTEST_SKIP() << "With kSecurePaymentConfirmationBrowserBoundKeys the "
+                    "SecurePaymentConfirmationService directs the request to "
+                    "the internal authenticator.";
+  }
   if (!base::FeatureList::IsEnabled(features::kSecurePaymentConfirmation)) {
     // SPC is not enabled in this configuration and so the `payment` extension
     // in the Javascript will be ignored.
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 5c50f911..1d346c6 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1751629895-a0e1063d20f4cdb32d818bbb530825a14e83769d-a45b004cd101710300ba4667e872f1d385c36537.profdata
+chrome-android32-main-1751651981-2d256c5c7de1d8c198899dc6214556a10d77b5f8-1446001e5083528a76d05852435a634a2f969ae0.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 315fdec..af085344 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1751624912-a64b16641c2546b496501f7e9052f9311a550ba5-75d02344ab4feb8ec4473268374a01f6ab21d977.profdata
+chrome-android64-main-1751648639-3e82b0955be92510edc64400ec206d09a721e28b-ca6f87d449e32c21e1ffd80ef4d66dc9555a75c8.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 53c967d..4f607925 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1751629895-8d269dfe47f1ac8cf75a3fbf744bb5c44370994b-a45b004cd101710300ba4667e872f1d385c36537.profdata
+chrome-mac-arm-main-1751651981-d28316d4c4e236887b99790985ebe24fc2430a42-1446001e5083528a76d05852435a634a2f969ae0.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 678a0a7..dbd87fab 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1751608768-d7e867faad9529623dcf32a065973bfcde01b448-2e8d92341e8709bc95ea42c9acb0cd5599db9372.profdata
+chrome-win-arm64-main-1751629895-1d2fb388286356a4e2202ab1360377c966566f17-a45b004cd101710300ba4667e872f1d385c36537.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 5290f1f..a3545ae 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1751608768-1690c3ebfc29909ec3fffc842712df25dbd718cb-2e8d92341e8709bc95ea42c9acb0cd5599db9372.profdata
+chrome-win32-main-1751629895-e60877b1a401100e39e1a364a7b8205cf2a634f0-a45b004cd101710300ba4667e872f1d385c36537.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 4399301..57f5ffd 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1751597292-840d870b0bd5df1abd7bd442f449fbd42074071b-4f926adf0e474c41f6d4cd90bd83df5f148a7595.profdata
+chrome-win64-main-1751629895-65fa60aad17089bb1b1e192b45c20e746c448a56-a45b004cd101710300ba4667e872f1d385c36537.profdata
diff --git a/chrome/renderer/controlled_frame/controlled_frame_extensions_renderer_api_provider.cc b/chrome/renderer/controlled_frame/controlled_frame_extensions_renderer_api_provider.cc
index 1bb461c..d783235 100644
--- a/chrome/renderer/controlled_frame/controlled_frame_extensions_renderer_api_provider.cc
+++ b/chrome/renderer/controlled_frame/controlled_frame_extensions_renderer_api_provider.cc
@@ -35,6 +35,8 @@
                              IDR_CONTROLLED_FRAME_INTERNAL_CUSTOM_BINDINGS_JS);
   source_map->RegisterSource("controlledFrameWebRequest",
                              IDR_CONTROLLED_FRAME_WEB_REQUEST_JS);
+  source_map->RegisterSource("controlledFrameContextMenus",
+                             IDR_CONTROLLED_FRAME_CONTEXT_MENUS_JS);
 }
 
 void ControlledFrameExtensionsRendererAPIProvider::
diff --git a/chrome/renderer/resources/controlled_frame/controlled_frame_context_menus.js b/chrome/renderer/resources/controlled_frame/controlled_frame_context_menus.js
new file mode 100644
index 0000000..376318df
--- /dev/null
+++ b/chrome/renderer/resources/controlled_frame/controlled_frame_context_menus.js
@@ -0,0 +1,201 @@
+// 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.
+
+// This file defines a ControlledFrameContextMenus class that wraps WebView's
+// ContextMenus implementation and provides a more web-friendly API that uses
+// EventTarget and Web naming conventions for enums. ControlledFrameContextMenus
+// doesn't provide any new functionality; it translates its API to the
+// WebView API.
+
+const CHROME_WEB_VIEW_CONTEXT_MENUS_PROMISE_API_METHODS =
+    require('chromeWebViewContextMenusApiMethods').PROMISE_API_METHODS;
+const logging = requireNative('logging');
+const promiseWrap = require('guestViewContainerElement').promiseWrap;
+const utils = require('utils');
+const $Headers = require('safeMethods').SafeMethods.$Headers;
+const WebViewContextMenusImpl =
+    require('chromeWebView').WebViewContextMenusImpl;
+const ControlledFrameInternal = getInternalApi('controlledFrameInternal');
+
+function ControlledFrameContextMenusImpl(webView, viewInstanceId) {
+  this.viewInstanceId_ = viewInstanceId;
+  $Function.apply(WebViewContextMenusImpl, this, [webView, viewInstanceId]);
+}
+$Object.setPrototypeOf(
+    ControlledFrameContextMenusImpl.prototype,
+    WebViewContextMenusImpl.prototype);
+
+function getCallbackIndex(name) {
+  let foundMethodDetails = undefined;
+  for (const methodDetails of
+           CHROME_WEB_VIEW_CONTEXT_MENUS_PROMISE_API_METHODS) {
+    if (methodDetails.name === name) {
+      foundMethodDetails = methodDetails;
+      break;
+    }
+  }
+  logging.CHECK(
+      foundMethodDetails !== undefined,
+      'could not find context menus method details');
+  return foundMethodDetails.callbackIndex;
+}
+
+ControlledFrameContextMenusImpl.prototype.convertMethodToPromiseBased =
+    function(handler, name) {
+  let callbackIndex = getCallbackIndex(name);
+  // TODO(crbug.com/378956568): Verify these methods don't require an instance
+  // ID check.
+  function verifyEnvironment(reject) {
+    return true;
+  }
+  return function(var_args) {
+    return promiseWrap(
+        handler.bind(this), arguments, callbackIndex, verifyEnvironment,
+        /*callbackAllowed=*/ true);
+  };
+}
+
+    // Controlled Frame has its own internal definition of Context Menus
+    // create().
+    ControlledFrameContextMenusImpl.prototype.createImpl =
+        function() {
+  const args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments));
+  return $Function.apply(
+      ControlledFrameInternal.contextMenusCreate, null, args);
+}
+
+        // Controlled Frame has its own internal definition of Context Menus
+        // update().
+        ControlledFrameContextMenusImpl.prototype.updateImpl =
+            function() {
+  let args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments));
+  return $Function.apply(
+      ControlledFrameInternal.contextMenusUpdate, null, args);
+}
+
+            ControlledFrameContextMenusImpl.prototype.create =
+                ControlledFrameContextMenusImpl.prototype
+                    .convertMethodToPromiseBased(
+                        ControlledFrameContextMenusImpl.prototype.createImpl,
+                        'create');
+
+ControlledFrameContextMenusImpl.prototype.remove =
+    ControlledFrameContextMenusImpl.prototype.convertMethodToPromiseBased(
+        WebViewContextMenusImpl.prototype.remove, 'remove');
+
+ControlledFrameContextMenusImpl.prototype.removeAll =
+    ControlledFrameContextMenusImpl.prototype.convertMethodToPromiseBased(
+        WebViewContextMenusImpl.prototype.removeAll, 'removeAll');
+
+ControlledFrameContextMenusImpl.prototype.update =
+    ControlledFrameContextMenusImpl.prototype.convertMethodToPromiseBased(
+        ControlledFrameContextMenusImpl.prototype.updateImpl, 'update');
+
+function createEventInfo(contextMenusEventName) {
+  return {
+    contextMenusEventName,
+    registeredListeners: $Object.create(null),
+  };
+}
+
+function webifyEventDetails(event) {
+  // TODO(crbug.com/429109629): Clarify the changes needed for the event and
+  // implement them.
+  return event;
+}
+
+class ControlledFrameContextMenus extends EventTarget {
+  #contextMenusImpl;
+
+  #events = {
+    show: createEventInfo('onShow'),
+    click: createEventInfo('onClicked'),
+  };
+
+  constructor(webView, viewInstanceId) {
+    super();
+    this.#contextMenusImpl =
+        new ControlledFrameContextMenusImpl(webView, viewInstanceId);
+  }
+
+  // TODO(crbug.com/429109311): Implement enum mappings.
+  create(...args) {
+    return this.#contextMenusImpl.create(...args);
+  }
+  update(...args) {
+    return this.#contextMenusImpl.update(...args);
+  }
+  remove(...args) {
+    return this.#contextMenusImpl.remove(...args);
+  }
+  removeAll(...args) {
+    return this.#contextMenusImpl.removeAll(...args);
+  }
+
+  addEventListener(type, listener, options) {
+    const eventInfo = this.#events[type];
+    if (eventInfo === undefined) {
+      $Function.apply(super.addEventListener, this, arguments);
+      return;
+    }
+
+    const contextMenusListener =
+        $Function.bind(this.#onEvent, this, type, listener);
+    eventInfo.registeredListeners[listener] = contextMenusListener;
+    this.#contextMenusImpl[eventInfo.contextMenusEventName].addListener(
+        contextMenusListener);
+  }
+
+  removeEventListener(type, listener, options) {
+    const eventInfo = this.#events[type];
+    if (eventInfo === undefined) {
+      $Function.apply(super.removeEventListener, this, arguments);
+      return;
+    }
+
+    if (listener in eventInfo.registeredListeners) {
+      this.#contextMenusImpl[eventInfo.contextMenusEventName].removeListener(
+          eventInfo.registeredListeners[listener]);
+      delete eventInfo.registeredListeners[listener];
+    }
+  }
+
+  #onEvent(type, listener, details) {
+    let menuEvent;
+    const webDetails = webifyEventDetails(details);
+    switch (type) {
+      case 'show':
+        menuEvent = new ShowEvent(webDetails);
+        break;
+      case 'click':
+        menuEvent = new ClickEvent(webDetails);
+        break;
+    }
+
+    const listenerReturnValue = listener(menuEvent);
+    if (listenerReturnValue instanceof Promise) {
+      console.error(`ContextMenus ${type} handlers must be synchronous`);
+    }
+    return {__proto__: null};
+  }
+}
+
+class ShowEvent extends Event {
+  constructor(details) {
+    super('show');
+    $Object.assign(this, details);
+    $Object.freeze(this);
+  }
+}
+
+class ClickEvent extends Event {
+  constructor(details) {
+    super('click');
+    $Object.assign(this, details);
+    $Object.freeze(this);
+  }
+}
+
+// Exports.
+exports.$set('ControlledFrameContextMenus', ControlledFrameContextMenus);
diff --git a/chrome/renderer/resources/controlled_frame/controlled_frame_impl.js b/chrome/renderer/resources/controlled_frame/controlled_frame_impl.js
index c3536ad..b47b34fd 100644
--- a/chrome/renderer/resources/controlled_frame/controlled_frame_impl.js
+++ b/chrome/renderer/resources/controlled_frame/controlled_frame_impl.js
@@ -2,93 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-var CHROME_WEB_VIEW_CONTEXT_MENUS_PROMISE_API_METHODS =
-    require('chromeWebViewContextMenusApiMethods').PROMISE_API_METHODS;
 var ChromeWebViewImpl = require('chromeWebView').ChromeWebViewImpl;
-var WebViewContextMenusImpl = require('chromeWebView').WebViewContextMenusImpl;
 var ControlledFrameInternal = getInternalApi('controlledFrameInternal');
 var ControlledFrameEvents =
     require('controlledFrameEvents').ControlledFrameEvents;
-var logging = requireNative('logging');
-var promiseWrap = require('guestViewContainerElement').promiseWrap;
-var utils = require('utils');
-
-function ControlledFrameContextMenusImpl(viewInstanceId) {
-  this.viewInstanceId_ = viewInstanceId;
-}
-$Object.setPrototypeOf(ControlledFrameContextMenusImpl.prototype,
-  WebViewContextMenusImpl.prototype);
-
-function getCallbackIndex(name) {
-  var foundMethodDetails = undefined;
-  for (const methodDetails of
-        CHROME_WEB_VIEW_CONTEXT_MENUS_PROMISE_API_METHODS) {
-    if (methodDetails.name === name) {
-      foundMethodDetails = methodDetails;
-      break;
-    }
-  }
-  logging.CHECK(
-      foundMethodDetails !== undefined,
-      "could not find context menus method details");
-  return foundMethodDetails.callbackIndex;
-}
-
-ControlledFrameContextMenusImpl.prototype.convertMethodToPromiseBased =
-    function(handler, name) {
-  var callbackIndex = getCallbackIndex(name);
-  // TODO(crbug.com/378956568): Verify these methods don't require an instance
-  // ID check.
-  function verifyEnvironment(reject) { return true; }
-  return function(var_args) {
-    return promiseWrap(handler.bind(this), arguments, callbackIndex,
-                       verifyEnvironment, /*callbackAllowed=*/true);
-  };
-}
-
-// Controlled Frame has its own internal definition of Context Menus create().
-ControlledFrameContextMenusImpl.prototype.createImpl = function() {
-  const args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments));
-  return $Function.apply(
-      ControlledFrameInternal.contextMenusCreate, null, args);
-}
-
-// Controlled Frame has its own internal definition of Context Menus update().
-ControlledFrameContextMenusImpl.prototype.updateImpl = function() {
-  var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments));
-  return $Function.apply(
-    ControlledFrameInternal.contextMenusUpdate, null, args);
-}
-
-ControlledFrameContextMenusImpl.prototype.create =
-    ControlledFrameContextMenusImpl.prototype.convertMethodToPromiseBased(
-        ControlledFrameContextMenusImpl.prototype.createImpl, "create");
-
-ControlledFrameContextMenusImpl.prototype.remove =
-    ControlledFrameContextMenusImpl.prototype.convertMethodToPromiseBased(
-        WebViewContextMenusImpl.prototype.remove, "remove");
-
-ControlledFrameContextMenusImpl.prototype.removeAll =
-    ControlledFrameContextMenusImpl.prototype.convertMethodToPromiseBased(
-        WebViewContextMenusImpl.prototype.removeAll,
-        "removeAll");
-
-ControlledFrameContextMenusImpl.prototype.update =
-    ControlledFrameContextMenusImpl.prototype.convertMethodToPromiseBased(
-      ControlledFrameContextMenusImpl.prototype.updateImpl, "update");
-
-function ControlledFrameContextMenus() {
-  privates(ControlledFrameContextMenus).constructPrivate(this, arguments);
-}
-
-utils.expose(ControlledFrameContextMenus, ControlledFrameContextMenusImpl, {
-  functions: [
-    'create',
-    'remove',
-    'removeAll',
-    'update',
-  ]
-});
+var ControlledFrameContextMenus =
+  require('controlledFrameContextMenus').ControlledFrameContextMenus;
 
 class ControlledFrameImpl extends ChromeWebViewImpl {
   constructor(webviewElement) {
@@ -100,7 +19,7 @@
   }
 
   createWebViewContextMenus() {
-    return new ControlledFrameContextMenus(this.viewInstanceId);
+    return new ControlledFrameContextMenus(this, this.viewInstanceId);
   }
 
   setClientHintsUABrandEnabled(enable) {
diff --git a/chrome/renderer/resources/extensions/web_view/chrome_web_view.js b/chrome/renderer/resources/extensions/web_view/chrome_web_view.js
index 7f13fd5..4255ef5 100644
--- a/chrome/renderer/resources/extensions/web_view/chrome_web_view.js
+++ b/chrome/renderer/resources/extensions/web_view/chrome_web_view.js
@@ -54,10 +54,8 @@
 }
 
 // This event is exposed as <webview>.contextMenus.onShow.
-function createContextMenusOnContextMenuEvent(webViewInstanceId,
-                                              opt_eventName,
-                                              opt_argSchemas,
-                                              opt_eventOptions) {
+function createContextMenusOnContextMenuEvent(
+    webViewInstanceId, opt_eventName, opt_argSchemas, opt_eventOptions) {
   var subEventName = GetUniqueSubEventName(opt_eventName);
   var newEvent =
       bindingUtil.createCustomEvent(subEventName, false, false);
@@ -65,8 +63,7 @@
   var view = GuestViewInternalNatives.GetViewFromID(webViewInstanceId);
   if (view) {
     view.events.addScopedListener(
-        ContextMenusHandlerEvent,
-        $Function.bind(function(e) {
+        ContextMenusHandlerEvent, $Function.bind(function(e) {
           var defaultPrevented = false;
           var event = {
             preventDefault: function() { defaultPrevented = true; }
@@ -76,16 +73,16 @@
           $Function.apply(newEvent.dispatch, newEvent, [event]);
 
           if (!defaultPrevented) {
-          // TODO(lazyboy): Remove |items| parameter completely from
-          // ChromeWebView.showContextMenu as we don't do anything useful with
-          // it currently.
-          var items = [];
-          var guestInstanceId = GuestViewInternalNatives.
-              GetViewFromID(webViewInstanceId).guest.getId();
-          ChromeWebView.showContextMenu(guestInstanceId, e.requestId, items);
-        }
-      }, newEvent),
-      {instanceId: webViewInstanceId});
+            // TODO(lazyboy): Remove |items| parameter completely from
+            // ChromeWebView.showContextMenu as we don't do anything useful with
+            // it currently.
+            var items = [];
+            var guestInstanceId =
+                GuestViewInternalNatives.GetViewFromID(webViewInstanceId)
+                    .guest.getId();
+            ChromeWebView.showContextMenu(guestInstanceId, e.requestId, items);
+          }
+        }, newEvent), {instanceId: webViewInstanceId});
   }
 
   return newEvent;
@@ -95,8 +92,9 @@
 // WebViewContextMenusImpl object.
 
 // An instance of this class is exposed as <webview>.contextMenus.
-function WebViewContextMenusImpl(viewInstanceId) {
+function WebViewContextMenusImpl(webView, viewInstanceId) {
   this.viewInstanceId_ = viewInstanceId;
+  this.setupEvents(webView);
 }
 $Object.setPrototypeOf(WebViewContextMenusImpl.prototype, null);
 
@@ -120,9 +118,42 @@
   return $Function.apply(ChromeWebView.contextMenusUpdate, null, args);
 };
 
+WebViewContextMenusImpl.prototype.setupEvents = function(webView) {
+  // Define 'onClicked' event property on |contextMenus|.
+  var getOnClickedEvent = $Function.bind(function() {
+    return webView.weakWrapper(function() {
+      if (!webView.contextMenusOnClickedEvent_) {
+        var eventName = 'chromeWebViewInternal.onClicked';
+        var eventSchema =
+            utils.lookup(ChromeWebViewSchema.events, 'name', 'onClicked');
+        var eventOptions = {
+          supportsListeners: true,
+          supportsLazyListeners: false
+        };
+        var onClickedEvent = createContextMenusOnClickedEvent(
+            webView.viewInstanceId, eventName, eventSchema, eventOptions);
+        webView.contextMenusOnClickedEvent_ = onClickedEvent;
+        return onClickedEvent;
+      }
+      return webView.contextMenusOnClickedEvent_;
+    });
+  }, webView);
+
+  $Object.defineProperty(
+      this, 'onClicked', {get: getOnClickedEvent(), enumerable: true});
+  $Object.defineProperty(this, 'onShow', {
+    get: webView.weakWrapper(function() {
+      return webView.contextMenusOnContextMenuEvent_;
+    }),
+    enumerable: true
+  });
+};
+
+// Arguments: webView, viewInstanceId
 function WebViewContextMenus() {
   privates(WebViewContextMenus).constructPrivate(this, arguments);
 }
+
 utils.expose(WebViewContextMenus, WebViewContextMenusImpl, {
   functions: [
     'create',
@@ -130,6 +161,7 @@
     'removeAll',
     'update',
   ],
+  readonly: ['onClicked', 'onShow'],
 });
 
 // -----------------------------------------------------------------------------
@@ -141,16 +173,19 @@
   }
 }
 
-ChromeWebViewImpl.prototype.createWebViewContextMenus = function () {
-  return new WebViewContextMenus(this.viewInstanceId);
+ChromeWebViewImpl.prototype.createWebViewContextMenus =
+    function() {
+  return new WebViewContextMenus(this, this.viewInstanceId);
 }
 
-ChromeWebViewImpl.prototype.setupContextMenus = function() {
+    ChromeWebViewImpl.prototype.setupContextMenus = function() {
   if (!this.contextMenusOnContextMenuEvent_) {
     var eventName = 'chromeWebViewInternal.onContextMenuShow';
     var eventSchema =
         utils.lookup(ChromeWebViewSchema.events, 'name', 'onShow');
     var eventOptions = {supportsListeners: true, supportsLazyListeners: false};
+    // TODO(crbug.com/429599984): Move this member to the context menus
+    // instance.
     this.contextMenusOnContextMenuEvent_ = createContextMenusOnContextMenuEvent(
         this.viewInstanceId, eventName, eventSchema, eventOptions);
   }
@@ -162,37 +197,6 @@
       }
 
       this.contextMenus_ = this.createWebViewContextMenus();
-
-      // Define 'onClicked' event property on |this.contextMenus_|.
-      var getOnClickedEvent = $Function.bind(function() {
-        return this.weakWrapper(function() {
-          if (!this.contextMenusOnClickedEvent_) {
-            var eventName = 'chromeWebViewInternal.onClicked';
-            var eventSchema =
-                utils.lookup(ChromeWebViewSchema.events, 'name', 'onClicked');
-            var eventOptions =
-                {supportsListeners: true, supportsLazyListeners: false};
-            var onClickedEvent = createContextMenusOnClickedEvent(
-                this.viewInstanceId, eventName, eventSchema, eventOptions);
-            this.contextMenusOnClickedEvent_ = onClickedEvent;
-            return onClickedEvent;
-          }
-          return this.contextMenusOnClickedEvent_;
-        });
-      }, this);
-      $Object.defineProperty(
-          this.contextMenus_,
-          'onClicked',
-          {get: getOnClickedEvent(), enumerable: true});
-      $Object.defineProperty(
-          this.contextMenus_,
-          'onShow',
-          {
-            get: this.weakWrapper(function() {
-              return this.contextMenusOnContextMenuEvent_;
-            }),
-            enumerable: true
-          });
       return this.contextMenus_;
     });
   }, this);
diff --git a/chrome/renderer/resources/renderer_resources.grd b/chrome/renderer/resources/renderer_resources.grd
index 408978c..1a12ef1273 100644
--- a/chrome/renderer/resources/renderer_resources.grd
+++ b/chrome/renderer/resources/renderer_resources.grd
@@ -25,6 +25,7 @@
         <include name="IDR_CONTROLLED_FRAME_IMPL_JS" file="controlled_frame\controlled_frame_impl.js" type="BINDATA" />
         <include name="IDR_CONTROLLED_FRAME_API_METHODS_JS" file="controlled_frame\controlled_frame_api_methods.js" type="BINDATA" />
         <include name="IDR_CONTROLLED_FRAME_WEB_REQUEST_JS" file="controlled_frame\controlled_frame_web_request.js" type="BINDATA" />
+        <include name="IDR_CONTROLLED_FRAME_CONTEXT_MENUS_JS" file="controlled_frame\controlled_frame_context_menus.js" type="BINDATA" />
         <include name="IDR_CHROME_WEB_VIEW_CONTEXT_MENUS_API_METHODS_JS" file="extensions\web_view\chrome_web_view_context_menus_api_methods.js" type="BINDATA" />
         <include name="IDR_CHROME_WEB_VIEW_ELEMENT_JS" file="extensions\web_view\chrome_web_view_element.js" type="BINDATA" />
         <include name="IDR_CHROME_WEB_VIEW_INTERNAL_CUSTOM_BINDINGS_JS" file="extensions\web_view\chrome_web_view_internal_custom_bindings.js" type="BINDATA" />
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 0b7dd0f..6e7ba06 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2976,6 +2976,7 @@
       "//chrome/browser/page_load_metrics/integration_tests/data/",
       "//chrome/renderer/resources/extensions/",
       "//chrome/test/data/cart/",
+      "//chrome/test/data/devtools/protocol/flash_embed_test.html",
       "//components/test/data/ad_tagging/",
       "//components/test/data/ads_observer/",
       "//components/test/data/autofill/",
@@ -3130,6 +3131,7 @@
       "../browser/devtools/device/port_forwarding_browsertest.cc",
       "../browser/devtools/device/usb/android_usb_browsertest.cc",
       "../browser/devtools/global_confirm_info_bar_browsertest.cc",
+      "../browser/devtools/protocol/audits_protocol_test.cc",
       "../browser/devtools/protocol/devtools_autofill_browsertest.cc",
       "../browser/devtools/protocol/devtools_extensions_browsertest.cc",
       "../browser/devtools/protocol/devtools_iwa_browsertest.cc",
@@ -5116,6 +5118,7 @@
       allow_circular_includes_from +=
           [ "//chrome/browser/ui/lens:browser_tests" ]
       if (use_aura) {
+        sources += [ "../browser/web_applications/isolated_web_apps/window_management/isolated_web_apps_opened_tabs_counter_service_browsertest.cc" ]
         deps += [ "//ui/aura:test_support" ]
       }
     }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AppMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AppMenuFacility.java
index b547a66..74c98eba 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AppMenuFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/AppMenuFacility.java
@@ -24,7 +24,7 @@
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.ScrollableFacility;
 import org.chromium.base.test.transit.Station;
-import org.chromium.base.test.transit.Transition;
+import org.chromium.base.test.transit.TripBuilder;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.base.test.transit.ViewSpec;
 import org.chromium.chrome.R;
@@ -190,6 +190,11 @@
 
     /** Clicks outside the menu to close it. */
     public void clickOutsideToClose() {
+        clickOutsideTo().exitFacility();
+    }
+
+    /** Click outside the menu to start a Transition. */
+    public TripBuilder clickOutsideTo() {
         GeneralClickAction clickBetweenViewAndLeftEdge =
                 new GeneralClickAction(
                         Tap.SINGLE,
@@ -202,16 +207,13 @@
                             return new float[] {clickX, clickY};
                         },
                         Press.FINGER);
-        mHostStation.exitFacilitySync(
-                this, menuListElement.getPerformTrigger(clickBetweenViewAndLeftEdge));
+        return menuListElement.performViewActionTo(clickBetweenViewAndLeftEdge);
     }
 
     /** Close the menu programmatically. */
     public void closeProgrammatically() {
-        mHostStation.exitFacilitySync(
-                this,
-                Transition.runTriggerOnUiThreadOption(),
-                () -> getAppMenuCoordinator().getAppMenuHandler().hideAppMenu());
+        runOnUiThreadTo(() -> getAppMenuCoordinator().getAppMenuHandler().hideAppMenu())
+                .exitFacility();
     }
 
     /** Verify that the menu model has the expected menu item ids and nothing beyond them. */
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/Journeys.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/Journeys.java
index 632bc121..5f57c97 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/Journeys.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/Journeys.java
@@ -10,8 +10,8 @@
 import org.chromium.base.Token;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.base.test.transit.Condition;
-import org.chromium.base.test.transit.Transition.Trigger;
 import org.chromium.base.test.transit.TravelException;
+import org.chromium.base.test.transit.TripBuilder;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
@@ -312,18 +312,17 @@
      * Begins a new tab group creation UI flow. See {@link TabGroupCreationUiDelegate}
      *
      * @param <HostStationT> The type of station this is scoped to.
-     * @param station the station to begin the flow from.
-     * @param trigger The trigger used to begin the flow.
+     * @param tripBuilder TripBuilder with the Trigger to begin the flow from.
      */
     public static <HostStationT extends ChromeActivityTabModelBoundStation<ChromeTabbedActivity>>
             NewTabGroupDialogFacility<HostStationT> beginNewTabGroupUiFlow(
-                    HostStationT station, Trigger trigger) {
+                    TripBuilder tripBuilder) {
         assertTrue(ChromeFeatureList.sTabGroupEntryPointsAndroid.isEnabled());
 
         SoftKeyboardFacility softKeyboard = new SoftKeyboardFacility();
         NewTabGroupDialogFacility<HostStationT> dialog =
                 new NewTabGroupDialogFacility<>(softKeyboard);
-        station.enterFacilitiesSync(List.of(dialog, softKeyboard), trigger);
+        tripBuilder.enterFacilities(dialog, softKeyboard);
         return dialog;
     }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/SoftKeyboardFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/SoftKeyboardFacility.java
index 5218d8f..5868b48a 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/SoftKeyboardFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/SoftKeyboardFacility.java
@@ -8,7 +8,7 @@
 
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Station;
-import org.chromium.base.test.transit.Transition;
+import org.chromium.base.test.transit.TripBuilder;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.ui.test.transit.SoftKeyboardElement;
 
@@ -38,15 +38,15 @@
 
             // If this fails, the keyboard was closed before, but not by this facility.
             recheckActiveConditions();
-            Transition.TransitionOptions.Builder options = Transition.newOptions();
-            options.withRetry();
+
+            TripBuilder tripBuilder = runTo(Espresso::closeSoftKeyboard);
             for (ViewElement<?> viewElement : viewElementsToSettle) {
-                options.withCondition(viewElement.createSettleCondition());
+                tripBuilder = tripBuilder.waitForAnd(viewElement.createSettleCondition());
             }
-            mHostStation.exitFacilitySync(this, options.build(), Espresso::closeSoftKeyboard);
+            tripBuilder.withRetry().exitFacility();
         } else {
             // Keyboard was not expected to be shown
-            mHostStation.exitFacilitySync(this, /* trigger= */ null);
+            noopTo().exitFacility();
         }
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/context_menu/LinkContextMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/context_menu/LinkContextMenuFacility.java
index 928e145..7e4e38b 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/context_menu/LinkContextMenuFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/context_menu/LinkContextMenuFacility.java
@@ -6,15 +6,11 @@
 
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
-import org.chromium.base.test.transit.Condition;
-import org.chromium.base.test.transit.Transition;
 import org.chromium.chrome.R;
 import org.chromium.chrome.test.transit.page.WebPageStation;
 import org.chromium.chrome.test.transit.tabmodel.TabCountChangedCondition;
 import org.chromium.chrome.test.transit.tabmodel.TabGroupUiFacility;
 
-import java.util.List;
-
 /**
  * Facility represents a context menu triggered for a text link. This has to be used for a webpage.
  */
@@ -53,11 +49,11 @@
 
     private Void createTabInBackground(ItemOnScreenFacility<Void> itemOnScreen) {
         assert mHostStation != null;
-        Condition tabCountIncrease = new TabCountChangedCondition(mHostStation.getTabModel(), 1);
-        mHostStation.exitFacilitiesSync(
-                List.of(this, itemOnScreen),
-                Transition.conditionOption(tabCountIncrease),
-                itemOnScreen.viewElement.getClickTrigger());
+        itemOnScreen
+                .viewElement
+                .clickTo()
+                .waitForAnd(new TabCountChangedCondition(mHostStation.getTabModel(), 1))
+                .exitFacilities(this, itemOnScreen);
         return null;
     }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HistoryPaneStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HistoryPaneStation.java
index dfd6fd9..80921fed 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HistoryPaneStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HistoryPaneStation.java
@@ -39,7 +39,7 @@
 
     /** Expect history entries to be displayed in the history pane. */
     public HistoryWithEntriesFacility expectEntries() {
-        return enterFacilitySync(new HistoryWithEntriesFacility(), /* trigger= */ null);
+        return noopTo().enterFacility(new HistoryWithEntriesFacility());
     }
 
     /** Expect no history to be displayed in the history pane. */
@@ -49,11 +49,11 @@
         emptyHistory.declareView(
                 withText("You can see the pages you’ve visited or delete them from your history"));
         emptyHistory.declareNoView(withId(R.id.history_page_recycler_view));
-        enterFacilitySync(emptyHistory, /* trigger= */ null);
+        noopTo().enterFacility(emptyHistory);
     }
 
     /** Non-empty state of the history pane. */
-    public class HistoryWithEntriesFacility extends Facility<HistoryPaneStation> {
+    public static class HistoryWithEntriesFacility extends Facility<HistoryPaneStation> {
         public final ViewElement<View> recyclerViewElement;
         public final ViewElement<View> searchButtonElement;
 
@@ -64,8 +64,7 @@
 
         /** Expect an entry to be displayed in the history pane. */
         public HistoryEntryFacility expectEntry(String text) {
-            return mHostStation.enterFacilitySync(
-                    new HistoryEntryFacility(this, text), /* trigger= */ null);
+            return noopTo().enterFacility(new HistoryEntryFacility(this, text));
         }
 
         /** Expect an entry to be not displayed in the history pane. */
@@ -75,8 +74,7 @@
 
         /** Open the history search. */
         public HistorySearchFacility openSearch() {
-            return enterFacilitySync(
-                    new HistorySearchFacility(), searchButtonElement.getClickTrigger());
+            return searchButtonElement.clickTo().enterFacility(new HistorySearchFacility());
         }
     }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HubBaseStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HubBaseStation.java
index 85271c0ca..db9399d 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HubBaseStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HubBaseStation.java
@@ -101,8 +101,7 @@
         String contentDescription =
                 HubStationUtils.getContentDescriptionSubstringForIdPaneSelection(paneId);
         SwitchPaneButtonFacility button =
-                enterFacilitySync(
-                        new SwitchPaneButtonFacility(contentDescription), /* trigger= */ null);
+                noopTo().enterFacility(new SwitchPaneButtonFacility(contentDescription));
         return button.buttonElement.clickTo().arriveAt(destinationStation);
     }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/NewTabGroupDialogFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/NewTabGroupDialogFacility.java
index 3a24ccf..9e814e0 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/NewTabGroupDialogFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/NewTabGroupDialogFacility.java
@@ -228,7 +228,7 @@
      */
     public void pressDoneToExit() {
         ensureSoftKeyboardClosed();
-        mHostStation.exitFacilitySync(this, doneButtonElement.getClickTrigger());
+        doneButtonElement.clickTo().exitFacility();
     }
 
     /** Press "Done" to confirm the tab group name and color, but no-op from an invalid title. */
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/RegularTabSwitcherStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/RegularTabSwitcherStation.java
index b1165ec..0d2e8ca 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/RegularTabSwitcherStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/RegularTabSwitcherStation.java
@@ -57,7 +57,7 @@
     }
 
     public ArchiveMessageCardFacility expectArchiveMessageCard() {
-        return enterFacilitySync(
-                new ArchiveMessageCardFacility(/* tabSwitcherStation= */ this), null);
+        return noopTo().enterFacility(
+                        new ArchiveMessageCardFacility(/* tabSwitcherStation= */ this));
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupDialogFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupDialogFacility.java
index 47bd3ab..f42c873e 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupDialogFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupDialogFacility.java
@@ -157,7 +157,7 @@
 
     /** Press back to exit the facility. */
     public void pressBackArrowToExit() {
-        mHostStation.exitFacilitySync(this, backButtonElement.getClickTrigger());
+        backButtonElement.clickTo().exitFacility();
     }
 
     /**
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupPaneStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupPaneStation.java
index 152bb86..6c00a29 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupPaneStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupPaneStation.java
@@ -50,7 +50,7 @@
 
     public NewTabGroupDialogFacility<TabGroupPaneStation> createNewTabGroup() {
         assertNotNull(newTabGroupButtonElement);
-        return Journeys.beginNewTabGroupUiFlow(this, newTabGroupButtonElement.getClickTrigger());
+        return Journeys.beginNewTabGroupUiFlow(newTabGroupButtonElement.clickTo());
     }
 
     // TODO(crbug.com/413652567): Implement actions.
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabListEditorAppMenu.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabListEditorAppMenu.java
index 968f65f..81e019ef 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabListEditorAppMenu.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabListEditorAppMenu.java
@@ -14,7 +14,6 @@
 import org.chromium.base.test.transit.Condition;
 import org.chromium.base.test.transit.ScrollableFacility;
 import org.chromium.base.test.transit.Station;
-import org.chromium.base.test.transit.Transition;
 import org.chromium.base.test.transit.ViewSpec;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tabmodel.TabModel;
@@ -173,10 +172,11 @@
         TabModel tabModel = mHostStation.tabModelElement.get();
         Condition tabCountDecreased =
                 new TabCountChangedCondition(tabModel, -mListEditor.getNumTabsSelected());
-        mHostStation.exitFacilitiesSync(
-                List.of(this, mListEditor, itemOnScreen),
-                Transition.conditionOption(tabCountDecreased),
-                itemOnScreen.viewElement.getClickTrigger());
+        itemOnScreen
+                .viewElement
+                .clickTo()
+                .waitForAnd(tabCountDecreased)
+                .exitFacilities(this, mListEditor, itemOnScreen);
 
         return null;
     }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherAppMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherAppMenuFacility.java
index 32fb8fd28..269ba036 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherAppMenuFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherAppMenuFacility.java
@@ -115,7 +115,7 @@
 
     private NewTabGroupDialogFacility<HostStationT> createNewTabGroupFacility(
             ItemOnScreenFacility<NewTabGroupDialogFacility<HostStationT>> item) {
-        return Journeys.beginNewTabGroupUiFlow(mHostStation, item.viewElement.getClickTrigger());
+        return Journeys.beginNewTabGroupUiFlow(item.viewElement.clickTo());
     }
 
     /** Select "Settings" from the app menu. */
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardFacility.java
index 024de178..a7bf15f1 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherGroupCardFacility.java
@@ -77,21 +77,25 @@
 
     /** Clicks the group card to open the tab group dialog. */
     public TabGroupDialogFacility<TabSwitcherStation> clickCard() {
-        return mHostStation.enterFacilitySync(
-                new TabGroupDialogFacility<>(mTabIdsToGroup), titleElement.getClickTrigger());
+        return titleElement.clickTo().enterFacility(new TabGroupDialogFacility<>(mTabIdsToGroup));
     }
 
     /** Clicks the ("...") action button on a tab group to open the overflow menu. */
     public TabSwitcherGroupCardAppMenuFacility openAppMenu() {
         boolean isIncognito = mHostStation.isIncognito();
-        return mHostStation.enterFacilitySync(
-                new TabSwitcherGroupCardAppMenuFacility(isIncognito, mTitle),
-                menuButtonElement.getClickTrigger());
+        return menuButtonElement
+                .clickTo()
+                .enterFacility(new TabSwitcherGroupCardAppMenuFacility(isIncognito, mTitle));
     }
 
+    /**
+     * Waits for the tab group card to have a specific color.
+     *
+     * @param color The expected {@link TabGroupColorId}.
+     * @return The {@link TabGroupCardColorFacility} for the given color.
+     */
     public TabGroupCardColorFacility expectColor(@TabGroupColorId int color) {
-        return mHostStation.enterFacilitySync(
-                new TabGroupCardColorFacility(color), /* trigger= */ null);
+        return noopTo().enterFacility(new TabGroupCardColorFacility(color));
     }
 
     public class TabGroupCardColorFacility extends Facility<TabSwitcherStation> {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherListEditorFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherListEditorFacility.java
index 4694db78..2e71895 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherListEditorFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherListEditorFacility.java
@@ -14,7 +14,6 @@
 import android.view.View;
 
 import androidx.recyclerview.widget.RecyclerView;
-import androidx.test.espresso.Espresso;
 
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Station;
@@ -83,11 +82,6 @@
         return totalTabs;
     }
 
-    /** Presses back to exit the facility. */
-    public void pressBackToExit() {
-        mHostStation.exitFacilitySync(this, Espresso::pressBack);
-    }
-
     /** Add a tab in the grid to the selection. */
     public TabSwitcherListEditorFacility<HostStationT> addTabToSelection(int index, int tabId) {
         List<Integer> newTabIdsSelected = new ArrayList<>(mTabIdsSelected);
@@ -119,8 +113,10 @@
 
     /** Open the app menu, which looks different while selecting tabs. */
     public TabListEditorAppMenu<HostStationT> openAppMenuWithEditor() {
-        return mHostStation.enterFacilitySync(
-                new TabListEditorAppMenu<>(this), mHostStation.menuButtonElement.getClickTrigger());
+        return mHostStation
+                .menuButtonElement
+                .clickTo()
+                .enterFacility(new TabListEditorAppMenu<>(this));
     }
 
     /**
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherSearchStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherSearchStation.java
index 74b5d12e..6f96281 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherSearchStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherSearchStation.java
@@ -106,7 +106,7 @@
     public SuggestionFacility findSuggestion(
             @Nullable Integer index, @Nullable String title, @Nullable String text) {
         SUGGESTIONS_LIST.printFromRoot();
-        return enterFacilitySync(new SuggestionFacility(index, title, text), /* trigger= */ null);
+        return noopTo().enterFacility(new SuggestionFacility(index, title, text));
     }
 
     /** Expect suggestions with all the given |texts|. */
@@ -117,13 +117,13 @@
             allSuggestionFacilities.add(
                     new SuggestionFacility(/* index= */ null, /* title= */ null, prefix + text));
         }
-        enterFacilitiesSync(allSuggestionFacilities, /* trigger= */ null);
+        noopTo().enterFacilities(allSuggestionFacilities.toArray(new Facility[0]));
     }
 
     /** Expect a suggestion with the given |index| and |text|. */
     public SectionHeaderFacility findSectionHeaderByIndexAndText(int index, String text) {
         SUGGESTIONS_LIST.printFromRoot();
-        return enterFacilitySync(new SectionHeaderFacility(index, text), /* trigger= */ null);
+        return noopTo().enterFacility(new SectionHeaderFacility(index, text));
     }
 
     /** A suggestion in the search results. */
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherStation.java
index 970811c..5d9f85e0 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherStation.java
@@ -98,9 +98,9 @@
     public TabSwitcherAppMenuFacility openAppMenu() {
         recheckActiveConditions();
 
-        return enterFacilitySync(
-                new TabSwitcherAppMenuFacility<>(mIsIncognito),
-                menuButtonElement.getClickTrigger());
+        return menuButtonElement
+                .clickTo()
+                .enterFacility(new TabSwitcherAppMenuFacility<>(mIsIncognito));
     }
 
     /**
@@ -197,18 +197,16 @@
     public TabSwitcherGroupCardFacility expectGroupCard(List<Integer> tabIdsInGroup, String title) {
         TabModel currentModel = tabModelElement.get();
         int expectedCardIndex = TabBinningUtil.getBinIndex(currentModel, tabIdsInGroup);
-        return enterFacilitySync(
-                new TabSwitcherGroupCardFacility(expectedCardIndex, tabIdsInGroup, title),
-                /* trigger= */ null);
+        return noopTo().enterFacility(
+                        new TabSwitcherGroupCardFacility(expectedCardIndex, tabIdsInGroup, title));
     }
 
     /** Expect a tab card to exist. */
     public TabSwitcherTabCardFacility expectTabCard(int tabId, String title) {
         TabModel currentModel = tabModelElement.get();
         int expectedCardIndex = TabBinningUtil.getBinIndex(currentModel, tabId);
-        return enterFacilitySync(
-                new TabSwitcherTabCardFacility(expectedCardIndex, tabId, title),
-                /* trigger= */ null);
+        return noopTo().enterFacility(
+                        new TabSwitcherTabCardFacility(expectedCardIndex, tabId, title));
     }
 
     /** Verify the tab switcher card count. */
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/UndoSnackbarFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/UndoSnackbarFacility.java
index 9fcde3b..2aafa800 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/UndoSnackbarFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/UndoSnackbarFacility.java
@@ -21,6 +21,6 @@
 
     /** Press undo to revert the operation. */
     public void pressUndo() {
-        mHostStation.exitFacilitySync(this, buttonElement.getClickTrigger());
+        buttonElement.clickTo().exitFacility();
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/IncognitoNewTabPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/IncognitoNewTabPageStation.java
index 50f82ee..f36833f8 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/IncognitoNewTabPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/IncognitoNewTabPageStation.java
@@ -25,8 +25,6 @@
 import org.chromium.chrome.test.transit.page.PageStation;
 import org.chromium.components.embedder_support.util.UrlConstants;
 
-import java.util.List;
-
 /** The Incognito New Tab Page screen, with text about Incognito mode. */
 public class IncognitoNewTabPageStation extends PageStation {
     public ViewElement<UrlBar> urlBarElement;
@@ -56,8 +54,7 @@
 
     /** Opens the app menu by pressing the toolbar "..." button */
     public IncognitoNewTabPageAppMenuFacility openAppMenu() {
-        return enterFacilitySync(
-                new IncognitoNewTabPageAppMenuFacility(), menuButtonElement.getClickTrigger());
+        return menuButtonElement.clickTo().enterFacility(new IncognitoNewTabPageAppMenuFacility());
     }
 
     /** Click the URL bar to enter the Omnibox. */
@@ -66,8 +63,7 @@
         OmniboxFacility omniboxFacility =
                 new OmniboxFacility(/* incognito= */ true, fakeSuggestions);
         SoftKeyboardFacility softKeyboard = new SoftKeyboardFacility();
-        enterFacilitiesSync(
-                List.of(omniboxFacility, softKeyboard), urlBarElement.getClickTrigger());
+        urlBarElement.clickTo().enterFacilities(omniboxFacility, softKeyboard);
         return Pair.create(omniboxFacility, softKeyboard);
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/MvtsFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/MvtsFacility.java
index c5b9d58..d0db49dd 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/MvtsFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/MvtsFacility.java
@@ -121,9 +121,9 @@
         assert 0 <= i && i < tileItems.size()
                 : String.format("%d is out of bounds [0, %d]", i, tileItems.size());
 
-        tileItems.get(i).scrollTo();
+        tileItems.get(i).scrollToItemIfNeeded();
 
         SiteSuggestion siteSuggestion = mSiteSuggestionsByTileIndex.get(i);
-        return mHostStation.enterFacilitySync(new MvtsTileFacility(this, i, siteSuggestion), null);
+        return noopTo().enterFacility(new MvtsTileFacility(this, i, siteSuggestion));
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java
index 8f8b397..c6db084 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ntp/RegularNewTabPageStation.java
@@ -71,8 +71,7 @@
 
     /** Opens the app menu by pressing the toolbar "..." button */
     public RegularNewTabPageAppMenuFacility openAppMenu() {
-        return enterFacilitySync(
-                new RegularNewTabPageAppMenuFacility(), menuButtonElement.getClickTrigger());
+        return menuButtonElement.clickTo().enterFacility(new RegularNewTabPageAppMenuFacility());
     }
 
     /**
@@ -86,8 +85,7 @@
     public MvtsFacility focusOnMvts(
             List<SiteSuggestion> siteSuggestions, Set<Integer> separatorIndices) {
         // Assume MVTs are on the screen; if this assumption changes, make sure to scroll to them.
-        return enterFacilitySync(
-                new MvtsFacility(siteSuggestions, separatorIndices), /* trigger= */ null);
+        return noopTo().enterFacility(new MvtsFacility(siteSuggestions, separatorIndices));
     }
 
     /** Same as {@link #focusOnMvts(List, Set)} expecting no separatorIndices. */
@@ -101,8 +99,7 @@
         OmniboxFacility omniboxFacility =
                 new OmniboxFacility(/* incognito= */ false, fakeSuggestions);
         SoftKeyboardFacility softKeyboard = new SoftKeyboardFacility();
-        enterFacilitiesSync(
-                List.of(omniboxFacility, softKeyboard), searchBoxElement.getClickTrigger());
+        searchBoxElement.clickTo().enterFacilities(omniboxFacility, softKeyboard);
         return Pair.create(omniboxFacility, softKeyboard);
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxEnteredTextFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxEnteredTextFacility.java
index 1749c87..07c0f732 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxEnteredTextFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxEnteredTextFacility.java
@@ -55,6 +55,6 @@
 
     /** Click the delete button to erase the text entered. */
     public void clickDelete() {
-        mHostStation.exitFacilitySync(this, deleteButtonElement.getForgivingClickTrigger());
+        deleteButtonElement.clickEvenIfPartiallyOccludedTo().exitFacility();
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java
index 5e1ce03..6c9bc7d 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/omnibox/OmniboxFacility.java
@@ -12,8 +12,6 @@
 
 import android.view.View;
 
-import androidx.test.espresso.Espresso;
-
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.base.test.transit.ViewSpec;
@@ -90,13 +88,8 @@
 
     /** Enter text into the omnibox. */
     public OmniboxEnteredTextFacility typeText(String textToTypeAndExpect) {
-        return mHostStation.enterFacilitySync(
-                new OmniboxEnteredTextFacility(this, textToTypeAndExpect),
-                urlBarElement.getTypeTextTrigger(textToTypeAndExpect));
-    }
-
-    /** Press back expecting the Omnibox to be closed. */
-    public void pressBackToClose() {
-        mHostStation.exitFacilitySync(this, Espresso::pressBack);
+        return urlBarElement
+                .typeTextTo(textToTypeAndExpect)
+                .enterFacility(new OmniboxEnteredTextFacility(this, textToTypeAndExpect));
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java
index 1a375083..114c2bf 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/PageStation.java
@@ -14,7 +14,6 @@
 import android.widget.ImageButton;
 
 import org.chromium.base.supplier.Supplier;
-import org.chromium.base.test.transit.Transition.Trigger;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.base.test.transit.ViewSpec;
 import org.chromium.chrome.R;
@@ -224,7 +223,7 @@
         long downTime = SystemClock.uptimeMillis();
         Activity activity = getActivity();
 
-        Trigger firstPartTrigger =
+        Runnable firstPartTrigger =
                 () -> {
                     TouchCommon.dragStart(activity, coords.mFromX, coords.mY, downTime);
                     TouchCommon.dragTo(
@@ -240,7 +239,7 @@
                 () -> {
                     TouchCommon.dragEnd(activity, coords.mToX, coords.mY, downTime);
                 };
-        return enterFacilitySync(new SwipingToTabFacility(secondPartTrigger), firstPartTrigger);
+        return runTo(firstPartTrigger).enterFacility(new SwipingToTabFacility(secondPartTrigger));
     }
 
     private static class ToolbarSwipeCoordinates {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/quick_delete/QuickDeleteDialogFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/quick_delete/QuickDeleteDialogFacility.java
index 9b55cd5..bc5bbf40 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/quick_delete/QuickDeleteDialogFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/quick_delete/QuickDeleteDialogFacility.java
@@ -128,7 +128,7 @@
         } else {
             facility.declareNoView(spec);
         }
-        mHostStation.enterFacilitySync(facility, /* trigger= */ null);
+        noopTo().enterFacility(facility);
     }
 
     public void expectMoreOnSyncedDevices(boolean shown) {
@@ -140,7 +140,7 @@
         } else {
             facility.declareNoView(spec);
         }
-        mHostStation.enterFacilitySync(facility, /* trigger= */ null);
+        noopTo().enterFacility(facility);
     }
 
     private class TimePeriodSelectedCondition extends UiThreadCondition {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/settings/SettingsStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/settings/SettingsStation.java
index 009bdfc..822c8e3 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/settings/SettingsStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/settings/SettingsStation.java
@@ -6,7 +6,6 @@
 
 import org.chromium.base.test.transit.FragmentElement;
 import org.chromium.base.test.transit.Station;
-import org.chromium.base.test.transit.Transition;
 import org.chromium.chrome.browser.settings.ChromeBaseSettingsFragment;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 
@@ -27,12 +26,8 @@
     public PreferenceFacility scrollToPref(String prefKey) {
         assertInPhase(Phase.ACTIVE);
         String title = fragmentElement.get().findPreference(prefKey).getTitle().toString();
-        return enterFacilitySync(
-                new PreferenceFacility(title),
-                Transition.newOptions()
-                        .withPossiblyAlreadyFulfilled()
-                        .withRunTriggerOnUiThread()
-                        .build(),
-                () -> fragmentElement.get().scrollToPreference(prefKey));
+        return runOnUiThreadTo(() -> fragmentElement.get().scrollToPreference(prefKey))
+                .withPossiblyAlreadyFulfilled()
+                .enterFacility(new PreferenceFacility(title));
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/PopupOnClickPageStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/PopupOnClickPageStation.java
index ffcc015..719ac58 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/PopupOnClickPageStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/testhtmls/PopupOnClickPageStation.java
@@ -70,7 +70,6 @@
      * message to be shown.
      */
     public PopupBlockedMessageFacility clickLinkAndExpectPopupBlockedMessage() {
-        return enterFacilitySync(
-                new PopupBlockedMessageFacility<>(1), linkToPopup.getClickTrigger());
+        return linkToPopup.clickTo().enterFacility(new PopupBlockedMessageFacility<>(1));
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ui/BottomSheetFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ui/BottomSheetFacility.java
index a3e0389..4b3c57e 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ui/BottomSheetFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ui/BottomSheetFacility.java
@@ -8,8 +8,6 @@
 
 import android.view.View;
 
-import androidx.test.espresso.Espresso;
-
 import org.hamcrest.Matcher;
 
 import org.chromium.base.test.transit.Facility;
@@ -45,6 +43,6 @@
 
     /** Press the system backpress to close the bottom sheet. */
     public void close() {
-        mHostStation.exitFacilitySync(this, Espresso::pressBack);
+        pressBackTo().exitFacility();
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ui/MessageFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ui/MessageFacility.java
index 1f8784d..ed8aa1d 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ui/MessageFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ui/MessageFacility.java
@@ -20,7 +20,7 @@
 
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Station;
-import org.chromium.base.test.transit.Transition;
+import org.chromium.base.test.transit.TripBuilder;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.chrome.test.R;
 import org.chromium.chrome.test.transit.page.PageStation;
@@ -53,13 +53,16 @@
 
     /** Dismiss the message banner. */
     public void dismiss() {
+        swipeTo().exitFacility();
+    }
+
+    /** Swipe the message banner to start a Transition. */
+    public TripBuilder swipeTo() {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
             // b/329707221: For S+, GeneralSwipeAction is not working in the current version of
             // Espresso (3.2). Workaround by using the test helpers to dismiss the popup
             // programmatically.
-            mHostStation.exitFacilitySync(
-                    this,
-                    Transition.runTriggerOnUiThreadOption(),
+            return runOnUiThreadTo(
                     () -> {
                         MessageDispatcher messageDispatcher =
                                 MessageDispatcherProvider.from(
@@ -73,15 +76,13 @@
                         messageDispatcher.dismissMessage(m, DismissReason.GESTURE);
                     });
         } else {
-            mHostStation.exitFacilitySync(
-                    this,
-                    bannerElement.getPerformTrigger(
-                            ViewActions.actionWithAssertions(
-                                    new GeneralSwipeAction(
-                                            Swipe.FAST,
-                                            GeneralLocation.CENTER,
-                                            GeneralLocation.TOP_CENTER,
-                                            Press.FINGER))));
+            return bannerElement.performViewActionTo(
+                    ViewActions.actionWithAssertions(
+                            new GeneralSwipeAction(
+                                    Swipe.FAST,
+                                    GeneralLocation.CENTER,
+                                    GeneralLocation.TOP_CENTER,
+                                    Press.FINGER)));
         }
     }
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ui/ModalDialogFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ui/ModalDialogFacility.java
index 7148c0d..ca536a1 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ui/ModalDialogFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/ui/ModalDialogFacility.java
@@ -11,8 +11,6 @@
 import android.widget.Button;
 import android.widget.TextView;
 
-import androidx.test.espresso.Espresso;
-
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.ViewElement;
@@ -78,11 +76,11 @@
 
     /** Click Cancel to close the dialog with no action. */
     public void clickCancel() {
-        mHostStation.exitFacilitySync(this, negativeButtonElement.getClickTrigger());
+        negativeButtonElement.clickTo().exitFacility();
     }
 
     /** Press the back button to dismiss the dialog. */
     public void pressBackToDismiss() {
-        mHostStation.exitFacilitySync(this, Espresso::pressBack);
+        pressBackTo().exitFacility();
     }
 }
diff --git a/chrome/test/data/devtools/protocol/flash_embed_test.html b/chrome/test/data/devtools/protocol/flash_embed_test.html
new file mode 100644
index 0000000..15b2cd08
--- /dev/null
+++ b/chrome/test/data/devtools/protocol/flash_embed_test.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Flash Embed Test</title>
+</head>
+<body>
+<h1>This page attempts to embed Flash based video</h1>
+<script>
+  // This script dynamically adds the flash embed to the DOM.
+  // This ensures the page loads first, then the resource is requested,
+  // which is a common real-world scenario.
+  function appendFlashEmbed() {
+    const object = document.createElement("object");
+    const embed = document.createElement("embed");
+    embed.src = "https://www.youtube.com/v/1AoZTDftNIU"; // Classic Flash embed URL
+    embed.setAttribute("type", "application/x-shockwave-flash");
+    object.appendChild(embed);
+    document.body.appendChild(object);
+  }
+  appendFlashEmbed();
+</script>
+</body>
+</html>
diff --git a/chrome/test/data/webui/settings/clear_browsing_data_dialog_v2_test.ts b/chrome/test/data/webui/settings/clear_browsing_data_dialog_v2_test.ts
index b6967ba..73d8076 100644
--- a/chrome/test/data/webui/settings/clear_browsing_data_dialog_v2_test.ts
+++ b/chrome/test/data/webui/settings/clear_browsing_data_dialog_v2_test.ts
@@ -10,12 +10,13 @@
 import type {ClearBrowsingDataResult, SettingsCheckboxElement, SettingsClearBrowsingDataDialogV2Element, SettingsHistoryDeletionDialogElement} from 'chrome://settings/lazy_load.js';
 import {BrowsingDataType, ClearBrowsingDataBrowserProxyImpl, getDataTypePrefName, getTimePeriodString, TimePeriod} from 'chrome://settings/lazy_load.js';
 import type {SettingsPrefsElement} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs, SignedInState, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, MetricsBrowserProxyImpl, SignedInState, StatusAction, SyncBrowserProxyImpl, Router, routes, resetRouterForTesting} from 'chrome://settings/settings.js';
 import {assertArrayEquals, assertEquals, assertFalse, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';
 
 import {TestClearBrowsingDataBrowserProxy} from './test_clear_browsing_data_browser_proxy.js';
+import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
 import {TestSyncBrowserProxy} from './test_sync_browser_proxy.js';
 
 // clang-format on
@@ -23,6 +24,7 @@
 suite('DeleteBrowsingDataDialog', function() {
   let testClearBrowsingDataBrowserProxy: TestClearBrowsingDataBrowserProxy;
   let testSyncBrowserProxy: TestSyncBrowserProxy;
+  let testMetricsBrowserProxy: TestMetricsBrowserProxy;
   let dialog: SettingsClearBrowsingDataDialogV2Element;
   let settingsPrefs: SettingsPrefsElement;
 
@@ -37,11 +39,17 @@
         testClearBrowsingDataBrowserProxy);
     testSyncBrowserProxy = new TestSyncBrowserProxy();
     SyncBrowserProxyImpl.setInstance(testSyncBrowserProxy);
+    testMetricsBrowserProxy = new TestMetricsBrowserProxy();
+    MetricsBrowserProxyImpl.setInstance(testMetricsBrowserProxy);
 
     setClearBrowsingDataPrefs(false);
     return createDialog();
   });
 
+  teardown(function() {
+    resetRouterForTesting();
+  });
+
   function setClearBrowsingDataPrefs(enableCheckboxes: boolean) {
     settingsPrefs.set(
         `prefs.${getDataTypePrefName(BrowsingDataType.HISTORY)}.value`,
@@ -265,6 +273,13 @@
     // </if>
   });
 
+  test('MetricsDialogCreated', async function() {
+    Router.getInstance().navigateTo(routes.CLEAR_BROWSER_DATA);
+    assertEquals(
+        'ClearBrowsingData_DialogCreated',
+        await testMetricsBrowserProxy.whenCalled('recordAction'));
+  });
+
   test('ShowMoreButton', async function() {
     assertTrue(isVisible(dialog.$.showMoreButton));
 
@@ -288,6 +303,11 @@
 
     dialog.$.showMoreButton.click();
     await waitAfterNextRender(dialog);
+
+    assertEquals(
+        'Settings.DeleteBrowsingData.CheckboxesShowMoreClick',
+        await testMetricsBrowserProxy.whenCalled('recordAction'));
+
     // On show more click, all checkboxes should be visible in default order.
     verifyCheckboxesVisibleForDataTypesInOrder([
       BrowsingDataType.HISTORY,
@@ -619,6 +639,11 @@
         {showHistoryNotice: false, showPasswordsNotice: false});
     await promiseResolver.promise;
 
+
+    const metricTimePeriod = await testClearBrowsingDataBrowserProxy.whenCalled(
+        'recordSettingsClearBrowsingDataAdvancedTimePeriodHistogram');
+    assertEquals(TimePeriod.LAST_DAY, metricTimePeriod);
+
     // Verify dialog is closed after deletion is completed.
     assertFalse(dialog.$.deleteBrowsingDataDialog.open);
   });
@@ -710,6 +735,9 @@
 
     dialog.$.manageOtherGoogleDataRow.click();
     await flushTasks();
+    assertEquals(
+        'Settings.DeleteBrowsingData.OtherDataEntryPointClick',
+        await testMetricsBrowserProxy.whenCalled('recordAction'));
 
     otherGoogleDataDialog =
         dialog.shadowRoot!.querySelector('settings-other-google-data-dialog');
@@ -829,6 +857,9 @@
     assertTrue(!!signOutLink);
     signOutLink.click();
     await testSyncBrowserProxy.whenCalled('signOut');
+    assertEquals(
+        'Settings.DeleteBrowsingData.CookiesSignOutLinkClick',
+        await testMetricsBrowserProxy.whenCalled('recordAction'));
   });
   // </if>
 });
diff --git a/chrome/test/data/webui/settings/clear_browsing_data_time_picker_test.ts b/chrome/test/data/webui/settings/clear_browsing_data_time_picker_test.ts
index 7c5e60f5..dff9109 100644
--- a/chrome/test/data/webui/settings/clear_browsing_data_time_picker_test.ts
+++ b/chrome/test/data/webui/settings/clear_browsing_data_time_picker_test.ts
@@ -7,15 +7,18 @@
 import type {SettingsClearBrowsingDataTimePicker} from 'chrome://settings/lazy_load.js';
 import {getTimePeriodString, TimePeriod} from 'chrome://settings/lazy_load.js';
 import type {SettingsPrefsElement} from 'chrome://settings/settings.js';
-import {CrSettingsPrefs} from 'chrome://settings/settings.js';
+import {CrSettingsPrefs, MetricsBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
 
+import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
+
 // clang-format on
 
 suite('DeleteBrowsingDataTimePicker', function() {
   let timePicker: SettingsClearBrowsingDataTimePicker;
+  let testMetricsBrowserProxy: TestMetricsBrowserProxy;
   let settingsPrefs: SettingsPrefsElement;
 
   suiteSetup(function() {
@@ -30,6 +33,8 @@
     timePicker.prefs = settingsPrefs.prefs;
     timePicker.setPrefValue(
         'browser.clear_data.time_period', TimePeriod.LAST_HOUR);
+    testMetricsBrowserProxy = new TestMetricsBrowserProxy();
+    MetricsBrowserProxyImpl.setInstance(testMetricsBrowserProxy);
 
     document.body.appendChild(timePicker);
     return flushTasks();
@@ -257,4 +262,14 @@
         TimePeriod.LAST_DAY,
         timePicker.getPref('browser.clear_data.time_period').value);
   });
+
+  test('MetricsTimePickerMoreClick', async function() {
+    // Open the 'More' dropdown menu.
+    timePicker.$.moreButton.click();
+    flush();
+
+    assertEquals(
+        'Settings.DeleteBrowsingData.TimePickerMoreClick',
+        await testMetricsBrowserProxy.whenCalled('recordAction'));
+  });
 });
diff --git a/chrome/test/data/webui/settings/other_google_data_dialog_test.ts b/chrome/test/data/webui/settings/other_google_data_dialog_test.ts
index dbcb352..1afa97e 100644
--- a/chrome/test/data/webui/settings/other_google_data_dialog_test.ts
+++ b/chrome/test/data/webui/settings/other_google_data_dialog_test.ts
@@ -6,12 +6,13 @@
 
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import type {SettingsOtherGoogleDataDialogElement} from 'chrome://settings/lazy_load.js';
-import {loadTimeData, OpenWindowProxyImpl, PasswordManagerImpl, PasswordManagerPage, SignedInState} from 'chrome://settings/settings.js';
+import {loadTimeData, MetricsBrowserProxyImpl, OpenWindowProxyImpl, PasswordManagerImpl, PasswordManagerPage, SignedInState} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {TestOpenWindowProxy} from 'chrome://webui-test/test_open_window_proxy.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
 
+import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
 import {TestPasswordManagerProxy} from './test_password_manager_proxy.js';
 
 // TODO(crbug.com/422340428): Add tests for back & cancel buttons.
@@ -19,6 +20,8 @@
   let dialog: SettingsOtherGoogleDataDialogElement;
   let passwordManagerProxy: TestPasswordManagerProxy;
   let testOpenWindowProxy: TestOpenWindowProxy;
+  let testMetricsBrowserProxy: TestMetricsBrowserProxy;
+
 
   setup(function() {
     passwordManagerProxy = new TestPasswordManagerProxy();
@@ -27,6 +30,9 @@
     testOpenWindowProxy = new TestOpenWindowProxy();
     OpenWindowProxyImpl.setInstance(testOpenWindowProxy);
 
+    testMetricsBrowserProxy = new TestMetricsBrowserProxy();
+    MetricsBrowserProxyImpl.setInstance(testMetricsBrowserProxy);
+
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
     dialog = document.createElement('settings-other-google-data-dialog');
     document.body.appendChild(dialog);
@@ -51,6 +57,10 @@
     assertEquals(
         PasswordManagerPage.PASSWORDS,
         await passwordManagerProxy.whenCalled('showPasswordManager'));
+
+    assertEquals(
+        'Settings.DeleteBrowsingData.PasswordManagerLinkClick',
+        await testMetricsBrowserProxy.whenCalled('recordAction'));
   });
 
   test('MyActivityLinkClick', async function() {
@@ -60,6 +70,10 @@
     const url = await testOpenWindowProxy.whenCalled('openUrl');
     assertEquals(
         loadTimeData.getString('deleteBrowsingDataMyActivityUrl'), url);
+
+    assertEquals(
+        'Settings.DeleteBrowsingData.MyActivityLinkClick',
+        await testMetricsBrowserProxy.whenCalled('recordAction'));
   });
 
   test('GoogleSearchHistoryLinkClick', async function() {
@@ -69,6 +83,10 @@
     const url = await testOpenWindowProxy.whenCalled('openUrl');
     assertEquals(
         loadTimeData.getString('deleteBrowsingDataSearchHistoryUrl'), url);
+
+    assertEquals(
+        'Settings.DeleteBrowsingData.GoogleSearchHistoryLinkClick',
+        await testMetricsBrowserProxy.whenCalled('recordAction'));
   });
 
   test('NonGoogleSearchHistorySubLabel', async function() {
diff --git a/chrome/windows_services/elevated_tracing_service/elevated_tracing_service_delegate.cc b/chrome/windows_services/elevated_tracing_service/elevated_tracing_service_delegate.cc
index 78bc9af..aab08c7 100644
--- a/chrome/windows_services/elevated_tracing_service/elevated_tracing_service_delegate.cc
+++ b/chrome/windows_services/elevated_tracing_service/elevated_tracing_service_delegate.cc
@@ -123,13 +123,14 @@
   ipc_support_.emplace(ipc_thread_.task_runner(),
                        mojo::core::ScopedIPCSupport::ShutdownPolicy::FAST);
 
+  // Initialize tracing in the process.
+  tracing::InitTracingPostFeatureList(/*enable_consumer=*/false,
+                                      /*will_trace_thread_restart=*/false);
+
   // Run a ThreadPool.
   base::ThreadPoolInstance::CreateAndStartWithDefaultParams(
       "elevated_tracing_service");
 
-  // Initialize tracing in the process.
-  tracing::InitTracingPostFeatureList(/*enable_consumer=*/false);
-
   // Create the global SessionRegistry.
   session_registry_ = base::MakeRefCounted<SessionRegistry>();
 
diff --git a/chromeos/ash/experiences/arc/intent_helper/DIR_METADATA b/chromeos/ash/experiences/arc/intent_helper/DIR_METADATA
index 3d15d120..bda997ea 100644
--- a/chromeos/ash/experiences/arc/intent_helper/DIR_METADATA
+++ b/chromeos/ash/experiences/arc/intent_helper/DIR_METADATA
@@ -1,4 +1,4 @@
-team_email: "cros-web-apps-team@google.com"
+team_email: "lt-web-apps-team@google.com"
 os: CHROME_OS
 buganizer: {
   component_id: 1389907
diff --git a/clank b/clank
index 9ce226d..60b638f 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 9ce226d8f9370de9fddcec8d604728541108e8dd
+Subproject commit 60b638f88d1ae3076b23e34c18e0be8c61726f93
diff --git a/components/app_restore/COMMON_METADATA b/components/app_restore/COMMON_METADATA
index fd074a9..96ae79b 100644
--- a/components/app_restore/COMMON_METADATA
+++ b/components/app_restore/COMMON_METADATA
@@ -2,4 +2,4 @@
   component_id:1389907
 }
 os: CHROME_OS
-team_email: "cros-web-apps-team@google.com"
\ No newline at end of file
+team_email: "lt-web-apps-team@google.com"
\ No newline at end of file
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 6009535..a7941c8 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
@@ -71,10 +71,6 @@
 // `AttributeInstance::GetNormalizedType()` and the getter/setter methods for
 // how this problem is handled.
 class AttributeInstance final {
-  using StateInfo = base::StrongAlias<class StateInfoTag, std::u16string>;
-  using InfoStructure =
-      std::variant<CountryInfo, DateInfo, NameInfo, StateInfo, std::u16string>;
-
  public:
   // Transparent less-than relation based on the AttributeType.
   struct CompareByType;
@@ -173,6 +169,10 @@
                          const AttributeInstance& rhs) = default;
 
  private:
+  using StateInfo = base::StrongAlias<class StateInfoTag, std::u16string>;
+  using InfoStructure =
+      std::variant<CountryInfo, DateInfo, NameInfo, StateInfo, std::u16string>;
+
   FieldType GetNormalizedFieldType(FieldType field_type) const;
 
   AttributeType type_;
diff --git a/components/autofill/core/browser/filling/autofill_ai/field_filling_entity_util.cc b/components/autofill/core/browser/filling/autofill_ai/field_filling_entity_util.cc
index 60bbd805..20d5788 100644
--- a/components/autofill/core/browser/filling/autofill_ai/field_filling_entity_util.cc
+++ b/components/autofill/core/browser/filling/autofill_ai/field_filling_entity_util.cc
@@ -27,7 +27,7 @@
 namespace {
 
 // Looks for the day, month, or year from `attribute` to fill into `field`.
-std::optional<std::u16string> GetValueForDateSelectControl(
+std::optional<std::u16string> GetValueForDateSelect(
     const AttributeInstance& attribute,
     const AutofillField& field,
     const std::string& app_locale) {
@@ -36,7 +36,7 @@
     return std::nullopt;
   }
 
-  auto get_part = [&](std::u16string format_string, uint32_t min = 0,
+  auto get_part = [&](const std::u16string& format_string, uint32_t min = 0,
                       uint32_t max =
                           std::numeric_limits<uint32_t>::max()) -> uint32_t {
     std::u16string s = attribute.GetInfo(field.Type().GetStorableType(),
@@ -84,14 +84,13 @@
   }
 }
 
-std::u16string GetValueForSelectControl(const AttributeInstance& attribute,
-                                        const AutofillField& field,
-                                        const std::string& app_locale,
-                                        AddressNormalizer* address_normalizer) {
+std::u16string GetValueForSelect(const AttributeInstance& attribute,
+                                 const AutofillField& field,
+                                 const std::string& app_locale,
+                                 AddressNormalizer* address_normalizer) {
   FieldType type = field.Type().GetStorableType();
   if (IsDateFieldType(type)) {
-    return GetValueForDateSelectControl(attribute, field, app_locale)
-        .value_or(u"");
+    return GetValueForDateSelect(attribute, field, app_locale).value_or(u"");
   }
   std::u16string fill_value = GetValueForInput(attribute, field, app_locale);
   if (fill_value.empty()) {
@@ -195,8 +194,7 @@
 
   std::u16string fill_value =
       field.IsSelectElement()
-          ? GetValueForSelectControl(*attribute, field, app_locale,
-                                     address_normalizer)
+          ? GetValueForSelect(*attribute, field, app_locale, address_normalizer)
           : GetValueForInput(*attribute, field, app_locale);
 
   const bool should_obfuscate =
diff --git a/components/autofill/core/browser/form_structure_rationalizer.cc b/components/autofill/core/browser/form_structure_rationalizer.cc
index eca17ad9..1ba91f5 100644
--- a/components/autofill/core/browser/form_structure_rationalizer.cc
+++ b/components/autofill/core/browser/form_structure_rationalizer.cc
@@ -949,15 +949,18 @@
 void FormStructureRationalizer::RationalizeRepeatedZipCodeFields(
     LogManager* log_manager) {
   auto has_zip_type = [](const std::unique_ptr<AutofillField>& field) {
-    return field->ComputedType().GetStorableType() == ADDRESS_HOME_ZIP;
+    return field->is_visible() &&
+           field->ComputedType().GetStorableType() == ADDRESS_HOME_ZIP;
   };
-  auto it = std::ranges::find_if(*fields_, has_zip_type);
-  while (it != fields_->end()) {
-    auto it2 = std::find_if_not(it, fields_->end(), has_zip_type);
-    // All fields in [it, it2[ are ADDRESS_HOME_ZIP.
-    if (it2 - it == 2) {
-      AutofillField& first_zip = **it;
-      AutofillField& second_zip = **(it + 1);
+  // Invariant: All fields in [begin, end[ are ADDRESS_HOME_ZIP.
+  auto begin = fields_->begin();
+  auto end = begin;
+  while ((begin = std::find_if(end, fields_->end(), has_zip_type)) !=
+         fields_->end()) {
+    end = std::find_if_not(begin + 1, fields_->end(), has_zip_type);
+    if (end - begin == 2) {
+      AutofillField& first_zip = **begin;
+      AutofillField& second_zip = **(begin + 1);
       LOG_AF(log_manager)
           << LoggingScope::kRationalization << LogMessage::kRationalization
           << "Zip Code Rationalization: Converting sequence of (zip, "
@@ -967,7 +970,6 @@
       second_zip.SetTypeTo(AutofillType(ADDRESS_HOME_ZIP_SUFFIX),
                            AutofillPredictionSource::kRationalization);
     }
-    it = std::find_if(it2, fields_->end(), has_zip_type);
   }
 }
 
diff --git a/components/autofill/core/browser/form_structure_rationalizer.h b/components/autofill/core/browser/form_structure_rationalizer.h
index b0268626..d5300e59 100644
--- a/components/autofill/core/browser/form_structure_rationalizer.h
+++ b/components/autofill/core/browser/form_structure_rationalizer.h
@@ -103,7 +103,8 @@
   // 2 and 3.
   void RationalizeRepeatedStreetAddressFields(LogManager* log_manager);
 
-  // Rewrites sequence of (zip, zip) into (zip_prefix, zip_suffix)
+  // Rewrites sequence of visible (zip, zip) fields into (zip_prefix,
+  // zip_suffix).
   void RationalizeRepeatedZipCodeFields(LogManager* log_manager);
 
   // Rewrites sequences of (street address, address_line2) into (address_line1,
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager.cc b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
index dae678c..ac80ec8 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
@@ -2993,6 +2993,19 @@
   return suggestions;
 }
 
+std::vector<Suggestion> BrowserAutofillManager::GetLoyaltyCardSuggestions(
+    const GURL& url,
+    const FormFieldData& trigger_field) {
+  ValuablesDataManager* valuables_manager = client().GetValuablesDataManager();
+  if (!valuables_manager) {
+    return {};
+  }
+  metrics_->loyalty_card_form_event_logger.OnDidPollSuggestions(
+      trigger_field.global_id());
+  return GetSuggestionsForLoyaltyCards(*valuables_manager, url,
+                                       trigger_field.is_autofilled());
+}
+
 // TODO(crbug.com/40219607) Eliminate and replace with a listener?
 // Should we do the same with all the other BrowserAutofillManager events?
 void BrowserAutofillManager::OnBeforeProcessParsedForms() {
@@ -3203,9 +3216,7 @@
                 client().GetValuablesDataManager()) {
           if (suggestions.empty()) {
             suggestions = GetLoyaltyCardSuggestions(
-                *valuables_manager,
-                client().GetLastCommittedPrimaryMainFrameURL(),
-                field.is_autofilled());
+                client().GetLastCommittedPrimaryMainFrameURL(), field);
           } else {
             ExtendEmailSuggestionsWithLoyaltyCardSuggestions(
                 *valuables_manager,
@@ -3224,13 +3235,8 @@
               features::kAutofillEnableLoyaltyCardsFilling)) {
         // Only loyalty card numbers filling is supported.
         if (autofill_field->Type().GetStorableType() == LOYALTY_MEMBERSHIP_ID) {
-          if (ValuablesDataManager* valuables_manager =
-                  client().GetValuablesDataManager()) {
-            suggestions = GetLoyaltyCardSuggestions(
-                *valuables_manager,
-                client().GetLastCommittedPrimaryMainFrameURL(),
-                field.is_autofilled());
-          }
+          suggestions = GetLoyaltyCardSuggestions(
+              client().GetLastCommittedPrimaryMainFrameURL(), field);
         }
       }
       break;
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager.h b/components/autofill/core/browser/foundations/browser_autofill_manager.h
index fb7328a..2b490df3 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager.h
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager.h
@@ -422,6 +422,12 @@
       const AutofillField& autofill_trigger_field,
       autofill_metrics::SuggestionRankingContext& ranking_context);
 
+  // Returns a list of suggestions from the stored loyalty cards for the given
+  // `url` and value of `trigger_field`
+  std::vector<Suggestion> GetLoyaltyCardSuggestions(
+      const GURL& url,
+      const FormFieldData& trigger_field);
+
   // Fills or previews `form` with the information in `credit_card`.
   // `autofill_field` is the field that triggered the filling operation.
   // `trigger_source` is the reason for triggering the filling operation.
diff --git a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
index b0c5797..6759e62 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
@@ -1030,8 +1030,13 @@
   }
 
   // Simulate an Autofill query on a loyalty card field.
-  autofill_manager().OnAskForValuesToFillTest(form,
-                                              form.fields()[1].global_id());
+  {
+    base::UserActionTester user_action_tester;
+    autofill_manager().OnAskForValuesToFillTest(form,
+                                                form.fields()[1].global_id());
+    EXPECT_EQ(1, user_action_tester.GetActionCount(
+                     "Autofill_PolledLoyaltyCardSuggestions"));
+  }
   // Simulate showing a loyalty card suggestion polled from "Loyalty Number"
   // field.
   {
diff --git a/components/autofill/core/browser/metrics/form_events/loyalty_card_form_event_logger.cc b/components/autofill/core/browser/metrics/form_events/loyalty_card_form_event_logger.cc
index 25ee6d3..24765a4 100644
--- a/components/autofill/core/browser/metrics/form_events/loyalty_card_form_event_logger.cc
+++ b/components/autofill/core/browser/metrics/form_events/loyalty_card_form_event_logger.cc
@@ -28,7 +28,7 @@
   if (affiliation_categories.empty()) {
     return AffiliationCategoryMetricBucket::kNone;
   }
-  if (affiliation_categories.size() > 1) {
+  if (affiliation_categories.size() > 1u) {
     return AffiliationCategoryMetricBucket::kMixed;
   }
   switch (*affiliation_categories.begin()) {
@@ -56,7 +56,6 @@
 
 }  // namespace
 
-// TODO(crbug.com/422366498): Complete the class implementation.
 LoyaltyCardFormEventLogger::LoyaltyCardFormEventLogger(
     BrowserAutofillManager* owner)
     : FormEventLoggerBase("LoyaltyCard", owner) {}
@@ -93,7 +92,10 @@
   card_categories_filled_.insert(loyalty_card.GetAffiliationCategory(url));
 }
 
-void LoyaltyCardFormEventLogger::RecordPollSuggestions() {}
+void LoyaltyCardFormEventLogger::RecordPollSuggestions() {
+  base::RecordAction(
+      base::UserMetricsAction("Autofill_PolledLoyaltyCardSuggestions"));
+}
 
 void LoyaltyCardFormEventLogger::RecordParseForm() {
   base::RecordAction(base::UserMetricsAction("Autofill_ParsedLoyaltyCardForm"));
diff --git a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc
index 5d4ae82..34d5275 100644
--- a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc
@@ -128,7 +128,7 @@
 
 }  // namespace
 
-std::vector<Suggestion> GetLoyaltyCardSuggestions(
+std::vector<Suggestion> GetSuggestionsForLoyaltyCards(
     const ValuablesDataManager& valuables_manager,
     const GURL& url,
     bool trigger_field_is_autofilled) {
diff --git a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.h b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.h
index 8d1e83a..48a4699 100644
--- a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.h
+++ b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.h
@@ -16,7 +16,7 @@
 
 // Generates loyalty card suggestions for given `origin`. Loyalty cards are
 // extracted from the `valuables_manager`.
-std::vector<Suggestion> GetLoyaltyCardSuggestions(
+std::vector<Suggestion> GetSuggestionsForLoyaltyCards(
     const ValuablesDataManager& valuables_manager,
     const GURL& url,
     bool trigger_field_is_autofilled);
diff --git a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc
index 7bc2caa..0dd2a8a 100644
--- a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc
@@ -111,9 +111,9 @@
 };
 
 TEST_F(ValuableSuggestionGeneratorTest,
-       GetLoyaltyCardSuggestions_NoMatchingDomain) {
+       GetSuggestionsForLoyaltyCards_NoMatchingDomain) {
   EXPECT_THAT(
-      GetLoyaltyCardSuggestions(
+      GetSuggestionsForLoyaltyCards(
           valuables_data_manager(),
           GURL("https://not-existing-domain.example/test"),
           /*trigger_field_is_autofilled=*/false),
@@ -129,9 +129,9 @@
 }
 
 TEST_F(ValuableSuggestionGeneratorTest,
-       GetLoyaltyCardSuggestions_NoMatchingDomainAndFieldAutofilled) {
+       GetSuggestionsForLoyaltyCards_NoMatchingDomainAndFieldAutofilled) {
   EXPECT_THAT(
-      GetLoyaltyCardSuggestions(
+      GetSuggestionsForLoyaltyCards(
           valuables_data_manager(),
           GURL("https://not-existing-domain.example/test"),
           /*trigger_field_is_autofilled=*/true),
@@ -148,11 +148,11 @@
 }
 
 TEST_F(ValuableSuggestionGeneratorTest,
-       GetLoyaltyCardSuggestions_WithMatchingDomain) {
+       GetSuggestionsForLoyaltyCards_WithMatchingDomain) {
   std::vector<Suggestion> suggestions_with_matching_domain =
-      GetLoyaltyCardSuggestions(valuables_data_manager(),
-                                GURL("https://domain2.example/test"),
-                                /*trigger_field_is_autofilled=*/false);
+      GetSuggestionsForLoyaltyCards(valuables_data_manager(),
+                                    GURL("https://domain2.example/test"),
+                                    /*trigger_field_is_autofilled=*/false);
   EXPECT_THAT(
       suggestions_with_matching_domain,
       testing::ElementsAre(
@@ -194,11 +194,11 @@
 }
 
 TEST_F(ValuableSuggestionGeneratorTest,
-       GetLoyaltyCardSuggestions_WithMatchingDomainAndFieldAutofilled) {
+       GetSuggestionsForLoyaltyCards_WithMatchingDomainAndFieldAutofilled) {
   std::vector<Suggestion> suggestions_with_matching_domain =
-      GetLoyaltyCardSuggestions(valuables_data_manager(),
-                                GURL("https://domain2.example/test"),
-                                /*trigger_field_is_autofilled=*/true);
+      GetSuggestionsForLoyaltyCards(valuables_data_manager(),
+                                    GURL("https://domain2.example/test"),
+                                    /*trigger_field_is_autofilled=*/true);
   EXPECT_THAT(
       suggestions_with_matching_domain,
       testing::ElementsAre(
@@ -241,11 +241,11 @@
 }
 
 TEST_F(ValuableSuggestionGeneratorTest,
-       GetLoyaltyCardSuggestions_AllMatchDomain) {
+       GetSuggestionsForLoyaltyCards_AllMatchDomain) {
   EXPECT_THAT(
-      GetLoyaltyCardSuggestions(valuables_data_manager(),
-                                GURL("https://common-domain.example/test"),
-                                /*trigger_field_is_autofilled=*/false),
+      GetSuggestionsForLoyaltyCards(valuables_data_manager(),
+                                    GURL("https://common-domain.example/test"),
+                                    /*trigger_field_is_autofilled=*/false),
       testing::ElementsAre(
           EqualsLoyaltyCardSuggestion(u"987654321987654321", u"CVS Pharmacy",
                                       "loyalty_card_id_1"),
@@ -258,7 +258,7 @@
 }
 
 TEST_F(ValuableSuggestionGeneratorTest,
-       GetLoyaltyCardSuggestions_SuggestionsCustomIcon) {
+       GetSuggestionsForLoyaltyCards_SuggestionsCustomIcon) {
   test_api(valuables_data_manager()).ClearLoyaltyCards();
   const GURL program_logo = GURL("https://empty.url.com");
   gfx::Image fake_image = CustomIconForTest();
@@ -273,7 +273,7 @@
   valuables_data_manager().CacheImage(program_logo, fake_image);
   test_api(valuables_data_manager()).NotifyObservers();
 
-  std::vector<Suggestion> suggestions = GetLoyaltyCardSuggestions(
+  std::vector<Suggestion> suggestions = GetSuggestionsForLoyaltyCards(
       valuables_data_manager(), GURL("https://common-domain.example/test"),
       /*trigger_field_is_autofilled=*/false);
 
@@ -493,7 +493,7 @@
 }
 
 TEST_F(ValuableSuggestionGeneratorTest,
-       GetLoyaltyCardSuggestions_SuggestionsIPH) {
+       GetSuggestionsForLoyaltyCards_SuggestionsIPH) {
   test_api(valuables_data_manager()).ClearLoyaltyCards();
   test_api(valuables_data_manager())
       .AddLoyaltyCard(LoyaltyCard(
@@ -507,9 +507,9 @@
   raw_ptr<const base::Feature> kIphFeature =
       &feature_engagement::kIPHAutofillEnableLoyaltyCardsFeature;
   EXPECT_THAT(
-      GetLoyaltyCardSuggestions(valuables_data_manager(),
-                                GURL("https://common-domain.example/test"),
-                                /*trigger_field_is_autofilled=*/false),
+      GetSuggestionsForLoyaltyCards(valuables_data_manager(),
+                                    GURL("https://common-domain.example/test"),
+                                    /*trigger_field_is_autofilled=*/false),
       testing::ElementsAre(HasIphFeature(kIphFeature), HasNoIphFeature(),
                            HasNoIphFeature()));
 }
diff --git a/components/autofill/core/browser/webdata/valuables/valuable_data_type_controller.h b/components/autofill/core/browser/webdata/valuables/valuable_data_type_controller.h
index c3ca8df4..8613e44 100644
--- a/components/autofill/core/browser/webdata/valuables/valuable_data_type_controller.h
+++ b/components/autofill/core/browser/webdata/valuables/valuable_data_type_controller.h
@@ -12,8 +12,8 @@
 
 namespace autofill {
 
-// A class that manages the startup and shutdown of Autofill Loyalty Cards.
-// This custom implementation clears the data on sync pause in kTransportMode.
+// A class that manages the startup and shutdown of Valuables. This custom
+// implementation clears the data on sync pause in kTransportMode.
 class AutofillValuableDataTypeController : public syncer::DataTypeController {
  public:
   AutofillValuableDataTypeController(
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 461a747..ded430b 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -452,8 +452,9 @@
              "AutofillReplaceFormElementObserver",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// If enabled, FormFieldData::is_visible is a heuristic for actual visibility.
-// Otherwise, it's an alias for FormFieldData::is_focusable.
+// If enabled, FormFieldData::is_visible is a heuristic for actual visibility on
+// Blink platforms.
+// Otherwise and on iOS, it's an alias for FormFieldData::is_focusable.
 // TODO(crbug.com/324199622) When abandoned, remove FormFieldData::is_visible.
 BASE_FEATURE(kAutofillDetectFieldVisibility,
              "AutofillDetectFieldVisibility",
diff --git a/components/autofill/ios/browser/autofill_util.mm b/components/autofill/ios/browser/autofill_util.mm
index e9962c0..b5cba9e 100644
--- a/components/autofill/ios/browser/autofill_util.mm
+++ b/components/autofill/ios/browser/autofill_util.mm
@@ -380,6 +380,7 @@
 
   field_data->set_is_focusable(
       field.FindBool("is_focusable").value_or(field_data->is_focusable()));
+  field_data->set_is_visible(field_data->is_focusable());
   field_data->set_should_autocomplete(
       field.FindBool("should_autocomplete")
           .value_or(field_data->should_autocomplete()));
diff --git a/components/autofill/ios/common/features.mm b/components/autofill/ios/common/features.mm
index 760bb77..5a5569b 100644
--- a/components/autofill/ios/common/features.mm
+++ b/components/autofill/ios/common/features.mm
@@ -8,7 +8,7 @@
 
 BASE_FEATURE(kAddAddressManually,
              "AddAddressManually",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 bool IsAddAddressManuallyEnabled() {
   return base::FeatureList::IsEnabled(kAddAddressManually) &&
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index eb31181..09a48d89 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@
 {
-  "version": "57.15",
-  "log_list_timestamp": "2025-07-03T12:55:46Z",
+  "version": "57.16",
+  "log_list_timestamp": "2025-07-04T12:53:36Z",
   "operators": [
     {
       "name": "Google",
diff --git a/components/cronet/gn2bp/gen_android_bp.py b/components/cronet/gn2bp/gen_android_bp.py
index a940caf..74ebd01 100755
--- a/components/cronet/gn2bp/gen_android_bp.py
+++ b/components/cronet/gn2bp/gen_android_bp.py
@@ -1160,6 +1160,12 @@
     module.edition = list(rust_flags_dict["--edition"])[0]
 
   for cfg in rust_flags_dict.get("--cfg", set()):
+    # This cfg is not actually used in code; Chromium only uses it to force
+    # rebuilds on rustc rolls. It doesn't hurt, per se, but it does create
+    # annoying diff noise on Android.bp files, so we drop it for
+    # aesthetic/convenience reasons.
+    if cfg.startswith("cr_rustc_revision="):
+      continue
     feature_regex = re.match(_FEATURE_REGEX, cfg)
     if feature_regex:
       module.features.add(feature_regex.group(1))
diff --git a/components/exo/buffer.cc b/components/exo/buffer.cc
index 33dd5274..52a4d72 100644
--- a/components/exo/buffer.cc
+++ b/components/exo/buffer.cc
@@ -78,6 +78,11 @@
              "kExoDisableRG88Format",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// Killswitch for fixing SyncToken issue.
+BASE_FEATURE(kExoAlwaysUseSyncTokenFromTexture,
+             "ExoAlwaysUseSyncTokenFromTexture",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 // Gets the color type of |format| for creating bitmap. If it returns
 // SkColorType::kUnknown_SkColorType, it means with this format, this buffer
 // contents should not be used to create bitmap.
@@ -726,6 +731,12 @@
       sync_token = contents_texture->sync_token();
     }
 
+    // TODO(crbug.com/369003507): Remove this post safe roll out and clean up
+    // `prev_sync_token` which will be not needed anymore.
+    if (base::FeatureList::IsEnabled(kExoAlwaysUseSyncTokenFromTexture)) {
+      sync_token = contents_texture->sync_token();
+    }
+
     auto resource = viz::TransferableResource::Make(
         contents_texture_->shared_image(),
         viz::TransferableResource::ResourceSource::kExoBuffer, sync_token,
diff --git a/components/password_manager/core/common/password_manager_ui.h b/components/password_manager/core/common/password_manager_ui.h
index 93c9e62..c890a53 100644
--- a/components/password_manager/core/common/password_manager_ui.h
+++ b/components/password_manager/core/common/password_manager_ui.h
@@ -94,7 +94,8 @@
   // the same website and user.
   PASSKEY_UPGRADE_STATE,
 
-  // Password change flow was started.
+  // Password change flow ended successfully. User can trigger this state
+  // from the password change success toast.
   PASSWORD_CHANGE_STATE,
 };
 
diff --git a/components/payments/content/browser_binding/browser_bound_keys_deleter.cc b/components/payments/content/browser_binding/browser_bound_keys_deleter.cc
index 96815a94..0c48a3b 100644
--- a/components/payments/content/browser_binding/browser_bound_keys_deleter.cc
+++ b/components/payments/content/browser_binding/browser_bound_keys_deleter.cc
@@ -18,6 +18,11 @@
 BrowserBoundKeyDeleter::~BrowserBoundKeyDeleter() = default;
 
 void BrowserBoundKeyDeleter::RemoveInvalidBBKs() {
+  if (!web_data_service_) {
+    // There are no browser bound keys to be removed when there is no
+    // PaymentManifestWebDataService.
+    return;
+  }
   if (!base::FeatureList::IsEnabled(
           blink::features::kSecurePaymentConfirmationBrowserBoundKeys)) {
     return;
diff --git a/components/pdf/renderer/pdf_view_web_plugin_client.cc b/components/pdf/renderer/pdf_view_web_plugin_client.cc
index 9ac219e..0f26f6a1 100644
--- a/components/pdf/renderer/pdf_view_web_plugin_client.cc
+++ b/components/pdf/renderer/pdf_view_web_plugin_client.cc
@@ -101,7 +101,7 @@
   v8::Isolate::Scope isolate_scope(isolate_);
   v8::HandleScope handle_scope(isolate_);
   v8::Local<v8::Context> context = frame->MainWorldScriptContext();
-  DCHECK_EQ(isolate_, context->GetIsolate());
+  DCHECK_EQ(isolate_, v8::Isolate::GetCurrent());
   v8::Context::Scope context_scope(context);
 
   v8::Local<v8::Value> converted_message =
diff --git a/components/permissions/permission_prompt.h b/components/permissions/permission_prompt.h
index c515b19..22a9706 100644
--- a/components/permissions/permission_prompt.h
+++ b/components/permissions/permission_prompt.h
@@ -13,6 +13,7 @@
 #include "base/memory/raw_ptr.h"
 #include "components/permissions/features.h"
 #include "components/permissions/permission_ui_selector.h"
+#include "components/permissions/permission_uma_util.h"
 #include "ui/gfx/geometry/rect.h"
 #include "url/gurl.h"
 
diff --git a/components/permissions/permission_request_manager.h b/components/permissions/permission_request_manager.h
index bb281ea0..ae74558 100644
--- a/components/permissions/permission_request_manager.h
+++ b/components/permissions/permission_request_manager.h
@@ -237,7 +237,7 @@
     current_request_first_display_time_ = time;
   }
 
-  std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+  std::optional<PermissionUiSelector::PredictionGrantLikelihood>
   prediction_grant_likelihood_for_testing() const {
     return prediction_grant_likelihood_;
   }
@@ -542,7 +542,7 @@
 
   // The likelihood value returned by the Web Permission Predictions Service,
   // to be recorded in UKM.
-  std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+  std::optional<PermissionUiSelector::PredictionGrantLikelihood>
       prediction_grant_likelihood_;
 
   // The permission request relevance returned by an on-device ML model,
diff --git a/components/permissions/permission_request_manager_unittest.cc b/components/permissions/permission_request_manager_unittest.cc
index bdd0e7f..88f45892 100644
--- a/components/permissions/permission_request_manager_unittest.cc
+++ b/components/permissions/permission_request_manager_unittest.cc
@@ -946,7 +946,7 @@
  public:
   explicit MockNotificationPermissionUiSelector(
       std::optional<QuietUiReason> quiet_ui_reason,
-      std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+      std::optional<PermissionUiSelector::PredictionGrantLikelihood>
           prediction_likelihood,
       std::optional<base::TimeDelta> async_delay)
       : quiet_ui_reason_(quiet_ui_reason),
@@ -972,7 +972,7 @@
            request_type == RequestType::kGeolocation;
   }
 
-  std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+  std::optional<PermissionUiSelector::PredictionGrantLikelihood>
   PredictedGrantLikelihoodForUKM() override {
     return prediction_likelihood_;
   }
@@ -981,7 +981,7 @@
       PermissionRequestManager* manager,
       std::optional<QuietUiReason> quiet_ui_reason,
       std::optional<base::TimeDelta> async_delay,
-      std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+      std::optional<PermissionUiSelector::PredictionGrantLikelihood>
           prediction_likelihood = std::nullopt) {
     manager->add_permission_ui_selector_for_testing(
         std::make_unique<MockNotificationPermissionUiSelector>(
@@ -992,7 +992,7 @@
 
  private:
   std::optional<QuietUiReason> quiet_ui_reason_;
-  std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+  std::optional<PermissionUiSelector::PredictionGrantLikelihood>
       prediction_likelihood_;
   std::optional<base::TimeDelta> async_delay_;
   bool selected_ui_to_use_ = false;
@@ -1005,7 +1005,7 @@
  public:
   explicit MockCameraStreamPermissionUiSelector(
       std::optional<QuietUiReason> quiet_ui_reason,
-      std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+      std::optional<PermissionUiSelector::PredictionGrantLikelihood>
           prediction_likelihood,
       std::optional<base::TimeDelta> async_delay)
       : MockNotificationPermissionUiSelector(quiet_ui_reason,
@@ -1020,7 +1020,7 @@
       PermissionRequestManager* manager,
       std::optional<QuietUiReason> quiet_ui_reason,
       std::optional<base::TimeDelta> async_delay,
-      std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+      std::optional<PermissionUiSelector::PredictionGrantLikelihood>
           prediction_likelihood = std::nullopt) {
     manager->add_permission_ui_selector_for_testing(
         std::make_unique<MockCameraStreamPermissionUiSelector>(
@@ -1233,7 +1233,7 @@
 }
 
 TEST_F(PermissionRequestManagerTest, SelectorsPredictionLikelihood) {
-  using PredictionLikelihood = PermissionUmaUtil::PredictionGrantLikelihood;
+  using PredictionLikelihood = PermissionUiSelector::PredictionGrantLikelihood;
   const auto VeryLikely = PredictionLikelihood::
       PermissionPrediction_Likelihood_DiscretizedLikelihood_VERY_LIKELY;
   const auto Neutral = PredictionLikelihood::
diff --git a/components/permissions/permission_ui_selector.cc b/components/permissions/permission_ui_selector.cc
index eb8f655..a98006a 100644
--- a/components/permissions/permission_ui_selector.cc
+++ b/components/permissions/permission_ui_selector.cc
@@ -43,7 +43,7 @@
   return Decision(UseNormalUi(), ShowNoWarning());
 }
 
-std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+std::optional<PermissionUiSelector::PredictionGrantLikelihood>
 PermissionUiSelector::PredictedGrantLikelihoodForUKM() {
   return std::nullopt;
 }
diff --git a/components/permissions/permission_ui_selector.h b/components/permissions/permission_ui_selector.h
index b24f148..3be1bd0b 100644
--- a/components/permissions/permission_ui_selector.h
+++ b/components/permissions/permission_ui_selector.h
@@ -9,7 +9,8 @@
 
 #include "base/functional/callback_forward.h"
 #include "components/permissions/permission_request_enums.h"
-#include "components/permissions/permission_uma_util.h"
+#include "components/permissions/prediction_service/prediction_service_messages.pb.h"
+#include "components/permissions/request_type.h"
 
 namespace content {
 class WebContents;
@@ -27,6 +28,9 @@
 // can support multiple requests, but only one at a time.
 class PermissionUiSelector {
  public:
+  using PredictionGrantLikelihood =
+      PermissionPrediction_Likelihood_DiscretizedLikelihood;
+
   enum class QuietUiReason {
     kEnabledInPrefs,
     kTriggeredByCrowdDeny,
@@ -97,7 +101,7 @@
   // Will return the selector's discretized prediction value, if any is
   // applicable to be recorded in UKMs. This is specific only to a selector that
   // uses of the Web Permission Predictions Service to make decisions.
-  virtual std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+  virtual std::optional<PredictionGrantLikelihood>
   PredictedGrantLikelihoodForUKM();
 
   // Will return the selector's discretized permission request relevance, if any
diff --git a/components/permissions/permission_uma_util.cc b/components/permissions/permission_uma_util.cc
index 6a7717a..69e833e 100644
--- a/components/permissions/permission_uma_util.cc
+++ b/components/permissions/permission_uma_util.cc
@@ -305,7 +305,7 @@
     std::optional<std::vector<ElementAnchoredBubbleVariant>> variants,
     std::optional<bool> has_three_consecutive_denies,
     std::optional<bool> has_previously_revoked_permission,
-    std::optional<PermissionUmaUtil::PredictionGrantLikelihood>
+    std::optional<PermissionUiSelector::PredictionGrantLikelihood>
         predicted_grant_likelihood,
     std::optional<PermissionRequestRelevance> permission_request_relevance,
     PredictionRequestFeatures::ActionCounts
@@ -963,7 +963,8 @@
     PermissionPromptDisposition ui_disposition,
     std::optional<PermissionPromptDispositionReason> ui_reason,
     std::optional<std::vector<ElementAnchoredBubbleVariant>> variants,
-    std::optional<PredictionGrantLikelihood> predicted_grant_likelihood,
+    std::optional<PermissionUiSelector::PredictionGrantLikelihood>
+        predicted_grant_likelihood,
     std::optional<PermissionRequestRelevance> permission_request_relevance,
     std::optional<bool> prediction_decision_held_back,
     std::optional<permissions::PermissionIgnoredReason> ignored_reason,
@@ -1251,7 +1252,8 @@
     content::WebContents* web_contents,
     content::BrowserContext* browser_context,
     content::RenderFrameHost* render_frame_host,
-    std::optional<PredictionGrantLikelihood> predicted_grant_likelihood,
+    std::optional<PermissionUiSelector::PredictionGrantLikelihood>
+        predicted_grant_likelihood,
     std::optional<PermissionRequestRelevance> permission_request_relevance,
     std::optional<bool> prediction_decision_held_back) {
   DCHECK(PermissionUtil::IsPermission(permission));
diff --git a/components/permissions/permission_uma_util.h b/components/permissions/permission_uma_util.h
index b2b8c74..1a307273 100644
--- a/components/permissions/permission_uma_util.h
+++ b/components/permissions/permission_uma_util.h
@@ -17,7 +17,7 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/permissions/permission_request_enums.h"
-#include "components/permissions/prediction_service/prediction_service_messages.pb.h"
+#include "components/permissions/permission_ui_selector.h"
 #include "components/permissions/request_type.h"
 #include "content/public/browser/permission_result.h"
 #include "url/gurl.h"
@@ -609,9 +609,6 @@
 // Provides a convenient way of logging UMA for permission related operations.
 class PermissionUmaUtil {
  public:
-  using PredictionGrantLikelihood =
-      PermissionPrediction_Likelihood_DiscretizedLikelihood;
-
   static const char kPermissionsPromptShown[];
   static const char kPermissionsPromptShownGesture[];
   static const char kPermissionsPromptShownNoGesture[];
@@ -699,7 +696,8 @@
       PermissionPromptDisposition ui_disposition,
       std::optional<PermissionPromptDispositionReason> ui_reason,
       std::optional<std::vector<ElementAnchoredBubbleVariant>> variants,
-      std::optional<PredictionGrantLikelihood> predicted_grant_likelihood,
+      std::optional<PermissionUiSelector::PredictionGrantLikelihood>
+          predicted_grant_likelihood,
       std::optional<PermissionRequestRelevance> permission_request_relevance,
       std::optional<bool> prediction_decision_held_back,
       std::optional<permissions::PermissionIgnoredReason> ignored_reason,
@@ -941,7 +939,8 @@
       content::WebContents* web_contents,
       content::BrowserContext* browser_context,
       content::RenderFrameHost* render_frame_host,
-      std::optional<PredictionGrantLikelihood> predicted_grant_likelihood,
+      std::optional<PermissionUiSelector::PredictionGrantLikelihood>
+          predicted_grant_likelihood,
       std::optional<PermissionRequestRelevance> permission_request_relevance,
       std::optional<bool> prediction_decision_held_back);
 
diff --git a/components/permissions/permission_util.h b/components/permissions/permission_util.h
index edcb78f2..9722908 100644
--- a/components/permissions/permission_util.h
+++ b/components/permissions/permission_util.h
@@ -12,6 +12,7 @@
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/permissions/permission_decision.h"
 #include "components/permissions/permission_prompt.h"
+#include "components/permissions/permission_uma_util.h"
 #include "content/public/browser/permission_result.h"
 #include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-forward.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
diff --git a/components/permissions/test/mock_permission_ui_selector.cc b/components/permissions/test/mock_permission_ui_selector.cc
index 8c19facd..bf764db 100644
--- a/components/permissions/test/mock_permission_ui_selector.cc
+++ b/components/permissions/test/mock_permission_ui_selector.cc
@@ -17,7 +17,7 @@
          request_type == permissions::RequestType::kGeolocation;
 }
 
-std::optional<permissions::PermissionUmaUtil::PredictionGrantLikelihood>
+std::optional<permissions::PermissionUiSelector::PredictionGrantLikelihood>
 MockPermissionUiSelector::PredictedGrantLikelihoodForUKM() {
   return last_request_grant_likelihood_;
 }
diff --git a/components/permissions/test/mock_permission_ui_selector.h b/components/permissions/test/mock_permission_ui_selector.h
index a94aba9..79e9fcd 100644
--- a/components/permissions/test/mock_permission_ui_selector.h
+++ b/components/permissions/test/mock_permission_ui_selector.h
@@ -19,7 +19,7 @@
 
   ~MockPermissionUiSelector() override = default;
 
-  std::optional<permissions::PermissionUmaUtil::PredictionGrantLikelihood>
+  std::optional<permissions::PermissionUiSelector::PredictionGrantLikelihood>
       last_request_grant_likelihood_;
   std::optional<permissions::PermissionRequestRelevance>
       last_permission_request_relevance_;
@@ -34,7 +34,7 @@
   bool IsPermissionRequestSupported(
       permissions::RequestType request_type) override;
 
-  std::optional<permissions::PermissionUmaUtil::PredictionGrantLikelihood>
+  std::optional<permissions::PermissionUiSelector::PredictionGrantLikelihood>
   PredictedGrantLikelihoodForUKM() override;
 
   std::optional<permissions::PermissionRequestRelevance>
diff --git a/components/persistent_cache/backend_params_manager.cc b/components/persistent_cache/backend_params_manager.cc
index 361984bd..1fcaef1 100644
--- a/components/persistent_cache/backend_params_manager.cc
+++ b/components/persistent_cache/backend_params_manager.cc
@@ -201,7 +201,7 @@
   base::CreateDirectory(top_directory_);
 }
 
-int64_t BackendParamsManager::BringDownTotalFootprintOfFiles(
+FootprintReductionResult BackendParamsManager::BringDownTotalFootprintOfFiles(
     int64_t target_footprint) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -232,7 +232,8 @@
 
   // Nothing to do.
   if (total_footprint <= target_footprint) {
-    return 0;
+    return FootprintReductionResult{.current_footprint = total_footprint,
+                                    .number_of_bytes_deleted = 0};
   }
 
   // Order files from least to most recently modified to prioritize deleting
@@ -277,7 +278,9 @@
     };
   }
 
-  return deleted_size;
+  return FootprintReductionResult{
+      .current_footprint = total_footprint - deleted_size,
+      .number_of_bytes_deleted = deleted_size};
 }
 
 // static
diff --git a/components/persistent_cache/backend_params_manager.h b/components/persistent_cache/backend_params_manager.h
index 808a5f1..c87543e 100644
--- a/components/persistent_cache/backend_params_manager.h
+++ b/components/persistent_cache/backend_params_manager.h
@@ -20,6 +20,11 @@
 
 namespace persistent_cache {
 
+struct FootprintReductionResult {
+  int64_t current_footprint = 0;
+  int64_t number_of_bytes_deleted = 0;
+};
+
 // Use to retrieve or create BackendParams to open a PersistentCache. Existing
 // params are cached so that they can be retrieved synchronously when possible.
 //
@@ -63,7 +68,8 @@
   // than `target_footprint`. Use when enforcing a quota or proactively saving
   // space. If the goal is to get rid of all files use `DeleteAllFiles()`
   // instead. Returns the number of bytes deleted.
-  int64_t BringDownTotalFootprintOfFiles(int64_t target_footprint);
+  FootprintReductionResult BringDownTotalFootprintOfFiles(
+      int64_t target_footprint);
 
   // Use to get a string containing all characters supported in keys.
   static std::string GetAllAllowedCharactersInKeysForTesting();
diff --git a/components/persistent_cache/backend_params_manager_unittest.cc b/components/persistent_cache/backend_params_manager_unittest.cc
index 31ae499..6771089 100644
--- a/components/persistent_cache/backend_params_manager_unittest.cc
+++ b/components/persistent_cache/backend_params_manager_unittest.cc
@@ -210,9 +210,10 @@
 
   // The footprint of the files do not approach the requested target size so
   // nothing is done.
-  EXPECT_EQ(params_manager.BringDownTotalFootprintOfFiles(file_count *
-                                                          file_size * 10),
-            0);
+  EXPECT_EQ(
+      params_manager.BringDownTotalFootprintOfFiles(file_count * file_size * 10)
+          .number_of_bytes_deleted,
+      0);
 }
 
 TEST_F(BackendParamsManagerTest, BringDownTotalFootPrint) {
@@ -221,7 +222,9 @@
   OverFill(params_manager);
   EXPECT_GE(base::ComputeDirectorySize(temp_dir_.GetPath()), kTargetFootprint);
 
-  EXPECT_GT(params_manager.BringDownTotalFootprintOfFiles(kTargetFootprint), 0);
+  EXPECT_GT(params_manager.BringDownTotalFootprintOfFiles(kTargetFootprint)
+                .number_of_bytes_deleted,
+            0);
 
   EXPECT_LE(base::ComputeDirectorySize(temp_dir_.GetPath()), kTargetFootprint);
 }
diff --git a/components/persistent_cache/persistent_cache_collection.cc b/components/persistent_cache/persistent_cache_collection.cc
index 8f936d2..d6c3fc7 100644
--- a/components/persistent_cache/persistent_cache_collection.cc
+++ b/components/persistent_cache/persistent_cache_collection.cc
@@ -13,14 +13,25 @@
 
 namespace {
 constexpr size_t kLruCacheCapacity = 100;
+
+// Reducing the footprint of the collection to exactly the desired target could
+// have the effect of rapidly going over the limit again. This might end up
+// issuing more reductions than desirable. This defines some headroom to try and
+// mitigate the issue.
+constexpr int64_t kFootPrintReductionHeadroomPercent = 10;
 }  // namespace
 
 namespace persistent_cache {
 
 PersistentCacheCollection::PersistentCacheCollection(
-    std::unique_ptr<BackendParamsManager> backend_params_manager)
+    std::unique_ptr<BackendParamsManager> backend_params_manager,
+    int64_t target_footprint)
     : backend_params_manager_(std::move(backend_params_manager)),
-      persistent_caches_(kLruCacheCapacity) {}
+      persistent_caches_(kLruCacheCapacity),
+      target_footprint_(target_footprint) {
+  ReduceFootPrint();
+}
+
 PersistentCacheCollection::~PersistentCacheCollection() = default;
 
 std::unique_ptr<Entry> PersistentCacheCollection::Find(
@@ -31,12 +42,37 @@
   return GetOrCreateCache(cache_id)->Find(key);
 }
 
+void PersistentCacheCollection::ReduceFootPrint() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Clear all managed persistent caches so they don't hold on to files or
+  // prevent their deletion.
+  persistent_caches_.Clear();
+
+  int64_t adjusted_target =
+      target_footprint_ / 100 * kFootPrintReductionHeadroomPercent;
+  int64_t current_footprint =
+      backend_params_manager_->BringDownTotalFootprintOfFiles(adjusted_target)
+          .current_footprint;
+
+  bytes_until_footprint_reduction_ = target_footprint_ - current_footprint;
+}
+
 void PersistentCacheCollection::Insert(const std::string& cache_id,
                                        std::string_view key,
                                        base::span<const uint8_t> content,
                                        EntryMetadata metadata) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  // Approximate the footprint of this insert to the size of the key and value
+  // combined. This is optimistic in some ways since it doesn't account for any
+  // overhead and pessimimistic as it assumes every single write is both new and
+  // doesn't evict something else.
+  bytes_until_footprint_reduction_ -= (key.size() + content.size());
+  if (bytes_until_footprint_reduction_ <= 0) {
+    ReduceFootPrint();
+  }
+
   GetOrCreateCache(cache_id)->Insert(key, content, metadata);
 }
 
@@ -48,7 +84,7 @@
 void PersistentCacheCollection::DeleteAllFiles() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // Delete all managed parsistent caches so they don't hold on to files or
+  // Clear all managed persistent caches so they don't hold on to files or
   // prevent their deletion.
   persistent_caches_.Clear();
 
diff --git a/components/persistent_cache/persistent_cache_collection.h b/components/persistent_cache/persistent_cache_collection.h
index a01cd02..b8cc1e167 100644
--- a/components/persistent_cache/persistent_cache_collection.h
+++ b/components/persistent_cache/persistent_cache_collection.h
@@ -32,7 +32,8 @@
 class COMPONENT_EXPORT(PERSISTENT_CACHE) PersistentCacheCollection {
  public:
   PersistentCacheCollection(
-      std::unique_ptr<BackendParamsManager> params_manager);
+      std::unique_ptr<BackendParamsManager> params_manager,
+      int64_t target_footprint);
   ~PersistentCacheCollection();
 
   // Not copyable or moveable.
@@ -58,6 +59,8 @@
   void DeleteAllFiles();
 
  private:
+  void ReduceFootPrint();
+
   PersistentCache* GetOrCreateCache(const std::string& cache_id);
 
   std::unique_ptr<BackendParamsManager> backend_params_manager_
@@ -66,6 +69,13 @@
   base::HashingLRUCache<std::string, std::unique_ptr<PersistentCache>>
       persistent_caches_ GUARDED_BY_CONTEXT(sequence_checker_);
 
+  // Desired maximum disk footprint for the cache collection in bytes.
+  const int64_t target_footprint_;
+
+  // Running tally of how many bytes can be inserted before a footprint
+  // reduction is triggered.
+  int64_t bytes_until_footprint_reduction_ = 0;
+
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
diff --git a/components/persistent_cache/persistent_cache_collection_unittest.cc b/components/persistent_cache/persistent_cache_collection_unittest.cc
index 426cce5..814f7c0 100644
--- a/components/persistent_cache/persistent_cache_collection_unittest.cc
+++ b/components/persistent_cache/persistent_cache_collection_unittest.cc
@@ -5,7 +5,9 @@
 #include "components/persistent_cache/persistent_cache_collection.h"
 
 #include "base/containers/span.h"
+#include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/strings/string_number_conversions.h"
 #include "components/persistent_cache/backend_params_manager.h"
 #include "components/persistent_cache/entry.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -13,11 +15,15 @@
 namespace persistent_cache {
 namespace {
 
+// Default value large enough to no interfere with functioning of tests.
+constexpr size_t kTargetFootprint = 1024 * 1024 * 100;
+
 TEST(PersistentCacheCollection, Retrieval) {
   base::ScopedTempDir temp_dir;
   CHECK(temp_dir.CreateUniqueTempDir());
   PersistentCacheCollection collection(
-      std::make_unique<BackendParamsManager>(temp_dir.GetPath()));
+      std::make_unique<BackendParamsManager>(temp_dir.GetPath()),
+      kTargetFootprint);
 
   constexpr char first_cache_id[] = "first_cache_id";
   constexpr char second_cache_id[] = "second_cache_id";
@@ -48,7 +54,8 @@
   base::ScopedTempDir temp_dir;
   CHECK(temp_dir.CreateUniqueTempDir());
   PersistentCacheCollection collection(
-      std::make_unique<BackendParamsManager>(temp_dir.GetPath()));
+      std::make_unique<BackendParamsManager>(temp_dir.GetPath()),
+      kTargetFootprint);
 
   std::string first_cache_id = "first_cache_id";
   std::string first_key = "first_key";
@@ -70,7 +77,8 @@
   base::ScopedTempDir temp_dir;
   CHECK(temp_dir.CreateUniqueTempDir());
   PersistentCacheCollection collection(
-      std::make_unique<BackendParamsManager>(temp_dir.GetPath()));
+      std::make_unique<BackendParamsManager>(temp_dir.GetPath()),
+      kTargetFootprint);
 
   std::string first_cache_id = "first_cache_id";
   std::string first_key = "first_key";
@@ -87,5 +95,68 @@
   EXPECT_EQ(collection.Find(first_cache_id, first_key), nullptr);
 }
 
+TEST(PersistentCacheCollection, ContinuousFootPrintReduction) {
+  base::ScopedTempDir temp_dir;
+  CHECK(temp_dir.CreateUniqueTempDir());
+  constexpr int64_t kSmallFootprint = 128;
+
+  PersistentCacheCollection collection(
+      std::make_unique<BackendParamsManager>(temp_dir.GetPath()),
+      kSmallFootprint);
+
+  int i = 0;
+  int64_t added_footprint = 0;
+
+  // Add things right up to the limit where files start to be deleted.
+  while (added_footprint < kSmallFootprint) {
+    std::string number = base::NumberToString(i);
+
+    // Account for size of key and value.
+    int64_t footprint_after_insertion = added_footprint + number.length() * 2;
+
+    if (footprint_after_insertion < kSmallFootprint) {
+      int64_t directory_size_before =
+          base::ComputeDirectorySize(temp_dir.GetPath());
+
+      collection.Insert(number, number, base::as_byte_span(number));
+
+      int64_t directory_size_after =
+          base::ComputeDirectorySize(temp_dir.GetPath());
+
+      // If there's no footprint reduction and the new values are being stored
+      // then directory size is just going up.
+      EXPECT_GT(directory_size_after, directory_size_before);
+    }
+
+    added_footprint = footprint_after_insertion;
+    ++i;
+  }
+
+  // If `kSmallFootprint` is not large enough to trigger at least two successful
+  // insertions into the cache the test does not provide sufficient coverage.
+  ASSERT_GT(i, 2);
+
+  int64_t directory_size_before =
+      base::ComputeDirectorySize(temp_dir.GetPath());
+
+  // Since no footprint reduction should have been triggered all values added
+  // should still be available.
+  for (int j = 0; j < i - 1; ++j) {
+    std::string number = base::NumberToString(j);
+    EXPECT_NE(collection.Find(number, number), nullptr);
+  }
+
+  // Add one more item which should bring things over the limit.
+  std::string number = base::NumberToString(i + 1);
+  collection.Insert(number, number, base::as_byte_span(number));
+
+  int64_t directory_size_after = base::ComputeDirectorySize(temp_dir.GetPath());
+
+  // Footprint reduction happened automatically. Note that's it's not possible
+  // to specifically know what the current footprint is since the last insert
+  // took place after the footprint reduction.
+  EXPECT_LT(directory_size_after, directory_size_before);
+}
+
 }  // namespace
 }  // namespace persistent_cache
diff --git a/components/policy/resources/templates/policy_definitions/Arc/ArcAppToWebAppSharingEnabled.yaml b/components/policy/resources/templates/policy_definitions/Arc/ArcAppToWebAppSharingEnabled.yaml
index 55c7f00..5e271ca 100644
--- a/components/policy/resources/templates/policy_definitions/Arc/ArcAppToWebAppSharingEnabled.yaml
+++ b/components/policy/resources/templates/policy_definitions/Arc/ArcAppToWebAppSharingEnabled.yaml
@@ -15,7 +15,7 @@
   value: false
 owners:
 - ovn@google.com
-- cros-web-apps-team@google.com
+- lt-web-apps-team@google.com
 schema:
   type: boolean
 supported_on:
diff --git a/components/policy/resources/templates/policy_definitions/Arc/ArcOpenLinksInBrowserByDefault.yaml b/components/policy/resources/templates/policy_definitions/Arc/ArcOpenLinksInBrowserByDefault.yaml
index dbe2cc7..ca05484 100644
--- a/components/policy/resources/templates/policy_definitions/Arc/ArcOpenLinksInBrowserByDefault.yaml
+++ b/components/policy/resources/templates/policy_definitions/Arc/ArcOpenLinksInBrowserByDefault.yaml
@@ -14,7 +14,7 @@
 - caption: Open links in Android apps by default
   value: false
 owners:
-- cros-web-apps-team@google.com
+- lt-web-apps-team@google.com
 - ovn@google.com
 schema:
   type: boolean
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/ManagedConfigurationPerOrigin.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/ManagedConfigurationPerOrigin.yaml
index 70c827b..7e77d21 100644
--- a/components/policy/resources/templates/policy_definitions/Miscellaneous/ManagedConfigurationPerOrigin.yaml
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/ManagedConfigurationPerOrigin.yaml
@@ -18,8 +18,8 @@
 future_on:
 - fuchsia
 owners:
-- file://chrome/browser/device_api/OWNERS
-- apotapchuk@chromium.org
+- pwa-commercial@google.com
+- anqing@chromium.org
 schema:
   items:
     properties:
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 eafb53f..d5b0a40 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
@@ -13,15 +13,11 @@
 #include "base/check_deref.h"
 #include "base/check_is_test.h"
 #include "base/command_line.h"
-#include "base/debug/crash_logging.h"
-#include "base/debug/dump_without_crashing.h"
-#include "base/debug/stack_trace.h"
 #include "base/feature_list.h"
 #include "base/json/json_reader.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
-#include "base/not_fatal_until.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_restrictions.h"
@@ -29,7 +25,6 @@
 #include "base/version.h"
 #include "base/version_info/version_info.h"
 #include "components/country_codes/country_codes.h"
-#include "components/crash/core/common/crash_key.h"
 #include "components/policy/core/common/policy_service.h"
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -482,52 +477,24 @@
     return;
   }
 
-  // This block adds some debugging data for b/344899110, where the method
-  // is called from the choice moment while a display state is already cached.
-  // TODO(b/344899110): Clean up the debugging info when the bug is fixed.
+  // This block monitors the prevalence of some hard to reproduce case where
+  // this method is called more than once per profile session with
+  // `is_from_cached_state == true`, which seems to indicate a choice being made
+  // more than once per profile during the same session. If this had been
+  // actually triggered by a user flow, it could imply that they had to complete
+  // the choice screen more than once, which is bad UX.
+  // See crbug.com/390272573 for context and past debugging attempts.
   if (!is_from_cached_state) {
-    if (!display_state_record_caller_) {
+    if (!has_recorded_display_state_) {
       CHECK(!profile_prefs_->HasPrefPath(
           prefs::kDefaultSearchProviderPendingChoiceScreenDisplayState));
-      display_state_record_caller_ =
-          std::make_unique<base::debug::StackTrace>();
+      has_recorded_display_state_ = true;
     } else {
-      // Recording a stack trace to crash keys, based on
-      // https://crsrc.org/c/docs/debugging_with_crash_keys.md
-      static crash_reporter::CrashKeyString<1024> caller_trace_key(
-          "ChoiceService-og_caller_trace");
-      crash_reporter::SetCrashKeyStringToStackTrace(
-          &caller_trace_key, *display_state_record_caller_.get());
-
-      SCOPED_CRASH_KEY_BOOL(
-          "ChoiceService", "ds_pref_has_value",
-          profile_prefs_->HasPrefPath(
-              prefs::kDefaultSearchProviderPendingChoiceScreenDisplayState));
-
-      std::optional<ChoiceScreenDisplayState> already_cached_display_state =
-          ChoiceScreenDisplayState::FromDict(profile_prefs_->GetDict(
-              prefs::kDefaultSearchProviderPendingChoiceScreenDisplayState));
-      std::optional<base::Time> completion_time =
-          GetChoiceScreenCompletionTimestamp(profile_prefs_.get());
-
-      SCOPED_CRASH_KEY_STRING64(
-          "ChoiceService", "choice_time_delta",
-          completion_time.has_value()
-              ? base::StringPrintf("%" PRId64 "ms",
-                                   (base::Time::Now() - completion_time.value())
-                                       .InMilliseconds())
-              : "<null>");
-      SCOPED_CRASH_KEY_STRING32(
-          "ChoiceService", "screen_items_equal",
-          already_cached_display_state.has_value()
-              ? (already_cached_display_state.value().search_engines ==
-                         display_state.search_engines
-                     ? "yes"
-                     : "no")
-              : "no value");
-
-      NOTREACHED(base::NotFatalUntil::M141);
-      caller_trace_key.Clear();
+      // Re-entry, we just record a histogram and let the code otherwise
+      // proceed.
+      base::UmaHistogramBoolean(
+          "Search.ChoiceDebug.UnexpectedRecordDisplayStateReentryHasCompletion",
+          GetChoiceCompletionMetadata(profile_prefs_.get()).has_value());
     }
   }
 
@@ -666,7 +633,7 @@
 }
 
 void SearchEngineChoiceService::ResetState() {
-  display_state_record_caller_.reset();
+  has_recorded_display_state_ = false;
 }
 
 // static
diff --git a/components/search_engines/search_engine_choice/search_engine_choice_service.h b/components/search_engines/search_engine_choice/search_engine_choice_service.h
index 1307dcd..217b1b1 100644
--- a/components/search_engines/search_engine_choice/search_engine_choice_service.h
+++ b/components/search_engines/search_engine_choice/search_engine_choice_service.h
@@ -7,8 +7,6 @@
 
 #include <optional>
 
-#include "base/debug/stack_trace.h"
-#include "base/gtest_prod_util.h"
 #include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
@@ -198,9 +196,10 @@
   // in runtime (different runs can still return different values, though).
   std::optional<int> country_id_cache_;
 
-  // Used to track caller of `MaybeRecordChoiceScreenDisplayState()` to debug
-  // some unmet expectations, see b/344899110.
-  std::unique_ptr<base::debug::StackTrace> display_state_record_caller_;
+  // Used to track whether `MaybeRecordChoiceScreenDisplayState()` has already
+  // been called for this profile, to monitor the prevalence of some unexpected
+  // behaviour, see crbug.com/390272573.
+  bool has_recorded_display_state_ = false;
 
   base::WeakPtrFactory<SearchEngineChoiceService> weak_ptr_factory_{this};
 };
diff --git a/components/services/app_service/COMMON_METADATA b/components/services/app_service/COMMON_METADATA
index 23334c7..3fc32c7d 100644
--- a/components/services/app_service/COMMON_METADATA
+++ b/components/services/app_service/COMMON_METADATA
@@ -1,4 +1,4 @@
-team_email: "cros-web-apps-team@google.com"
+team_email: "lt-web-apps-team@google.com"
 buganizer: {
   component_id: 1389907
 }
diff --git a/components/signin/internal/identity_manager/primary_account_manager.cc b/components/signin/internal/identity_manager/primary_account_manager.cc
index c74ef73..c14327e3 100644
--- a/components/signin/internal/identity_manager/primary_account_manager.cc
+++ b/components/signin/internal/identity_manager/primary_account_manager.cc
@@ -325,8 +325,6 @@
                                std::string());
   registry->RegisterStringPref(prefs::kGoogleServicesLastSignedInUsername,
                                std::string());
-  registry->RegisterStringPref(prefs::kGoogleServicesSecondLastSyncingGaiaId,
-                               std::string());
   registry->RegisterStringPref(prefs::kGoogleServicesAccountId, std::string());
   registry->RegisterBooleanPref(prefs::kGoogleServicesConsentedToSync, false);
   registry->RegisterStringPref(
@@ -552,12 +550,6 @@
   SetPrimaryAccountInternal(account_info, /*consented_to_sync=*/true,
                             scoped_pref_commit);
 
-  // Before `kGoogleServicesLastSyncingGaiaId` is updated, keep a copy of the
-  // previous value, and store it in a separate pref.
-  scoped_pref_commit.SetString(
-      prefs::kGoogleServicesSecondLastSyncingGaiaId,
-      client_->GetPrefs()->GetString(prefs::kGoogleServicesLastSyncingGaiaId));
-
   // Go ahead and update the last signed in account info here as well. Once a
   // user is signed in the corresponding preferences should match. Doing it here
   // as opposed to on signin allows us to catch the upgrade scenario.
diff --git a/components/signin/public/base/signin_pref_names.cc b/components/signin/public/base/signin_pref_names.cc
index 6ff7d8b..ecd494e 100644
--- a/components/signin/public/base/signin_pref_names.cc
+++ b/components/signin/public/base/signin_pref_names.cc
@@ -75,12 +75,6 @@
 const char kGoogleServicesLastSignedInUsername[] =
     "google.services.last_signed_in_username";
 
-// Holds a copy of what `kGoogleServicesLastSyncingGaiaId` contained before it
-// was updated to contain the latest value, which happens when the Sync consent
-// is granted.
-const char kGoogleServicesSecondLastSyncingGaiaId[] =
-    "google.services.second_last_gaia_id";
-
 // Device id scoped to single signin. This device id will be regenerated if user
 // signs out and signs back in. When refresh token is requested for this user it
 // will be annotated with this device id.
diff --git a/components/signin/public/base/signin_pref_names.h b/components/signin/public/base/signin_pref_names.h
index 85cea0b..3d01e57 100644
--- a/components/signin/public/base/signin_pref_names.h
+++ b/components/signin/public/base/signin_pref_names.h
@@ -38,8 +38,6 @@
 COMPONENT_EXPORT(SIGNIN_SWITCHES)
 extern const char kGoogleServicesLastSignedInUsername[];
 COMPONENT_EXPORT(SIGNIN_SWITCHES)
-extern const char kGoogleServicesSecondLastSyncingGaiaId[];
-COMPONENT_EXPORT(SIGNIN_SWITCHES)
 extern const char kGoogleServicesSigninScopedDeviceId[];
 COMPONENT_EXPORT(SIGNIN_SWITCHES)
 extern const char kGoogleServicesSyncingGaiaIdMigratedToSignedIn[];
diff --git a/components/sync/base/BUILD.gn b/components/sync/base/BUILD.gn
index 6277a37..5713ef42 100644
--- a/components/sync/base/BUILD.gn
+++ b/components/sync/base/BUILD.gn
@@ -44,7 +44,6 @@
     "passphrase_enums.cc",
     "passphrase_enums.h",
     "pref_names.h",
-    "previously_syncing_gaia_id_info_for_metrics.h",
     "progress_marker_map.cc",
     "progress_marker_map.h",
     "report_unrecoverable_error.cc",
diff --git a/components/sync/base/pref_names.h b/components/sync/base/pref_names.h
index 3f17b32..dd4822a 100644
--- a/components/sync/base/pref_names.h
+++ b/components/sync/base/pref_names.h
@@ -31,11 +31,6 @@
     "sync.has_setup_completed";
 #endif  // !BUILDFLAG(IS_CHROMEOS)
 
-// A boolean representing whether or not configuration has completed at least
-// once since the legacy sync-the-feature was turned on.
-inline constexpr char kFirstSyncCompletedInFullSyncMode[] =
-    "sync.first_full_sync_completed";
-
 // Boolean specifying whether to automatically sync all data types (including
 // future ones, as they're added).  If this is true, the following preferences
 // (kSyncBookmarks, kSyncPasswords, etc.) can all be ignored.
diff --git a/components/sync/base/previously_syncing_gaia_id_info_for_metrics.h b/components/sync/base/previously_syncing_gaia_id_info_for_metrics.h
deleted file mode 100644
index a983ca05..0000000
--- a/components/sync/base/previously_syncing_gaia_id_info_for_metrics.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_BASE_PREVIOUSLY_SYNCING_GAIA_ID_INFO_FOR_METRICS_H_
-#define COMPONENTS_SYNC_BASE_PREVIOUSLY_SYNCING_GAIA_ID_INFO_FOR_METRICS_H_
-
-namespace syncer {
-
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused. Keep in sync with the homonym enum
-// in tools/metrics/histograms/metadata/sync/enums.xml.
-// Exposed in the header file for testing.
-// LINT.IfChange(PreviouslySyncingGaiaIdInfoForMetrics)
-enum class PreviouslySyncingGaiaIdInfoForMetrics {
-  // Information not available or current state doesn't fall within any of the
-  // buckets listed below. Note that this value is also used to represent
-  // irrelevant scenarios such as local sync (roaming profiles) being enabled
-  // or datatypes being reconfigured as a result of the user customizing sync
-  // settings.
-  kUnspecified = 0,
-  // Deprecated: kNotEnoughInformationToTell = 1,
-  kSyncFeatureNeverPreviouslyTurnedOn = 2,
-  kCurrentGaiaIdMatchesPreviousWithSyncFeatureOn = 3,
-  kCurrentGaiaIdIfDiffersPreviousWithSyncFeatureOn = 4,
-  kMaxValue = kCurrentGaiaIdIfDiffersPreviousWithSyncFeatureOn
-};
-// LINT.ThenChange(/tools/metrics/histograms/metadata/sync/enums.xml:PreviouslySyncingGaiaIdInfoForMetrics)
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_SYNC_BASE_PREVIOUSLY_SYNCING_GAIA_ID_INFO_FOR_METRICS_H_
diff --git a/components/sync/model/data_type_activation_request.h b/components/sync/model/data_type_activation_request.h
index df2d15ed..8fcdfe48 100644
--- a/components/sync/model/data_type_activation_request.h
+++ b/components/sync/model/data_type_activation_request.h
@@ -8,7 +8,6 @@
 #include <string>
 
 #include "base/time/time.h"
-#include "components/sync/base/previously_syncing_gaia_id_info_for_metrics.h"
 #include "components/sync/base/sync_mode.h"
 #include "components/sync/model/model_error.h"
 #include "google_apis/gaia/gaia_id.h"
@@ -31,8 +30,6 @@
 
   ModelErrorHandler error_handler;
   GaiaId authenticated_gaia_id;
-  PreviouslySyncingGaiaIdInfoForMetrics previously_syncing_gaia_id_info =
-      PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified;
   std::string cache_guid;
   SyncMode sync_mode = SyncMode::kFull;
 
diff --git a/components/sync/service/BUILD.gn b/components/sync/service/BUILD.gn
index d8380a2..b6123c7 100644
--- a/components/sync/service/BUILD.gn
+++ b/components/sync/service/BUILD.gn
@@ -9,7 +9,6 @@
     "active_devices_provider.h",
     "backend_migrator.cc",
     "backend_migrator.h",
-    "configure_context.cc",
     "configure_context.h",
     "data_type_controller.cc",
     "data_type_controller.h",
diff --git a/components/sync/service/configure_context.cc b/components/sync/service/configure_context.cc
deleted file mode 100644
index a0fc36e..0000000
--- a/components/sync/service/configure_context.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/service/configure_context.h"
-
-namespace syncer {
-
-ConfigureContext::ConfigureContext() = default;
-
-ConfigureContext::ConfigureContext(const ConfigureContext&) = default;
-
-ConfigureContext::~ConfigureContext() = default;
-
-}  // namespace syncer
diff --git a/components/sync/service/configure_context.h b/components/sync/service/configure_context.h
index 1bdc067e..086dcccc 100644
--- a/components/sync/service/configure_context.h
+++ b/components/sync/service/configure_context.h
@@ -8,7 +8,6 @@
 #include <string>
 
 #include "base/time/time.h"
-#include "components/sync/base/previously_syncing_gaia_id_info_for_metrics.h"
 #include "components/sync/base/sync_mode.h"
 #include "components/sync/engine/configure_reason.h"
 #include "google_apis/gaia/gaia_id.h"
@@ -22,17 +21,11 @@
 // controllers, which propagate analogous information to the processor/bridge
 // via DataTypeActivationRequest.
 struct ConfigureContext {
-  ConfigureContext();
-  ConfigureContext(const ConfigureContext&);
-  ~ConfigureContext();
-
   GaiaId authenticated_gaia_id;
   std::string cache_guid;
   SyncMode sync_mode = SyncMode::kFull;
   ConfigureReason reason = CONFIGURE_REASON_UNKNOWN;
   base::Time configuration_start_time;
-  PreviouslySyncingGaiaIdInfoForMetrics previously_syncing_gaia_id_info =
-      PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified;
 };
 
 }  // namespace syncer
diff --git a/components/sync/service/data_type_controller.cc b/components/sync/service/data_type_controller.cc
index 763c553..5708d9c 100644
--- a/components/sync/service/data_type_controller.cc
+++ b/components/sync/service/data_type_controller.cc
@@ -170,8 +170,6 @@
   request.error_handler = base::BindRepeating(
       &DataTypeController::ReportModelError, weak_ptr_factory_.GetWeakPtr());
   request.authenticated_gaia_id = configure_context.authenticated_gaia_id;
-  request.previously_syncing_gaia_id_info =
-      configure_context.previously_syncing_gaia_id_info;
   request.cache_guid = configure_context.cache_guid;
   request.sync_mode = configure_context.sync_mode;
   request.configuration_start_time = configure_context.configuration_start_time;
diff --git a/components/sync/service/data_type_manager.h b/components/sync/service/data_type_manager.h
index 232326d8..e8e32c95 100644
--- a/components/sync/service/data_type_manager.h
+++ b/components/sync/service/data_type_manager.h
@@ -11,7 +11,6 @@
 #include "base/functional/callback_forward.h"
 #include "base/values.h"
 #include "components/sync/base/data_type.h"
-#include "components/sync/base/sync_mode.h"
 #include "components/sync/base/sync_stop_metadata_fate.h"
 #include "components/sync/engine/configure_reason.h"
 #include "components/sync/model/type_entities_count.h"
@@ -49,7 +48,6 @@
   struct ConfigureResult {
     ConfigureStatus status = ABORTED;
     DataTypeSet requested_types;
-    SyncMode sync_mode = SyncMode::kFull;
   };
 
   virtual ~DataTypeManager() = default;
diff --git a/components/sync/service/data_type_manager_impl.cc b/components/sync/service/data_type_manager_impl.cc
index 27bc5bd..59c877f7 100644
--- a/components/sync/service/data_type_manager_impl.cc
+++ b/components/sync/service/data_type_manager_impl.cc
@@ -806,8 +806,7 @@
   base::TimeDelta configure_time = base::Time::Now() - last_restart_time_;
 
   ConfigureResult result = {.status = status,
-                            .requested_types = preferred_types_,
-                            .sync_mode = last_requested_context_.sync_mode};
+                            .requested_types = preferred_types_};
 
   const std::string prefix_uma =
       (last_requested_context_.reason == CONFIGURE_REASON_NEW_CLIENT)
diff --git a/components/sync/service/sync_prefs.cc b/components/sync/service/sync_prefs.cc
index c3ea885..bab87a9a 100644
--- a/components/sync/service/sync_prefs.cc
+++ b/components/sync/service/sync_prefs.cc
@@ -156,8 +156,6 @@
       prefs::internal::kSyncInitialSyncFeatureSetupComplete, false);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
-  registry->RegisterBooleanPref(
-      prefs::internal::kFirstSyncCompletedInFullSyncMode, false);
   registry->RegisterBooleanPref(kObsoleteAutofillWalletImportEnabledMigrated,
                                 false);
   registry->RegisterIntegerPref(prefs::internal::kSyncToSigninMigrationState,
@@ -252,23 +250,6 @@
 }
 #endif  // !BUILDFLAG(IS_CHROMEOS)
 
-bool SyncPrefs::IsFirstSyncCompletedInFullSyncMode() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return pref_service_->GetBoolean(
-      prefs::internal::kFirstSyncCompletedInFullSyncMode);
-}
-
-void SyncPrefs::SetFirstSyncCompletedInFullSyncMode() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  pref_service_->SetBoolean(prefs::internal::kFirstSyncCompletedInFullSyncMode,
-                            true);
-}
-
-void SyncPrefs::ClearFirstSyncCompletedInFullSyncMode() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  pref_service_->ClearPref(prefs::internal::kFirstSyncCompletedInFullSyncMode);
-}
-
 bool SyncPrefs::HasKeepEverythingSynced() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return pref_service_->GetBoolean(prefs::internal::kSyncKeepEverythingSynced);
diff --git a/components/sync/service/sync_prefs.h b/components/sync/service/sync_prefs.h
index d636ba47..c7470f9a 100644
--- a/components/sync/service/sync_prefs.h
+++ b/components/sync/service/sync_prefs.h
@@ -82,12 +82,6 @@
   void ClearInitialSyncFeatureSetupComplete();
 #endif  // !BUILDFLAG(IS_CHROMEOS)
 
-  // A boolean representing whether or not configuration has completed at least
-  // once since the legacy sync-the-feature was turned on.
-  bool IsFirstSyncCompletedInFullSyncMode() const;
-  void SetFirstSyncCompletedInFullSyncMode();
-  void ClearFirstSyncCompletedInFullSyncMode();
-
   // Whether the "Sync everything" toggle is enabled. This flag only has an
   // effect if Sync-the-feature is enabled. Note that even if this is true, some
   // types may be disabled e.g. due to enterprise policy.
diff --git a/components/sync/service/sync_service_impl.cc b/components/sync/service/sync_service_impl.cc
index eb80d55..68a923d 100644
--- a/components/sync/service/sync_service_impl.cc
+++ b/components/sync/service/sync_service_impl.cc
@@ -26,10 +26,8 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
-#include "components/prefs/pref_service.h"
 #include "components/signin/public/base/gaia_id_hash.h"
 #include "components/signin/public/base/signin_metrics.h"
-#include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/base/signin_switches.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
@@ -1174,10 +1172,6 @@
   DCHECK(!user_settings_->IsPassphraseRequiredForPreferredDataTypes() ||
          user_settings_->IsEncryptedDatatypePreferred());
 
-  if (result.sync_mode == SyncMode::kFull) {
-    sync_prefs_.SetFirstSyncCompletedInFullSyncMode();
-  }
-
   DVLOG(2) << "Notify observers OnConfigureDone";
   NotifyObservers();
 
@@ -1608,8 +1602,6 @@
 
   ConfigureContext configure_context;
   configure_context.authenticated_gaia_id = GetAccountInfo().gaia;
-  configure_context.previously_syncing_gaia_id_info =
-      DeterminePreviouslySyncingGaiaIdInfoForMetrics();
   configure_context.cache_guid = engine_->GetCacheGuid();
   configure_context.sync_mode = SyncMode::kFull;
   configure_context.reason = reason;
@@ -2109,7 +2101,6 @@
   // If the migration didn't finish before StopAndClear() was called, mark it as
   // done so it doesn't trigger again if the user signs in later.
   sync_prefs_.MarkPartialSyncToSigninMigrationFullyDone();
-  sync_prefs_.ClearFirstSyncCompletedInFullSyncMode();
 
   if (reset_engine_reason == ResetEngineReason::kNotSignedIn) {
     sync_prefs_.ClearCachedPersistentAuthErrorForMetrics();
@@ -2218,47 +2209,6 @@
       user_settings_->GetSelectedTypes().Has(UserSelectableType::kHistory));
 }
 
-PreviouslySyncingGaiaIdInfoForMetrics
-SyncServiceImpl::DeterminePreviouslySyncingGaiaIdInfoForMetrics() const {
-  if (IsLocalSyncEnabled()) {
-    return PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified;
-  }
-
-  // If a configuration cycle already completed in full-sync mode, return
-  // `kUnspecified` because this field is used to record metrics that are
-  // relevant immediately when the user turns sync on. Later
-  // reconfigurations, such as when the user toggles sync settings, should be
-  // excluded from metrics.
-  if (sync_prefs_.IsFirstSyncCompletedInFullSyncMode()) {
-    return PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified;
-  }
-
-  // Depending on whether sync the feature is currently on or not, the gaia ID
-  // corresponding to the previous user is stored in one pref or another. That's
-  // because `kGoogleServicesLastSyncingGaiaId` is updated early, as soon as
-  // the sync consent is granted, and before the notification reaches
-  // SyncServiceImpl.
-  const GaiaId previously_syncing_gaia_id = GaiaId(
-      HasSyncConsent() ? sync_client_->GetPrefService()->GetString(
-                             prefs::kGoogleServicesSecondLastSyncingGaiaId)
-                       : sync_client_->GetPrefService()->GetString(
-                             prefs::kGoogleServicesLastSyncingGaiaId));
-
-  if (previously_syncing_gaia_id.empty()) {
-    // It is known that no previous gaia ID existed that turned sync on.
-    return PreviouslySyncingGaiaIdInfoForMetrics::
-        kSyncFeatureNeverPreviouslyTurnedOn;
-  }
-
-  const GaiaId current_gaia_id = GetAccountInfo().gaia;
-
-  return current_gaia_id == previously_syncing_gaia_id
-             ? PreviouslySyncingGaiaIdInfoForMetrics::
-                   kCurrentGaiaIdMatchesPreviousWithSyncFeatureOn
-             : PreviouslySyncingGaiaIdInfoForMetrics::
-                   kCurrentGaiaIdIfDiffersPreviousWithSyncFeatureOn;
-}
-
 const GURL& SyncServiceImpl::GetSyncServiceUrlForDebugging() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return sync_service_url_;
diff --git a/components/sync/service/sync_service_impl.h b/components/sync/service/sync_service_impl.h
index 5878140..96ef73f5 100644
--- a/components/sync/service/sync_service_impl.h
+++ b/components/sync/service/sync_service_impl.h
@@ -364,10 +364,6 @@
       signin_metrics::AccessPoint access_point,
       signin::ConsentLevel consent_level);
 
-  // Computes the enum value that should be propagated via ConfigureContext.
-  PreviouslySyncingGaiaIdInfoForMetrics
-  DeterminePreviouslySyncingGaiaIdInfoForMetrics() const;
-
   // Called when a SetupInProgressHandle issued by this instance is destroyed.
   void OnSetupInProgressHandleDestroyed();
 
diff --git a/components/sync/service/sync_service_impl_unittest.cc b/components/sync/service/sync_service_impl_unittest.cc
index 7c171e4..d9b0a39 100644
--- a/components/sync/service/sync_service_impl_unittest.cc
+++ b/components/sync/service/sync_service_impl_unittest.cc
@@ -34,7 +34,6 @@
 #include "components/sync/base/data_type.h"
 #include "components/sync/base/features.h"
 #include "components/sync/base/pref_names.h"
-#include "components/sync/base/previously_syncing_gaia_id_info_for_metrics.h"
 #include "components/sync/base/sync_util.h"
 #include "components/sync/base/user_selectable_type.h"
 #include "components/sync/engine/nigori/key_derivation_params.h"
@@ -606,9 +605,6 @@
   ASSERT_TRUE(
       engine_factory()->HasTransportDataIncludingFirstSync(gaia_id_hash()));
 
-  SyncPrefs sync_prefs(prefs());
-  ASSERT_TRUE(sync_prefs.IsFirstSyncCompletedInFullSyncMode());
-
   // Sign-out.
   signin::PrimaryAccountMutator* account_mutator =
       identity_manager()->GetPrimaryAccountMutator();
@@ -624,7 +620,6 @@
             service()->GetDisableReasons());
   EXPECT_FALSE(
       engine_factory()->HasTransportDataIncludingFirstSync(gaia_id_hash()));
-  EXPECT_FALSE(sync_prefs.IsFirstSyncCompletedInFullSyncMode());
 }
 
 TEST_F(SyncServiceImplTest, SignInWhilePausedClearsCachedPersistentAuthError) {
@@ -1630,179 +1625,6 @@
   ShutdownAndReleaseService();
 }
 
-TEST_F(SyncServiceImplTest, PreviouslySyncingGaiaIdInfoWithLocalSync) {
-  InitializeServiceWithLocalSyncBackend();
-  base::RunLoop().RunUntilIdle();
-
-  ASSERT_EQ(SyncService::TransportState::ACTIVE,
-            service()->GetTransportState());
-  EXPECT_EQ(PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified,
-            get_controller(DEVICE_INFO)
-                ->model(SyncMode::kTransportOnly)
-                ->previously_syncing_gaia_id_info());
-}
-
-TEST_F(
-    SyncServiceImplTest,
-    DifferentPreviouslySyncingGaiaIdInfoWithSyncFeatureAlreadyEnabledUponStartup) {
-  PopulatePrefsForInitialSyncFeatureSetupComplete();
-  SignInWithSyncConsent();
-  // If sync is on, the pref is already populated with the current gaia ID.
-  prefs()->SetString(::prefs::kGoogleServicesLastSyncingGaiaId,
-                     identity_test_env()
-                         ->identity_manager()
-                         ->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)
-                         .gaia.ToString());
-  // In this case the second pref becomes relevant and is different to the
-  // current one.
-  prefs()->SetString(::prefs::kGoogleServicesSecondLastSyncingGaiaId, "other");
-  InitializeService();
-  base::RunLoop().RunUntilIdle();
-
-  ASSERT_EQ(SyncService::TransportState::ACTIVE,
-            service()->GetTransportState());
-  EXPECT_EQ(
-      PreviouslySyncingGaiaIdInfoForMetrics::
-          kCurrentGaiaIdIfDiffersPreviousWithSyncFeatureOn,
-      get_controller(DEVICE_INFO)->model()->previously_syncing_gaia_id_info());
-}
-
-TEST_F(
-    SyncServiceImplTest,
-    SamePreviouslySyncingGaiaIdInfoWithSyncFeatureAlreadyEnabledUponStartup) {
-  PopulatePrefsForInitialSyncFeatureSetupComplete();
-  SignInWithSyncConsent();
-  // If sync is on, the pref is already populated with the current gaia ID.
-  prefs()->SetString(::prefs::kGoogleServicesLastSyncingGaiaId,
-                     identity_test_env()
-                         ->identity_manager()
-                         ->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)
-                         .gaia.ToString());
-  // In this case the second pref becomes relevant and matches the current one.
-  prefs()->SetString(::prefs::kGoogleServicesSecondLastSyncingGaiaId,
-                     identity_test_env()
-                         ->identity_manager()
-                         ->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)
-                         .gaia.ToString());
-  InitializeService();
-  base::RunLoop().RunUntilIdle();
-
-  ASSERT_EQ(SyncService::TransportState::ACTIVE,
-            service()->GetTransportState());
-  EXPECT_EQ(
-      PreviouslySyncingGaiaIdInfoForMetrics::
-          kCurrentGaiaIdMatchesPreviousWithSyncFeatureOn,
-      get_controller(DEVICE_INFO)->model()->previously_syncing_gaia_id_info());
-}
-
-TEST_F(SyncServiceImplTest, PreviouslySyncingGaiaIdInfoWithNoPreviousAccount) {
-  InitializeService();
-  base::RunLoop().RunUntilIdle();
-
-  SignInWithoutSyncConsent();
-  base::RunLoop().RunUntilIdle();
-
-  ASSERT_EQ(SyncService::TransportState::ACTIVE,
-            service()->GetTransportState());
-  EXPECT_EQ(PreviouslySyncingGaiaIdInfoForMetrics::
-                kSyncFeatureNeverPreviouslyTurnedOn,
-            get_controller(DEVICE_INFO)
-                ->model(SyncMode::kTransportOnly)
-                ->previously_syncing_gaia_id_info());
-}
-
-TEST_F(SyncServiceImplTest,
-       PreviouslySyncingGaiaIdInfoWithSamePreviousAccount) {
-  prefs()->SetString(::prefs::kGoogleServicesLastSyncingGaiaId,
-                     signin::GetTestGaiaIdForEmail(kTestUser).ToString());
-
-  InitializeService();
-  base::RunLoop().RunUntilIdle();
-  ASSERT_EQ(SyncService::TransportState::DISABLED,
-            service()->GetTransportState());
-
-  SignInWithoutSyncConsent();
-  base::RunLoop().RunUntilIdle();
-
-  ASSERT_EQ(SyncService::TransportState::ACTIVE,
-            service()->GetTransportState());
-  EXPECT_EQ(PreviouslySyncingGaiaIdInfoForMetrics::
-                kCurrentGaiaIdMatchesPreviousWithSyncFeatureOn,
-            get_controller(DEVICE_INFO)
-                ->model(SyncMode::kTransportOnly)
-                ->previously_syncing_gaia_id_info());
-}
-
-TEST_F(SyncServiceImplTest,
-       PreviouslySyncingGaiaIdInfoWithDifferentPreviousAccount) {
-  prefs()->SetString(::prefs::kGoogleServicesLastSyncingGaiaId,
-                     "DifferentGaiaId");
-
-  InitializeService();
-  base::RunLoop().RunUntilIdle();
-  ASSERT_EQ(SyncService::TransportState::DISABLED,
-            service()->GetTransportState());
-
-  SignInWithoutSyncConsent();
-  base::RunLoop().RunUntilIdle();
-
-  ASSERT_EQ(SyncService::TransportState::ACTIVE,
-            service()->GetTransportState());
-  EXPECT_EQ(PreviouslySyncingGaiaIdInfoForMetrics::
-                kCurrentGaiaIdIfDiffersPreviousWithSyncFeatureOn,
-            get_controller(DEVICE_INFO)
-                ->model(SyncMode::kTransportOnly)
-                ->previously_syncing_gaia_id_info());
-}
-
-TEST_F(SyncServiceImplTest,
-       PreviouslySyncingGaiaIdInfoWithFirstSyncAlreadyCompleted) {
-  // Sign in and enable sync.
-  InitializeService();
-  SignInWithSyncConsent();
-
-#if BUILDFLAG(IS_CHROMEOS)
-  // On ChromeOS Ash, the first setup is marked as complete automatically.
-  ASSERT_TRUE(
-      service()->GetUserSettings()->IsInitialSyncFeatureSetupComplete());
-#else
-  // For any platform except ChromeOS Ash, the user needs to turn sync on
-  // manually.
-  ASSERT_FALSE(
-      service()->GetUserSettings()->IsInitialSyncFeatureSetupComplete());
-  service()->GetUserSettings()->SetInitialSyncFeatureSetupComplete(
-      syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
-#endif  // BUILDFLAG(IS_CHROMEOS)
-
-  base::RunLoop().RunUntilIdle();
-
-  ASSERT_EQ(SyncService::TransportState::ACTIVE,
-            service()->GetTransportState());
-  ASSERT_EQ(PreviouslySyncingGaiaIdInfoForMetrics::
-                kSyncFeatureNeverPreviouslyTurnedOn,
-            get_controller(BOOKMARKS)
-                ->model(SyncMode::kFull)
-                ->previously_syncing_gaia_id_info());
-  ASSERT_TRUE(SyncPrefs(prefs()).IsFirstSyncCompletedInFullSyncMode());
-
-  // Disable all types from settings.
-  service()->GetUserSettings()->SetSelectedTypes(/*sync_everything=*/false,
-                                                 /*types=*/{});
-  base::RunLoop().RunUntilIdle();
-
-  // Re-enable all types from settings.
-  service()->GetUserSettings()->SetSelectedTypes(/*sync_everything=*/true,
-                                                 /*types=*/{});
-  base::RunLoop().RunUntilIdle();
-
-  // Reconfiguration as a result of customizing toggles in settings should be
-  // reported as `kUnspecified` (i.e. not relevant for metric recording).
-  EXPECT_EQ(PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified,
-            get_controller(BOOKMARKS)
-                ->model(SyncMode::kFull)
-                ->previously_syncing_gaia_id_info());
-}
-
 // Regression test for crbug.com/1043642, can be removed once
 // SyncServiceImpl usages after shutdown are addressed.
 TEST_F(SyncServiceImplTest, ShouldProvideDisableReasonsAfterShutdown) {
diff --git a/components/sync/test/fake_data_type_controller_delegate.cc b/components/sync/test/fake_data_type_controller_delegate.cc
index de1da522..f0b736d 100644
--- a/components/sync/test/fake_data_type_controller_delegate.cc
+++ b/components/sync/test/fake_data_type_controller_delegate.cc
@@ -70,7 +70,6 @@
   sync_started_ = true;
 
   error_handler_ = request.error_handler;
-  previously_syncing_gaia_id_info_ = request.previously_syncing_gaia_id_info;
 
   // If the model has already experienced the error, report it immediately.
   if (model_error_) {
diff --git a/components/sync/test/fake_data_type_controller_delegate.h b/components/sync/test/fake_data_type_controller_delegate.h
index f8148deb..af9591be9 100644
--- a/components/sync/test/fake_data_type_controller_delegate.h
+++ b/components/sync/test/fake_data_type_controller_delegate.h
@@ -11,7 +11,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
 #include "components/sync/base/data_type.h"
-#include "components/sync/base/previously_syncing_gaia_id_info_for_metrics.h"
 #include "components/sync/engine/data_type_activation_response.h"
 #include "components/sync/model/data_type_controller_delegate.h"
 #include "components/sync/model/model_error.h"
@@ -60,12 +59,6 @@
   // TODO(crbug.com/40945017): Replace this with something like "HasMetadata".
   int clear_metadata_count() const;
 
-  // The last value of the enum received via OnSyncStarting().
-  PreviouslySyncingGaiaIdInfoForMetrics previously_syncing_gaia_id_info()
-      const {
-    return previously_syncing_gaia_id_info_;
-  }
-
   // The value that will be returned for GetAllNodesForDebugging().
   void SetNodesForDebugging(base::Value::List nodes);
 
@@ -95,8 +88,6 @@
   std::optional<ModelError> model_error_;
   StartCallback start_callback_;
   ModelErrorHandler error_handler_;
-  PreviouslySyncingGaiaIdInfoForMetrics previously_syncing_gaia_id_info_ =
-      PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified;
   base::Value::List all_nodes_for_debugging_;
   base::WeakPtrFactory<FakeDataTypeControllerDelegate> weak_ptr_factory_{this};
 };
diff --git a/components/sync_bookmarks/BUILD.gn b/components/sync_bookmarks/BUILD.gn
index c70a3f9..e93045c 100644
--- a/components/sync_bookmarks/BUILD.gn
+++ b/components/sync_bookmarks/BUILD.gn
@@ -38,13 +38,6 @@
     "synced_bookmark_tracker_entity.h",
   ]
 
-  if (!is_android && !is_ios && !is_chromeos) {
-    sources += [
-      "bookmark_model_merger_comparison_metrics.cc",
-      "bookmark_model_merger_comparison_metrics.h",
-    ]
-  }
-
   deps = [
     "//base",
     "//components/bookmarks/browser",
@@ -77,10 +70,6 @@
     "test_bookmark_model_view.h",
   ]
 
-  if (!is_android && !is_ios && !is_chromeos) {
-    sources += [ "bookmark_model_merger_comparison_metrics_unittest.cc" ]
-  }
-
   deps = [
     ":sync_bookmarks",
     "//base",
diff --git a/components/sync_bookmarks/bookmark_data_type_processor.cc b/components/sync_bookmarks/bookmark_data_type_processor.cc
index 72b6b006..28187cb 100644
--- a/components/sync_bookmarks/bookmark_data_type_processor.cc
+++ b/components/sync_bookmarks/bookmark_data_type_processor.cc
@@ -121,22 +121,6 @@
       {{syncer::BOOKMARKS, tracker.GetUnsyncedDataCount()}});
 }
 
-// Gaia-ID-related metrics should not be recorded on mobile platforms, where
-// Sync-the-feature is no longer a thing (excluding edge cases pending
-// migration). On desktop, use `wipe_model_upon_sync_disabled_behavior` as
-// a workaround to distinguish transport mode from full-sync mode, as
-// metrics should only be recorded for the latter.
-bool ShouldRecordPreviouslySyncingGaiaIdMetrics(
-    syncer::WipeModelUponSyncDisabledBehavior
-        wipe_model_upon_sync_disabled_behavior) {
-#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) || BUILDFLAG(IS_CHROMEOS)
-  return false;
-#else   // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) || BUILDFLAG(IS_CHROMEOS)
-  return wipe_model_upon_sync_disabled_behavior ==
-         syncer::WipeModelUponSyncDisabledBehavior::kNever;
-#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) || BUILDFLAG(IS_CHROMEOS)
-}
-
 }  // namespace
 
 BookmarkDataTypeProcessor::BookmarkDataTypeProcessor(
@@ -608,13 +592,8 @@
         bookmark_model_, bookmark_model_observer_.get());
 
     bookmark_model_->EnsurePermanentNodesExist();
-    BookmarkModelMerger model_merger(
-        std::move(updates), bookmark_model_, favicon_service_,
-        bookmark_tracker_.get(),
-        ShouldRecordPreviouslySyncingGaiaIdMetrics(
-            wipe_model_upon_sync_disabled_behavior_)
-            ? activation_request_.previously_syncing_gaia_id_info
-            : syncer::PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified);
+    BookmarkModelMerger model_merger(std::move(updates), bookmark_model_,
+                                     favicon_service_, bookmark_tracker_.get());
     model_merger.Merge();
   }
 
diff --git a/components/sync_bookmarks/bookmark_model_merger.cc b/components/sync_bookmarks/bookmark_model_merger.cc
index 04d81d6..9032cdde 100644
--- a/components/sync_bookmarks/bookmark_model_merger.cc
+++ b/components/sync_bookmarks/bookmark_model_merger.cc
@@ -16,7 +16,6 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "base/uuid.h"
-#include "build/build_config.h"
 #include "components/bookmarks/browser/bookmark_node.h"
 #include "components/bookmarks/browser/bookmark_uuids.h"
 #include "components/sync/base/data_type.h"
@@ -31,11 +30,6 @@
 #include "components/sync_bookmarks/synced_bookmark_tracker_entity.h"
 #include "ui/base/models/tree_node_iterator.h"
 
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_CHROMEOS)
-#include "components/sync_bookmarks/bookmark_model_merger_comparison_metrics.h"
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) &&
-        // !BUILDFLAG(IS_CHROMEOS)
-
 namespace sync_bookmarks {
 
 namespace {
@@ -65,14 +59,6 @@
 const char kMobileBookmarksTag[] = "synced_bookmarks";
 const char kOtherBookmarksTag[] = "other_bookmarks";
 
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_CHROMEOS)
-// Enabled by default, intended as a kill switch.
-BASE_FEATURE(kSyncRecordBookmarkComparisonMetrics,
-             "SyncRecordBookmarkComparisonMetrics",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) &&
-        // !BUILDFLAG(IS_CHROMEOS)
-
 // Maximum depth to sync bookmarks tree to protect against stack overflow.
 // Keep in sync with |base::internal::kAbsoluteMaxDepth| in json_common.h.
 const size_t kMaxBookmarkTreeDepth = 200;
@@ -542,31 +528,14 @@
   return node;
 }
 
-// static
-BookmarkModelMerger::RemoteTreeNode
-BookmarkModelMerger::RemoteTreeNode::BuildForTesting(
-    syncer::UpdateResponseData update,
-    std::vector<RemoteTreeNode> children) {
-  RemoteTreeNode node;
-  node.update_ = std::move(update);
-  node.children_ = std::move(children);
-  return node;
-}
-
 BookmarkModelMerger::BookmarkModelMerger(
     UpdateResponseDataList updates,
     BookmarkModelView* bookmark_model,
     favicon::FaviconService* favicon_service,
-    SyncedBookmarkTracker* bookmark_tracker,
-    syncer::PreviouslySyncingGaiaIdInfoForMetrics
-        previously_syncing_gaia_id_info)
+    SyncedBookmarkTracker* bookmark_tracker)
     : bookmark_model_(bookmark_model),
       favicon_service_(favicon_service),
       bookmark_tracker_(bookmark_tracker),
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_CHROMEOS)
-      previously_syncing_gaia_id_info_(previously_syncing_gaia_id_info),
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) &&
-        // !BUILDFLAG(IS_CHROMEOS)
       remote_updates_size_(updates.size()),
       remote_forest_(BuildRemoteForest(std::move(updates), bookmark_tracker)),
       uuid_to_match_map_(
@@ -591,23 +560,6 @@
 void BookmarkModelMerger::Merge() {
   TRACE_EVENT0("sync", "BookmarkModelMerger::Merge");
 
-  // These metrics are only recorded on desktop platforms, so there is no need
-  // to increase the binary size elsewhere.
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_CHROMEOS)
-  if (previously_syncing_gaia_id_info_ !=
-      syncer::PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified) {
-    if (base::FeatureList::IsEnabled(kSyncRecordBookmarkComparisonMetrics)) {
-      metrics::CompareBookmarkModelAndLogHistograms(
-          *bookmark_model_, remote_forest_, previously_syncing_gaia_id_info_);
-    }
-
-    base::UmaHistogramEnumeration(
-        "Sync.BookmarkModelMerger.PreviouslySyncingGaiaId",
-        previously_syncing_gaia_id_info_);
-  }
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) &&
-        // !BUILDFLAG(IS_CHROMEOS)
-
   // Algorithm description:
   // Match up the roots and recursively do the following:
   // * For each remote node for the current remote (sync) parent node, either
diff --git a/components/sync_bookmarks/bookmark_model_merger.h b/components/sync_bookmarks/bookmark_model_merger.h
index 319e06c..ce154f90d 100644
--- a/components/sync_bookmarks/bookmark_model_merger.h
+++ b/components/sync_bookmarks/bookmark_model_merger.h
@@ -14,8 +14,6 @@
 #include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
 #include "base/uuid.h"
-#include "build/build_config.h"
-#include "components/sync/base/previously_syncing_gaia_id_info_for_metrics.h"
 #include "components/sync/base/unique_position.h"
 #include "components/sync/engine/commit_and_get_updates_types.h"
 
@@ -43,9 +41,7 @@
   BookmarkModelMerger(syncer::UpdateResponseDataList updates,
                       BookmarkModelView* bookmark_model,
                       favicon::FaviconService* favicon_service,
-                      SyncedBookmarkTracker* bookmark_tracker,
-                      syncer::PreviouslySyncingGaiaIdInfoForMetrics
-                          previously_syncing_gaia_id_info);
+                      SyncedBookmarkTracker* bookmark_tracker);
 
   BookmarkModelMerger(const BookmarkModelMerger&) = delete;
   BookmarkModelMerger& operator=(const BookmarkModelMerger&) = delete;
@@ -59,8 +55,8 @@
   // and metadata entities in the injected tracker.
   void Merge();
 
-  // Internal representation of a remote tree, composed of nodes. Exposed
-  // publicly for metric recording.
+ private:
+  // Internal representation of a remote tree, composed of nodes.
   class RemoteTreeNode final {
    private:
     using UpdatesPerParentUuid =
@@ -81,10 +77,6 @@
         size_t max_depth,
         UpdatesPerParentUuid* updates_per_parent_uuid);
 
-    // Test-only factory function.
-    static RemoteTreeNode BuildForTesting(syncer::UpdateResponseData update,
-                                          std::vector<RemoteTreeNode> children);
-
     ~RemoteTreeNode();
 
     // Allow moves, useful during construction.
@@ -121,7 +113,6 @@
   // a permanent node, keyed by server-defined unique tag of the root.
   using RemoteForest = std::unordered_map<std::string, RemoteTreeNode>;
 
- private:
   // Represents a pair of bookmarks, one local and one remote, that have been
   // matched by UUID. They are guaranteed to have the same type and URL (if
   // applicable).
@@ -237,11 +228,6 @@
   const raw_ptr<BookmarkModelView> bookmark_model_;
   const raw_ptr<favicon::FaviconService> favicon_service_;
   const raw_ptr<SyncedBookmarkTracker> bookmark_tracker_;
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_CHROMEOS)
-  const syncer::PreviouslySyncingGaiaIdInfoForMetrics
-      previously_syncing_gaia_id_info_;
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) &&
-        // !BUILDFLAG(IS_CHROMEOS)
   const size_t remote_updates_size_;
   // Preprocessed remote nodes in the form a forest where each tree's root is a
   // permanent node. Computed upon construction via BuildRemoteForest().
diff --git a/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.cc b/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.cc
deleted file mode 100644
index 3086518..0000000
--- a/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.cc
+++ /dev/null
@@ -1,686 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync_bookmarks/bookmark_model_merger_comparison_metrics.h"
-
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/containers/contains.h"
-#include "base/containers/flat_set.h"
-#include "base/logging.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/notreached.h"
-#include "base/stl_util.h"
-#include "base/strings/strcat.h"
-#include "base/uuid.h"
-#include "components/bookmarks/browser/bookmark_node.h"
-#include "components/sync/base/previously_syncing_gaia_id_info_for_metrics.h"
-#include "components/sync/protocol/bookmark_specifics.pb.h"
-#include "components/sync_bookmarks/bookmark_model_merger.h"
-#include "components/sync_bookmarks/bookmark_model_view.h"
-#include "components/sync_bookmarks/bookmark_specifics_conversions.h"
-
-namespace sync_bookmarks::metrics {
-namespace {
-
-// Constants forked from bookmark_model_merger.cc.
-const char kBookmarkBarTag[] = "bookmark_bar";
-const char kMobileBookmarksTag[] = "synced_bookmarks";
-const char kOtherBookmarksTag[] = "other_bookmarks";
-
-// Constants used when the parent's title is relevant during grouping. They
-// resemble the real titles but the precise value isn't important, as long as
-// they are unlikely to collide with user-generated folders.
-constexpr char16_t kBookmarkBarFolderName[] = u"__Bookmarks bar__";
-constexpr char16_t kOtherBookmarksFolderName[] = u"__Other bookmarks__";
-constexpr char16_t kMobileBookmarksFolderName[] = u"__Mobile bookmarks__";
-
-using RemoteForest = BookmarkModelMerger::RemoteForest;
-using RemoteTreeNode = BookmarkModelMerger::RemoteTreeNode;
-
-// Enum representing the number of URL bookmarks stored locally, bucketized into
-// a few notable ranges. This enum is used as suffix when recording metrics.
-enum BookmarkCountSuffix {
-  kZeroLocalUrlBookmarks,
-  kBetween1And19LocalUrlBookmarks,
-  kBetween20and999LocalUrlBookmarks,
-  k1000OrMoreLocalUrlBookmarks,
-};
-
-std::string_view SubtreeSelectionToInfix(SubtreeSelection value) {
-  // LINT.IfChange(BookmarkComparisonSubtreeSelection)
-  switch (value) {
-    case SubtreeSelection::kConsideringAllBookmarks:
-      return "ConsideringAllBookmarks";
-    case SubtreeSelection::kUnderBookmarksBar:
-      return "UnderBookmarksBar";
-  }
-  // LINT.ThenChange(/tools/metrics/histograms/metadata/sync/histograms.xml:BookmarkComparisonSubtreeSelection)
-  NOTREACHED();
-}
-
-std::string_view GroupingKeyInfixToString(GroupingKeyInfix value) {
-  // LINT.IfChange(BookmarkComparisonGroupingKey)
-  switch (value) {
-    case GroupingKeyInfix::kByUrl:
-      return "ByUrl";
-    case GroupingKeyInfix::kByUrlAndTitle:
-      return "ByUrlAndTitle";
-    case GroupingKeyInfix::kByUrlAndUuid:
-      return "ByUrlAndUuid";
-    case GroupingKeyInfix::kByUrlAndTitleAndPath:
-      return "ByUrlAndTitleAndPath";
-    case GroupingKeyInfix::kByUrlAndTitleAndPathAndUuid:
-      return "ByUrlAndTitleAndPathAndUuid";
-  }
-  // LINT.ThenChange(/tools/metrics/histograms/metadata/sync/histograms.xml:BookmarkComparisonGroupingKey)
-  NOTREACHED();
-}
-
-std::string_view BookmarkCountSuffixToString(BookmarkCountSuffix value) {
-  // LINT.IfChange(BookmarkComparisonBookmarkCount)
-  switch (value) {
-    case kZeroLocalUrlBookmarks:
-      return ".ZeroLocalUrlBookmarks";
-    case kBetween1And19LocalUrlBookmarks:
-      return ".Between1And19LocalUrlBookmarks";
-    case kBetween20and999LocalUrlBookmarks:
-      return ".Between20and999LocalUrlBookmarks";
-    case k1000OrMoreLocalUrlBookmarks:
-      return ".1000OrMoreLocalUrlBookmarks";
-  }
-  // LINT.ThenChange(/tools/metrics/histograms/metadata/sync/histograms.xml:BookmarkComparisonBookmarkCount)
-  NOTREACHED();
-}
-
-std::string_view PreviouslySyncingGaiaIdInfoToInfix(
-    syncer::PreviouslySyncingGaiaIdInfoForMetrics value) {
-  // LINT.IfChange(BookmarkComparisonPreviouslySyncingGaiaId)
-  switch (value) {
-    case syncer::PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified:
-      NOTREACHED();
-    case syncer::PreviouslySyncingGaiaIdInfoForMetrics::
-        kSyncFeatureNeverPreviouslyTurnedOn:
-      return ".NoPreviousGaiaId";
-    case syncer::PreviouslySyncingGaiaIdInfoForMetrics::
-        kCurrentGaiaIdMatchesPreviousWithSyncFeatureOn:
-      return ".MatchesPreviousGaiaId";
-    case syncer::PreviouslySyncingGaiaIdInfoForMetrics::
-        kCurrentGaiaIdIfDiffersPreviousWithSyncFeatureOn:
-      return ".DiffersPreviousGaiaId";
-  }
-  // LINT.ThenChange(/tools/metrics/histograms/metadata/sync/histograms.xml:BookmarkComparisonPreviouslySyncingGaiaId)
-  NOTREACHED();
-}
-
-std::u16string_view GetBookmarkNodeTitle(const bookmarks::BookmarkNode* node) {
-  CHECK(node);
-  switch (node->type()) {
-    case bookmarks::BookmarkNode::URL:
-    case bookmarks::BookmarkNode::FOLDER:
-      return node->GetTitle();
-    case bookmarks::BookmarkNode::BOOKMARK_BAR:
-      return kBookmarkBarFolderName;
-    case bookmarks::BookmarkNode::OTHER_NODE:
-      return kOtherBookmarksFolderName;
-    case bookmarks::BookmarkNode::MOBILE:
-      return kMobileBookmarksFolderName;
-  }
-  NOTREACHED();
-}
-
-std::u16string NodeTitleFromEntityData(const syncer::EntityData& entity_data) {
-  if (entity_data.server_defined_unique_tag == kBookmarkBarTag) {
-    return kBookmarkBarFolderName;
-  } else if (entity_data.server_defined_unique_tag == kOtherBookmarksTag) {
-    return kOtherBookmarksFolderName;
-  } else if (entity_data.server_defined_unique_tag == kMobileBookmarksTag) {
-    return kMobileBookmarksFolderName;
-  } else {
-    return NodeTitleFromSpecifics(entity_data.specifics.bookmark());
-  }
-}
-
-// Returns the subset of top-level permanent bookmark folders in
-// `all_local_data` as selected by `subtree_selection`.
-std::vector<const bookmarks::BookmarkNode*> GetRelevantLocalSubtrees(
-    const BookmarkModelView& all_local_data,
-    SubtreeSelection subtree_selection) {
-  switch (subtree_selection) {
-    case SubtreeSelection::kConsideringAllBookmarks:
-      return {all_local_data.bookmark_bar_node(), all_local_data.other_node(),
-              all_local_data.mobile_node()};
-    case SubtreeSelection::kUnderBookmarksBar:
-      return {all_local_data.bookmark_bar_node()};
-  }
-}
-
-// Returns the subset of top-level permanent bookmark folder updates in
-// `all_account_data` as selected by `subtree_selection`. Note that these
-// updates also contain updates for their descendants.
-std::vector<const RemoteTreeNode*> GetRelevantAccountSubtrees(
-    const RemoteForest& all_account_data,
-    SubtreeSelection subtree_selection) {
-  std::vector<std::string> relevant_tags;
-  switch (subtree_selection) {
-    case SubtreeSelection::kConsideringAllBookmarks:
-      relevant_tags.push_back(kMobileBookmarksTag);
-      relevant_tags.push_back(kOtherBookmarksTag);
-      [[fallthrough]];
-    case SubtreeSelection::kUnderBookmarksBar:
-      relevant_tags.push_back(kBookmarkBarTag);
-      break;
-  }
-  std::vector<const RemoteTreeNode*> result;
-  for (const std::string& tag : relevant_tags) {
-    auto it = all_account_data.find(tag);
-    if (it != all_account_data.end()) {
-      result.push_back(&it->second);
-    }
-  }
-  return result;
-}
-
-size_t CountUrlNodesInSubtree(const bookmarks::BookmarkNode* node) {
-  CHECK(node);
-  if (node->is_url()) {
-    return 1;
-  }
-  size_t result = 0;
-  for (const std::unique_ptr<bookmarks::BookmarkNode>& child :
-       node->children()) {
-    result += CountUrlNodesInSubtree(child.get());
-  }
-  return result;
-}
-
-BookmarkCountSuffix CountLocalBookmarks(
-    const std::vector<const bookmarks::BookmarkNode*>& local_data) {
-  size_t num_url_nodes = 0;
-  for (const bookmarks::BookmarkNode* permanent_node : local_data) {
-    num_url_nodes += CountUrlNodesInSubtree(permanent_node);
-  }
-  if (num_url_nodes == 0) {
-    return kZeroLocalUrlBookmarks;
-  }
-  if (num_url_nodes < 20) {
-    return kBetween1And19LocalUrlBookmarks;
-  }
-  if (num_url_nodes < 1000) {
-    return kBetween20and999LocalUrlBookmarks;
-  }
-  return k1000OrMoreLocalUrlBookmarks;
-}
-
-// Function template declaration that must be specialized per supported grouping
-// criterion that allows mapping local data to keys used for grouping.
-template <typename Key>
-Key GroupingKeyFromLocalData(const bookmarks::BookmarkNode* node);
-
-// Same as above but for account data.
-template <typename Key>
-Key GroupingKeyFromLocalData(const bookmarks::BookmarkNode* node,
-                             std::u16string path);
-
-template <typename Key>
-Key GroupingKeyFromAccountData(const sync_pb::BookmarkSpecifics& specifics,
-                               std::u16string path);
-
-template <>
-UrlOnly GroupingKeyFromLocalData(const bookmarks::BookmarkNode* node,
-                                 std::u16string path) {
-  UrlOnly key;
-  key.url = node->url();
-  // `path` ignored but required in signture for template code.
-  return key;
-}
-
-template <>
-UrlOnly GroupingKeyFromAccountData(const sync_pb::BookmarkSpecifics& specifics,
-                                   std::u16string path) {
-  UrlOnly key;
-  key.url = GURL(specifics.url());
-  // `path` ignored but required in signture for template code.
-  return key;
-}
-
-template <>
-UrlAndTitle GroupingKeyFromLocalData(const bookmarks::BookmarkNode* node,
-                                     std::u16string path) {
-  UrlAndTitle key;
-  key.url = node->url();
-  key.title = node->GetTitle();
-  // `path` ignored but required in signture for template code.
-  return key;
-}
-
-template <>
-UrlAndTitle GroupingKeyFromAccountData(
-    const sync_pb::BookmarkSpecifics& specifics,
-    std::u16string path) {
-  UrlAndTitle key;
-  key.url = GURL(specifics.url());
-  key.title = NodeTitleFromSpecifics(specifics);
-  // `path` ignored but required in signture for template code.
-  return key;
-}
-
-template <>
-UrlAndUuid GroupingKeyFromLocalData(const bookmarks::BookmarkNode* node,
-                                    std::u16string path) {
-  UrlAndUuid key;
-  key.url = node->url();
-  key.uuid = node->uuid();
-  // `path` ignored but required in signture for template code.
-  return key;
-}
-
-template <>
-UrlAndUuid GroupingKeyFromAccountData(
-    const sync_pb::BookmarkSpecifics& specifics,
-    std::u16string path) {
-  UrlAndUuid key;
-  key.url = GURL(specifics.url());
-  key.uuid = base::Uuid::ParseLowercase(specifics.guid());
-  // `path` ignored but required in signture for template code.
-  return key;
-}
-
-template <>
-UrlAndTitleAndPath GroupingKeyFromLocalData(const bookmarks::BookmarkNode* node,
-                                            std::u16string path) {
-  UrlAndTitleAndPath key;
-  key.url = node->url();
-  key.title = node->GetTitle();
-  key.path = std::move(path);
-  return key;
-}
-
-template <>
-UrlAndTitleAndPath GroupingKeyFromAccountData(
-    const sync_pb::BookmarkSpecifics& specifics,
-    std::u16string path) {
-  UrlAndTitleAndPath key;
-  key.url = GURL(specifics.url());
-  key.title = NodeTitleFromSpecifics(specifics);
-  key.path = std::move(path);
-  return key;
-}
-
-template <>
-UrlAndTitleAndPathAndUuid GroupingKeyFromLocalData(
-    const bookmarks::BookmarkNode* node,
-    std::u16string path) {
-  UrlAndTitleAndPathAndUuid key;
-  key.url = node->url();
-  key.title = node->GetTitle();
-  key.path = std::move(path);
-  key.uuid = node->uuid();
-  return key;
-}
-
-template <>
-UrlAndTitleAndPathAndUuid GroupingKeyFromAccountData(
-    const sync_pb::BookmarkSpecifics& specifics,
-    std::u16string path) {
-  UrlAndTitleAndPathAndUuid key;
-  key.url = GURL(specifics.url());
-  key.title = NodeTitleFromSpecifics(specifics);
-  key.path = std::move(path);
-  key.uuid = base::Uuid::ParseLowercase(specifics.guid());
-  return key;
-}
-
-// Given two sets `set1` and `set2`, returns the number of values that exist in
-// both.
-template <typename Key>
-size_t GetSetIntersectionSize(const base::flat_set<Key>& set1,
-                              const base::flat_set<Key>& set2) {
-  size_t intersection_size = 0;
-  auto it1 = set1.begin();
-  auto it2 = set2.begin();
-  while (it1 != set1.end() && it2 != set2.end()) {
-    if (*it1 == *it2) {
-      ++intersection_size;
-      ++it1;
-      ++it2;
-    } else if (*it1 < *it2) {
-      ++it1;
-    } else {
-      ++it2;
-    }
-  }
-  // The implementation above could be replaced the size resulting of calling
-  // base::STLSetIntersection(), but this is avoided for performance reasons,
-  // because base::STLSetIntersection() would compute a full set.
-  DCHECK_EQ(intersection_size,
-            base::STLSetIntersection<base::flat_set<Key>>(set1, set2).size());
-  return intersection_size;
-}
-
-// Given two sets `account_data` and `local_data`, it returns how the two
-// relate to each other as represented in enum `SetComparisonOutcome`.
-template <typename Key>
-SetComparisonOutcome CompareSets(const base::flat_set<Key>& account_data,
-                                 const base::flat_set<Key>& local_data) {
-  const size_t account_data_set_size = account_data.size();
-  const size_t local_data_set_size = local_data.size();
-  const size_t intersection_size =
-      GetSetIntersectionSize(local_data, account_data);
-
-  CHECK_LE(intersection_size, local_data_set_size);
-  CHECK_LE(intersection_size, account_data_set_size);
-
-  if (local_data_set_size == 0 && account_data_set_size == 0) {
-    return SetComparisonOutcome::kBothEmpty;
-  } else if (local_data_set_size == 0) {
-    return SetComparisonOutcome::kLocalDataEmpty;
-  } else if (account_data_set_size == 0) {
-    return SetComparisonOutcome::kAccountDataEmpty;
-  } else if (local_data_set_size == intersection_size) {
-    if (account_data_set_size == intersection_size) {
-      return SetComparisonOutcome::kExactMatchNonEmpty;
-    } else {
-      return SetComparisonOutcome::kLocalDataIsStrictSubsetOfAccountData;
-    }
-  }
-
-  // Produce a notion of similarity based on which fraction of the total data
-  // items (union) is included in the intersection.
-  const size_t union_size =
-      local_data_set_size + account_data_set_size - intersection_size;
-  DCHECK_EQ(
-      union_size,
-      base::STLSetUnion<base::flat_set<Key>>(account_data, local_data).size());
-
-  CHECK_LT(intersection_size, union_size);
-  const double intersecting_fraction = 1.0 * intersection_size / union_size;
-
-  if (intersecting_fraction >= 0.99) {
-    return SetComparisonOutcome::kIntersectionBetween99And100Percent;
-  } else if (intersecting_fraction >= 0.95) {
-    return SetComparisonOutcome::kIntersectionBetween95And99Percent;
-  } else if (intersecting_fraction >= 0.90) {
-    return SetComparisonOutcome::kIntersectionBetween90And95Percent;
-  } else if (intersecting_fraction >= 0.50) {
-    return SetComparisonOutcome::kIntersectionBetween50And90Percent;
-  } else if (intersecting_fraction >= 0.10) {
-    return SetComparisonOutcome::kIntersectionBetween10And50Percent;
-  } else if (intersecting_fraction > 0) {
-    return SetComparisonOutcome::kIntersectionBelow10PercentExcludingZero;
-  } else {
-    return SetComparisonOutcome::kIntersectionEmpty;
-  }
-}
-
-// Recursive function used to implement `ExtractLocalDataSet()` below.
-template <typename Key>
-void ExtractLocalDataSetRecursive(std::u16string path,
-                                  const bookmarks::BookmarkNode* node,
-                                  std::vector<Key>& keys) {
-  CHECK(node);
-  if (node->is_url()) {
-    keys.emplace_back(GroupingKeyFromLocalData<Key>(node, std::move(path)));
-    return;
-  }
-  for (const std::unique_ptr<bookmarks::BookmarkNode>& child :
-       node->children()) {
-    ExtractLocalDataSetRecursive(
-        base::StrCat({path, u"/", GetBookmarkNodeTitle(node)}), child.get(),
-        keys);
-  }
-}
-
-// Returns unique data items representing URL bookmarks in
-// `relevant_local_subtrees` after being grouped by `Key`.
-template <typename Key>
-base::flat_set<Key> ExtractLocalDataSet(
-    const std::vector<const bookmarks::BookmarkNode*>&
-        relevant_local_subtrees) {
-  std::vector<Key> keys;
-  for (const bookmarks::BookmarkNode* node : relevant_local_subtrees) {
-    ExtractLocalDataSetRecursive(/*path=*/u"", node, keys);
-  }
-  return base::flat_set<Key>(std::move(keys));
-}
-
-// Recursive function used to implement `ExtractAccountDataSet()` below.
-template <typename Key>
-void ExtractAccountDataSetRecursive(std::u16string path,
-                                    const RemoteTreeNode& node,
-                                    std::vector<Key>& keys) {
-  if (node.entity().specifics.bookmark().type() ==
-      sync_pb::BookmarkSpecifics::URL) {
-    keys.emplace_back(GroupingKeyFromAccountData<Key>(
-        node.entity().specifics.bookmark(), std::move(path)));
-    return;
-  }
-  for (const RemoteTreeNode& child : node.children()) {
-    ExtractAccountDataSetRecursive(
-        base::StrCat({path, u"/", NodeTitleFromEntityData(node.entity())}),
-        child, keys);
-  }
-}
-
-// Returns unique data items representing URL bookmarks in
-// `relevant_account_subtrees` after being grouped by `Key`.
-template <typename Key>
-base::flat_set<Key> ExtractAccountDataSet(
-    const std::vector<const RemoteTreeNode*>& relevant_account_subtrees) {
-  std::vector<Key> keys;
-  for (const RemoteTreeNode* node : relevant_account_subtrees) {
-    CHECK(node);
-    ExtractAccountDataSetRecursive(/*path=*/u"", *node, keys);
-  }
-  return base::flat_set<Key>(std::move(keys));
-}
-
-template <typename Key>
-void CompareAndLogHistogramsWithKey(
-    SubtreeSelection subtree_selection,
-    syncer::PreviouslySyncingGaiaIdInfoForMetrics
-        previously_syncing_gaia_id_info,
-    BookmarkCountSuffix bookmark_count_suffix,
-    const std::vector<const bookmarks::BookmarkNode*>& relevant_local_subtrees,
-    const std::vector<const RemoteTreeNode*>& relevant_account_subtrees) {
-  const base::flat_set<Key> local_data_set =
-      ExtractLocalDataSet<Key>(relevant_local_subtrees);
-  const base::flat_set<Key> account_data_set =
-      ExtractAccountDataSet<Key>(relevant_account_subtrees);
-
-  // When recording the metric, always record four metrics, resulting from
-  // the combinatorial cases for:
-  // 1. With and without the infix representing
-  //    PreviouslySyncingGaiaIdInfoForMetrics.
-  // 2. With and without the suffix representing the number of local URL
-  //    bookmarks.
-  for (std::string_view optional_previously_syncing_gaia_id_info_infix :
-       {std::string_view(),
-        PreviouslySyncingGaiaIdInfoToInfix(previously_syncing_gaia_id_info)}) {
-    for (std::string_view optional_bookmark_count_suffix :
-         {std::string_view(),
-          BookmarkCountSuffixToString(bookmark_count_suffix)}) {
-      const std::string legacy_histogram_name =
-          base::StrCat({"Sync.BookmarkModelMerger.Comparison",
-                        optional_previously_syncing_gaia_id_info_infix, ".",
-                        SubtreeSelectionToInfix(subtree_selection), ".",
-                        GroupingKeyInfixToString(Key::kGroupingKeyInfix),
-                        optional_bookmark_count_suffix});
-
-      // The call below to CompareSets() mixes up local data with account data.
-      // Such implementation was accidental, but the resulting metric is anyway
-      // meaningful, and the resulting behavior documented in histograms.xml.
-      // For a fixed version of this, see `fixed_histogram_name` immediately
-      // below.
-      // TODO(crbug.com/424551547): Clean up the legacy metric, if not both,
-      // in upcoming milestones (M139 or M140).
-      base::UmaHistogramEnumeration(
-          legacy_histogram_name, CompareSets(/*account_data=*/local_data_set,
-                                             /*local_data=*/account_data_set));
-
-      const std::string fixed_histogram_name =
-          base::StrCat({"Sync.BookmarkModelMerger.Comparison2",
-                        optional_previously_syncing_gaia_id_info_infix, ".",
-                        SubtreeSelectionToInfix(subtree_selection), ".",
-                        GroupingKeyInfixToString(Key::kGroupingKeyInfix),
-                        optional_bookmark_count_suffix});
-
-      base::UmaHistogramEnumeration(
-          fixed_histogram_name, CompareSets(/*account_data=*/account_data_set,
-                                            /*local_data=*/local_data_set));
-    }
-  }
-}
-
-}  // namespace
-
-UrlAndTitleAndPathAndUuid::UrlAndTitleAndPathAndUuid() = default;
-
-UrlAndTitleAndPathAndUuid::UrlAndTitleAndPathAndUuid(
-    const GURL& url,
-    const std::u16string& title,
-    const std::u16string& path,
-    const base::Uuid& uuid)
-    : url(url), title(title), path(path), uuid(uuid) {}
-
-UrlAndTitleAndPathAndUuid::UrlAndTitleAndPathAndUuid(
-    const UrlAndTitleAndPathAndUuid&) = default;
-
-UrlAndTitleAndPathAndUuid::UrlAndTitleAndPathAndUuid(
-    UrlAndTitleAndPathAndUuid&&) = default;
-
-UrlAndTitleAndPathAndUuid::~UrlAndTitleAndPathAndUuid() = default;
-
-UrlAndTitleAndPathAndUuid& UrlAndTitleAndPathAndUuid::operator=(
-    const UrlAndTitleAndPathAndUuid&) = default;
-
-UrlAndTitleAndPathAndUuid& UrlAndTitleAndPathAndUuid::operator=(
-    UrlAndTitleAndPathAndUuid&&) = default;
-
-base::flat_set<UrlOnly> ExtractUniqueLocalNodesByUrlForTesting(
-    const BookmarkModelView& all_local_data,
-    SubtreeSelection subtree_selection) {
-  return ExtractLocalDataSet<UrlOnly>(
-      GetRelevantLocalSubtrees(all_local_data, subtree_selection));
-}
-
-base::flat_set<UrlAndTitle> ExtractUniqueLocalNodesByUrlAndTitleForTesting(
-    const BookmarkModelView& all_local_data,
-    SubtreeSelection subtree_selection) {
-  return ExtractLocalDataSet<UrlAndTitle>(
-      GetRelevantLocalSubtrees(all_local_data, subtree_selection));
-}
-
-base::flat_set<UrlAndUuid> ExtractUniqueLocalNodesByUrlAndUuidForTesting(
-    const BookmarkModelView& all_local_data,
-    SubtreeSelection subtree_selection) {
-  return ExtractLocalDataSet<UrlAndUuid>(
-      GetRelevantLocalSubtrees(all_local_data, subtree_selection));
-}
-
-base::flat_set<UrlAndTitleAndPath>
-ExtractUniqueLocalNodesByUrlAndTitleAndPathForTesting(
-    const BookmarkModelView& all_local_data,
-    SubtreeSelection subtree_selection) {
-  return ExtractLocalDataSet<UrlAndTitleAndPath>(
-      GetRelevantLocalSubtrees(all_local_data, subtree_selection));
-}
-
-base::flat_set<UrlAndTitleAndPathAndUuid>
-ExtractUniqueLocalNodesByUrlAndTitleAndPathAndUuidForTesting(
-    const BookmarkModelView& all_local_data,
-    SubtreeSelection subtree_selection) {
-  return ExtractLocalDataSet<UrlAndTitleAndPathAndUuid>(
-      GetRelevantLocalSubtrees(all_local_data, subtree_selection));
-}
-
-base::flat_set<UrlOnly> ExtractUniqueAccountNodesByUrlForTesting(
-    const BookmarkModelMerger::RemoteForest& all_account_data,
-    SubtreeSelection subtree_selection) {
-  return ExtractAccountDataSet<UrlOnly>(
-      GetRelevantAccountSubtrees(all_account_data, subtree_selection));
-}
-
-base::flat_set<UrlAndTitle> ExtractUniqueAccountNodesByUrlAndTitleForTesting(
-    const BookmarkModelMerger::RemoteForest& all_account_data,
-    SubtreeSelection subtree_selection) {
-  return ExtractAccountDataSet<UrlAndTitle>(
-      GetRelevantAccountSubtrees(all_account_data, subtree_selection));
-}
-
-base::flat_set<UrlAndUuid> ExtractUniqueAccountNodesByUrlAndUuidForTesting(
-    const BookmarkModelMerger::RemoteForest& all_account_data,
-    SubtreeSelection subtree_selection) {
-  return ExtractAccountDataSet<UrlAndUuid>(
-      GetRelevantAccountSubtrees(all_account_data, subtree_selection));
-}
-
-base::flat_set<UrlAndTitleAndPath>
-ExtractUniqueAccountNodesByUrlAndTitleAndPathForTesting(
-    const BookmarkModelMerger::RemoteForest& all_account_data,
-    SubtreeSelection subtree_selection) {
-  return ExtractAccountDataSet<UrlAndTitleAndPath>(
-      GetRelevantAccountSubtrees(all_account_data, subtree_selection));
-}
-
-base::flat_set<UrlAndTitleAndPathAndUuid>
-ExtractUniqueAccountNodesByUrlAndTitleAndPathAndUuidForTesting(
-    const BookmarkModelMerger::RemoteForest& all_account_data,
-    SubtreeSelection subtree_selection) {
-  return ExtractAccountDataSet<UrlAndTitleAndPathAndUuid>(
-      GetRelevantAccountSubtrees(all_account_data, subtree_selection));
-}
-
-SetComparisonOutcome CompareSetsForTesting(
-    const base::flat_set<int>& account_data,
-    const base::flat_set<int>& local_data) {
-  return CompareSets<int>(account_data, local_data);
-}
-
-void CompareBookmarkModelAndLogHistograms(
-    const BookmarkModelView& all_local_data,
-    const BookmarkModelMerger::RemoteForest& all_account_data,
-    syncer::PreviouslySyncingGaiaIdInfoForMetrics
-        previously_syncing_gaia_id_info) {
-  CHECK_NE(previously_syncing_gaia_id_info,
-           syncer::PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified);
-
-  for (SubtreeSelection subtree_selection :
-       {SubtreeSelection::kConsideringAllBookmarks,
-        SubtreeSelection::kUnderBookmarksBar}) {
-    const std::vector<const bookmarks::BookmarkNode*> relevant_local_subtrees =
-        GetRelevantLocalSubtrees(all_local_data, subtree_selection);
-    const std::vector<const RemoteTreeNode*> relevant_account_subtrees =
-        GetRelevantAccountSubtrees(all_account_data, subtree_selection);
-
-    const BookmarkCountSuffix bookmark_count_suffix =
-        CountLocalBookmarks(relevant_local_subtrees);
-
-    CompareAndLogHistogramsWithKey<UrlOnly>(
-        subtree_selection, previously_syncing_gaia_id_info,
-        bookmark_count_suffix, relevant_local_subtrees,
-        relevant_account_subtrees);
-    CompareAndLogHistogramsWithKey<UrlAndTitle>(
-        subtree_selection, previously_syncing_gaia_id_info,
-        bookmark_count_suffix, relevant_local_subtrees,
-        relevant_account_subtrees);
-    CompareAndLogHistogramsWithKey<UrlAndUuid>(
-        subtree_selection, previously_syncing_gaia_id_info,
-        bookmark_count_suffix, relevant_local_subtrees,
-        relevant_account_subtrees);
-    CompareAndLogHistogramsWithKey<UrlAndTitleAndPath>(
-        subtree_selection, previously_syncing_gaia_id_info,
-        bookmark_count_suffix, relevant_local_subtrees,
-        relevant_account_subtrees);
-    CompareAndLogHistogramsWithKey<UrlAndTitleAndPathAndUuid>(
-        subtree_selection, previously_syncing_gaia_id_info,
-        bookmark_count_suffix, relevant_local_subtrees,
-        relevant_account_subtrees);
-  }
-}
-
-}  // namespace sync_bookmarks::metrics
diff --git a/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.h b/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.h
deleted file mode 100644
index b68cebf..0000000
--- a/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.h
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_MERGER_COMPARISON_METRICS_H_
-#define COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_MERGER_COMPARISON_METRICS_H_
-
-#include "base/containers/flat_set.h"
-#include "base/uuid.h"
-#include "components/sync/base/previously_syncing_gaia_id_info_for_metrics.h"
-#include "components/sync_bookmarks/bookmark_model_merger.h"
-#include "url/gurl.h"
-
-namespace sync_bookmarks {
-
-class BookmarkModelView;
-
-namespace metrics {
-
-// Enum representing which subtree of the bookmark model was used when
-// comparing local bookmarks with account bookmarks. This enum is used as
-// infix when recording metrics.
-enum class SubtreeSelection {
-  kConsideringAllBookmarks,
-  kUnderBookmarksBar,
-};
-
-// Enum representing the method or criterion used as uniqueness key when
-// comparing local bookmarks with account bookmarks. This enum is used as
-// infix when recording metrics.
-enum class GroupingKeyInfix {
-  kByUrl,
-  kByUrlAndTitle,
-  kByUrlAndUuid,
-  kByUrlAndTitleAndPath,
-  kByUrlAndTitleAndPathAndUuid,
-};
-
-// Result of comparing two datasets for the purpose of logging metrics. Note
-// that enum values listed first take precedence (are evaluated earlier) than
-// those following.
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused. Keep in sync with the homonym enum
-// in tools/metrics/histograms/metadata/sync/enums.xml.
-// Exposed in the header file for testing.
-// LINT.IfChange(BookmarkSetComparisonOutcome)
-enum class SetComparisonOutcome {
-  kBothEmpty = 0,
-  kLocalDataEmpty = 1,
-  kAccountDataEmpty = 2,
-  kExactMatchNonEmpty = 3,
-  kLocalDataIsStrictSubsetOfAccountData = 4,
-  kIntersectionBetween99And100Percent = 5,
-  kIntersectionBetween95And99Percent = 6,
-  kIntersectionBetween90And95Percent = 7,
-  kIntersectionBetween50And90Percent = 8,
-  kIntersectionBetween10And50Percent = 9,
-  kIntersectionBelow10PercentExcludingZero = 10,
-  kIntersectionEmpty = 11,
-  kMaxValue = kIntersectionEmpty
-};
-// LINT.ThenChange(/tools/metrics/histograms/metadata/sync/enums.xml:BookmarkSetComparisonOutcome)
-
-struct UrlOnly {
-  static constexpr GroupingKeyInfix kGroupingKeyInfix =
-      GroupingKeyInfix::kByUrl;
-
-  auto operator<=>(const UrlOnly&) const = default;
-
-  GURL url;
-};
-
-struct UrlAndTitle {
-  static constexpr GroupingKeyInfix kGroupingKeyInfix =
-      GroupingKeyInfix::kByUrlAndTitle;
-
-  auto operator<=>(const UrlAndTitle&) const = default;
-
-  GURL url;
-  std::u16string title;
-};
-
-struct UrlAndUuid {
-  static constexpr GroupingKeyInfix kGroupingKeyInfix =
-      GroupingKeyInfix::kByUrlAndUuid;
-
-  auto operator<=>(const UrlAndUuid&) const = default;
-
-  GURL url;
-  base::Uuid uuid;
-};
-
-struct UrlAndTitleAndPath {
-  static constexpr GroupingKeyInfix kGroupingKeyInfix =
-      GroupingKeyInfix::kByUrlAndTitleAndPath;
-
-  auto operator<=>(const UrlAndTitleAndPath&) const = default;
-
-  GURL url;
-  std::u16string title;
-  // Ancestor folder titles concatenated with '/'.
-  std::u16string path;
-};
-
-struct UrlAndTitleAndPathAndUuid {
-  static constexpr GroupingKeyInfix kGroupingKeyInfix =
-      GroupingKeyInfix::kByUrlAndTitleAndPathAndUuid;
-
-  UrlAndTitleAndPathAndUuid();
-  UrlAndTitleAndPathAndUuid(const GURL& url,
-                            const std::u16string& title,
-                            const std::u16string& path,
-                            const base::Uuid& uuid);
-  UrlAndTitleAndPathAndUuid(const UrlAndTitleAndPathAndUuid&);
-  UrlAndTitleAndPathAndUuid(UrlAndTitleAndPathAndUuid&&);
-  ~UrlAndTitleAndPathAndUuid();
-
-  UrlAndTitleAndPathAndUuid& operator=(const UrlAndTitleAndPathAndUuid&);
-  UrlAndTitleAndPathAndUuid& operator=(UrlAndTitleAndPathAndUuid&&);
-
-  auto operator<=>(const UrlAndTitleAndPathAndUuid&) const = default;
-
-  GURL url;
-  std::u16string title;
-  // Ancestor folder titles concatenated with '/'.
-  std::u16string path;
-  base::Uuid uuid;
-};
-
-// Test-only functions to verify the logic related to extracting local data.
-base::flat_set<UrlOnly> ExtractUniqueLocalNodesByUrlForTesting(
-    const BookmarkModelView& all_local_data,
-    SubtreeSelection subtree_selection);
-base::flat_set<UrlAndTitle> ExtractUniqueLocalNodesByUrlAndTitleForTesting(
-    const BookmarkModelView& all_local_data,
-    SubtreeSelection subtree_selection);
-base::flat_set<UrlAndUuid> ExtractUniqueLocalNodesByUrlAndUuidForTesting(
-    const BookmarkModelView& all_local_data,
-    SubtreeSelection subtree_selection);
-base::flat_set<UrlAndTitleAndPath>
-ExtractUniqueLocalNodesByUrlAndTitleAndPathForTesting(
-    const BookmarkModelView& all_local_data,
-    SubtreeSelection subtree_selection);
-base::flat_set<UrlAndTitleAndPathAndUuid>
-ExtractUniqueLocalNodesByUrlAndTitleAndPathAndUuidForTesting(
-    const BookmarkModelView& all_local_data,
-    SubtreeSelection subtree_selection);
-
-// Test-only functions to verify the logic related to extracting account data.
-base::flat_set<UrlOnly> ExtractUniqueAccountNodesByUrlForTesting(
-    const BookmarkModelMerger::RemoteForest& all_account_data,
-    SubtreeSelection subtree_selection);
-base::flat_set<UrlAndTitle> ExtractUniqueAccountNodesByUrlAndTitleForTesting(
-    const BookmarkModelMerger::RemoteForest& all_account_data,
-    SubtreeSelection subtree_selection);
-base::flat_set<UrlAndUuid> ExtractUniqueAccountNodesByUrlAndUuidForTesting(
-    const BookmarkModelMerger::RemoteForest& all_account_data,
-    SubtreeSelection subtree_selection);
-base::flat_set<UrlAndTitleAndPath>
-ExtractUniqueAccountNodesByUrlAndTitleAndPathForTesting(
-    const BookmarkModelMerger::RemoteForest& all_account_data,
-    SubtreeSelection subtree_selection);
-base::flat_set<UrlAndTitleAndPathAndUuid>
-ExtractUniqueAccountNodesByUrlAndTitleAndPathAndUuidForTesting(
-    const BookmarkModelMerger::RemoteForest& all_account_data,
-    SubtreeSelection subtree_selection);
-
-// Test-only function to compare two sets.
-SetComparisonOutcome CompareSetsForTesting(
-    const base::flat_set<int>& account_data,
-    const base::flat_set<int>& local_data);
-
-void CompareBookmarkModelAndLogHistograms(
-    const BookmarkModelView& all_local_data,
-    const BookmarkModelMerger::RemoteForest& all_account_data,
-    syncer::PreviouslySyncingGaiaIdInfoForMetrics
-        previously_syncing_gaia_id_info);
-
-}  // namespace metrics
-}  // namespace sync_bookmarks
-
-#endif  // COMPONENTS_SYNC_BOOKMARKS_BOOKMARK_MODEL_MERGER_COMPARISON_METRICS_H_
diff --git a/components/sync_bookmarks/bookmark_model_merger_comparison_metrics_unittest.cc b/components/sync_bookmarks/bookmark_model_merger_comparison_metrics_unittest.cc
deleted file mode 100644
index 0fa1fa68..0000000
--- a/components/sync_bookmarks/bookmark_model_merger_comparison_metrics_unittest.cc
+++ /dev/null
@@ -1,858 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync_bookmarks/bookmark_model_merger_comparison_metrics.h"
-
-#include <memory>
-#include <ostream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/containers/flat_set.h"
-#include "base/strings/strcat.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/uuid.h"
-#include "components/bookmarks/browser/bookmark_model.h"
-#include "components/bookmarks/browser/bookmark_node.h"
-#include "components/bookmarks/browser/bookmark_test_util.h"
-#include "components/bookmarks/browser/bookmark_uuids.h"
-#include "components/bookmarks/test/test_bookmark_client.h"
-#include "components/sync_bookmarks/bookmark_model_merger.h"
-#include "components/sync_bookmarks/bookmark_model_view.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace sync_bookmarks {
-namespace metrics {
-
-void PrintTo(const UrlOnly& value, std::ostream* os) {
-  *os << "{\"" << value.url << "\"}";
-}
-
-void PrintTo(const UrlAndTitle& value, std::ostream* os) {
-  *os << "{\"" << value.url << "\", \"" << base::UTF16ToUTF8(value.title)
-      << "\"}";
-}
-
-void PrintTo(const UrlAndUuid& value, std::ostream* os) {
-  *os << "{\"" << value.url << "\", \"" << value.uuid << "\"}";
-}
-
-void PrintTo(const UrlAndTitleAndPath& value, std::ostream* os) {
-  *os << "{\"" << value.url << "\", \"" << base::UTF16ToUTF8(value.title)
-      << "\", \"" << base::UTF16ToUTF8(value.path) << "\"}";
-}
-
-void PrintTo(const UrlAndTitleAndPathAndUuid& value, std::ostream* os) {
-  *os << "{\"" << value.url << "\", \"" << base::UTF16ToUTF8(value.title)
-      << "\", \"" << base::UTF16ToUTF8(value.path) << "\", \"" << value.uuid
-      << "\"}";
-}
-
-namespace {
-
-using RemoteTreeNode = BookmarkModelMerger::RemoteTreeNode;
-
-using testing::Eq;
-using testing::UnorderedElementsAre;
-
-// Similar to SetComparisonOutcome but differs in buckets 1, 2 and 4, due to a
-// bug in implementation. This also indirectly affects buckets [5..10], just
-// because bucket 4 takes precedence.
-// LINT.IfChange(LegacyBookmarkSetComparisonOutcome)
-enum class LegacySetComparisonOutcome {
-  kBothEmpty = 0,
-  kAccountDataEmpty = 1,
-  kLocalDataEmpty = 2,
-  kExactMatchNonEmpty = 3,
-  kAccountDataIsStrictSubsetOfLocalData = 4,
-  kIntersectionBetween99And100Percent = 5,
-  kIntersectionBetween95And99Percent = 6,
-  kIntersectionBetween90And95Percent = 7,
-  kIntersectionBetween50And90Percent = 8,
-  kIntersectionBetween10And50Percent = 9,
-  kIntersectionBelow10PercentExcludingZero = 10,
-  kIntersectionEmpty = 11,
-  kMaxValue = kIntersectionEmpty
-};
-// LINT.ThenChange(/tools/metrics/histograms/metadata/sync/enums.xml:LegacyBookmarkSetComparisonOutcome)
-
-// Constants forked from bookmark_model_merger.cc.
-const char kBookmarkBarTag[] = "bookmark_bar";
-const char kMobileBookmarksTag[] = "synced_bookmarks";
-const char kOtherBookmarksTag[] = "other_bookmarks";
-
-// Forked from bookmark_model_merger_comparison_metrics.cc.
-constexpr char16_t kBookmarkBarFolderName[] = u"__Bookmarks bar__";
-constexpr char16_t kOtherBookmarksFolderName[] = u"__Other bookmarks__";
-constexpr char16_t kMobileBookmarksFolderName[] = u"__Mobile bookmarks__";
-
-base::flat_set<int> ContiguousSetBetween(int start, int end) {
-  base::flat_set<int> result;
-  for (int i = start; i <= end; ++i) {
-    result.insert(i);
-  }
-  return result;
-}
-
-// Test class to build bookmark URLs conveniently and compactly in tests.
-class UrlBookmarkBuilder {
- public:
-  UrlBookmarkBuilder(const std::u16string& title, const GURL& url)
-      : title_(title), url_(url), uuid_(base::Uuid::GenerateRandomV4()) {}
-  UrlBookmarkBuilder(const UrlBookmarkBuilder&) = default;
-  ~UrlBookmarkBuilder() = default;
-
-  UrlBookmarkBuilder& SetUuid(const base::Uuid& uuid) {
-    uuid_ = uuid;
-    return *this;
-  }
-
-  void BuildLocal(bookmarks::BookmarkModel* model,
-                  const bookmarks::BookmarkNode* parent) const {
-    model->AddURL(parent, parent->children().size(), title_, url_,
-                  /*meta_info=*/nullptr, /*creation_time=*/std::nullopt, uuid_);
-  }
-
-  RemoteTreeNode BuildRemoteNode(const base::Uuid& parent_uuid) const {
-    syncer::UpdateResponseData data;
-    sync_pb::BookmarkSpecifics* bookmark_specifics =
-        data.entity.specifics.mutable_bookmark();
-    bookmark_specifics->set_full_title(base::UTF16ToUTF8(title_));
-    bookmark_specifics->set_type(sync_pb::BookmarkSpecifics::URL);
-    bookmark_specifics->set_url(url_.spec());
-    bookmark_specifics->set_guid(uuid_.AsLowercaseString());
-    return RemoteTreeNode::BuildForTesting(std::move(data), {});
-  }
-
- private:
-  const std::u16string title_;
-  const GURL url_;
-  base::Uuid uuid_;
-};
-
-// Test class to build bookmark folders conveniently and compactly in tests.
-class FolderBuilder {
- public:
-  using FolderOrUrl = std::variant<FolderBuilder, UrlBookmarkBuilder>;
-
-  static void AddLocalChildTo(bookmarks::BookmarkModel* model,
-                              const bookmarks::BookmarkNode* parent,
-                              const FolderOrUrl& folder_or_url) {
-    if (std::holds_alternative<UrlBookmarkBuilder>(folder_or_url)) {
-      std::get<UrlBookmarkBuilder>(folder_or_url).BuildLocal(model, parent);
-    } else {
-      CHECK(std::holds_alternative<FolderBuilder>(folder_or_url));
-      std::get<FolderBuilder>(folder_or_url).BuildLocal(model, parent);
-    }
-  }
-
-  static void AddLocalChildrenTo(bookmarks::BookmarkModel* model,
-                                 const bookmarks::BookmarkNode* parent,
-                                 const std::vector<FolderOrUrl>& children) {
-    for (const FolderOrUrl& folder_or_url : children) {
-      AddLocalChildTo(model, parent, folder_or_url);
-    }
-  }
-
-  static RemoteTreeNode BuildRemoteNode(const FolderOrUrl& folder_or_url,
-                                        const base::Uuid& parent_uuid) {
-    if (std::holds_alternative<UrlBookmarkBuilder>(folder_or_url)) {
-      return std::get<UrlBookmarkBuilder>(folder_or_url)
-          .BuildRemoteNode(parent_uuid);
-    } else {
-      CHECK(std::holds_alternative<FolderBuilder>(folder_or_url));
-      return std::get<FolderBuilder>(folder_or_url)
-          .BuildRemoteNode(parent_uuid);
-    }
-  }
-
-  static std::vector<RemoteTreeNode> BuildRemoteNodes(
-      const std::vector<FolderOrUrl>& children,
-      const base::Uuid& parent_uuid) {
-    std::vector<RemoteTreeNode> nodes;
-    for (const FolderOrUrl& folder_or_url : children) {
-      nodes.push_back(BuildRemoteNode(folder_or_url, parent_uuid));
-    }
-    return nodes;
-  }
-
-  explicit FolderBuilder(const std::u16string& title)
-      : title_(title), uuid_(base::Uuid::GenerateRandomV4()) {}
-  FolderBuilder(const FolderBuilder&) = default;
-  ~FolderBuilder() = default;
-
-  FolderBuilder& SetChildren(std::vector<FolderOrUrl> children) {
-    children_ = std::move(children);
-    return *this;
-  }
-
-  FolderBuilder& SetUuid(const base::Uuid& uuid) {
-    uuid_ = uuid;
-    return *this;
-  }
-
-  void BuildLocal(bookmarks::BookmarkModel* model,
-                  const bookmarks::BookmarkNode* parent) const {
-    const bookmarks::BookmarkNode* folder = model->AddFolder(
-        parent, parent->children().size(), title_,
-        /*meta_info=*/nullptr, /*creation_time=*/std::nullopt, uuid_);
-    AddLocalChildrenTo(model, folder, children_);
-  }
-
-  RemoteTreeNode BuildRemoteNode(const base::Uuid& parent_uuid) const {
-    syncer::UpdateResponseData data;
-    sync_pb::BookmarkSpecifics* bookmark_specifics =
-        data.entity.specifics.mutable_bookmark();
-    bookmark_specifics->set_full_title(base::UTF16ToUTF8(title_));
-    bookmark_specifics->set_type(sync_pb::BookmarkSpecifics::FOLDER);
-    bookmark_specifics->set_guid(uuid_.AsLowercaseString());
-    return RemoteTreeNode::BuildForTesting(
-        std::move(data), BuildRemoteNodes(children_,
-                                          /*parent_uuid=*/uuid_));
-  }
-
- private:
-  const std::u16string title_;
-  std::vector<FolderOrUrl> children_;
-  base::Uuid uuid_;
-};
-
-class BookmarkModelMergerComparisonMetricsTest : public testing::Test {
- protected:
-  BookmarkModelMergerComparisonMetricsTest() = default;
-
-  ~BookmarkModelMergerComparisonMetricsTest() override = default;
-
-  void AddLocalNodes(
-      const std::vector<FolderBuilder::FolderOrUrl>& children_of_bookmark_bar,
-      const std::vector<FolderBuilder::FolderOrUrl>& children_of_mobile_node,
-      const std::vector<FolderBuilder::FolderOrUrl>& children_of_other_node) {
-    FolderBuilder::AddLocalChildrenTo(model_.get(), model_->bookmark_bar_node(),
-                                      children_of_bookmark_bar);
-    FolderBuilder::AddLocalChildrenTo(model_.get(), model_->mobile_node(),
-                                      children_of_mobile_node);
-    FolderBuilder::AddLocalChildrenTo(model_.get(), model_->other_node(),
-                                      children_of_other_node);
-  }
-
-  static RemoteTreeNode BuildRemoteNodeForPermanentFolder(
-      std::string_view server_defined_unique_tag,
-      std::string_view uuid,
-      const std::vector<FolderBuilder::FolderOrUrl>& children) {
-    // Use semi-empty UpdateResponseData() as these tests don't rely on
-    // realistic updates for permanent folders, beyond having the tag.
-    syncer::UpdateResponseData data;
-    data.entity.server_defined_unique_tag = server_defined_unique_tag;
-    return RemoteTreeNode::BuildForTesting(
-        std::move(data), FolderBuilder::BuildRemoteNodes(
-                             children,
-                             /*parent_uuid=*/base::Uuid::ParseLowercase(uuid)));
-  }
-
-  static BookmarkModelMerger::RemoteForest BuildAccountNodes(
-      const std::vector<FolderBuilder::FolderOrUrl>& children_of_bookmark_bar,
-      const std::vector<FolderBuilder::FolderOrUrl>& children_of_mobile_node,
-      const std::vector<FolderBuilder::FolderOrUrl>& children_of_other_node) {
-    BookmarkModelMerger::RemoteForest forest;
-    // String literals used below are part of the sync protocol and listed in
-    // bookmark_model_merger.cc.
-    forest.emplace(kBookmarkBarTag,
-                   BuildRemoteNodeForPermanentFolder(
-                       kBookmarkBarTag, bookmarks::kBookmarkBarNodeUuid,
-                       children_of_bookmark_bar));
-    forest.emplace(kMobileBookmarksTag,
-                   BuildRemoteNodeForPermanentFolder(
-                       kMobileBookmarksTag, bookmarks::kMobileBookmarksNodeUuid,
-                       children_of_mobile_node));
-    forest.emplace(kOtherBookmarksTag,
-                   BuildRemoteNodeForPermanentFolder(
-                       kOtherBookmarksTag, bookmarks::kOtherBookmarksNodeUuid,
-                       children_of_other_node));
-    return forest;
-  }
-
-  const std::unique_ptr<bookmarks::BookmarkModel> model_ =
-      bookmarks::TestBookmarkClient::CreateModel();
-};
-
-TEST_F(BookmarkModelMergerComparisonMetricsTest, ShouldExtractLocalNodes) {
-  const std::u16string kFolder1Title = u"folder1";
-
-  const std::u16string kUrl1Title = u"url1";
-  const std::u16string kUrl2Title = u"url2";
-  const std::u16string kUrl3Title = u"url3";
-  const std::u16string kUrl4Title = u"url4";
-
-  const GURL kUrl1("http://www.url1.com/");
-  const GURL kUrl2("http://www.url2.com/");
-  const GURL kUrl3("http://www.url3.com/");
-  const GURL kUrl4("http://www.url4.com/");
-
-  const base::Uuid kUrl1Uuid = base::Uuid::GenerateRandomV4();
-  const base::Uuid kUrl2Uuid = base::Uuid::GenerateRandomV4();
-  const base::Uuid kUrl3Uuid = base::Uuid::GenerateRandomV4();
-  const base::Uuid kUrl4Uuid = base::Uuid::GenerateRandomV4();
-
-  // -------- Local bookmarks --------
-  // bookmark_bar
-  //  |- url1(http://www.url1.com)
-  //  |- folder 1
-  //    |- url2(http://www.url2.com)
-  // mobile_node
-  //  |- url3(http://www.url3.com)
-  // other_node
-  //  |- url4(http://www.url4.com)
-  AddLocalNodes(
-      /*children_of_bookmark_bar=*/{UrlBookmarkBuilder(kUrl1Title, kUrl1)
-                                        .SetUuid(kUrl1Uuid),
-                                    FolderBuilder(kFolder1Title)
-                                        .SetChildren(
-                                            {UrlBookmarkBuilder(kUrl2Title,
-                                                                kUrl2)
-                                                 .SetUuid(kUrl2Uuid)})},
-      /*children_of_mobile_node=*/
-      {UrlBookmarkBuilder(kUrl3Title, kUrl3).SetUuid(kUrl3Uuid)},
-      /*children_of_other_node=*/
-      {UrlBookmarkBuilder(kUrl4Title, kUrl4).SetUuid(kUrl4Uuid)});
-
-  // By URL.
-  EXPECT_THAT(ExtractUniqueLocalNodesByUrlForTesting(
-                  BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()),
-                  SubtreeSelection::kConsideringAllBookmarks),
-              UnorderedElementsAre(UrlOnly{kUrl1}, UrlOnly{kUrl2},
-                                   UrlOnly{kUrl3}, UrlOnly{kUrl4}));
-  EXPECT_THAT(ExtractUniqueLocalNodesByUrlForTesting(
-                  BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()),
-                  SubtreeSelection::kUnderBookmarksBar),
-              UnorderedElementsAre(UrlOnly{kUrl1}, UrlOnly{kUrl2}));
-
-  // By URL and title.
-  EXPECT_THAT(ExtractUniqueLocalNodesByUrlAndTitleForTesting(
-                  BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()),
-                  SubtreeSelection::kConsideringAllBookmarks),
-              UnorderedElementsAre(UrlAndTitle{kUrl1, kUrl1Title},
-                                   UrlAndTitle{kUrl2, kUrl2Title},
-                                   UrlAndTitle{kUrl3, kUrl3Title},
-                                   UrlAndTitle{kUrl4, kUrl4Title}));
-  EXPECT_THAT(ExtractUniqueLocalNodesByUrlAndTitleForTesting(
-                  BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()),
-                  SubtreeSelection::kUnderBookmarksBar),
-              UnorderedElementsAre(UrlAndTitle{kUrl1, kUrl1Title},
-                                   UrlAndTitle{kUrl2, kUrl2Title}));
-
-  // By URL and UUID.
-  EXPECT_THAT(ExtractUniqueLocalNodesByUrlAndUuidForTesting(
-                  BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()),
-                  SubtreeSelection::kConsideringAllBookmarks),
-              UnorderedElementsAre(
-                  UrlAndUuid{kUrl1, kUrl1Uuid}, UrlAndUuid{kUrl2, kUrl2Uuid},
-                  UrlAndUuid{kUrl3, kUrl3Uuid}, UrlAndUuid{kUrl4, kUrl4Uuid}));
-  EXPECT_THAT(ExtractUniqueLocalNodesByUrlAndUuidForTesting(
-                  BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()),
-                  SubtreeSelection::kUnderBookmarksBar),
-              UnorderedElementsAre(UrlAndUuid{kUrl1, kUrl1Uuid},
-                                   UrlAndUuid{kUrl2, kUrl2Uuid}));
-
-  // By URL, title and path.
-  EXPECT_THAT(
-      ExtractUniqueLocalNodesByUrlAndTitleAndPathForTesting(
-          BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()),
-          SubtreeSelection::kConsideringAllBookmarks),
-      UnorderedElementsAre(
-          UrlAndTitleAndPath{kUrl1, kUrl1Title,
-                             base::StrCat({u"/", kBookmarkBarFolderName})},
-          UrlAndTitleAndPath{kUrl2, kUrl2Title,
-                             base::StrCat({u"/", kBookmarkBarFolderName, u"/",
-                                           kFolder1Title})},
-          UrlAndTitleAndPath{kUrl3, kUrl3Title,
-                             base::StrCat({u"/", kMobileBookmarksFolderName})},
-          UrlAndTitleAndPath{kUrl4, kUrl4Title,
-                             base::StrCat({u"/", kOtherBookmarksFolderName})}));
-  EXPECT_THAT(
-      ExtractUniqueLocalNodesByUrlAndTitleAndPathForTesting(
-          BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()),
-          SubtreeSelection::kUnderBookmarksBar),
-      UnorderedElementsAre(
-          UrlAndTitleAndPath{kUrl1, kUrl1Title,
-                             base::StrCat({u"/", kBookmarkBarFolderName})},
-          UrlAndTitleAndPath{kUrl2, kUrl2Title,
-                             base::StrCat({u"/", kBookmarkBarFolderName, u"/",
-                                           kFolder1Title})}));
-
-  // By URL, title, path and UUID.
-  EXPECT_THAT(
-      ExtractUniqueLocalNodesByUrlAndTitleAndPathAndUuidForTesting(
-          BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()),
-          SubtreeSelection::kConsideringAllBookmarks),
-      UnorderedElementsAre(
-          UrlAndTitleAndPathAndUuid{
-              kUrl1, kUrl1Title, base::StrCat({u"/", kBookmarkBarFolderName}),
-              kUrl1Uuid},
-          UrlAndTitleAndPathAndUuid{
-              kUrl2, kUrl2Title,
-              base::StrCat({u"/", kBookmarkBarFolderName, u"/", kFolder1Title}),
-              kUrl2Uuid},
-          UrlAndTitleAndPathAndUuid{
-              kUrl3, kUrl3Title,
-              base::StrCat({u"/", kMobileBookmarksFolderName}), kUrl3Uuid},
-          UrlAndTitleAndPathAndUuid{
-              kUrl4, kUrl4Title,
-              base::StrCat({u"/", kOtherBookmarksFolderName}), kUrl4Uuid}));
-  EXPECT_THAT(
-      ExtractUniqueLocalNodesByUrlAndTitleAndPathAndUuidForTesting(
-          BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()),
-          SubtreeSelection::kUnderBookmarksBar),
-      UnorderedElementsAre(
-          UrlAndTitleAndPathAndUuid{
-              kUrl1, kUrl1Title, base::StrCat({u"/", kBookmarkBarFolderName}),
-              kUrl1Uuid},
-          UrlAndTitleAndPathAndUuid{
-              kUrl2, kUrl2Title,
-              base::StrCat({u"/", kBookmarkBarFolderName, u"/", kFolder1Title}),
-              kUrl2Uuid}));
-}
-
-TEST_F(BookmarkModelMergerComparisonMetricsTest, ShouldExtractAccountNodes) {
-  const std::u16string kFolder1Title = u"folder1";
-
-  const std::u16string kUrl1Title = u"url1";
-  const std::u16string kUrl2Title = u"url2";
-  const std::u16string kUrl3Title = u"url3";
-  const std::u16string kUrl4Title = u"url4";
-
-  const GURL kUrl1("http://www.url1.com/");
-  const GURL kUrl2("http://www.url2.com/");
-  const GURL kUrl3("http://www.url3.com/");
-  const GURL kUrl4("http://www.url4.com/");
-
-  const base::Uuid kUrl1Uuid = base::Uuid::GenerateRandomV4();
-  const base::Uuid kUrl2Uuid = base::Uuid::GenerateRandomV4();
-  const base::Uuid kUrl3Uuid = base::Uuid::GenerateRandomV4();
-  const base::Uuid kUrl4Uuid = base::Uuid::GenerateRandomV4();
-
-  // -------- Account bookmarks --------
-  // bookmark_bar
-  //  |- url1(http://www.url1.com)
-  //  |- folder 1
-  //    |- url2(http://www.url2.com)
-  // mobile_node
-  //  |- url3(http://www.url3.com)
-  // other_node
-  //  |- url4(http://www.url4.com)
-  BookmarkModelMerger::RemoteForest account_data = BuildAccountNodes(
-      /*children_of_bookmark_bar=*/{UrlBookmarkBuilder(kUrl1Title, kUrl1)
-                                        .SetUuid(kUrl1Uuid),
-                                    FolderBuilder(kFolder1Title)
-                                        .SetChildren(
-                                            {UrlBookmarkBuilder(kUrl2Title,
-                                                                kUrl2)
-                                                 .SetUuid(kUrl2Uuid)})},
-      /*children_of_mobile_node=*/
-      {UrlBookmarkBuilder(kUrl3Title, kUrl3).SetUuid(kUrl3Uuid)},
-      /*children_of_other_node=*/
-      {UrlBookmarkBuilder(kUrl4Title, kUrl4).SetUuid(kUrl4Uuid)});
-
-  // By URL.
-  EXPECT_THAT(ExtractUniqueAccountNodesByUrlForTesting(
-                  account_data, SubtreeSelection::kConsideringAllBookmarks),
-              UnorderedElementsAre(UrlOnly{kUrl1}, UrlOnly{kUrl2},
-                                   UrlOnly{kUrl3}, UrlOnly{kUrl4}));
-  EXPECT_THAT(ExtractUniqueAccountNodesByUrlForTesting(
-                  account_data, SubtreeSelection::kUnderBookmarksBar),
-              UnorderedElementsAre(UrlOnly{kUrl1}, UrlOnly{kUrl2}));
-
-  // By URL and title.
-  EXPECT_THAT(ExtractUniqueAccountNodesByUrlAndTitleForTesting(
-                  account_data, SubtreeSelection::kConsideringAllBookmarks),
-              UnorderedElementsAre(UrlAndTitle{kUrl1, kUrl1Title},
-                                   UrlAndTitle{kUrl2, kUrl2Title},
-                                   UrlAndTitle{kUrl3, kUrl3Title},
-                                   UrlAndTitle{kUrl4, kUrl4Title}));
-  EXPECT_THAT(ExtractUniqueAccountNodesByUrlAndTitleForTesting(
-                  account_data, SubtreeSelection::kUnderBookmarksBar),
-              UnorderedElementsAre(UrlAndTitle{kUrl1, kUrl1Title},
-                                   UrlAndTitle{kUrl2, kUrl2Title}));
-
-  // By URL and UUID.
-  EXPECT_THAT(ExtractUniqueAccountNodesByUrlAndUuidForTesting(
-                  account_data, SubtreeSelection::kConsideringAllBookmarks),
-              UnorderedElementsAre(
-                  UrlAndUuid{kUrl1, kUrl1Uuid}, UrlAndUuid{kUrl2, kUrl2Uuid},
-                  UrlAndUuid{kUrl3, kUrl3Uuid}, UrlAndUuid{kUrl4, kUrl4Uuid}));
-  EXPECT_THAT(ExtractUniqueAccountNodesByUrlAndUuidForTesting(
-                  account_data, SubtreeSelection::kUnderBookmarksBar),
-              UnorderedElementsAre(UrlAndUuid{kUrl1, kUrl1Uuid},
-                                   UrlAndUuid{kUrl2, kUrl2Uuid}));
-
-  // By URL, title and path.
-  EXPECT_THAT(
-      ExtractUniqueAccountNodesByUrlAndTitleAndPathForTesting(
-          account_data, SubtreeSelection::kConsideringAllBookmarks),
-      UnorderedElementsAre(
-          UrlAndTitleAndPath{kUrl1, kUrl1Title,
-                             base::StrCat({u"/", kBookmarkBarFolderName})},
-          UrlAndTitleAndPath{kUrl2, kUrl2Title,
-                             base::StrCat({u"/", kBookmarkBarFolderName, u"/",
-                                           kFolder1Title})},
-          UrlAndTitleAndPath{kUrl3, kUrl3Title,
-                             base::StrCat({u"/", kMobileBookmarksFolderName})},
-          UrlAndTitleAndPath{kUrl4, kUrl4Title,
-                             base::StrCat({u"/", kOtherBookmarksFolderName})}));
-  EXPECT_THAT(
-      ExtractUniqueAccountNodesByUrlAndTitleAndPathForTesting(
-          account_data, SubtreeSelection::kUnderBookmarksBar),
-      UnorderedElementsAre(
-          UrlAndTitleAndPath{kUrl1, kUrl1Title,
-                             base::StrCat({u"/", kBookmarkBarFolderName})},
-          UrlAndTitleAndPath{kUrl2, kUrl2Title,
-                             base::StrCat({u"/", kBookmarkBarFolderName, u"/",
-                                           kFolder1Title})}));
-
-  // By URL, title, path and UUID.
-  EXPECT_THAT(
-      ExtractUniqueAccountNodesByUrlAndTitleAndPathAndUuidForTesting(
-          account_data, SubtreeSelection::kConsideringAllBookmarks),
-      UnorderedElementsAre(
-          UrlAndTitleAndPathAndUuid{
-              kUrl1, kUrl1Title, base::StrCat({u"/", kBookmarkBarFolderName}),
-              kUrl1Uuid},
-          UrlAndTitleAndPathAndUuid{
-              kUrl2, kUrl2Title,
-              base::StrCat({u"/", kBookmarkBarFolderName, u"/", kFolder1Title}),
-              kUrl2Uuid},
-          UrlAndTitleAndPathAndUuid{
-              kUrl3, kUrl3Title,
-              base::StrCat({u"/", kMobileBookmarksFolderName}), kUrl3Uuid},
-          UrlAndTitleAndPathAndUuid{
-              kUrl4, kUrl4Title,
-              base::StrCat({u"/", kOtherBookmarksFolderName}), kUrl4Uuid}));
-  EXPECT_THAT(
-      ExtractUniqueAccountNodesByUrlAndTitleAndPathAndUuidForTesting(
-          account_data, SubtreeSelection::kUnderBookmarksBar),
-      UnorderedElementsAre(
-          UrlAndTitleAndPathAndUuid{
-              kUrl1, kUrl1Title, base::StrCat({u"/", kBookmarkBarFolderName}),
-              kUrl1Uuid},
-          UrlAndTitleAndPathAndUuid{
-              kUrl2, kUrl2Title,
-              base::StrCat({u"/", kBookmarkBarFolderName, u"/", kFolder1Title}),
-              kUrl2Uuid}));
-}
-
-TEST_F(BookmarkModelMergerComparisonMetricsTest, ShouldCompareSets) {
-  // kBothEmpty.
-  EXPECT_THAT(CompareSetsForTesting(/*account_data=*/{}, /*local_data=*/{}),
-              Eq(SetComparisonOutcome::kBothEmpty));
-
-  // kLocalDataEmpty.
-  EXPECT_THAT(CompareSetsForTesting(/*account_data=*/{1}, /*local_data=*/{}),
-              Eq(SetComparisonOutcome::kLocalDataEmpty));
-
-  // kAccountDataEmpty.
-  EXPECT_THAT(CompareSetsForTesting(/*account_data=*/{}, /*local_data=*/{1}),
-              Eq(SetComparisonOutcome::kAccountDataEmpty));
-
-  // kExactMatchNonEmpty.
-  EXPECT_THAT(CompareSetsForTesting(/*account_data=*/{1}, /*local_data=*/{1}),
-              Eq(SetComparisonOutcome::kExactMatchNonEmpty));
-  EXPECT_THAT(
-      CompareSetsForTesting(/*account_data=*/{1, 2}, /*local_data=*/{1, 2}),
-      Eq(SetComparisonOutcome::kExactMatchNonEmpty));
-
-  // kLocalDataIsStrictSubsetOfAccountData.
-  EXPECT_THAT(
-      CompareSetsForTesting(/*account_data=*/{1, 2}, /*local_data=*/{1}),
-      Eq(SetComparisonOutcome::kLocalDataIsStrictSubsetOfAccountData));
-  EXPECT_THAT(
-      CompareSetsForTesting(/*account_data=*/{1, 2}, /*local_data=*/{2}),
-      Eq(SetComparisonOutcome::kLocalDataIsStrictSubsetOfAccountData));
-
-  // kIntersectionBetween99And100Percent.
-  EXPECT_THAT(
-      CompareSetsForTesting(/*account_data=*/ContiguousSetBetween(1, 200),
-                            /*local_data=*/ContiguousSetBetween(2, 201)),
-      Eq(SetComparisonOutcome::kIntersectionBetween99And100Percent));
-
-  // kIntersectionBetween95And99Percent.
-  EXPECT_THAT(
-      CompareSetsForTesting(/*account_data=*/ContiguousSetBetween(1, 50),
-                            /*local_data=*/ContiguousSetBetween(2, 51)),
-      Eq(SetComparisonOutcome::kIntersectionBetween95And99Percent));
-
-  // kIntersectionBetween90And95Percent.
-  EXPECT_THAT(
-      CompareSetsForTesting(/*account_data=*/ContiguousSetBetween(1, 30),
-                            /*local_data=*/ContiguousSetBetween(2, 31)),
-      Eq(SetComparisonOutcome::kIntersectionBetween90And95Percent));
-
-  // kIntersectionBetween50And90Percent.
-  EXPECT_THAT(CompareSetsForTesting(/*account_data=*/{1, 2, 3, 4},
-                                    /*local_data=*/{2, 3, 4, 5}),
-              Eq(SetComparisonOutcome::kIntersectionBetween50And90Percent));
-
-  // kIntersectionBetween10And50Percent.
-  EXPECT_THAT(CompareSetsForTesting(/*account_data=*/{1, 2},
-                                    /*local_data=*/{2, 3}),
-              Eq(SetComparisonOutcome::kIntersectionBetween10And50Percent));
-
-  // kIntersectionBelow10Percent.
-  EXPECT_THAT(
-      CompareSetsForTesting(/*account_data=*/{1, 2},
-                            /*local_data=*/ContiguousSetBetween(2, 11)),
-      Eq(SetComparisonOutcome::kIntersectionBelow10PercentExcludingZero));
-
-  // IntersectionEmpty.
-}
-
-TEST_F(BookmarkModelMergerComparisonMetricsTest,
-       ShouldSplitByBookmarkBarAndAllNodes) {
-  const std::u16string kFolder1Title = u"folder1";
-  const std::u16string kFolder2Title = u"folder2";
-
-  const std::u16string kUrl1Title = u"url1";
-  const std::u16string kUrl2Title = u"url2";
-  const std::u16string kUrl3Title = u"url3";
-  const std::u16string kUrl4Title = u"url4";
-
-  const GURL kUrl1("http://www.url1.com/");
-  const GURL kUrl2("http://www.url2.com/");
-  const GURL kUrl3("http://www.url3.com/");
-  const GURL kUrl4("http://www.url4.com/");
-
-  // -------- Local bookmarks --------
-  // bookmark_bar
-  //  |- folder 1
-  //    |- url1(http://www.url1.com)
-  //    |- url2(http://www.url2.com)
-  //  |- folder 2
-  //    |- url3(http://www.url3.com)
-  //    |- url4(http://www.url4.com)
-  AddLocalNodes(
-      /*children_of_bookmark_bar=*/{FolderBuilder(kFolder1Title)
-                                        .SetChildren({UrlBookmarkBuilder(
-                                                          kUrl1Title, kUrl1),
-                                                      UrlBookmarkBuilder(
-                                                          kUrl2Title, kUrl2)}),
-                                    FolderBuilder(kFolder2Title)
-                                        .SetChildren({UrlBookmarkBuilder(
-                                                          kUrl3Title, kUrl3),
-                                                      UrlBookmarkBuilder(
-                                                          kUrl4Title, kUrl4)})},
-      /*children_of_mobile_node=*/{},
-      /*children_of_other_node=*/{});
-
-  // -------- Account bookmarks --------
-  // bookmark_bar
-  //  |- folder 1
-  //    |- url1(http://www.url1.com)
-  //    |- url2(http://www.url2.com)
-  // mobile_node
-  //  |- folder 2
-  //    |- url3(http://www.url3.com)
-  //    |- url4(http://www.url4.com)
-  BookmarkModelMerger::RemoteForest account_data = BuildAccountNodes(
-      /*children_of_bookmark_bar=*/{FolderBuilder(kFolder1Title)
-                                        .SetChildren({UrlBookmarkBuilder(
-                                                          kUrl1Title, kUrl1),
-                                                      UrlBookmarkBuilder(
-                                                          kUrl2Title, kUrl2)})},
-      /*children_of_mobile_node=*/
-      {FolderBuilder(kFolder2Title)
-           .SetChildren({UrlBookmarkBuilder(kUrl3Title, kUrl3),
-                         UrlBookmarkBuilder(kUrl4Title, kUrl4)})},
-      /*children_of_other_node=*/{});
-
-  // -------- The expected metrics --------
-  base::HistogramTester histogram_tester;
-  CompareBookmarkModelAndLogHistograms(
-      BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()), account_data,
-      syncer::PreviouslySyncingGaiaIdInfoForMetrics::
-          kCurrentGaiaIdMatchesPreviousWithSyncFeatureOn);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndTitle",
-      /*sample=*/LegacySetComparisonOutcome::kExactMatchNonEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndUuid",
-      /*sample=*/LegacySetComparisonOutcome::kIntersectionEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndTitleAndPath",
-      /*sample=*/LegacySetComparisonOutcome::kIntersectionBetween10And50Percent,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndTitleAndPathAndUuid",
-      /*sample=*/LegacySetComparisonOutcome::kIntersectionEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "UnderBookmarksBar.ByUrlAndTitle",
-      /*sample=*/
-      LegacySetComparisonOutcome::kAccountDataIsStrictSubsetOfLocalData,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "UnderBookmarksBar.ByUrlAndUuid",
-      /*sample=*/LegacySetComparisonOutcome::kIntersectionEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "UnderBookmarksBar.ByUrlAndTitleAndPath",
-      /*sample=*/
-      LegacySetComparisonOutcome::kAccountDataIsStrictSubsetOfLocalData,
-      /*expected_bucket_count=*/1);
-
-  // Same as above but with the bookmark count suffix.
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndTitle.Between1And19LocalUrlBookmarks",
-      /*sample=*/LegacySetComparisonOutcome::kExactMatchNonEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndUuid.Between1And19LocalUrlBookmarks",
-      /*sample=*/LegacySetComparisonOutcome::kIntersectionEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndTitleAndPath."
-      "Between1And19LocalUrlBookmarks",
-      /*sample=*/LegacySetComparisonOutcome::kIntersectionBetween10And50Percent,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "UnderBookmarksBar.ByUrlAndTitle.Between1And19LocalUrlBookmarks",
-      /*sample=*/
-      LegacySetComparisonOutcome::kAccountDataIsStrictSubsetOfLocalData,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "UnderBookmarksBar.ByUrlAndUuid.Between1And19LocalUrlBookmarks",
-      /*sample=*/LegacySetComparisonOutcome::kIntersectionEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId."
-      "UnderBookmarksBar.ByUrlAndTitleAndPath.Between1And19LocalUrlBookmarks",
-      /*sample=*/
-      LegacySetComparisonOutcome::kAccountDataIsStrictSubsetOfLocalData,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndTitle",
-      /*sample=*/SetComparisonOutcome::kExactMatchNonEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndUuid",
-      /*sample=*/SetComparisonOutcome::kIntersectionEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndTitleAndPath",
-      /*sample=*/SetComparisonOutcome::kIntersectionBetween10And50Percent,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndTitleAndPathAndUuid",
-      /*sample=*/SetComparisonOutcome::kIntersectionEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "UnderBookmarksBar.ByUrlAndTitle",
-      /*sample=*/SetComparisonOutcome::kIntersectionBetween50And90Percent,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "UnderBookmarksBar.ByUrlAndUuid",
-      /*sample=*/SetComparisonOutcome::kIntersectionEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "UnderBookmarksBar.ByUrlAndTitleAndPath",
-      /*sample=*/SetComparisonOutcome::kIntersectionBetween50And90Percent,
-      /*expected_bucket_count=*/1);
-
-  // Same as above but with the bookmark count suffix.
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndTitle.Between1And19LocalUrlBookmarks",
-      /*sample=*/SetComparisonOutcome::kExactMatchNonEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndUuid.Between1And19LocalUrlBookmarks",
-      /*sample=*/SetComparisonOutcome::kIntersectionEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "ConsideringAllBookmarks.ByUrlAndTitleAndPath."
-      "Between1And19LocalUrlBookmarks",
-      /*sample=*/SetComparisonOutcome::kIntersectionBetween10And50Percent,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "UnderBookmarksBar.ByUrlAndTitle.Between1And19LocalUrlBookmarks",
-      /*sample=*/SetComparisonOutcome::kIntersectionBetween50And90Percent,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "UnderBookmarksBar.ByUrlAndUuid.Between1And19LocalUrlBookmarks",
-      /*sample=*/SetComparisonOutcome::kIntersectionEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2.MatchesPreviousGaiaId."
-      "UnderBookmarksBar.ByUrlAndTitleAndPath.Between1And19LocalUrlBookmarks",
-      /*sample=*/SetComparisonOutcome::kIntersectionBetween50And90Percent,
-      /*expected_bucket_count=*/1);
-
-  // Sanity-check the recording of a few metrics without the gaia ID info
-  // breakdown.
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2."
-      "ConsideringAllBookmarks.ByUrlAndTitle",
-      /*sample=*/LegacySetComparisonOutcome::kExactMatchNonEmpty,
-      /*expected_bucket_count=*/1);
-
-  histogram_tester.ExpectUniqueSample(
-      "Sync.BookmarkModelMerger.Comparison2."
-      "ConsideringAllBookmarks.ByUrlAndUuid",
-      /*sample=*/LegacySetComparisonOutcome::kIntersectionEmpty,
-      /*expected_bucket_count=*/1);
-}
-
-}  // namespace
-}  // namespace metrics
-}  // namespace sync_bookmarks
diff --git a/components/sync_bookmarks/bookmark_model_merger_unittest.cc b/components/sync_bookmarks/bookmark_model_merger_unittest.cc
index 2ce0935..3d9a2f4 100644
--- a/components/sync_bookmarks/bookmark_model_merger_unittest.cc
+++ b/components/sync_bookmarks/bookmark_model_merger_unittest.cc
@@ -23,7 +23,6 @@
 #include "components/favicon/core/test/mock_favicon_service.h"
 #include "components/signin/public/base/signin_switches.h"
 #include "components/sync/base/client_tag_hash.h"
-#include "components/sync/base/previously_syncing_gaia_id_info_for_metrics.h"
 #include "components/sync/base/unique_position.h"
 #include "components/sync/protocol/entity_metadata.pb.h"
 #include "components/sync_bookmarks/bookmark_model_view.h"
@@ -273,9 +272,8 @@
   std::unique_ptr<SyncedBookmarkTracker> tracker =
       SyncedBookmarkTracker::CreateEmpty(sync_pb::DataTypeState());
   testing::NiceMock<favicon::MockFaviconService> favicon_service;
-  BookmarkModelMerger(
-      std::move(updates), bookmark_model, &favicon_service, tracker.get(),
-      syncer::PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified)
+  BookmarkModelMerger(std::move(updates), bookmark_model, &favicon_service,
+                      tracker.get())
       .Merge();
   return tracker;
 }
@@ -696,9 +694,8 @@
   EXPECT_CALL(favicon_service, AddPageNoVisitForBookmark(kUrl2, kTitle2));
   EXPECT_CALL(favicon_service, MergeFavicon(kUrl2, _, _, _, _));
 
-  BookmarkModelMerger(
-      std::move(updates), &bookmark_model, &favicon_service, tracker.get(),
-      syncer::PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified)
+  BookmarkModelMerger(std::move(updates), &bookmark_model, &favicon_service,
+                      tracker.get())
       .Merge();
 }
 
@@ -776,9 +773,8 @@
   std::unique_ptr<SyncedBookmarkTracker> tracker =
       SyncedBookmarkTracker::CreateEmpty(sync_pb::DataTypeState());
   testing::NiceMock<favicon::MockFaviconService> favicon_service;
-  BookmarkModelMerger(
-      std::move(updates), &bookmark_model, &favicon_service, tracker.get(),
-      syncer::PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified)
+  BookmarkModelMerger(std::move(updates), &bookmark_model, &favicon_service,
+                      tracker.get())
       .Merge();
 
   // Both titles should have matched against each other and only node is in the
@@ -817,9 +813,8 @@
   std::unique_ptr<SyncedBookmarkTracker> tracker =
       SyncedBookmarkTracker::CreateEmpty(sync_pb::DataTypeState());
   testing::NiceMock<favicon::MockFaviconService> favicon_service;
-  BookmarkModelMerger(
-      std::move(updates), &bookmark_model, &favicon_service, tracker.get(),
-      syncer::PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified)
+  BookmarkModelMerger(std::move(updates), &bookmark_model, &favicon_service,
+                      tracker.get())
       .Merge();
 
   // Both titles should have matched against each other and only node is in the
@@ -864,9 +859,8 @@
   std::unique_ptr<SyncedBookmarkTracker> tracker =
       SyncedBookmarkTracker::CreateEmpty(sync_pb::DataTypeState());
   testing::NiceMock<favicon::MockFaviconService> favicon_service;
-  BookmarkModelMerger(
-      std::move(updates), &bookmark_model, &favicon_service, tracker.get(),
-      syncer::PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified)
+  BookmarkModelMerger(std::move(updates), &bookmark_model, &favicon_service,
+                      tracker.get())
       .Merge();
 
   // Both titles should have matched against each other and only node is in the
@@ -2182,9 +2176,8 @@
   std::unique_ptr<SyncedBookmarkTracker> tracker =
       SyncedBookmarkTracker::CreateEmpty(sync_pb::DataTypeState());
   testing::NiceMock<favicon::MockFaviconService> favicon_service;
-  BookmarkModelMerger(
-      std::move(updates), &bookmark_model, &favicon_service, tracker.get(),
-      syncer::PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified)
+  BookmarkModelMerger(std::move(updates), &bookmark_model, &favicon_service,
+                      tracker.get())
       .Merge();
 
   // Check max depth hasn't been exceeded. Take into account root of the
diff --git a/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc b/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
index bf1837f..6034789 100644
--- a/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
+++ b/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
@@ -21,7 +21,6 @@
 #include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/data_type.h"
 #include "components/sync/base/hash_util.h"
-#include "components/sync/base/previously_syncing_gaia_id_info_for_metrics.h"
 #include "components/sync/base/unique_position.h"
 #include "components/sync/model/conflict_resolution.h"
 #include "components/sync/protocol/bookmark_model_metadata.pb.h"
@@ -246,10 +245,8 @@
   BookmarkRemoteUpdatesHandlerWithInitialMergeTest()
       : tracker_(SyncedBookmarkTracker::CreateEmpty(sync_pb::DataTypeState())),
         updates_handler_(&bookmark_model_, &favicon_service_, tracker_.get()) {
-    BookmarkModelMerger(
-        CreatePermanentFoldersUpdateData(), &bookmark_model_, &favicon_service_,
-        tracker_.get(),
-        syncer::PreviouslySyncingGaiaIdInfoForMetrics::kUnspecified)
+    BookmarkModelMerger(CreatePermanentFoldersUpdateData(), &bookmark_model_,
+                        &favicon_service_, tracker_.get())
         .Merge();
   }
 
diff --git a/components/sync_bookmarks/bookmark_specifics_conversions.cc b/components/sync_bookmarks/bookmark_specifics_conversions.cc
index 7f535b7..77a283a 100644
--- a/components/sync_bookmarks/bookmark_specifics_conversions.cc
+++ b/components/sync_bookmarks/bookmark_specifics_conversions.cc
@@ -200,6 +200,22 @@
       base::TrimWhitespaceASCII(title, base::TrimPositions::TRIM_TRAILING));
 }
 
+std::u16string NodeTitleFromSpecifics(
+    const sync_pb::BookmarkSpecifics& specifics) {
+  if (specifics.has_full_title()) {
+    return base::UTF8ToUTF16(specifics.full_title());
+  }
+
+  std::string node_title = specifics.legacy_canonicalized_title();
+  if (base::EndsWith(node_title, " ") &&
+      IsForbiddenTitleWithMaybeTrailingSpaces(node_title)) {
+    // Legacy clients added an extra space to the real title, so remove it here.
+    // See also FullTitleToLegacyCanonicalizedTitle().
+    node_title.pop_back();
+  }
+  return base::UTF8ToUTF16(node_title);
+}
+
 void MoveAllChildren(BookmarkModelView* model,
                      const bookmarks::BookmarkNode* old_parent,
                      const bookmarks::BookmarkNode* new_parent) {
@@ -242,22 +258,6 @@
   return specifics_title;
 }
 
-std::u16string NodeTitleFromSpecifics(
-    const sync_pb::BookmarkSpecifics& specifics) {
-  if (specifics.has_full_title()) {
-    return base::UTF8ToUTF16(specifics.full_title());
-  }
-
-  std::string node_title = specifics.legacy_canonicalized_title();
-  if (base::EndsWith(node_title, " ") &&
-      IsForbiddenTitleWithMaybeTrailingSpaces(node_title)) {
-    // Legacy clients added an extra space to the real title, so remove it here.
-    // See also FullTitleToLegacyCanonicalizedTitle().
-    node_title.pop_back();
-  }
-  return base::UTF8ToUTF16(node_title);
-}
-
 bool IsBookmarkEntityReuploadNeeded(
     const syncer::EntityData& remote_entity_data) {
   DCHECK(remote_entity_data.server_defined_unique_tag.empty());
diff --git a/components/sync_bookmarks/bookmark_specifics_conversions.h b/components/sync_bookmarks/bookmark_specifics_conversions.h
index 571ba60..e8a727c 100644
--- a/components/sync_bookmarks/bookmark_specifics_conversions.h
+++ b/components/sync_bookmarks/bookmark_specifics_conversions.h
@@ -38,10 +38,6 @@
 // truncating and the appending ' ' in some cases.
 std::string FullTitleToLegacyCanonicalizedTitle(const std::string& node_title);
 
-// Returns the title for a bookmark node considering all supported fields.
-std::u16string NodeTitleFromSpecifics(
-    const sync_pb::BookmarkSpecifics& specifics);
-
 // Used to decide if entity needs to be reuploaded for each remote change.
 bool IsBookmarkEntityReuploadNeeded(
     const syncer::EntityData& remote_entity_data);
diff --git a/components/test/data/autofill/heuristics/output/127_bug_463986.out b/components/test/data/autofill/heuristics/output/127_bug_463986.out
index 4489f415..4d10b1f 100644
--- a/components/test/data/autofill/heuristics/output/127_bug_463986.out
+++ b/components/test/data/autofill/heuristics/output/127_bug_463986.out
@@ -39,8 +39,8 @@
 UNKNOWN_TYPE | codeCIN | Code CIN |  | -default
 UNKNOWN_TYPE | codeABI | Code ABI |  | -default
 UNKNOWN_TYPE | codeCAB | Code CAB |  | -default
-ADDRESS_HOME_ZIP_PREFIX | codigoEntidad | Codigo de Entidad |  | -default
-ADDRESS_HOME_ZIP_SUFFIX | codigoOficina | Codigo de Oficina |  | -default
+ADDRESS_HOME_ZIP | codigoEntidad | Codigo de Entidad |  | -default
+ADDRESS_HOME_ZIP | codigoOficina | Codigo de Oficina |  | -default
 UNKNOWN_TYPE | controlDigits | Digitos de Control |  | -default
 UNKNOWN_TYPE | identificationPaper | Digitos de Control | IDCARD | -default
 UNKNOWN_TYPE | identificationValue | Digitos de Control |  | -default
diff --git a/components/test/data/autofill/heuristics/output/148_payment_dickblick.com.out b/components/test/data/autofill/heuristics/output/148_payment_dickblick.com.out
index b14e5e5f..744fe4a6 100644
--- a/components/test/data/autofill/heuristics/output/148_payment_dickblick.com.out
+++ b/components/test/data/autofill/heuristics/output/148_payment_dickblick.com.out
@@ -15,8 +15,8 @@
 ADDRESS_HOME_CITY | BillingAddressCity | City: |  | unbxd_q_1
 ADDRESS_HOME_STATE | BillingAddressState | State: | 0 | unbxd_q_1
 UNKNOWN_TYPE | BillingAddressNotUsState | State/Province: |  | -default
-ADDRESS_HOME_ZIP_PREFIX | BillingAddressZipCode | Zip Code: |  | unbxd_q_1
-ADDRESS_HOME_ZIP_SUFFIX | BillingAddressPostalCode | Postal Code: |  | -default
+ADDRESS_HOME_ZIP | BillingAddressZipCode | Zip Code: |  | unbxd_q_1
+ADDRESS_HOME_ZIP | BillingAddressPostalCode | Postal Code: |  | -default
 ADDRESS_HOME_COUNTRY | BillingAddressCountry | Country: | 0 | unbxd_q_1
 PHONE_HOME_CITY_AND_NUMBER | ContactDaytimePhoneNumber | Daytime Telephone: |  | unbxd_q_1
 PHONE_HOME_CITY_AND_NUMBER | ContactEveningPhoneNumber | Evening Telephone: |  | unbxd_q_1
@@ -38,8 +38,8 @@
 ADDRESS_HOME_CITY | BillingAddressCity | City: |  | BillingAddressFirstName_3
 ADDRESS_HOME_STATE | BillingAddressState | State: | 0 | BillingAddressFirstName_3
 UNKNOWN_TYPE | BillingAddressNotUsState | State/Province: |  | -default
-ADDRESS_HOME_ZIP_PREFIX | BillingAddressZipCode | Zip Code: |  | BillingAddressFirstName_3
-ADDRESS_HOME_ZIP_SUFFIX | BillingAddressPostalCode | Postal Code: |  | -default
+ADDRESS_HOME_ZIP | BillingAddressZipCode | Zip Code: |  | BillingAddressFirstName_3
+ADDRESS_HOME_ZIP | BillingAddressPostalCode | Postal Code: |  | -default
 ADDRESS_HOME_COUNTRY | BillingAddressCountry | Country: | 0 | BillingAddressFirstName_3
 PHONE_HOME_CITY_AND_NUMBER | ContactDaytimePhoneNumber | Daytime Telephone: |  | BillingAddressFirstName_3
 PHONE_HOME_CITY_AND_NUMBER | ContactEveningPhoneNumber | Evening Telephone: |  | BillingAddressFirstName_3
@@ -61,8 +61,8 @@
 ADDRESS_HOME_CITY | BillingAddressCity | City: |  | BillingAddressFirstName_4
 ADDRESS_HOME_STATE | BillingAddressState | State: | 0 | BillingAddressFirstName_4
 UNKNOWN_TYPE | BillingAddressNotUsState | State/Province: |  | -default
-ADDRESS_HOME_ZIP_PREFIX | BillingAddressZipCode | Zip Code: |  | BillingAddressFirstName_4
-ADDRESS_HOME_ZIP_SUFFIX | BillingAddressPostalCode | Postal Code: |  | -default
+ADDRESS_HOME_ZIP | BillingAddressZipCode | Zip Code: |  | BillingAddressFirstName_4
+ADDRESS_HOME_ZIP | BillingAddressPostalCode | Postal Code: |  | -default
 ADDRESS_HOME_COUNTRY | BillingAddressCountry | Country: | 0 | BillingAddressFirstName_4
 PHONE_HOME_CITY_AND_NUMBER | ContactDaytimePhoneNumber | Daytime Telephone: |  | BillingAddressFirstName_4
 PHONE_HOME_CITY_AND_NUMBER | ContactEveningPhoneNumber | Evening Telephone: |  | BillingAddressFirstName_4
diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc
index ddd7372..c163076 100644
--- a/content/app/content_main_runner_impl.cc
+++ b/content/app/content_main_runner_impl.cc
@@ -630,17 +630,19 @@
   MainFunctionParams main_params(command_line);
   main_params.zygote_child = true;
 
+  // Once Zygote forks and feature list initializes we can start a thread to
+  // begin tracing immediately.
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
   if (process_type == switches::kGpuProcess) {
-    // Once Zygote forks and feature list initializes we can start a thread to
-    // begin tracing immediately.
-    // TODO(https://crbug.com/380411640): Enable for more processes other than
-    // GPU process.
-    tracing::EnableStartupTracingIfNeeded(/*with_thread=*/true);
-    tracing::InitTracingPostFeatureList(/*enable_consumer=*/false);
-    main_params.needs_startup_tracing_after_mojo_init = false;
+    tracing::InitTracingPostFeatureList(/*enable_consumer=*/false,
+                                        /*will_trace_thread_restart=*/true);
   } else {
-    main_params.needs_startup_tracing_after_mojo_init = true;
+    main_params.needs_startup_tracing_after_sandbox_init = true;
   }
+#else
+  tracing::InitTracingPostFeatureList(/*enable_consumer=*/false,
+                                      /*will_trace_thread_restart=*/false);
+#endif
 
   // The hang watcher needs to be created once the feature list is available
   // but before the IO thread is started.
@@ -852,45 +854,6 @@
 #endif  // !BUILDFLAG(IS_WIN)
 
   is_initialized_ = true;
-
-  // Enable startup tracing asap now that mojo's core is initialized, to avoid
-  // early TRACE_EVENT calls being ignored.
-  //
-  // Startup tracing flags are not (and should not be) passed to Zygote
-  // processes. We will enable tracing when forked, if needed.
-  bool enable_startup_tracing = process_type != switches::kZygoteProcess;
-#if BUILDFLAG(USE_ZYGOTE)
-  // In the browser process, we have to enable startup tracing after
-  // InitializeZygoteSandboxForBrowserProcess() is run below, because that
-  // function forks and may call trace macros in the forked process.
-  if (process_type.empty()) {
-    enable_startup_tracing = false;
-  }
-#endif  // BUILDFLAG(USE_ZYGOTE)
-#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_MAC)
-  // A sandboxed process won't be able to allocate the SMB needed for startup
-  // tracing until Mojo IPC support is brought up, at which point the Mojo
-  // broker will transparently broker the SMB creation. Unless the sandboxed
-  // process stops the trace threads when entering sandbox.
-  // TODO(https://crbug.com/380411640): Implement for other processes other than
-  // GPU process.
-  if (process_type != switches::kGpuProcess &&
-      !IsUnsandboxedSandboxType(SandboxTypeFromCommandLine(command_line))) {
-    enable_startup_tracing = false;
-    needs_startup_tracing_after_mojo_init_ = true;
-  }
-#endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_MAC)
-  if (enable_startup_tracing) {
-    if (process_type == switches::kGpuProcess) {
-      // Without Zygote and posix sandbox we can start a thread to begin tracing
-      // immediately.
-      // TODO(https://crbug.com/380411640): Enable for more processes other than
-      // GPU process.
-      tracing::EnableStartupTracingIfNeeded(/*with_thread=*/true);
-    } else {
-      tracing::EnableStartupTracingIfNeeded(/*with_thread=*/false);
-    }
-  }
   TRACE_EVENT0("startup,benchmark,rail", "ContentMainRunnerImpl::Initialize");
 
 // The exit manager is in charge of calling the dtors of singleton objects.
@@ -1061,13 +1024,6 @@
     // SandboxInitialized().
     InitializeZygoteSandboxForBrowserProcess(
         *base::CommandLine::ForCurrentProcess());
-
-    // We can only enable startup tracing after
-    // InitializeZygoteSandboxForBrowserProcess(), because the latter may fork
-    // and run code that calls trace event macros in the forked process (which
-    // could cause all sorts of issues, like writing to the same tracing SMB
-    // from two processes).
-    tracing::EnableStartupTracingIfNeeded();
   }
 #endif  // BUILDFLAG(USE_ZYGOTE)
 
@@ -1108,15 +1064,24 @@
 #endif
 
   // Run this logic on all child processes.
+  bool needs_startup_tracing_after_sandbox_init = false;
   if (!process_type.empty()) {
     if (process_type != switches::kZygoteProcess) {
       if (delegate_->ShouldCreateFeatureList(
               ContentMainDelegate::InvokedInChildProcess())) {
         InitializeFieldTrialAndFeatureList();
       }
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
       if (process_type == switches::kGpuProcess) {
-        tracing::InitTracingPostFeatureList(/*enable_consumer=*/false);
+        tracing::InitTracingPostFeatureList(/*enable_consumer=*/false,
+                                            /*will_trace_thread_restart=*/true);
+      } else {
+        needs_startup_tracing_after_sandbox_init = true;
       }
+#else
+      tracing::InitTracingPostFeatureList(/*enable_consumer=*/false,
+                                          /*will_trace_thread_restart=*/false);
+#endif
       if (delegate_->ShouldInitializeMojo(
               ContentMainDelegate::InvokedInChildProcess())) {
         InitializeMojoCore();
@@ -1133,8 +1098,8 @@
   main_params.ui_task = std::move(content_main_params_->ui_task);
   main_params.created_main_parts_closure =
       std::move(content_main_params_->created_main_parts_closure);
-  main_params.needs_startup_tracing_after_mojo_init =
-      needs_startup_tracing_after_mojo_init_;
+  main_params.needs_startup_tracing_after_sandbox_init =
+      needs_startup_tracing_after_sandbox_init;
 #if BUILDFLAG(IS_WIN)
   main_params.sandbox_info = content_main_params_->sandbox_info;
 #elif BUILDFLAG(IS_MAC)
@@ -1233,13 +1198,13 @@
       base::HangWatcher::GetInstance()->Start();
     }
 
+    tracing::InitTracingPostFeatureList(
+        /*enable_consumer=*/true, /*will_trace_thread_restart=*/false,
+        base::BindRepeating(&ShouldAllowSystemTracingConsumer));
+
     // The FeatureList needs to be created before starting the ThreadPool.
     StartBrowserThreadPool();
 
-    tracing::PerfettoTracedProcess::Get().SetAllowSystemTracingConsumerCallback(
-        base::BindRepeating(&ShouldAllowSystemTracingConsumer));
-    tracing::InitTracingPostFeatureList(/*enable_consumer=*/true);
-
     // PowerMonitor is needed in reduced mode. BrowserMainLoop will safely skip
     // initializing it again if it has already been initialized.
     base::PowerMonitor::GetInstance()->Initialize(
diff --git a/content/app/content_main_runner_impl.h b/content/app/content_main_runner_impl.h
index cbbc2f3..c6bd628 100644
--- a/content/app/content_main_runner_impl.h
+++ b/content/app/content_main_runner_impl.h
@@ -62,10 +62,6 @@
   // True if the runner has been shut down.
   bool is_shutdown_ = false;
 
-  // Set to true if this content process's main function should enable startup
-  // tracing after initializing Mojo.
-  bool needs_startup_tracing_after_mojo_init_ = false;
-
   // The delegate will outlive this object.
   raw_ptr<ContentMainDelegate> delegate_ = nullptr;
 
diff --git a/content/browser/android/browser_startup_controller.cc b/content/browser/android/browser_startup_controller.cc
index 5c4ae53..a6bbe44 100644
--- a/content/browser/android/browser_startup_controller.cc
+++ b/content/browser/android/browser_startup_controller.cc
@@ -16,9 +16,11 @@
 
 namespace content {
 
-void BrowserStartupComplete(int result) {
+void BrowserStartupComplete(int result,
+                            base::TimeDelta longest_blocking_duration) {
   JNIEnv* env = base::android::AttachCurrentThread();
-  Java_BrowserStartupControllerImpl_browserStartupComplete(env, result);
+  Java_BrowserStartupControllerImpl_browserStartupComplete(
+      env, result, longest_blocking_duration.InMilliseconds());
 }
 
 void MinimalBrowserStartupComplete() {
diff --git a/content/browser/android/browser_startup_controller.h b/content/browser/android/browser_startup_controller.h
index 8609bd8b..7170809 100644
--- a/content/browser/android/browser_startup_controller.h
+++ b/content/browser/android/browser_startup_controller.h
@@ -5,9 +5,12 @@
 #ifndef CONTENT_BROWSER_ANDROID_BROWSER_STARTUP_CONTROLLER_H_
 #define CONTENT_BROWSER_ANDROID_BROWSER_STARTUP_CONTROLLER_H_
 
+#include "base/time/time.h"
+
 namespace content {
 
-void BrowserStartupComplete(int result);
+void BrowserStartupComplete(int result,
+                            base::TimeDelta longest_blocking_duration);
 bool ShouldStartGpuProcessOnBrowserStartup();
 void MinimalBrowserStartupComplete();
 
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 15d11d21..aaa58c6 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -880,10 +880,10 @@
 
   startup_task_runner_ = std::make_unique<StartupTaskRunner>(
       base::BindOnce(&BrowserStartupComplete),
-      GetUIThreadTaskRunner({BrowserTaskType::kDefault}));
+      GetUIThreadTaskRunner({BrowserTaskType::kStartup}));
 #else
   startup_task_runner_ = std::make_unique<StartupTaskRunner>(
-      base::OnceCallback<void(int)>(),
+      base::OnceCallback<void(int, base::TimeDelta)>(),
       base::SingleThreadTaskRunner::GetCurrentDefault());
 #endif
   StartupTask pre_create_threads = base::BindOnce(
diff --git a/content/browser/payments/stub_secure_payment_confirmation_service.cc b/content/browser/payments/stub_secure_payment_confirmation_service.cc
index 3992e01e..1895a2ea 100644
--- a/content/browser/payments/stub_secure_payment_confirmation_service.cc
+++ b/content/browser/payments/stub_secure_payment_confirmation_service.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/payments/stub_secure_payment_confirmation_service.h"
 
+#include "content/browser/webauth/authenticator_common_impl.h"
 #include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
 
 namespace content {
@@ -29,7 +30,15 @@
     RenderFrameHost& render_frame_host,
     mojo::PendingReceiver<payments::mojom::SecurePaymentConfirmationService>
         receiver)
-    : DocumentService(render_frame_host, std::move(receiver)) {}
+    : DocumentService(render_frame_host, std::move(receiver)),
+      authenticator_common_impl_(std::make_unique<AuthenticatorCommonImpl>(
+          &render_frame_host,
+          // The real SecurePaymentConfirmationService uses an
+          // InternalAuthenticator so kInternalUses is set here.
+          AuthenticatorCommonImpl::ServingRequestsFor::kInternalUses)) {}
+
+StubSecurePaymentConfirmationService::~StubSecurePaymentConfirmationService() =
+    default;
 
 void StubSecurePaymentConfirmationService::
     SecurePaymentConfirmationAvailability(
@@ -51,11 +60,8 @@
 void StubSecurePaymentConfirmationService::MakePaymentCredential(
     blink::mojom::PublicKeyCredentialCreationOptionsPtr options,
     MakePaymentCredentialCallback callback) {
-  // This method on this stub is not implemented.
-  std::move(callback).Run(
-      blink::mojom::AuthenticatorStatus::UNKNOWN_ERROR,
-      /*make_credential_authenticator_response_ptr=*/nullptr,
-      /*webauthn_dom_exception_details_ptr=*/nullptr);
+  authenticator_common_impl_->MakeCredential(origin(), std::move(options),
+                                             std::move(callback));
 }
 
 }  // namespace content
diff --git a/content/browser/payments/stub_secure_payment_confirmation_service.h b/content/browser/payments/stub_secure_payment_confirmation_service.h
index 361afe5f..05c2e85 100644
--- a/content/browser/payments/stub_secure_payment_confirmation_service.h
+++ b/content/browser/payments/stub_secure_payment_confirmation_service.h
@@ -5,11 +5,15 @@
 #ifndef CONTENT_BROWSER_PAYMENTS_STUB_SECURE_PAYMENT_CONFIRMATION_SERVICE_H_
 #define CONTENT_BROWSER_PAYMENTS_STUB_SECURE_PAYMENT_CONFIRMATION_SERVICE_H_
 
+#include <memory>
+
 #include "content/public/browser/document_service.h"
 #include "third_party/blink/public/mojom/payments/secure_payment_confirmation_service.mojom.h"
 
 namespace content {
 
+class AuthenticatorCommonImpl;
+
 // A stub implementation of the SecurePaymentConfirmationService interface.
 // This exists to support content browsertests which cannot use the real
 // implementation.
@@ -33,6 +37,7 @@
       const StubSecurePaymentConfirmationService&) = delete;
   StubSecurePaymentConfirmationService& operator=(
       const StubSecurePaymentConfirmationService&) = delete;
+  ~StubSecurePaymentConfirmationService() override;
 
   // mojom::SecurePaymentConfirmationService:
   void SecurePaymentConfirmationAvailability(
@@ -54,6 +59,8 @@
       RenderFrameHost& render_frame_host,
       mojo::PendingReceiver<payments::mojom::SecurePaymentConfirmationService>
           receiver);
+
+  std::unique_ptr<AuthenticatorCommonImpl> authenticator_common_impl_;
 };
 
 }  // namespace content
diff --git a/content/browser/startup_task_runner.cc b/content/browser/startup_task_runner.cc
index 67c4475..5afa1a5 100644
--- a/content/browser/startup_task_runner.cc
+++ b/content/browser/startup_task_runner.cc
@@ -7,11 +7,12 @@
 #include "base/functional/bind.h"
 #include "base/location.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/timer/elapsed_timer.h"
 
 namespace content {
 
 StartupTaskRunner::StartupTaskRunner(
-    base::OnceCallback<void(int)> startup_complete_callback,
+    base::OnceCallback<void(int, base::TimeDelta)> startup_complete_callback,
     scoped_refptr<base::SingleThreadTaskRunner> proxy)
     : startup_complete_callback_(std::move(startup_complete_callback)),
       proxy_(proxy) {}
@@ -27,7 +28,8 @@
   int result = 0;
   if (task_list_.empty()) {
     if (!startup_complete_callback_.is_null()) {
-      std::move(startup_complete_callback_).Run(result);
+      std::move(startup_complete_callback_)
+          .Run(result, longest_blocking_duration_);
     }
   } else {
     base::OnceClosure next_task =
@@ -38,13 +40,17 @@
 
 void StartupTaskRunner::RunAllTasksNow() {
   int result = 0;
-  for (auto it = task_list_.begin(); it != task_list_.end(); it++) {
-    result = std::move(*it).Run();
+  base::ElapsedTimer timer;
+  for (auto& it : task_list_) {
+    result = std::move(it).Run();
     if (result > 0) break;
   }
+  longest_blocking_duration_ =
+      std::max(longest_blocking_duration_, timer.Elapsed());
   task_list_.clear();
   if (!startup_complete_callback_.is_null()) {
-    std::move(startup_complete_callback_).Run(result);
+    std::move(startup_complete_callback_)
+        .Run(result, longest_blocking_duration_);
   }
 }
 
@@ -56,7 +62,10 @@
     return;
   }
 
+  base::ElapsedTimer timer;
   int result = std::move(task_list_.front()).Run();
+  longest_blocking_duration_ =
+      std::max(longest_blocking_duration_, timer.Elapsed());
   task_list_.pop_front();
   if (result > 0) {
     // Stop now and throw away the remaining tasks
@@ -64,7 +73,8 @@
   }
   if (task_list_.empty()) {
     if (!startup_complete_callback_.is_null()) {
-      std::move(startup_complete_callback_).Run(result);
+      std::move(startup_complete_callback_)
+          .Run(result, longest_blocking_duration_);
     }
   } else {
     base::OnceClosure next_task =
diff --git a/content/browser/startup_task_runner.h b/content/browser/startup_task_runner.h
index e3c6141..105aad10 100644
--- a/content/browser/startup_task_runner.h
+++ b/content/browser/startup_task_runner.h
@@ -9,11 +9,9 @@
 
 #include "base/functional/callback.h"
 #include "base/task/single_thread_task_runner.h"
-#include "content/common/content_export.h"
-
-#include "base/task/single_thread_task_runner.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
-
+#include "content/common/content_export.h"
 #include "content/public/browser/browser_main_runner.h"
 
 namespace content {
@@ -37,9 +35,11 @@
 
  public:
   // Constructor: Note that |startup_complete_callback| is optional. If it is
-  // not null it will be called once all the startup tasks have run.
-  StartupTaskRunner(base::OnceCallback<void(int)> startup_complete_callback,
-                    scoped_refptr<base::SingleThreadTaskRunner> proxy);
+  // not null it will be called, once all the startup tasks have run, with the
+  // result of running tasks and the duration spent blocking the UI thread.
+  StartupTaskRunner(
+      base::OnceCallback<void(int, base::TimeDelta)> startup_complete_callback,
+      scoped_refptr<base::SingleThreadTaskRunner> proxy);
 
   StartupTaskRunner(const StartupTaskRunner&) = delete;
   StartupTaskRunner& operator=(const StartupTaskRunner&) = delete;
@@ -61,8 +61,12 @@
   std::list<StartupTask> task_list_;
   void WrappedTask();
 
-  base::OnceCallback<void(int)> startup_complete_callback_;
+  base::OnceCallback<void(int, base::TimeDelta)> startup_complete_callback_;
   scoped_refptr<base::SingleThreadTaskRunner> proxy_;
+  // Longest time spent blocking the thread when running the tasks. This is
+  // either the max duration of each individual task when running async or the
+  // time it took to run all tasks synchronously.
+  base::TimeDelta longest_blocking_duration_;
 };
 
 }  // namespace content
diff --git a/content/browser/startup_task_runner_unittest.cc b/content/browser/startup_task_runner_unittest.cc
index c4affdd..a4dd6d6 100644
--- a/content/browser/startup_task_runner_unittest.cc
+++ b/content/browser/startup_task_runner_unittest.cc
@@ -29,7 +29,7 @@
 int task_count = 0;
 int observer_result;
 
-void Observer(int result) {
+void Observer(int result, base::TimeDelta max_task_duration) {
   observer_calls++;
   observer_result = result;
 }
@@ -85,7 +85,7 @@
 
 class TaskRunnerProxy : public base::SingleThreadTaskRunner {
  public:
-  TaskRunnerProxy(MockTaskRunner* mock) : mock_(mock) {}
+  explicit TaskRunnerProxy(MockTaskRunner* mock) : mock_(mock) {}
   bool RunsTasksInCurrentSequence() const override { return true; }
   bool PostDelayedTask(const base::Location& location,
                        base::OnceClosure closure,
@@ -154,7 +154,8 @@
   EXPECT_CALL(mock_runner, PostDelayedTask(_, _)).Times(0);
   EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _)).Times(0);
 
-  StartupTaskRunner runner(base::OnceCallback<void(int)>(), proxy);
+  StartupTaskRunner runner(base::OnceCallback<void(int, base::TimeDelta)>(),
+                           proxy);
 
   StartupTask task1 =
       base::BindOnce(&StartupTaskRunnerTest::Task1, base::Unretained(this));
@@ -287,10 +288,10 @@
   EXPECT_EQ(GetLastTask(), 0);
   runner.StartRunningTasksAsync();
 
-  // No tasks should have run yet, since we the message loop hasn't run.
+  // No tasks should have run yet, since the message loop hasn't run.
   EXPECT_EQ(GetLastTask(), 0);
 
-  // Fake the actual message loop. Each time a task is run a new task should
+  // Fake the actual message loop. Each time a task is run, a new task should
   // be added to the queue, hence updating "task". The loop should actually run
   // at most twice (once for the failed task plus possibly once for the
   // observer), the "4" is a backstop.
diff --git a/content/browser/tracing/startup_tracing_browsertest.cc b/content/browser/tracing/startup_tracing_browsertest.cc
index 4ad0039..a844e28 100644
--- a/content/browser/tracing/startup_tracing_browsertest.cc
+++ b/content/browser/tracing/startup_tracing_browsertest.cc
@@ -100,7 +100,11 @@
   auto config = tracing::TraceStartupConfig::GetInstance()
                     .GetDefaultBackgroundStartupConfig();
 
-  CHECK(tracing::EnableStartupTracingForProcess(config));
+  perfetto::Tracing::SetupStartupTracingOpts opts;
+  opts.timeout_ms = tracing::kStartupTracingTimeoutMs;
+  opts.backend = perfetto::kCustomBackend;
+
+  perfetto::Tracing::SetupStartupTracingBlocking(config, opts);
 
   for (int i = 0; i < 1024; ++i) {
     auto data = std::make_unique<LargeTraceEventData>();
diff --git a/content/browser/tracing/tracing_scenario.cc b/content/browser/tracing/tracing_scenario.cc
index 620a1256..7ed0fc4c 100644
--- a/content/browser/tracing/tracing_scenario.cc
+++ b/content/browser/tracing/tracing_scenario.cc
@@ -499,8 +499,7 @@
     perfetto::Tracing::SetupStartupTracingOpts opts;
     opts.timeout_ms = kStartupTracingTimeoutMs;
     opts.backend = perfetto::kCustomBackend;
-    tracing::PerfettoTracedProcess::Get().RequestStartupTracing(trace_config_,
-                                                                opts);
+    perfetto::Tracing::SetupStartupTracingBlocking(trace_config_, opts);
   }
 
   tracing_session_->SetOnStopCallback([task_runner = task_runner_,
diff --git a/content/child/child_process.cc b/content/child/child_process.cc
index b0a42ff..0c3fd41 100644
--- a/content/child/child_process.cc
+++ b/content/child/child_process.cc
@@ -107,8 +107,6 @@
     initialized_thread_pool_ = true;
   }
 
-  tracing::InitTracingPostFeatureList(/*enable_consumer=*/false);
-
   // Ensure the visibility tracker is created on the main thread.
   ProcessVisibilityTracker::GetInstance();
 
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
index 620a5f2..b6403600 100644
--- a/content/gpu/gpu_main.cc
+++ b/content/gpu/gpu_main.cc
@@ -381,6 +381,16 @@
     client->PostSandboxInitialized();
   }
 
+  // Startup tracing creates a tracing thread, which is incompatible on
+  // platforms that require single-threaded sandbox initialization. In these
+  // cases, startup tracing is either initialized right after sandbox
+  // initialization, or we restart the tracing thread during sandbox
+  // initialization.
+  if (parameters.needs_startup_tracing_after_sandbox_init) {
+    tracing::InitTracingPostFeatureList(/*enable_consumer=*/false,
+                                        /*will_trace_thread_restart=*/false);
+  }
+
   GetContentClient()->SetGpuInfo(gpu_init->gpu_info());
 
   base::ThreadType io_thread_type = base::ThreadType::kDisplayCritical;
@@ -400,13 +410,6 @@
 
   gpu_process.set_main_thread(child_thread);
 
-  // Mojo IPC support is brought up by GpuChildThread, so startup tracing is
-  // enabled here if it needs to start after mojo init (normally so the mojo
-  // broker can bypass the sandbox to allocate startup tracing's SMB).
-  if (parameters.needs_startup_tracing_after_mojo_init) {
-    tracing::EnableStartupTracingIfNeeded();
-  }
-
 #if BUILDFLAG(IS_MAC)
   // A GPUEjectPolicy of 'wait' is set in the Info.plist of the browser
   // process, meaning it is "responsible" for making sure it and its
diff --git a/content/public/android/java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java
index b3e5cb1..cf57cf7c 100644
--- a/content/public/android/java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java
@@ -64,9 +64,10 @@
 
     @VisibleForTesting
     @CalledByNative
-    static void browserStartupComplete(int result) {
+    static void browserStartupComplete(int result, long longestBlockingDuration) {
         if (sInstance != null) {
             sInstance.executeEnqueuedCallbacks(result);
+            sInstance.recordStartupTasksLongestBlockingDuration(longestBlockingDuration);
         }
     }
 
@@ -130,7 +131,7 @@
     private @Nullable TracingControllerAndroidImpl mTracingController;
 
     private long mContentStartDurationMs;
-    private long mFlushStartupTasksDurationMs;
+    private long mStartupTasksLongestBlockingDurationMs;
 
     BrowserStartupControllerImpl() {
         mAsyncStartupCallbacks = new ArrayList<>();
@@ -346,7 +347,7 @@
         try (ScopedSysTraceEvent e = ScopedSysTraceEvent.scoped("flushStartupTasks")) {
             long startTime = SystemClock.uptimeMillis();
             BrowserStartupControllerImplJni.get().flushStartupTasks();
-            recordFlushStartupTasksDuration(SystemClock.uptimeMillis() - startTime);
+            recordStartupTasksLongestBlockingDuration(SystemClock.uptimeMillis() - startTime);
         }
     }
 
@@ -390,8 +391,8 @@
     }
 
     @Override
-    public long getFlushStartupTasksDuration() {
-        return mFlushStartupTasksDurationMs;
+    public long getStartupTasksLongestBlockingDuration() {
+        return mStartupTasksLongestBlockingDurationMs;
     }
 
     /**
@@ -478,9 +479,9 @@
         mContentStartDurationMs = Math.max(mContentStartDurationMs, contentStartDurationMs);
     }
 
-    private void recordFlushStartupTasksDuration(long flushStartupTasksDurationMs) {
-        mFlushStartupTasksDurationMs =
-                Math.max(mFlushStartupTasksDurationMs, flushStartupTasksDurationMs);
+    private void recordStartupTasksLongestBlockingDuration(long startupTasksDurationMaxMs) {
+        mStartupTasksLongestBlockingDurationMs =
+                Math.max(mStartupTasksLongestBlockingDurationMs, startupTasksDurationMaxMs);
     }
 
     @VisibleForTesting
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/BrowserStartupController.java b/content/public/android/java/src/org/chromium/content_public/browser/BrowserStartupController.java
index 11cc6ec1..a460204 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/BrowserStartupController.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/BrowserStartupController.java
@@ -129,7 +129,8 @@
     long getContentStartDuration();
 
     /**
-     * @return how long it took to flush startup tasks.
+     * @return how long it took the longest startup task to run. If flushed, this is the total of
+     *     all the startup tasks.
      */
-    long getFlushStartupTasksDuration();
+    long getStartupTasksLongestBlockingDuration();
 }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
index ca40fef1..d990f383 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
@@ -65,7 +65,8 @@
         void flushStartupTasks() {
             assert mFullBrowserLaunchCounter > 0;
             mFlushStartupTasksCalled = true;
-            BrowserStartupControllerImpl.browserStartupComplete(mStartupResult);
+            BrowserStartupControllerImpl.browserStartupComplete(
+                    mStartupResult, /* longestBlockingDuration= */ 0);
         }
 
         private int kickOffStartup(boolean startMinimalBrowser) {
@@ -74,7 +75,8 @@
                 mMinimalBrowserStarted = true;
             }
             if (!startMinimalBrowser) {
-                BrowserStartupControllerImpl.browserStartupComplete(mStartupResult);
+                BrowserStartupControllerImpl.browserStartupComplete(
+                        mStartupResult, /* longestBlockingDuration= */ 0);
             }
             return mStartupResult;
         }
diff --git a/content/public/common/main_function_params.h b/content/public/common/main_function_params.h
index 8f0ea9b..97822bc 100644
--- a/content/public/common/main_function_params.h
+++ b/content/public/common/main_function_params.h
@@ -56,8 +56,8 @@
 #endif
 
   // Set to true if this content process's main function should enable startup
-  // tracing after initializing Mojo.
-  bool needs_startup_tracing_after_mojo_init = false;
+  // tracing after initializing the sandbox.
+  bool needs_startup_tracing_after_sandbox_init = false;
 
   // If non-null, this is the time the HangWatcher would have started if not
   // delayed until after sandbox initialization.
diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc
index 5880476..d6e057b 100644
--- a/content/public/test/browser_test_base.cc
+++ b/content/public/test/browser_test_base.cc
@@ -620,9 +620,6 @@
   std::optional<int> startup_error = delegate->BasicStartupComplete();
   ASSERT_FALSE(startup_error.has_value());
 
-  // We can only setup startup tracing after mojo is initialized above.
-  tracing::EnableStartupTracingIfNeeded();
-
   {
     ContentClient::SetBrowserClientAlwaysAllowForTesting(
         delegate->CreateContentBrowserClient());
@@ -658,9 +655,13 @@
         delegate->PostEarlyInitialization(invoked_in_browser);
     ASSERT_FALSE(post_early_initialization_exit_code.has_value());
 
+    // We can only setup startup tracing after feature list is initialized
+    // above.
+    tracing::InitTracingPostFeatureList(/*enable_consumer=*/true,
+                                        /*will_trace_thread_restart=*/false);
+
     StartBrowserThreadPool();
 
-    tracing::InitTracingPostFeatureList(/*enable_consumer=*/true);
     InitializeBrowserMemoryInstrumentationClient();
   }
 
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index 5c6e8084..de2c441 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -46,6 +46,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
+#include "services/tracing/public/cpp/trace_startup.h"
 #include "third_party/abseil-cpp/absl/strings/ascii.h"
 #include "third_party/blink/public/common/dom_storage/session_storage_namespace_id.h"
 #include "third_party/blink/public/common/input/web_gesture_event.h"
@@ -402,6 +403,9 @@
       test_io_thread_->task_runner(),
       mojo::core::ScopedIPCSupport::ShutdownPolicy::FAST);
 
+  tracing::InitTracingPostFeatureList(/*enable_consumer=*/false,
+                                      /*will_trace_thread_restart=*/false);
+
   // Subclasses can set render_thread_ with their own implementation before
   // calling RenderViewTest::SetUp().
   // The render thread needs to exist before blink::Initialize. It also mirrors
diff --git a/content/renderer/renderer_main.cc b/content/renderer/renderer_main.cc
index 9321f5d..be37933 100644
--- a/content/renderer/renderer_main.cc
+++ b/content/renderer/renderer_main.cc
@@ -262,6 +262,16 @@
             : base::ThreadType::kDisplayCritical;
     base::PlatformThread::SetCurrentThreadType(thread_type);
 
+    // Startup tracing creates a tracing thread, which is incompatible on
+    // platforms that require single-threaded sandbox initialization. In these
+    // cases, startup tracing is initialized right after sandbox initialization.
+    if (parameters.needs_startup_tracing_after_sandbox_init) {
+      tracing::InitTracingPostFeatureList(/*enable_consumer=*/false,
+                                          /*will_trace_thread_restart=*/false);
+      TRACE_EVENT_INSTANT1("startup", "RendererMain", TRACE_EVENT_SCOPE_THREAD,
+                           "needs_startup_tracing_after_sandbox_init", true);
+    }
+
     std::unique_ptr<RenderProcess> render_process = RenderProcessImpl::Create();
     // It's not a memory leak since RenderThread has the same lifetime
     // as a renderer process.
@@ -285,15 +295,6 @@
     }
 #endif
 
-    // Mojo IPC support is brought up by RenderThreadImpl, so startup tracing
-    // is enabled here if it needs to start after mojo init (normally so the
-    // mojo broker can bypass the sandbox to allocate startup tracing's SMB).
-    if (parameters.needs_startup_tracing_after_mojo_init) {
-      tracing::EnableStartupTracingIfNeeded();
-      TRACE_EVENT_INSTANT1("startup", "RendererMain", TRACE_EVENT_SCOPE_THREAD,
-                           "needs_startup_tracing_after_mojo_init", true);
-    }
-
 #if BUILDFLAG(IS_WIN)
     // Now that Mojo is initialized, but before the sandbox is enabled, set up
     // DirectReceiver.
diff --git a/content/utility/utility_main.cc b/content/utility/utility_main.cc
index 1a5d697c2..c05706f5 100644
--- a/content/utility/utility_main.cc
+++ b/content/utility/utility_main.cc
@@ -382,6 +382,14 @@
         sandbox_type, std::move(pre_sandbox_hook), sandbox_options);
   }
 
+  // Startup tracing creates a tracing thread, which is incompatible on
+  // platforms that require single-threaded sandbox initialization. In these
+  // cases, startup tracing is initialized right after sandbox initialization.
+  if (parameters.needs_startup_tracing_after_sandbox_init) {
+    tracing::InitTracingPostFeatureList(/*enable_consumer=*/false,
+                                        /*will_trace_thread_restart=*/false);
+  }
+
   // Start the HangWatcher now that the sandbox is engaged, if it hasn't
   // already been started.
   if (base::HangWatcher::IsEnabled() &&
@@ -421,13 +429,6 @@
   utility_process.set_main_thread(
       new UtilityThreadImpl(run_loop.QuitClosure()));
 
-  // Mojo IPC support is brought up by UtilityThreadImpl, so startup tracing
-  // is enabled here if it needs to start after mojo init (normally so the mojo
-  // broker can bypass the sandbox to allocate startup tracing's SMB).
-  if (parameters.needs_startup_tracing_after_mojo_init) {
-    tracing::EnableStartupTracingIfNeeded();
-  }
-
   // Both utility process and service utility process would come
   // here, but the later is launched without connection to service manager, so
   // there has no base::PowerMonitor be created(See ChildThreadImpl::Init()).
diff --git a/gpu/command_buffer/client/client_shared_image.h b/gpu/command_buffer/client/client_shared_image.h
index 50e0928..5807c3d 100644
--- a/gpu/command_buffer/client/client_shared_image.h
+++ b/gpu/command_buffer/client/client_shared_image.h
@@ -215,6 +215,14 @@
       const SharedImageMetadata& metadata,
       uint32_t texture_target);
 
+  // Used to defer execution of `MapAsync()` result callback. The callbacks
+  // will be passed to the configured instance of this class, which can execute
+  // them as the test requires.
+  class MapCallbackControllerForTesting {
+   public:
+    virtual void RegisterCallback(base::OnceCallback<void(bool)> result_cb) = 0;
+  };
+
   static scoped_refptr<ClientSharedImage> CreateForTesting(
       const Mailbox& mailbox,
       const SharedImageMetadata& metadata,
diff --git a/gpu/command_buffer/client/fake_gpu_memory_buffer.cc b/gpu/command_buffer/client/fake_gpu_memory_buffer.cc
index 6eb522a..0f6386ba 100644
--- a/gpu/command_buffer/client/fake_gpu_memory_buffer.cc
+++ b/gpu/command_buffer/client/fake_gpu_memory_buffer.cc
@@ -67,10 +67,11 @@
 }
 #endif
 
-FakeGpuMemoryBuffer::FakeGpuMemoryBuffer(const gfx::Size& size,
-                                         gfx::BufferFormat format,
-                                         bool premapped,
-                                         MapCallbackController* controller)
+FakeGpuMemoryBuffer::FakeGpuMemoryBuffer(
+    const gfx::Size& size,
+    gfx::BufferFormat format,
+    bool premapped,
+    ClientSharedImage::MapCallbackControllerForTesting* controller)
     : size_(size),
       format_(format),
       premapped_(premapped),
diff --git a/gpu/command_buffer/client/fake_gpu_memory_buffer.h b/gpu/command_buffer/client/fake_gpu_memory_buffer.h
index db983f2..0e03b30 100644
--- a/gpu/command_buffer/client/fake_gpu_memory_buffer.h
+++ b/gpu/command_buffer/client/fake_gpu_memory_buffer.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <vector>
 
+#include "gpu/command_buffer/client/client_shared_image.h"
 #include "media/base/video_types.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 
@@ -31,20 +32,13 @@
 // A fake implementation of gfx::GpuMemoryBuffer for testing purposes.
 class FakeGpuMemoryBuffer : public gfx::GpuMemoryBuffer {
  public:
-  // Used to defer execution of `MapAsync()` result callback.
-  // FakeGpuMemoryBuffer will pass the  callbacks to the configured instance
-  // of this class, which can execute them as the test requires.
-  class MapCallbackController {
-   public:
-    virtual void RegisterCallback(base::OnceCallback<void(bool)> result_cb) = 0;
-  };
-
   FakeGpuMemoryBuffer() = delete;
 
-  FakeGpuMemoryBuffer(const gfx::Size& size,
-                      gfx::BufferFormat format,
-                      bool premapped,
-                      MapCallbackController* controller);
+  FakeGpuMemoryBuffer(
+      const gfx::Size& size,
+      gfx::BufferFormat format,
+      bool premapped,
+      ClientSharedImage::MapCallbackControllerForTesting* controller);
 
   FakeGpuMemoryBuffer(const FakeGpuMemoryBuffer&) = delete;
   FakeGpuMemoryBuffer& operator=(const FakeGpuMemoryBuffer&) = delete;
@@ -69,7 +63,8 @@
   std::vector<uint8_t> data_;
   gfx::GpuMemoryBufferHandle handle_;
   bool premapped_ = true;
-  raw_ptr<MapCallbackController> map_callback_controller_ = nullptr;
+  raw_ptr<ClientSharedImage::MapCallbackControllerForTesting>
+      map_callback_controller_ = nullptr;
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/client/test_shared_image_interface.cc b/gpu/command_buffer/client/test_shared_image_interface.cc
index 39b8de1c..007a298a 100644
--- a/gpu/command_buffer/client/test_shared_image_interface.cc
+++ b/gpu/command_buffer/client/test_shared_image_interface.cc
@@ -438,7 +438,7 @@
     const SharedImageInfo& si_info,
     gfx::BufferUsage buffer_usage,
     bool premapped,
-    FakeGpuMemoryBuffer::MapCallbackController* controller) {
+    ClientSharedImage::MapCallbackControllerForTesting* controller) {
   CHECK(controller);
 
   // Create a FakeGpuMemoryBuffer.
diff --git a/gpu/command_buffer/client/test_shared_image_interface.h b/gpu/command_buffer/client/test_shared_image_interface.h
index bc3d761..d2214e6 100644
--- a/gpu/command_buffer/client/test_shared_image_interface.h
+++ b/gpu/command_buffer/client/test_shared_image_interface.h
@@ -9,6 +9,7 @@
 
 #include "base/synchronization/lock.h"
 #include "build/build_config.h"
+#include "gpu/command_buffer/client/client_shared_image.h"
 #include "gpu/command_buffer/client/fake_gpu_memory_buffer.h"
 #include "gpu/command_buffer/client/shared_image_interface.h"
 #include "gpu/command_buffer/client/test_gpu_memory_buffer_manager.h"
@@ -117,16 +118,16 @@
   // This is used only on windows for webrtc tests where test wants the
   // production code to trigger ClientSharedImage::MapAsync() but wants
   // to control when the callback runs from inside the test. This is achieved by
-  // using a custom MapCallbackController. The callback execution is deferred
-  // by registering the callback with the provided |controller|. The test
-  // manually triggers the mapping completion by invoking the |controller|
-  // later, simulating a delayed (asynchronous) mapping. This is required to
-  // test delayed mapping behavior.
+  // using a custom ClientSharedImage::MapCallbackControllerForTesting. The
+  // callback execution is deferred by registering the callback with the
+  // provided |controller|. The test manually triggers the mapping completion by
+  // invoking the |controller| later, simulating a delayed (asynchronous)
+  // mapping. This is required to test delayed mapping behavior.
   scoped_refptr<ClientSharedImage> CreateSharedImageWithMapCallbackController(
       const SharedImageInfo& si_info,
       gfx::BufferUsage buffer_usage,
       bool premapped,
-      FakeGpuMemoryBuffer::MapCallbackController* controller);
+      ClientSharedImage::MapCallbackControllerForTesting* controller);
 
   void CreateSharedImagePool(
       const SharedImagePoolId& pool_id,
diff --git a/ios/chrome/browser/browser_container/ui_bundled/browser_container_coordinator.mm b/ios/chrome/browser/browser_container/ui_bundled/browser_container_coordinator.mm
index c1c99f5..40e1341a 100644
--- a/ios/chrome/browser/browser_container/ui_bundled/browser_container_coordinator.mm
+++ b/ios/chrome/browser/browser_container/ui_bundled/browser_container_coordinator.mm
@@ -113,11 +113,10 @@
       FullscreenController::FromBrowser(self.browser);
 
   _partialTranslateMediator = [[PartialTranslateMediator alloc]
-        initWithWebStateList:webStateList
-      withBaseViewController:self.viewController
-                 prefService:prefService
-        fullscreenController:fullscreenController
-                   incognito:incognito];
+      initWithBaseViewController:self.viewController
+                     prefService:prefService
+            fullscreenController:fullscreenController
+                       incognito:incognito];
   _partialTranslateMediator.alertDelegate = self;
   CommandDispatcher* dispatcher = browser->GetCommandDispatcher();
   id<BrowserCoordinatorCommands> browserCommandsHandler =
diff --git a/ios/chrome/browser/browser_container/ui_bundled/browser_edit_menu_handler_unittest.mm b/ios/chrome/browser/browser_container/ui_bundled/browser_edit_menu_handler_unittest.mm
index cfbc281..6ce4f20 100644
--- a/ios/chrome/browser/browser_container/ui_bundled/browser_edit_menu_handler_unittest.mm
+++ b/ios/chrome/browser/browser_container/ui_bundled/browser_edit_menu_handler_unittest.mm
@@ -449,11 +449,10 @@
   SetupTranslateControllerFactory();
   PartialTranslateMediator* partial_translate_mediator =
       [[PartialTranslateMediator alloc]
-            initWithWebStateList:&web_state_list_
-          withBaseViewController:base_view_controller_
-                     prefService:profile_->GetPrefs()
-            fullscreenController:nullptr
-                       incognito:NO];
+          initWithBaseViewController:base_view_controller_
+                         prefService:profile_->GetPrefs()
+                fullscreenController:nullptr
+                           incognito:NO];
 
   LinkToTextMediator* link_to_text_mediator =
       [[LinkToTextMediator alloc] initWithWebStateList:&web_state_list_];
diff --git a/ios/chrome/browser/https_upgrades/model/https_only_mode_upgrade_tab_helper.h b/ios/chrome/browser/https_upgrades/model/https_only_mode_upgrade_tab_helper.h
index 54505f7..c9d8620 100644
--- a/ios/chrome/browser/https_upgrades/model/https_only_mode_upgrade_tab_helper.h
+++ b/ios/chrome/browser/https_upgrades/model/https_only_mode_upgrade_tab_helper.h
@@ -73,7 +73,7 @@
   // allowlisted).
   bool IsHttpAllowedForUrl(const GURL& url) const;
   // Called when the upgrade timer times out.
-  void OnHttpsLoadTimeout(base::WeakPtr<web::WebState> weak_web_state);
+  void OnHttpsLoadTimeout();
   // Stops the current navigation and sets the state so that an upgrade will be
   // started.
   void StopToUpgrade(
@@ -121,6 +121,7 @@
 
   base::OneShotTimer timer_;
 
+  raw_ptr<web::WebState> web_state_;
   raw_ptr<PrefService> prefs_;
   raw_ptr<PrerenderService> prerender_service_;
   raw_ptr<HttpsUpgradeService> service_;
diff --git a/ios/chrome/browser/https_upgrades/model/https_only_mode_upgrade_tab_helper.mm b/ios/chrome/browser/https_upgrades/model/https_only_mode_upgrade_tab_helper.mm
index bf92214..d6a4490 100644
--- a/ios/chrome/browser/https_upgrades/model/https_only_mode_upgrade_tab_helper.mm
+++ b/ios/chrome/browser/https_upgrades/model/https_only_mode_upgrade_tab_helper.mm
@@ -45,6 +45,7 @@
 
 void HttpsOnlyModeUpgradeTabHelper::WebStateDestroyed() {}
 
+// Public methods
 bool HttpsOnlyModeUpgradeTabHelper::IsTimerRunningForTesting() const {
   return timer_.IsRunning();
 }
@@ -53,97 +54,28 @@
   service_->ClearAllowlist(base::Time(), base::Time::Max());
 }
 
-bool HttpsOnlyModeUpgradeTabHelper::IsHttpAllowedForUrl(const GURL& url) const {
-  return service_->IsHttpAllowedForHost(url.host());
-}
-
+// Private methods
 HttpsOnlyModeUpgradeTabHelper::HttpsOnlyModeUpgradeTabHelper(
     web::WebState* web_state,
     PrefService* prefs,
     PrerenderService* prerender_service,
     HttpsUpgradeService* service)
     : web::WebStatePolicyDecider(web_state),
+      web_state_(web_state),
       prefs_(prefs),
       prerender_service_(prerender_service),
       service_(service) {
   web_state->AddObserver(this);
 }
 
-void HttpsOnlyModeUpgradeTabHelper::DidStartNavigation(
-    web::WebState* web_state,
-    web::NavigationContext* navigation_context) {
-  if (navigation_context->IsSameDocument()) {
-    return;
-  }
-  if (state_ == State::kUpgraded) {
-    DCHECK(!timer_.IsRunning());
-    // `timer_` is deleted when the tab helper is deleted, so it's safe to use
-    // Unretained here.
-    timer_.Start(
-        FROM_HERE, service_->GetFallbackDelay(),
-        base::BindOnce(&HttpsOnlyModeUpgradeTabHelper::OnHttpsLoadTimeout,
-                       base::Unretained(this), web_state->GetWeakPtr()));
-    return;
-  }
-  if (state_ == State::kNone) {
-    // Store navigation parameters on initial navigation.
-    navigation_transition_type_ = navigation_context->GetPageTransition();
-    navigation_is_renderer_initiated_ =
-        navigation_context->IsRendererInitiated();
-    navigation_is_post_ = navigation_context->IsPost();
-  }
+bool HttpsOnlyModeUpgradeTabHelper::IsHttpAllowedForUrl(const GURL& url) const {
+  return service_->IsHttpAllowedForHost(url.host());
 }
 
-void HttpsOnlyModeUpgradeTabHelper::DidFinishNavigation(
-    web::WebState* web_state,
-    web::NavigationContext* navigation_context) {
-  if (navigation_context->IsSameDocument()) {
-    return;
-  }
-  navigation_is_post_ = false;
-  if (state_ == State::kNone) {
-    return;
-  }
-
-  if (state_ == State::kStoppedToUpgrade) {
-    state_ = State::kUpgraded;
-    // Start an upgraded navigation.
-    RecordUMA(Event::kUpgradeAttempted);
-    web::NavigationManager::WebLoadParams params(upgraded_https_url_);
-    params.transition_type = navigation_transition_type_;
-    params.is_renderer_initiated = navigation_is_renderer_initiated_;
-    params.referrer = referrer_;
-    params.https_upgrade_type = web::HttpsUpgradeType::kHttpsOnlyMode;
-    web_state->GetNavigationManager()->LoadURLWithParams(params);
-    return;
-  }
-
-  if (state_ == State::kStoppedWithTimeout ||
-      state_ == State::kStoppedToFallback) {
-    DCHECK(!timer_.IsRunning());
-    RecordUMA(state_ == State::kStoppedWithTimeout ? Event::kUpgradeTimedOut
-                                                   : Event::kUpgradeFailed);
-    FallbackToHttp();
-    return;
-  }
-
-  DCHECK(state_ == State::kUpgraded || state_ == State::kDone);
-  // The upgrade either failed or succeeded. In both cases, stop the timer.
-  timer_.Stop();
-
-  if (navigation_context->GetFailedHttpsUpgradeType() ==
-      web::HttpsUpgradeType::kHttpsOnlyMode) {
-    RecordUMA(Event::kUpgradeFailed);
-    FallbackToHttp();
-    return;
-  }
-
-  if (state_ == State::kDone &&
-      (navigation_context->GetUrl().SchemeIs(url::kHttpsScheme) ||
-       service_->IsFakeHTTPSForTesting(navigation_context->GetUrl()))) {
-    RecordUMA(Event::kUpgradeSucceeded);
-  }
-  state_ = State::kNone;
+void HttpsOnlyModeUpgradeTabHelper::OnHttpsLoadTimeout() {
+  DCHECK(state_ == State::kUpgraded);
+  state_ = State::kStoppedWithTimeout;
+  web_state_->Stop();
 }
 
 void HttpsOnlyModeUpgradeTabHelper::StopToUpgrade(
@@ -193,15 +125,7 @@
   timer_.Stop();
 }
 
-void HttpsOnlyModeUpgradeTabHelper::OnHttpsLoadTimeout(
-    base::WeakPtr<web::WebState> weak_web_state) {
-  DCHECK(state_ == State::kUpgraded);
-  state_ = State::kStoppedWithTimeout;
-  if (weak_web_state) {
-    weak_web_state->Stop();
-  }
-}
-
+// web::WebStatePolicyDecider
 void HttpsOnlyModeUpgradeTabHelper::ShouldAllowResponse(
     NSURLResponse* response,
     WebStatePolicyDecider::ResponseInfo response_info,
@@ -344,3 +268,82 @@
   // Otherwise, this is a failed HTTPS-Upgrade. Allow the response.
   std::move(callback).Run(web::WebStatePolicyDecider::PolicyDecision::Allow());
 }
+
+// web::WebStateObserver
+void HttpsOnlyModeUpgradeTabHelper::DidStartNavigation(
+    web::WebState* web_state,
+    web::NavigationContext* navigation_context) {
+  CHECK(web_state == web_state_.get());
+  if (navigation_context->IsSameDocument()) {
+    return;
+  }
+  if (state_ == State::kUpgraded) {
+    DCHECK(!timer_.IsRunning());
+    // `timer_` is deleted when the tab helper is deleted, so it's safe to use
+    // Unretained here.
+    timer_.Start(
+        FROM_HERE, service_->GetFallbackDelay(),
+        base::BindOnce(&HttpsOnlyModeUpgradeTabHelper::OnHttpsLoadTimeout,
+                       base::Unretained(this)));
+    return;
+  }
+  if (state_ == State::kNone) {
+    // Store navigation parameters on initial navigation.
+    navigation_transition_type_ = navigation_context->GetPageTransition();
+    navigation_is_renderer_initiated_ =
+        navigation_context->IsRendererInitiated();
+    navigation_is_post_ = navigation_context->IsPost();
+  }
+}
+
+void HttpsOnlyModeUpgradeTabHelper::DidFinishNavigation(
+    web::WebState* web_state,
+    web::NavigationContext* navigation_context) {
+  if (navigation_context->IsSameDocument()) {
+    return;
+  }
+  navigation_is_post_ = false;
+  if (state_ == State::kNone) {
+    return;
+  }
+
+  if (state_ == State::kStoppedToUpgrade) {
+    state_ = State::kUpgraded;
+    // Start an upgraded navigation.
+    RecordUMA(Event::kUpgradeAttempted);
+    web::NavigationManager::WebLoadParams params(upgraded_https_url_);
+    params.transition_type = navigation_transition_type_;
+    params.is_renderer_initiated = navigation_is_renderer_initiated_;
+    params.referrer = referrer_;
+    params.https_upgrade_type = web::HttpsUpgradeType::kHttpsOnlyMode;
+    web_state->GetNavigationManager()->LoadURLWithParams(params);
+    return;
+  }
+
+  if (state_ == State::kStoppedWithTimeout ||
+      state_ == State::kStoppedToFallback) {
+    DCHECK(!timer_.IsRunning());
+    RecordUMA(state_ == State::kStoppedWithTimeout ? Event::kUpgradeTimedOut
+                                                   : Event::kUpgradeFailed);
+    FallbackToHttp();
+    return;
+  }
+
+  DCHECK(state_ == State::kUpgraded || state_ == State::kDone);
+  // The upgrade either failed or succeeded. In both cases, stop the timer.
+  timer_.Stop();
+
+  if (navigation_context->GetFailedHttpsUpgradeType() ==
+      web::HttpsUpgradeType::kHttpsOnlyMode) {
+    RecordUMA(Event::kUpgradeFailed);
+    FallbackToHttp();
+    return;
+  }
+
+  if (state_ == State::kDone &&
+      (navigation_context->GetUrl().SchemeIs(url::kHttpsScheme) ||
+       service_->IsFakeHTTPSForTesting(navigation_context->GetUrl()))) {
+    RecordUMA(Event::kUpgradeSucceeded);
+  }
+  state_ = State::kNone;
+}
diff --git a/ios/chrome/browser/intelligence/bwg/model/BUILD.gn b/ios/chrome/browser/intelligence/bwg/model/BUILD.gn
index c4c3c6f..6d3e809 100644
--- a/ios/chrome/browser/intelligence/bwg/model/BUILD.gn
+++ b/ios/chrome/browser/intelligence/bwg/model/BUILD.gn
@@ -32,15 +32,18 @@
     ":model",
     ":tab_helper",
     "//base",
+    "//components/favicon/ios",
     "//components/optimization_guide/proto:optimization_guide_proto",
     "//ios/chrome/browser/intelligence/proto_wrappers",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/web_state_list",
+    "//ios/chrome/browser/shared/ui/symbols",
     "//ios/chrome/browser/signin/model:authentication_service_factory",
     "//ios/chrome/browser/url_loading/model",
     "//ios/public/provider/chrome/browser/bwg:bwg_api",
+    "//ui/gfx",
   ]
 }
 
diff --git a/ios/chrome/browser/intelligence/bwg/model/bwg_browser_agent.mm b/ios/chrome/browser/intelligence/bwg/model/bwg_browser_agent.mm
index fb8811c..aea1e01 100644
--- a/ios/chrome/browser/intelligence/bwg/model/bwg_browser_agent.mm
+++ b/ios/chrome/browser/intelligence/bwg/model/bwg_browser_agent.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/intelligence/bwg/model/bwg_browser_agent.h"
 
 #import "base/strings/sys_string_conversions.h"
+#import "components/favicon/ios/web_favicon_driver.h"
 #import "components/optimization_guide/proto/features/common_quality_data.pb.h"
 #import "ios/chrome/browser/intelligence/bwg/model/bwg_configuration.h"
 #import "ios/chrome/browser/intelligence/bwg/model/bwg_link_opening_delegate.h"
@@ -17,10 +18,12 @@
 #import "ios/chrome/browser/shared/model/browser/browser.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
+#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/browser/signin/model/authentication_service_factory.h"
 #import "ios/chrome/browser/url_loading/model/url_loading_browser_agent.h"
 #import "ios/public/provider/chrome/browser/bwg/bwg_api.h"
 #import "ios/public/provider/chrome/browser/bwg/bwg_gateway_protocol.h"
+#import "ui/gfx/image/image.h"
 
 namespace {
 
@@ -57,6 +60,8 @@
     UIViewController* base_view_controller,
     base::expected<std::unique_ptr<optimization_guide::proto::PageContext>,
                    PageContextWrapperError> expected_page_context) {
+  web::WebState* web_state = browser_->GetWebStateList()->GetActiveWebState();
+
   BWGConfiguration* config = [[BWGConfiguration alloc] init];
   config.baseViewController = base_view_controller;
   config.authService =
@@ -65,8 +70,9 @@
       GetApplicationContext()->GetSingleSignOnService();
   config.gateway = bwg_gateway_;
 
-  BwgTabHelper* bwg_tab_helper = BwgTabHelper::FromWebState(
-      browser_->GetWebStateList()->GetActiveWebState());
+  // Use the tab helper to set the initial floaty state, which includes the chat
+  // IDs and whether it was backgrounded.
+  BwgTabHelper* bwg_tab_helper = BwgTabHelper::FromWebState(web_state);
   config.clientID = base::SysUTF8ToNSString(bwg_tab_helper->GetClientId());
   std::optional<std::string> maybe_server_id = bwg_tab_helper->GetServerId();
   config.serverID =
@@ -75,6 +81,8 @@
       !bwg_tab_helper->GetIsBwgSessionActiveInBackground();
   config.shouldShowZeroState = bwg_tab_helper->ShouldShowZeroState();
 
+  // Set the page context and page state for the current web state. If the page
+  // context is unavailable, the page state represents the error.
   std::unique_ptr<optimization_guide::proto::PageContext> pageContext = nullptr;
   if (expected_page_context.has_value()) {
     pageContext = std::move(expected_page_context.value());
@@ -86,6 +94,24 @@
   }
   config.uniquePageContext = std::move(pageContext);
 
+  // Use the cached favicon of the web state. If it's not available, use a
+  // default favicon instead.
+  gfx::Image cached_favicon =
+      favicon::WebFaviconDriver::FromWebState(web_state)->GetFavicon();
+  UIImage* page_favicon;
+  if (!cached_favicon.IsEmpty()) {
+    page_favicon = cached_favicon.ToUIImage();
+  } else {
+    UIImageConfiguration* configuration = [UIImageSymbolConfiguration
+        configurationWithPointSize:gfx::kFaviconSize
+                            weight:UIImageSymbolWeightBold
+                             scale:UIImageSymbolScaleMedium];
+    page_favicon =
+        DefaultSymbolWithConfiguration(kGlobeAmericasSymbol, configuration);
+  }
+  config.favicon = page_favicon;
+
+  // Start the overlay and update the tab helper to reflect this.
   ios::provider::StartBwgOverlay(config);
   bwg_tab_helper->SetBwgUiShowing(true);
 }
diff --git a/ios/chrome/browser/intelligence/bwg/model/bwg_configuration.h b/ios/chrome/browser/intelligence/bwg/model/bwg_configuration.h
index 68b2a4e..373a929 100644
--- a/ios/chrome/browser/intelligence/bwg/model/bwg_configuration.h
+++ b/ios/chrome/browser/intelligence/bwg/model/bwg_configuration.h
@@ -36,16 +36,19 @@
     std::unique_ptr<optimization_guide::proto::PageContext>
         uniquePageContext;
 
+// The state of the BWG PageContext.
+@property(nonatomic, assign)
+    ios::provider::BWGPageContextState BWGPageContextState;
+
+// The favicon of the attached page. Uses a default icon if it's unavailable.
+@property(nonatomic, strong) UIImage* favicon;
+
 // The authentication service to be used.
 @property(nonatomic, assign) AuthenticationService* authService;
 
 // The SingleSignOnService instance.
 @property(nonatomic, strong) id<SingleSignOnService> singleSignOnService;
 
-// The state of the BWG PageContext.
-@property(nonatomic, assign)
-    ios::provider::BWGPageContextState BWGPageContextState;
-
 // The BWG gateway for bridging internal protocols.
 @property(nonatomic, weak) id<BWGGatewayProtocol> gateway;
 
diff --git a/ios/chrome/browser/intelligence/bwg/ui/bwg_consent_view_controller.mm b/ios/chrome/browser/intelligence/bwg/ui/bwg_consent_view_controller.mm
index 8668cd6a..e310091f 100644
--- a/ios/chrome/browser/intelligence/bwg/ui/bwg_consent_view_controller.mm
+++ b/ios/chrome/browser/intelligence/bwg/ui/bwg_consent_view_controller.mm
@@ -54,8 +54,18 @@
 @end
 
 @implementation BWGConsentViewController {
-  // Main stack view containing all the others views.
+  // The root vertical stack view that arranges the UI sections of the
+  // screen. It holds the `_contentScrollView` and the
+  // fixed action buttons at the bottom. This view itself does not scroll.
   UIStackView* _mainStackView;
+  // A scroll view that contains the `_contentStackView`. This allows the main
+  // content (info boxes, footnote) to scroll vertically if it
+  // doesn't fit on the screen.
+  UIScrollView* _contentScrollView;
+  // The vertical stack view placed inside the `_contentScrollView`. It arranges
+  // the actual informational UI elements, such as the info boxes and the
+  // footnote, which are intended to be scrolled together.
+  UIStackView* _contentStackView;
   // Whether the account is managed.
   BOOL _isAccountManaged;
 }
@@ -75,7 +85,7 @@
   [super viewDidLoad];
   self.view.backgroundColor = [UIColor colorNamed:kPrimaryBackgroundColor];
   self.navigationItem.hidesBackButton = YES;
-  [self configureMainStackView];
+  [self setupStackViews];
 }
 
 #pragma mark - Public
@@ -83,11 +93,47 @@
 - (CGFloat)contentHeight {
   return
       [_mainStackView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]
+          .height +
+      [_contentStackView
+          systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]
           .height;
 }
 
 #pragma mark - Private
 
+// Configures all the stacks.
+- (void)setupStackViews {
+  [self configureMainStackView];
+  [self configureContentViews];
+  [_mainStackView addArrangedSubview:_contentScrollView];
+  [self configureButtons];
+}
+
+// Configures the scrollable content area, including the scroll view and its
+// content stack view.
+- (void)configureContentViews {
+  _contentScrollView = [[UIScrollView alloc] init];
+  _contentScrollView.translatesAutoresizingMaskIntoConstraints = NO;
+  _contentScrollView.showsVerticalScrollIndicator = NO;
+
+  _contentStackView = [[UIStackView alloc] init];
+  _contentStackView.axis = UILayoutConstraintAxisVertical;
+  _contentStackView.spacing = kMainStackSpacing;
+  _contentStackView.translatesAutoresizingMaskIntoConstraints = NO;
+
+  [_contentScrollView addSubview:_contentStackView];
+
+  AddSameConstraints(_contentStackView, _contentScrollView);
+
+  [NSLayoutConstraint activateConstraints:@[
+    [_contentStackView.widthAnchor
+        constraintEqualToAnchor:_contentScrollView.widthAnchor]
+  ]];
+
+  [_contentStackView addArrangedSubview:[self createBoxesStackView]];
+  [_contentStackView addArrangedSubview:[self createFootnoteView]];
+}
+
 // TODO(crbug.com/423816346): Manage links for attributes strings.
 // Creates an attributed string for the footnote with hyperlinks.
 - (NSAttributedString*)createFootnoteAttributedText {
@@ -149,8 +195,10 @@
       _mainStackView, self.view.safeAreaLayoutGuide,
       NSDirectionalEdgeInsetsMake(0, kMainStackHorizontalInset, 0,
                                   kMainStackHorizontalInset));
-  [_mainStackView addArrangedSubview:[self createBoxesStackView]];
-  [_mainStackView addArrangedSubview:[self createFootnoteView]];
+}
+
+// Configures primary and secondary buttons.
+- (void)configureButtons {
   UIView* primaryButtonView = [self createPrimaryButton];
   [_mainStackView addArrangedSubview:primaryButtonView];
   [_mainStackView setCustomSpacing:0.0 afterView:primaryButtonView];
@@ -161,7 +209,6 @@
 - (UIStackView*)createBoxesStackView {
   UIStackView* boxesStackView = [[UIStackView alloc] init];
   boxesStackView.axis = UILayoutConstraintAxisVertical;
-  boxesStackView.distribution = UIStackViewDistributionFillProportionally;
   boxesStackView.spacing = kBoxesStackViewSpacing;
   boxesStackView.layer.cornerRadius = kBoxesStackViewCornerRadius;
   boxesStackView.clipsToBounds = YES;
@@ -218,7 +265,6 @@
 - (UIView*)createHorizontalBoxWithIcon:(UIImageView*)iconImageView
                                boxView:(UIView*)boxView {
   UIStackView* horizontalStackView = [[UIStackView alloc] init];
-  horizontalStackView.distribution = UIStackViewDistributionFillProportionally;
   horizontalStackView.alignment = UIStackViewAlignmentTop;
   horizontalStackView.translatesAutoresizingMaskIntoConstraints = NO;
   horizontalStackView.backgroundColor =
@@ -274,14 +320,12 @@
   titleLabel.font =
       PreferredFontForTextStyle(UIFontTextStyleHeadline, UIFontWeightSemibold);
 
-  titleLabel.adjustsFontForContentSizeCategory = YES;
   titleLabel.numberOfLines = 0;
   [innerStackView addArrangedSubview:titleLabel];
 
   UILabel* bodyLabel = [[UILabel alloc] init];
   bodyLabel.text = bodyText;
   bodyLabel.font = PreferredFontForTextStyle(UIFontTextStyleBody);
-  bodyLabel.adjustsFontForContentSizeCategory = YES;
   bodyLabel.numberOfLines = 0;
   bodyLabel.textColor = [UIColor colorNamed:kGrey700Color];
   [innerStackView addArrangedSubview:bodyLabel];
diff --git a/ios/chrome/browser/intelligence/bwg/ui/bwg_promo_view_controller.mm b/ios/chrome/browser/intelligence/bwg/ui/bwg_promo_view_controller.mm
index c08ca6f..018d495a 100644
--- a/ios/chrome/browser/intelligence/bwg/ui/bwg_promo_view_controller.mm
+++ b/ios/chrome/browser/intelligence/bwg/ui/bwg_promo_view_controller.mm
@@ -145,6 +145,7 @@
 - (void)configureContentStackView {
   _contentScrollView = [[UIScrollView alloc] init];
   _contentScrollView.translatesAutoresizingMaskIntoConstraints = NO;
+  _contentScrollView.showsVerticalScrollIndicator = NO;
 
   _contentStackView = [[UIStackView alloc] init];
   _contentStackView.axis = UILayoutConstraintAxisVertical;
diff --git a/ios/chrome/browser/overlays/ui_bundled/infobar_modal/infobar_modal_overlay_coordinator.mm b/ios/chrome/browser/overlays/ui_bundled/infobar_modal/infobar_modal_overlay_coordinator.mm
index ce12f1c1..1fa0b7c 100644
--- a/ios/chrome/browser/overlays/ui_bundled/infobar_modal/infobar_modal_overlay_coordinator.mm
+++ b/ios/chrome/browser/overlays/ui_bundled/infobar_modal/infobar_modal_overlay_coordinator.mm
@@ -135,12 +135,15 @@
       initWithRootViewController:self.modalViewController];
   self.modalNavController.modalPresentationStyle = UIModalPresentationCustom;
   self.modalNavController.transitioningDelegate = self.modalTransitionDriver;
-  UINavigationBarAppearance* opaqueAppearance =
-      [[UINavigationBarAppearance alloc] init];
-  [opaqueAppearance configureWithOpaqueBackground];
-  self.modalNavController.navigationBar.standardAppearance = opaqueAppearance;
-  self.modalNavController.navigationBar.compactAppearance = opaqueAppearance;
-  self.modalNavController.navigationBar.scrollEdgeAppearance = opaqueAppearance;
+  if (!@available(iOS 26, *)) {
+    UINavigationBarAppearance* opaqueAppearance =
+        [[UINavigationBarAppearance alloc] init];
+    [opaqueAppearance configureWithOpaqueBackground];
+    self.modalNavController.navigationBar.standardAppearance = opaqueAppearance;
+    self.modalNavController.navigationBar.compactAppearance = opaqueAppearance;
+    self.modalNavController.navigationBar.scrollEdgeAppearance =
+        opaqueAppearance;
+  }
 }
 
 - (void)resetModal {
diff --git a/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_app_interface.mm b/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_app_interface.mm
index 64a5067..68fb3ec 100644
--- a/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_app_interface.mm
+++ b/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_app_interface.mm
@@ -25,17 +25,15 @@
   UIViewController* viewController =
       chrome_test_util::GetActiveViewController();
   Browser* browser = chrome_test_util::GetMainBrowser();
-  WebStateList* webStateList = browser ? browser->GetWebStateList() : nullptr;
   FullscreenController* fullscreenController =
       FullscreenController::FromBrowser(browser);
 
   PartialTranslateMediator* partialTranslateMediator =
       [[PartialTranslateMediator alloc]
-            initWithWebStateList:webStateList
-          withBaseViewController:viewController
-                     prefService:prefService
-            fullscreenController:fullscreenController
-                       incognito:incognito];
+          initWithBaseViewController:viewController
+                         prefService:prefService
+                fullscreenController:fullscreenController
+                           incognito:incognito];
   BOOL shouldInstallPartialTranslate =
       [partialTranslateMediator shouldInstallPartialTranslate];
   [partialTranslateMediator shutdown];
diff --git a/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_mediator.h b/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_mediator.h
index 0dc41ffe..82e56126 100644
--- a/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_mediator.h
+++ b/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_mediator.h
@@ -7,26 +7,25 @@
 
 #import <UIKit/UIKit.h>
 
+#import "base/memory/weak_ptr.h"
 #import "ios/chrome/browser/browser_container/model/edit_menu_builder.h"
 
 @protocol BrowserCoordinatorCommands;
 @protocol EditMenuAlertDelegate;
 class FullscreenController;
 class PrefService;
-class WebStateList;
 
 // Mediator that mediates between the browser container views and the
 // partial translate tab helpers.
 @interface PartialTranslateMediator : NSObject <EditMenuBuilder>
 
-// Initializer for a mediator. `webStateList` is the WebStateList for the
-// Browser whose content is shown within the BrowserContainerConsumer. It must
-// be non-null.
-- (instancetype)initWithWebStateList:(WebStateList*)webStateList
-              withBaseViewController:(UIViewController*)baseViewController
-                         prefService:(PrefService*)prefs
-                fullscreenController:(FullscreenController*)fullscreenController
-                           incognito:(BOOL)incognito NS_DESIGNATED_INITIALIZER;
+// Initializer for a mediator.
+- (instancetype)initWithBaseViewController:(UIViewController*)baseViewController
+                               prefService:(PrefService*)prefs
+                      fullscreenController:
+                          (FullscreenController*)fullscreenController
+                                 incognito:(BOOL)incognito
+    NS_DESIGNATED_INITIALIZER;
 - (instancetype)init NS_UNAVAILABLE;
 
 // Disconnects the mediator.
@@ -38,11 +37,13 @@
 // The delegate to present error message alerts.
 @property(nonatomic, weak) id<EditMenuAlertDelegate> alertDelegate;
 
-// Handles the link to text menu item selection.
-- (void)handlePartialTranslateSelection;
+// Handles the partial translate menu item selection.
+// Used for testing to bypass the UIDeferredMenuElement logic.
+- (void)handlePartialTranslateSelectionForTestingInWebState:
+    (web::WebState*)webState;
 
 // Returns whether a partial translate can be handled.
-- (BOOL)canHandlePartialTranslateSelection;
+- (BOOL)canHandlePartialTranslateSelectionInWebState:(web::WebState*)webState;
 
 // Whether partial translate action should be proposed (independently of the
 // current selection).
diff --git a/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_mediator.mm b/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_mediator.mm
index 6ab69a4..abfa98c58 100644
--- a/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_mediator.mm
+++ b/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_mediator.mm
@@ -110,15 +110,13 @@
   raw_ptr<FullscreenController> _fullscreenController;
 }
 
-- (instancetype)initWithWebStateList:(WebStateList*)webStateList
-              withBaseViewController:(UIViewController*)baseViewController
-                         prefService:(PrefService*)prefs
-                fullscreenController:(FullscreenController*)fullscreenController
-                           incognito:(BOOL)incognito {
+- (instancetype)initWithBaseViewController:(UIViewController*)baseViewController
+                               prefService:(PrefService*)prefs
+                      fullscreenController:
+                          (FullscreenController*)fullscreenController
+                                 incognito:(BOOL)incognito {
   if ((self = [super init])) {
-    DCHECK(webStateList);
     DCHECK(baseViewController);
-    _webStateList = webStateList->AsWeakPtr();
     _baseViewController = baseViewController;
     _fullscreenController = fullscreenController;
     _incognito = incognito;
@@ -132,20 +130,29 @@
   _fullscreenController = nullptr;
 }
 
-- (void)handlePartialTranslateSelection {
-  WebSelectionTabHelper* tabHelper = [self webSelectionTabHelper];
+- (void)handlePartialTranslateSelectionForTestingInWebState:
+    (web::WebState*)webState {
+  if (!webState) {
+    return;
+  }
+  WebSelectionTabHelper* tabHelper =
+      WebSelectionTabHelper::FromWebState(webState);
   if (!tabHelper) {
     return;
   }
-
+  GURL pageURL = webState->GetLastCommittedURL();
   __weak __typeof(self) weakSelf = self;
   tabHelper->GetSelectedText(base::BindOnce(^(WebSelectionResponse* response) {
-    [weakSelf receivedWebSelectionResponse:response];
+    [weakSelf receivedWebSelectionResponse:response forPageURL:pageURL];
   }));
 }
 
-- (BOOL)canHandlePartialTranslateSelection {
-  WebSelectionTabHelper* tabHelper = [self webSelectionTabHelper];
+- (BOOL)canHandlePartialTranslateSelectionInWebState:(web::WebState*)webState {
+  if (!webState) {
+    return NO;
+  }
+  WebSelectionTabHelper* tabHelper =
+      WebSelectionTabHelper::FromWebState(webState);
   if (!tabHelper) {
     return NO;
   }
@@ -165,11 +172,57 @@
   return YES;
 }
 
-- (void)switchToFullTranslateWithError:(PartialTranslateError)error {
+#pragma mark - EditMenuBuilder
+
+- (void)buildEditMenuWithBuilder:(id<UIMenuBuilder>)builder
+                      inWebState:(web::WebState*)webState {
+  if (!webState) {
+    return;
+  }
+  if (![self shouldInstallPartialTranslate]) {
+    return;
+  }
+
+  base::WeakPtr<web::WebState> weakWebState = webState->GetWeakPtr();
+  __weak __typeof(self) weakSelf = self;
+  ProceduralBlockWithBlockWithItemArray provider =
+      ^(ProceduralBlockWithItemArray completion) {
+        [weakSelf addItemWithCompletion:completion forWebState:weakWebState];
+      };
+  // Use a deferred element so that the item is displayed depending on the text
+  // selection and updated on selection change.
+  UIDeferredMenuElement* deferredMenuElement =
+      [UIDeferredMenuElement elementWithProvider:provider];
+  edit_menu::AddElementToChromeMenu(builder, deferredMenuElement,
+                                    /*primary*/ YES);
+
+  auto childrenTransformBlock =
+      ^NSArray<UIMenuElement*>*(NSArray<UIMenuElement*>* oldElements) {
+    return [oldElements
+        filteredArrayUsingPredicate:
+            [NSPredicate predicateWithBlock:^BOOL(
+                             id object, NSDictionary<NSString*, id>* bindings) {
+              if (![object isKindOfClass:[UICommand class]]) {
+                return YES;
+              }
+              UICommand* command = base::apple::ObjCCast<UICommand>(object);
+              return command.action != NSSelectorFromString(@"_translate:");
+            }]];
+  };
+
+  [builder replaceChildrenOfMenuForIdentifier:UIMenuLookup
+                            fromChildrenBlock:childrenTransformBlock];
+}
+
+#pragma mark - private
+
+// If the partial translate fails, try to switch to normal page translate.
+- (void)switchToFullTranslateWithError:(PartialTranslateError)error
+                            forPageURL:(const GURL&)pageURL {
   if (!self.alertDelegate) {
     return;
   }
-  if (![self URLIsTranslatable]) {
+  if (!TranslateServiceIOS::IsTranslatableURL(pageURL)) {
     ReportErrorOutcome(error, false);
     return;
   }
@@ -216,15 +269,19 @@
                  actions:@[ cancelAction, translateAction ]];
 }
 
-- (void)receivedWebSelectionResponse:(WebSelectionResponse*)response {
+// The selection was received from JS. Proceed to check it and trigger partial
+// translate.
+- (void)receivedWebSelectionResponse:(WebSelectionResponse*)response
+                          forPageURL:(const GURL&)pageURL {
   DCHECK(response);
   base::UmaHistogramCounts10000("IOS.PartialTranslate.SelectionLength",
                                 response.selectedText.length);
   if (response.selectedText.length >
       std::min(ios::provider::PartialTranslateLimitMaxCharacters(),
                kPartialTranslateCharactersLimit)) {
-    return [self switchToFullTranslateWithError:PartialTranslateError::
-                                                    kSelectionTooLong];
+    return [self
+        switchToFullTranslateWithError:PartialTranslateError::kSelectionTooLong
+                            forPageURL:pageURL];
   }
   if (!response.valid ||
       [[response.selectedText
@@ -232,7 +289,8 @@
                                               whitespaceAndNewlineCharacterSet]]
           length] == 0u) {
     return [self
-        switchToFullTranslateWithError:PartialTranslateError::kSelectionEmpty];
+        switchToFullTranslateWithError:PartialTranslateError::kSelectionEmpty
+                            forPageURL:pageURL];
   }
 
   CGRect sourceRect = response.sourceRect;
@@ -246,64 +304,69 @@
   self.controller = ios::provider::NewPartialTranslateController(
       response.selectedText, sourceRect, self.incognito);
   __weak __typeof(self) weakSelf = self;
+  GURL copyPageURL = pageURL;
   [self.controller presentOnViewController:self.baseViewController
                      flowCompletionHandler:^(BOOL success) {
-                       weakSelf.controller = nil;
-                       if (success) {
-                         ReportOutcome(PartialTranslateOutcomeStatus::kSuccess);
-                       } else {
-                         [weakSelf switchToFullTranslateWithError:
-                                       PartialTranslateError::kGenericError];
-                       }
+                       [weakSelf flowCompletedForPageURL:copyPageURL
+                                              withResult:success];
                      }];
 }
 
+// Flow ended. If it is a success, report it to metrics. Otherwise, try to
+// trigger page translate instead.
+- (void)flowCompletedForPageURL:(const GURL&)pageURL withResult:(BOOL)success {
+  self.controller = nil;
+  if (success) {
+    ReportOutcome(PartialTranslateOutcomeStatus::kSuccess);
+  } else {
+    [self switchToFullTranslateWithError:PartialTranslateError::kGenericError
+                              forPageURL:pageURL];
+  }
+}
+
+// Trigger a full translate as a fallback of partial translate.
 - (void)triggerFullTranslate {
   [self.browserHandler showTranslate];
 }
 
-- (WebSelectionTabHelper*)webSelectionTabHelper {
-  web::WebState* webState =
-      _webStateList ? _webStateList->GetActiveWebState() : nullptr;
-  if (!webState) {
-    return nullptr;
-  }
-  WebSelectionTabHelper* helper = WebSelectionTabHelper::FromWebState(webState);
-  return helper;
-}
-
-- (BOOL)URLIsTranslatable {
-  web::WebState* webState =
-      _webStateList ? _webStateList->GetActiveWebState() : nullptr;
-  if (!webState) {
-    return NO;
-  }
-  return TranslateServiceIOS::IsTranslatableURL(
-      webState->GetLastCommittedURL());
-}
-
-- (void)addItemWithCompletion:(ProceduralBlockWithItemArray)completion {
-  if (![self canHandlePartialTranslateSelection]) {
+// Create the menu item to trigger partial translate step 1.
+// This method is called from the UIDeferredElement handler.
+// This method triggers the fetch of the selection.
+- (void)addItemWithCompletion:(ProceduralBlockWithItemArray)completion
+                  forWebState:(base::WeakPtr<web::WebState>)weakWebState {
+  if (!weakWebState) {
     completion(@[]);
     return;
   }
-  WebSelectionTabHelper* tabHelper = [self webSelectionTabHelper];
+  web::WebState* webState = weakWebState.get();
+  if (![self canHandlePartialTranslateSelectionInWebState:webState]) {
+    completion(@[]);
+    return;
+  }
+  WebSelectionTabHelper* tabHelper =
+      WebSelectionTabHelper::FromWebState(webState);
   if (!tabHelper) {
     completion(@[]);
     return;
   }
+  GURL pageURL = webState->GetLastCommittedURL();
 
   __weak __typeof(self) weakSelf = self;
   tabHelper->GetSelectedText(base::BindOnce(^(WebSelectionResponse* response) {
     if (weakSelf) {
-      [weakSelf addItemWithResponse:response completion:completion];
+      [weakSelf addItemWithResponse:response
+                         forPageURL:pageURL
+                         completion:completion];
     } else {
       completion(@[]);
     }
   }));
 }
 
+// Create the menu item to trigger partial translate step 2.
+// This selection was fetched, if it is valid, add the action in the edit menu.
 - (void)addItemWithResponse:(WebSelectionResponse*)response
+                 forPageURL:(const GURL&)pageURL
                  completion:(ProceduralBlockWithItemArray)completion {
   __weak __typeof(self) weakSelf = self;
   if (!response.valid ||
@@ -317,54 +380,17 @@
   NSString* title =
       l10n_util::GetNSString(IDS_IOS_PARTIAL_TRANSLATE_EDIT_MENU_ENTRY);
   NSString* partialTranslateId = @"chromecommand.partialTranslate";
+  GURL pageURLCopy = pageURL;
   UIAction* action =
       [UIAction actionWithTitle:title
                           image:CustomSymbolWithPointSize(
                                     kTranslateSymbol, kSymbolActionPointSize)
                      identifier:partialTranslateId
                         handler:^(UIAction* a) {
-                          [weakSelf receivedWebSelectionResponse:response];
+                          [weakSelf receivedWebSelectionResponse:response
+                                                      forPageURL:pageURLCopy];
                         }];
   completion(@[ action ]);
 }
 
-#pragma mark - EditMenuBuilder
-
-- (void)buildEditMenuWithBuilder:(id<UIMenuBuilder>)builder
-                      inWebState:(web::WebState*)webState {
-  // TODO(crbug.com/427168159): use webState.
-  if (![self shouldInstallPartialTranslate]) {
-    return;
-  }
-
-  __weak __typeof(self) weakSelf = self;
-  ProceduralBlockWithBlockWithItemArray provider =
-      ^(ProceduralBlockWithItemArray completion) {
-        [weakSelf addItemWithCompletion:completion];
-      };
-  // Use a deferred element so that the item is displayed depending on the text
-  // selection and updated on selection change.
-  UIDeferredMenuElement* deferredMenuElement =
-      [UIDeferredMenuElement elementWithProvider:provider];
-  edit_menu::AddElementToChromeMenu(builder, deferredMenuElement,
-                                    /*primary*/ YES);
-
-  auto childrenTransformBlock =
-      ^NSArray<UIMenuElement*>*(NSArray<UIMenuElement*>* oldElements) {
-    return [oldElements
-        filteredArrayUsingPredicate:
-            [NSPredicate predicateWithBlock:^BOOL(
-                             id object, NSDictionary<NSString*, id>* bindings) {
-              if (![object isKindOfClass:[UICommand class]]) {
-                return YES;
-              }
-              UICommand* command = base::apple::ObjCCast<UICommand>(object);
-              return command.action != NSSelectorFromString(@"_translate:");
-            }]];
-  };
-
-  [builder replaceChildrenOfMenuForIdentifier:UIMenuLookup
-                            fromChildrenBlock:childrenTransformBlock];
-}
-
 @end
diff --git a/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_mediator_unittest.mm b/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_mediator_unittest.mm
index aea469d..aac041a4 100644
--- a/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_mediator_unittest.mm
+++ b/ios/chrome/browser/partial_translate/ui_bundled/partial_translate_mediator_unittest.mm
@@ -159,11 +159,10 @@
     mock_browser_coordinator_commands_handler_ =
         OCMStrictProtocolMock(@protocol(BrowserCoordinatorCommands));
     mediator_ = [[PartialTranslateMediator alloc]
-          initWithWebStateList:&web_state_list_
-        withBaseViewController:base_view_controller_
-                   prefService:profile_->GetSyncablePrefs()
-          fullscreenController:nullptr
-                     incognito:NO];
+        initWithBaseViewController:base_view_controller_
+                       prefService:profile_->GetSyncablePrefs()
+              fullscreenController:nullptr
+                         incognito:NO];
     mediator_.alertDelegate = fake_alert_controller_;
     mediator_.browserHandler = mock_browser_coordinator_commands_handler_;
   }
@@ -223,20 +222,13 @@
 
 // Tests the behavior if partial translate is not supported.
 TEST_F(PartialTranslateMediatorTest, NotSupported) {
-  if (!base::ios::IsRunningOnIOS16OrLater()) {
-    // Partial translate not supported before iOS16.
-    return;
-  }
   EXPECT_FALSE([mediator_ shouldInstallPartialTranslate]);
-  EXPECT_FALSE([mediator_ canHandlePartialTranslateSelection]);
+  EXPECT_FALSE(
+      [mediator_ canHandlePartialTranslateSelectionInWebState:web_state_]);
 }
 
 // Tests the behavior if partial translate is disabled by policy.
 TEST_F(PartialTranslateMediatorTest, EnterpriseDisabled) {
-  if (!base::ios::IsRunningOnIOS16OrLater()) {
-    // Partial translate not supported before iOS16.
-    return;
-  }
   LoadPageAndSelectSize(10);
   auto factory = SetupTranslateControllerFactory(true);
 
@@ -250,17 +242,17 @@
 // Tests the behavior in incognito.
 TEST_F(PartialTranslateMediatorTest, IncognitoSupportedSuccess) {
   PartialTranslateMediator* mediator = [[PartialTranslateMediator alloc]
-        initWithWebStateList:&web_state_list_
-      withBaseViewController:base_view_controller_
-                 prefService:profile_->GetSyncablePrefs()
-        fullscreenController:nullptr
-                   incognito:YES];
+      initWithBaseViewController:base_view_controller_
+                     prefService:profile_->GetSyncablePrefs()
+            fullscreenController:nullptr
+                       incognito:YES];
   base::HistogramTester histogram_tester;
   LoadPageAndSelectSize(10);
   auto factory = SetupTranslateControllerFactory(true);
   EXPECT_TRUE([mediator shouldInstallPartialTranslate]);
-  EXPECT_TRUE([mediator canHandlePartialTranslateSelection]);
-  [mediator handlePartialTranslateSelection];
+  EXPECT_TRUE(
+      [mediator canHandlePartialTranslateSelectionInWebState:web_state_]);
+  [mediator handlePartialTranslateSelectionForTestingInWebState:web_state_];
   ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
       base::test::ios::kWaitForJSCompletionTimeout, /*run_message_loop=*/true,
       ^{
@@ -273,31 +265,23 @@
 
 // Tests the behavior in incognito if not supported.
 TEST_F(PartialTranslateMediatorTest, IncognitoNotSupported) {
-  if (!base::ios::IsRunningOnIOS16OrLater()) {
-    // Partial translate not supported before iOS16.
-    return;
-  }
   PartialTranslateMediator* mediator = [[PartialTranslateMediator alloc]
-        initWithWebStateList:&web_state_list_
-      withBaseViewController:base_view_controller_
-                 prefService:profile_->GetSyncablePrefs()
-        fullscreenController:nullptr
-                   incognito:YES];
+      initWithBaseViewController:base_view_controller_
+                     prefService:profile_->GetSyncablePrefs()
+            fullscreenController:nullptr
+                       incognito:YES];
   EXPECT_FALSE([mediator shouldInstallPartialTranslate]);
 }
 
 // Tests the behavior if partial translate is supported.
 TEST_F(PartialTranslateMediatorTest, SupportedSuccess) {
-  if (!base::ios::IsRunningOnIOS16OrLater()) {
-    // Partial translate not supported before iOS16.
-    return;
-  }
   base::HistogramTester histogram_tester;
   LoadPageAndSelectSize(10);
   auto factory = SetupTranslateControllerFactory(true);
   EXPECT_TRUE([mediator_ shouldInstallPartialTranslate]);
-  EXPECT_TRUE([mediator_ canHandlePartialTranslateSelection]);
-  [mediator_ handlePartialTranslateSelection];
+  EXPECT_TRUE(
+      [mediator_ canHandlePartialTranslateSelectionInWebState:web_state_]);
+  [mediator_ handlePartialTranslateSelectionForTestingInWebState:web_state_];
 
   ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
       base::test::ios::kWaitForJSCompletionTimeout, /*run_message_loop=*/true,
@@ -311,17 +295,14 @@
 
 // Tests the behavior if selection is too long.
 TEST_F(PartialTranslateMediatorTest, StringTooLongCancel) {
-  if (!base::ios::IsRunningOnIOS16OrLater()) {
-    // Partial translate not supported before iOS16.
-    return;
-  }
   base::HistogramTester histogram_tester;
   LoadPageAndSelectSize(1001);
   auto factory = SetupTranslateControllerFactory(true);
 
   EXPECT_TRUE([mediator_ shouldInstallPartialTranslate]);
-  EXPECT_TRUE([mediator_ canHandlePartialTranslateSelection]);
-  [mediator_ handlePartialTranslateSelection];
+  EXPECT_TRUE(
+      [mediator_ canHandlePartialTranslateSelectionInWebState:web_state_]);
+  [mediator_ handlePartialTranslateSelectionForTestingInWebState:web_state_];
   ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
       base::test::ios::kWaitForJSCompletionTimeout, /*run_message_loop=*/true,
       ^{
@@ -334,19 +315,16 @@
 
 // Tests the behavior if selection is too long.
 TEST_F(PartialTranslateMediatorTest, StringTooLongFullTranslate) {
-  if (!base::ios::IsRunningOnIOS16OrLater()) {
-    // Partial translate not supported before iOS16.
-    return;
-  }
   base::HistogramTester histogram_tester;
   LoadPageAndSelectSize(1001);
   auto factory = SetupTranslateControllerFactory(true);
   fake_alert_controller_.selectedAction = 1;
 
   EXPECT_TRUE([mediator_ shouldInstallPartialTranslate]);
-  EXPECT_TRUE([mediator_ canHandlePartialTranslateSelection]);
+  EXPECT_TRUE(
+      [mediator_ canHandlePartialTranslateSelectionInWebState:web_state_]);
   ExpectShowTranslate();
-  [mediator_ handlePartialTranslateSelection];
+  [mediator_ handlePartialTranslateSelectionForTestingInWebState:web_state_];
   ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
       base::test::ios::kWaitForJSCompletionTimeout, /*run_message_loop=*/true,
       ^{
@@ -359,17 +337,14 @@
 
 // Tests the behavior if selection is empty.
 TEST_F(PartialTranslateMediatorTest, StringEmptyCancel) {
-  if (!base::ios::IsRunningOnIOS16OrLater()) {
-    // Partial translate not supported before iOS16.
-    return;
-  }
   base::HistogramTester histogram_tester;
   LoadPageAndSelectSize(0);
   auto factory = SetupTranslateControllerFactory(true);
 
   EXPECT_TRUE([mediator_ shouldInstallPartialTranslate]);
-  EXPECT_TRUE([mediator_ canHandlePartialTranslateSelection]);
-  [mediator_ handlePartialTranslateSelection];
+  EXPECT_TRUE(
+      [mediator_ canHandlePartialTranslateSelectionInWebState:web_state_]);
+  [mediator_ handlePartialTranslateSelectionForTestingInWebState:web_state_];
   ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
       base::test::ios::kWaitForJSCompletionTimeout, /*run_message_loop=*/true,
       ^{
@@ -382,17 +357,14 @@
 
 // Tests the behavior if selection is only spaces.
 TEST_F(PartialTranslateMediatorTest, StringSpacesCancel) {
-  if (!base::ios::IsRunningOnIOS16OrLater()) {
-    // Partial translate not supported before iOS16.
-    return;
-  }
   base::HistogramTester histogram_tester;
   LoadPageAndSelectSize(5, @" ");
   auto factory = SetupTranslateControllerFactory(true);
 
   EXPECT_TRUE([mediator_ shouldInstallPartialTranslate]);
-  EXPECT_TRUE([mediator_ canHandlePartialTranslateSelection]);
-  [mediator_ handlePartialTranslateSelection];
+  EXPECT_TRUE(
+      [mediator_ canHandlePartialTranslateSelectionInWebState:web_state_]);
+  [mediator_ handlePartialTranslateSelectionForTestingInWebState:web_state_];
   ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
       base::test::ios::kWaitForJSCompletionTimeout, /*run_message_loop=*/true,
       ^{
@@ -405,19 +377,16 @@
 
 // Tests the behavior if selection is empty.
 TEST_F(PartialTranslateMediatorTest, StringEmptyFullTranslate) {
-  if (!base::ios::IsRunningOnIOS16OrLater()) {
-    // Partial translate not supported before iOS16.
-    return;
-  }
   base::HistogramTester histogram_tester;
   LoadPageAndSelectSize(0);
   auto factory = SetupTranslateControllerFactory(true);
   fake_alert_controller_.selectedAction = 1;
 
   EXPECT_TRUE([mediator_ shouldInstallPartialTranslate]);
-  EXPECT_TRUE([mediator_ canHandlePartialTranslateSelection]);
+  EXPECT_TRUE(
+      [mediator_ canHandlePartialTranslateSelectionInWebState:web_state_]);
   ExpectShowTranslate();
-  [mediator_ handlePartialTranslateSelection];
+  [mediator_ handlePartialTranslateSelectionForTestingInWebState:web_state_];
   ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
       base::test::ios::kWaitForJSCompletionTimeout, /*run_message_loop=*/true,
       ^{
@@ -430,17 +399,14 @@
 
 // Tests the behavior if an error occurs.
 TEST_F(PartialTranslateMediatorTest, InternalErrorCancel) {
-  if (!base::ios::IsRunningOnIOS16OrLater()) {
-    // Partial translate not supported before iOS16.
-    return;
-  }
   base::HistogramTester histogram_tester;
   LoadPageAndSelectSize(10);
   auto factory = SetupTranslateControllerFactory(false);
 
   EXPECT_TRUE([mediator_ shouldInstallPartialTranslate]);
-  EXPECT_TRUE([mediator_ canHandlePartialTranslateSelection]);
-  [mediator_ handlePartialTranslateSelection];
+  EXPECT_TRUE(
+      [mediator_ canHandlePartialTranslateSelectionInWebState:web_state_]);
+  [mediator_ handlePartialTranslateSelectionForTestingInWebState:web_state_];
   ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
       base::test::ios::kWaitForJSCompletionTimeout, /*run_message_loop=*/true,
       ^{
@@ -453,19 +419,16 @@
 
 // Tests the behavior if an error occurs.
 TEST_F(PartialTranslateMediatorTest, InternalErrorFullTranslate) {
-  if (!base::ios::IsRunningOnIOS16OrLater()) {
-    // Partial translate not supported before iOS16.
-    return;
-  }
   base::HistogramTester histogram_tester;
   LoadPageAndSelectSize(10);
   auto factory = SetupTranslateControllerFactory(false);
   fake_alert_controller_.selectedAction = 1;
 
   EXPECT_TRUE([mediator_ shouldInstallPartialTranslate]);
-  EXPECT_TRUE([mediator_ canHandlePartialTranslateSelection]);
+  EXPECT_TRUE(
+      [mediator_ canHandlePartialTranslateSelectionInWebState:web_state_]);
   ExpectShowTranslate();
-  [mediator_ handlePartialTranslateSelection];
+  [mediator_ handlePartialTranslateSelectionForTestingInWebState:web_state_];
   ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
       base::test::ios::kWaitForJSCompletionTimeout, /*run_message_loop=*/true,
       ^{
diff --git a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
index 75f167b4..5dcac25c 100644
--- a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
@@ -209,6 +209,12 @@
 inline constexpr char kSyncedDefaultSearchProviderGUID[] =
     "default_search_provider.synced_guid";
 
+// Deprecated 07/2025.
+inline constexpr char kFirstSyncCompletedInFullSyncMode[] =
+    "sync.first_full_sync_completed";
+inline constexpr char kGoogleServicesSecondLastSyncingGaiaId[] =
+    "google.services.second_last_gaia_id";
+
 // Migrates a boolean pref from source to target PrefService.
 void MigrateBooleanPref(std::string_view pref_name,
                         PrefService* target_pref_service,
@@ -1074,7 +1080,7 @@
   registry->RegisterStringPref(kContextualSearchEnabled, std::string());
   registry->RegisterInt64Pref(kNtpShownBookmarksFolder, 3);
 
-  // Deprecated 11/2024
+  // Deprecated 11/2024.
   registry->RegisterBooleanPref(kEnableDoNotTrackIos, false);
 
   // Deprecated 12/2024.
@@ -1104,6 +1110,11 @@
   // Deprecated 06/2025.
   registry->RegisterDoublePref(kGaiaCookiePeriodicReportTimeDeprecated, 0);
   registry->RegisterStringPref(kSyncedDefaultSearchProviderGUID, std::string());
+
+  // Deprecated 07/2025.
+  registry->RegisterBooleanPref(kFirstSyncCompletedInFullSyncMode, false);
+  registry->RegisterStringPref(kGoogleServicesSecondLastSyncingGaiaId,
+                               std::string());
 }
 
 // This method should be periodically pruned of year+ old migrations.
@@ -1289,7 +1300,7 @@
   // Added 04/2025.
   prefs->ClearPref(kMixedContentAutoupgradeEnabled);
 
-  // Added 04/2025
+  // Added 04/2025.
   MigrateBooleanFromUserDefaultsToProfilePrefs(
       @"SyncDisabledAlertShown", policy::policy_prefs::kSyncDisabledAlertShown,
       prefs);
@@ -1315,6 +1326,10 @@
   prefs->ClearPref(safety_check_prefs::kSafetyCheckInMagicStackDisabledPref);
   prefs->ClearPref(tab_resumption_prefs::kTabResumptionDisabledPref);
   prefs->ClearPref(kSyncedDefaultSearchProviderGUID);
+
+  // Added 07/2025.
+  prefs->ClearPref(kFirstSyncCompletedInFullSyncMode);
+  prefs->ClearPref(kGoogleServicesSecondLastSyncingGaiaId);
 }
 
 void MigrateObsoleteUserDefault() {
diff --git a/ios/chrome/browser/shared/public/features/features.mm b/ios/chrome/browser/shared/public/features/features.mm
index 1144c7b..6d6fefd 100644
--- a/ios/chrome/browser/shared/public/features/features.mm
+++ b/ios/chrome/browser/shared/public/features/features.mm
@@ -1232,7 +1232,7 @@
 
 BASE_FEATURE(kRunDefaultStatusCheck,
              "RunDefaultStatusCheck",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 bool IsRunDefaultStatusCheckEnabled() {
   return base::FeatureList::IsEnabled(kRunDefaultStatusCheck);
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_strip/test/tab_strip_shared_group_egtest.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_strip/test/tab_strip_shared_group_egtest.mm
index 9dd2ad1..3a36d03 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_strip/test/tab_strip_shared_group_egtest.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_strip/test/tab_strip_shared_group_egtest.mm
@@ -221,8 +221,9 @@
   [[EarlGrey selectElementWithMatcher:KeepSharedConfirmationButton()]
       assertWithMatcher:grey_sufficientlyVisible()];
   // Cancel.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::CancelButton()]
-      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:
+                 chrome_test_util::ActionSheetItemWithAccessibilityLabelId(
+                     IDS_CANCEL)] performAction:grey_tap()];
   [ChromeEarlGrey waitForMainTabCount:1];
 
   // Tap the close button of the tab cell and verify the alert.
diff --git a/ios_internal b/ios_internal
index c0e9707..373e3df 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit c0e9707df578b2b9906f957fe6e9d64722db23f2
+Subproject commit 373e3df530fae297dc1f4663e6284052172e34a0
diff --git a/net/http/transport_security_state_static.pins b/net/http/transport_security_state_static.pins
index 49bb1282..fa035864 100644
--- a/net/http/transport_security_state_static.pins
+++ b/net/http/transport_security_state_static.pins
@@ -43,9 +43,9 @@
 #   hash function for preloaded entries again (we have already done so once).
 #
 
-# Last updated: 2025-07-03 12:55 UTC
+# Last updated: 2025-07-04 12:53 UTC
 PinsListTimestamp
-1751547346
+1751633616
 
 TestSPKI
 sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
diff --git a/net/http/transport_security_state_static_pins.json b/net/http/transport_security_state_static_pins.json
index 5dc6b36..d668642 100644
--- a/net/http/transport_security_state_static_pins.json
+++ b/net/http/transport_security_state_static_pins.json
@@ -31,7 +31,7 @@
 // the 'static_spki_hashes' and 'bad_static_spki_hashes' fields in 'pinsets'
 // refer to, and the timestamp at which the pins list was last updated.
 //
-// Last updated: 2025-07-03 12:55 UTC
+// Last updated: 2025-07-04 12:53 UTC
 //
 {
   "pinsets": [
diff --git a/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc b/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc
index 3d1f821c..71cbc32 100644
--- a/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc
+++ b/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc
@@ -205,15 +205,7 @@
 }
 
 // static
-PerfettoTracedProcess& PerfettoTracedProcess::MaybeCreateInstance() {
-  static base::NoDestructor<PerfettoTracedProcess> traced_process(
-      base::ThreadPool::CreateSequencedTaskRunner(
-          {base::MayBlock(), base::TaskPriority::USER_BLOCKING}));
-  return *traced_process;
-}
-
-// static
-PerfettoTracedProcess& PerfettoTracedProcess::MaybeCreateInstanceWithThread(
+PerfettoTracedProcess& PerfettoTracedProcess::MaybeCreateInstance(
     bool will_trace_thread_restart) {
   static base::NoDestructor<PerfettoTracedProcess> traced_process(
       will_trace_thread_restart);
@@ -294,14 +286,13 @@
   DataSourceBase::ResetTaskRunner(task_runner_);
 
   tracing_backend_ = std::make_unique<PerfettoTracingBackend>();
-  OnThreadPoolAvailable(
+  InitPostFeatureList(
       /* enable_consumer */ true);
   // Disassociate the PerfettoTracedProcess from any prior task runner.
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
 void PerfettoTracedProcess::ResetForTesting() {
-  startup_tracing_needed_ = false;
   base::WaitableEvent on_reset_done;
   // The tracing backend is used internally in Perfetto on the |task_runner_|
   // sequence. Reset and destroy the backend on the task runner to avoid racing
@@ -328,18 +319,6 @@
   task_runner_ = nullptr;
 }
 
-void PerfettoTracedProcess::RequestStartupTracing(
-    const perfetto::TraceConfig& config,
-    const perfetto::Tracing::SetupStartupTracingOpts& opts) {
-  if (thread_pool_started_) {
-    perfetto::Tracing::SetupStartupTracingBlocking(config, opts);
-  } else {
-    saved_config_ = config;
-    saved_opts_ = opts;
-    startup_tracing_needed_ = true;
-  }
-}
-
 void PerfettoTracedProcess::SetupClientLibrary(bool enable_consumer) {
   perfetto::TracingInitArgs init_args;
   init_args.platform = platform_.get();
@@ -402,14 +381,8 @@
 }
 #endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
 
-void PerfettoTracedProcess::OnThreadPoolAvailable(bool enable_consumer) {
-  thread_pool_started_ = true;
+void PerfettoTracedProcess::InitPostFeatureList(bool enable_consumer) {
   SetupClientLibrary(enable_consumer);
-
-  if (startup_tracing_needed_) {
-    perfetto::Tracing::SetupStartupTracingBlocking(saved_config_, saved_opts_);
-    startup_tracing_needed_ = false;
-  }
 }
 
 void PerfettoTracedProcess::SetAllowSystemTracingConsumerCallback(
diff --git a/services/tracing/public/cpp/perfetto/perfetto_traced_process.h b/services/tracing/public/cpp/perfetto/perfetto_traced_process.h
index a60a3ac..5b46fbe 100644
--- a/services/tracing/public/cpp/perfetto/perfetto_traced_process.h
+++ b/services/tracing/public/cpp/perfetto/perfetto_traced_process.h
@@ -163,8 +163,7 @@
   static base::Thread* GetTraceThread();
 
   // Creates the process-wide instance of the PerfettoTracedProcess.
-  static PerfettoTracedProcess& MaybeCreateInstance();
-  static PerfettoTracedProcess& MaybeCreateInstanceWithThread(
+  static PerfettoTracedProcess& MaybeCreateInstance(
       bool will_trace_thread_restart);
   static PerfettoTracedProcess& MaybeCreateInstanceForTesting();
 
@@ -198,7 +197,7 @@
                            bool privacy_filtering_enabled);
 
   // Called on the process's main thread once the thread pool is ready.
-  void OnThreadPoolAvailable(bool enable_consumer);
+  void InitPostFeatureList(bool enable_consumer);
 
   // Set a callback that returns whether a system tracing session is allowed.
   // The callback will be executed on the sequence that set it. Only a single
@@ -223,15 +222,6 @@
     return platform_.get();
   }
 
-  // Indicate that startup tracing will need to start when thread pool becomes
-  // available. This is used in Perfetto client library build, because currently
-  // it requires a threadpool to run tracing tasks.
-  // TODO(khokhlov): Remove this method once startup tracing no longer depends
-  // on threadpool in client library build.
-  void RequestStartupTracing(
-      const perfetto::TraceConfig& config,
-      const perfetto::Tracing::SetupStartupTracingOpts& opts);
-
 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
   void DeferOrConnectProducerSocket(perfetto::CreateSocketCallback cb);
 #endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
@@ -278,11 +268,6 @@
   std::unique_ptr<base::tracing::PerfettoPlatform> platform_;
   std::unique_ptr<PerfettoTracingBackend> tracing_backend_;
 
-  bool startup_tracing_needed_ = false;
-  bool thread_pool_started_ = false;
-  perfetto::TraceConfig saved_config_;
-  perfetto::Tracing::SetupStartupTracingOpts saved_opts_;
-
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
diff --git a/services/tracing/public/cpp/perfetto/track_name_recorder.cc b/services/tracing/public/cpp/perfetto/track_name_recorder.cc
index 539c5ed6..d59bd75 100644
--- a/services/tracing/public/cpp/perfetto/track_name_recorder.cc
+++ b/services/tracing/public/cpp/perfetto/track_name_recorder.cc
@@ -128,6 +128,7 @@
 TrackNameRecorder::TrackNameRecorder()
     : process_start_timestamp_(
           TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds()) {
+  perfetto::internal::TrackRegistry::InitializeInstance();
   base::ThreadIdNameManager::GetInstance()->AddObserver(this);
   base::CurrentProcess::GetInstance().SetDelegate(this, {});
   base::TrackEvent::AddSessionObserver(this);
diff --git a/services/tracing/public/cpp/trace_startup.cc b/services/tracing/public/cpp/trace_startup.cc
index fc94a93e..57fb453 100644
--- a/services/tracing/public/cpp/trace_startup.cc
+++ b/services/tracing/public/cpp/trace_startup.cc
@@ -38,48 +38,50 @@
 constexpr SharedMemoryMachPortRendezvousKey kTraceBufferRendezvousKey = 'trbc';
 #endif
 
-constexpr uint32_t kStartupTracingTimeoutMs = 30 * 1000;  // 30 sec
-
 using base::trace_event::TraceConfig;
 using base::trace_event::TraceLog;
 
 }  // namespace
 
 bool g_tracing_initialized_after_featurelist = false;
-bool g_tracing_with_thread = false;
 
 bool IsTracingInitialized() {
   return g_tracing_initialized_after_featurelist;
 }
 
-void EnableStartupTracingIfNeeded(bool with_thread) {
-  RegisterTracedValueProtoWriter();
+void InitTracingPostFeatureList(
+    bool enable_consumer,
+    bool will_trace_thread_restart,
+    base::RepeatingCallback<bool()> should_allow_system_tracing) {
+  DCHECK(base::FeatureList::GetInstance());
+  DCHECK(!g_tracing_initialized_after_featurelist);
+  g_tracing_initialized_after_featurelist = true;
 
   // Initialize the client library's TrackRegistry to support trace points
   // during startup tracing. We don't setup the client library completely here
   // yet, because we don't have field trials loaded yet (which influence which
   // backends we enable).
-  // TODO(eseckler): Make it possible to initialize client lib backends after
-  // setting up the client library?
   perfetto::internal::TrackRegistry::InitializeInstance();
 
   // Create the PerfettoTracedProcess.
-  if (with_thread) {
-    g_tracing_with_thread = true;
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-    PerfettoTracedProcess::MaybeCreateInstanceWithThread(
-        /*will_trace_thread_restart=*/true);
-#else
-    PerfettoTracedProcess::MaybeCreateInstanceWithThread(
-        /*will_trace_thread_restart=*/false);
-#endif
-  } else {
-    PerfettoTracedProcess::MaybeCreateInstance();
+  auto& traced_process =
+      PerfettoTracedProcess::MaybeCreateInstance(will_trace_thread_restart);
+  if (should_allow_system_tracing) {
+    traced_process.SetAllowSystemTracingConsumerCallback(
+        std::move(should_allow_system_tracing));
   }
+  traced_process.InitPostFeatureList(enable_consumer);
+
+  RegisterTracedValueProtoWriter();
 
   // Ensure TraceLog is initialized first.
   // https://crbug.com/764357
   TraceLog::GetInstance();
+
+#if BUILDFLAG(IS_WIN)
+  tracing::EnableETWExport();
+#endif  // BUILDFLAG(IS_WIN)
+
   auto& startup_config = TraceStartupConfig::GetInstance();
 
   if (startup_config.IsEnabled()) {
@@ -90,40 +92,9 @@
     // TODO(khokhlov): Support startup tracing with the system backend in the
     // SDK build.
     opts.backend = perfetto::kCustomBackend;
-    // TODO(khokhlov): After client library is moved onto a separate thread
-    // and it's possible to start startup tracing early, replace this call with
-    // perfetto::Tracing::SetupStartupTracing(perfetto_config, args).
-    PerfettoTracedProcess::Get().RequestStartupTracing(perfetto_config, opts);
-  }
-}
 
-bool EnableStartupTracingForProcess(
-    const perfetto::TraceConfig& perfetto_config) {
-  perfetto::Tracing::SetupStartupTracingOpts opts;
-  opts.timeout_ms = kStartupTracingTimeoutMs;
-  opts.backend = perfetto::kCustomBackend;
-  // TODO(khokhlov): After client library is moved onto a separate thread
-  // and it's possible to start startup tracing early, replace this call with
-  // perfetto::Tracing::SetupStartupTracing(perfetto_config, args).
-  PerfettoTracedProcess::Get().RequestStartupTracing(perfetto_config, opts);
-  return true;
-}
-
-void InitTracingPostFeatureList(bool enable_consumer) {
-  if (g_tracing_initialized_after_featurelist) {
-    return;
+    perfetto::Tracing::SetupStartupTracingBlocking(perfetto_config, opts);
   }
-  g_tracing_initialized_after_featurelist = true;
-  DCHECK(base::FeatureList::GetInstance());
-
-  // Create the PerfettoTracedProcess.
-  if (!g_tracing_with_thread) {
-    PerfettoTracedProcess::MaybeCreateInstance();
-  }
-  PerfettoTracedProcess::Get().OnThreadPoolAvailable(enable_consumer);
-#if BUILDFLAG(IS_WIN)
-  tracing::EnableETWExport();
-#endif  // BUILDFLAG(IS_WIN)
 }
 
 base::ReadOnlySharedMemoryRegion CreateTracingConfigSharedMemory() {
diff --git a/services/tracing/public/cpp/trace_startup.h b/services/tracing/public/cpp/trace_startup.h
index 1fcf57b..699d1176 100644
--- a/services/tracing/public/cpp/trace_startup.h
+++ b/services/tracing/public/cpp/trace_startup.h
@@ -6,6 +6,7 @@
 #define SERVICES_TRACING_PUBLIC_CPP_TRACE_STARTUP_H_
 
 #include "base/component_export.h"
+#include "base/functional/callback_helpers.h"
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/unsafe_shared_memory_region.h"
 #include "base/process/launch.h"
@@ -22,38 +23,24 @@
 
 namespace tracing {
 
+inline constexpr uint32_t kStartupTracingTimeoutMs = 30 * 1000;  // 30 sec
+
 // Returns true if `InitTracingPostFeatureList()` has been called
 // for this process.
 bool COMPONENT_EXPORT(TRACING_CPP) IsTracingInitialized();
 
-// Hooks up hooks up service callbacks in TraceLog for the perfetto backend and,
-// if startup tracing command line flags are present, enables TraceLog with a
-// config based on the flags. In zygote children, this should only be called
-// after mojo is initialized, as the zygote's sandbox prevents creation of the
-// tracing SMB before that point.
-//
-// TODO(eseckler): Consider allocating the SMB in parent processes outside the
-// sandbox and supply it via the command line. Then, we can revert to call this
-// earlier and from fewer places again.
-void COMPONENT_EXPORT(TRACING_CPP)
-    EnableStartupTracingIfNeeded(bool with_thread = false);
-
-// Enable startup tracing for the current process with the provided config. Sets
-// up ProducerClient and trace event and/or sampler profiler data sources, and
-// enables TraceLog. The caller should also instruct Chrome's tracing service to
-// start tracing, once the service is connected. Returns false on failure.
-//
-// TODO(eseckler): Figure out what startup tracing APIs should look like with
-// the client lib.
-bool COMPONENT_EXPORT(TRACING_CPP)
-    EnableStartupTracingForProcess(const perfetto::TraceConfig&);
-
-// Initialize tracing components that require task runners. Will switch
-// IsTracingInitialized() to return true.
-// |enable_consumer| should be true if the system consumer can be enabled.
-// Currently this is only the case if this is running in the browser process.
-void COMPONENT_EXPORT(TRACING_CPP)
-    InitTracingPostFeatureList(bool enable_consumer);
+// Initializes the perfetto backend and, if startup tracing command line flags
+// are present, enables startup tracing with a config based on the flags. This
+// should only be called after feature list initialization, and after sandbox
+// initialization on platforms that require single thread. Will switch
+// IsTracingInitialized() to return true. |enable_consumer| should be true if
+// the system consumer can be enabled. Currently this is only the case if this
+// is running in the browser process.
+void COMPONENT_EXPORT(TRACING_CPP) InitTracingPostFeatureList(
+    bool enable_consumer,
+    bool will_trace_thread_restart,
+    base::RepeatingCallback<bool()> should_allow_system_tracing =
+        base::NullCallback());
 
 // If tracing is enabled, grabs the current trace config & mode and tells the
 // child to begin tracing right away via startup tracing command line flags.
diff --git a/services/tracing/public/cpp/trace_startup_shared_memory_unittest.cc b/services/tracing/public/cpp/trace_startup_shared_memory_unittest.cc
index 18122c0b..86db4fa 100644
--- a/services/tracing/public/cpp/trace_startup_shared_memory_unittest.cc
+++ b/services/tracing/public/cpp/trace_startup_shared_memory_unittest.cc
@@ -69,10 +69,10 @@
 #if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_FUCHSIA)
   base::FeatureList::InitInstance("", "");
   base::ThreadPoolInstance::CreateAndStartWithDefaultParams("StartupTraceTest");
-  tracing::InitTracingPostFeatureList(/*enable_consumer=*/false);
+  tracing::InitTracingPostFeatureList(/*enable_consumer=*/false,
+                                      /*will_trace_thread_restart=*/false);
 
   // Simulate launching with the serialized parameters.
-  EnableStartupTracingIfNeeded();
   EXPECT_TRUE(IsTracingInitialized());
   EXPECT_TRUE(base::trace_event::TraceLog::GetInstance()->IsEnabled());
 #endif  // !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_FUCHSIA)
diff --git a/services/tracing/public/mojom/data_source_descriptor_mojom_traits.cc b/services/tracing/public/mojom/data_source_descriptor_mojom_traits.cc
index 5dc4fbe..6982a90 100644
--- a/services/tracing/public/mojom/data_source_descriptor_mojom_traits.cc
+++ b/services/tracing/public/mojom/data_source_descriptor_mojom_traits.cc
@@ -16,6 +16,7 @@
     return false;
   }
   out->set_name(name);
+  out->set_id(data.id());
   out->set_will_notify_on_start(data.will_notify_on_start());
   out->set_will_notify_on_stop(data.will_notify_on_stop());
   out->set_handles_incremental_state_clear(
diff --git a/services/tracing/public/mojom/data_source_descriptor_mojom_traits.h b/services/tracing/public/mojom/data_source_descriptor_mojom_traits.h
index efab0b9..88c2e04 100644
--- a/services/tracing/public/mojom/data_source_descriptor_mojom_traits.h
+++ b/services/tracing/public/mojom/data_source_descriptor_mojom_traits.h
@@ -22,6 +22,9 @@
   static const std::string& name(const perfetto::DataSourceDescriptor& src) {
     return src.name();
   }
+  static uint64_t id(const perfetto::DataSourceDescriptor& src) {
+    return src.id();
+  }
   static bool will_notify_on_start(const perfetto::DataSourceDescriptor& src) {
     return src.will_notify_on_start();
   }
diff --git a/services/tracing/public/mojom/perfetto_service.mojom b/services/tracing/public/mojom/perfetto_service.mojom
index caddcd7..6e5bc52d 100644
--- a/services/tracing/public/mojom/perfetto_service.mojom
+++ b/services/tracing/public/mojom/perfetto_service.mojom
@@ -140,6 +140,7 @@
 
 struct DataSourceRegistration {
   string name;
+  uint64 id;
   bool will_notify_on_start;
   bool will_notify_on_stop;
   bool handles_incremental_state_clear;
diff --git a/testing/buildbot/chrome.gpu.fyi.json b/testing/buildbot/chrome.gpu.fyi.json
index ae336e34..75dba79 100644
--- a/testing/buildbot/chrome.gpu.fyi.json
+++ b/testing/buildbot/chrome.gpu.fyi.json
@@ -3,8 +3,7 @@
   "AAAAA2 See generate_buildbot_json.py to make changes": {},
   "ChromeOS FYI Release Skylab (jacuzzi)": {
     "additional_compile_targets": [
-      "chromiumos_preflight",
-      "nacl_helper"
+      "chromiumos_preflight"
     ],
     "skylab_tests": [
       {
@@ -316,8 +315,7 @@
   },
   "gpu-fyi-chromeos-brya-chrome": {
     "additional_compile_targets": [
-      "chromiumos_preflight",
-      "nacl_helper"
+      "chromiumos_preflight"
     ],
     "skylab_tests": [
       {
@@ -346,8 +344,7 @@
   },
   "gpu-fyi-chromeos-corsola-chrome": {
     "additional_compile_targets": [
-      "chromiumos_preflight",
-      "nacl_helper"
+      "chromiumos_preflight"
     ],
     "skylab_tests": [
       {
@@ -376,8 +373,7 @@
   },
   "gpu-fyi-chromeos-skyrim-chrome": {
     "additional_compile_targets": [
-      "chromiumos_preflight",
-      "nacl_helper"
+      "chromiumos_preflight"
     ],
     "skylab_tests": [
       {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index ccea6ba..03e4686 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -315,7 +315,6 @@
       'ChromeOS FYI Release Skylab (jacuzzi)': {
         'additional_compile_targets': [
           'chromiumos_preflight',
-          'nacl_helper',
         ],
         'browser_config': 'cros-chrome',
         'os_type': 'chromeos',
@@ -335,7 +334,6 @@
       'gpu-fyi-chromeos-brya-chrome': {
         'additional_compile_targets': [
           'chromiumos_preflight',
-          'nacl_helper',
         ],
         'browser_config': 'cros-chrome',
         'os_type': 'chromeos',
@@ -352,7 +350,6 @@
       'gpu-fyi-chromeos-corsola-chrome': {
         'additional_compile_targets': [
           'chromiumos_preflight',
-          'nacl_helper',
         ],
         'browser_config': 'cros-chrome',
         'os_type': 'chromeos',
@@ -369,7 +366,6 @@
       'gpu-fyi-chromeos-skyrim-chrome': {
         'additional_compile_targets': [
           'chromiumos_preflight',
-          'nacl_helper',
         ],
         'browser_config': 'cros-chrome',
         'os_type': 'chromeos',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index a9c1578..94d248a 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -428,21 +428,6 @@
             ]
         }
     ],
-    "AddAddressManually": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AddAddressManually"
-                    ]
-                }
-            ]
-        }
-    ],
     "AddSuggestStrongPasswordFromAddPassword": [
         {
             "platforms": [
@@ -2712,6 +2697,26 @@
             ]
         }
     ],
+    "AutofillServerUploadMoreData": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "ios",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AutofillServerUploadMoreData"
+                    ]
+                }
+            ]
+        }
+    ],
     "AutofillSharedStorageServerCardData": [
         {
             "platforms": [
@@ -20210,6 +20215,7 @@
                     "enable_features": [
                         "AvoidCloneArgsOnExtensionFunctionDispatch",
                         "AvoidUnnecessaryGetMinimizeButtonOffset",
+                        "AvoidUnnecessaryShouldRenderRichAnimation",
                         "ReducePPMs",
                         "ScreenWinDisplayLookupByHMONITOR"
                     ]
diff --git a/third_party/androidx/build.gradle b/third_party/androidx/build.gradle
index edb218b..56de41b 100644
--- a/third_party/androidx/build.gradle
+++ b/third_party/androidx/build.gradle
@@ -306,7 +306,7 @@
     google()
     maven {
         // This URL is generated by the fetch_all_androidx.py script.
-        url 'https://androidx.dev/snapshots/builds/13730045/artifacts/repository'
+        url 'https://androidx.dev/snapshots/builds/13737040/artifacts/repository'
     }
     mavenCentral()
 }
diff --git a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/androidx_compose_foundation_foundation_android.info b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/androidx_compose_foundation_foundation_android.info
index 2b5aa62f..5db640f 100644
--- a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/androidx_compose_foundation_foundation_android.info
+++ b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/androidx_compose_foundation_foundation_android.info
@@ -10,7 +10,6 @@
 is_manifest_empty = true
 manifest_package = "androidx.compose.foundation"
 resources = [
-  "res/values/values.xml",
   "res/values-af/values-af.xml",
   "res/values-am/values-am.xml",
   "res/values-ar/values-ar.xml",
@@ -31,14 +30,14 @@
   "res/values-en-rGB/values-en-rGB.xml",
   "res/values-en-rIN/values-en-rIN.xml",
   "res/values-en-rXC/values-en-rXC.xml",
-  "res/values-es/values-es.xml",
   "res/values-es-rUS/values-es-rUS.xml",
+  "res/values-es/values-es.xml",
   "res/values-et/values-et.xml",
   "res/values-eu/values-eu.xml",
   "res/values-fa/values-fa.xml",
   "res/values-fi/values-fi.xml",
-  "res/values-fr/values-fr.xml",
   "res/values-fr-rCA/values-fr-rCA.xml",
+  "res/values-fr/values-fr.xml",
   "res/values-gl/values-gl.xml",
   "res/values-gu/values-gu.xml",
   "res/values-hi/values-hi.xml",
@@ -71,9 +70,9 @@
   "res/values-or/values-or.xml",
   "res/values-pa/values-pa.xml",
   "res/values-pl/values-pl.xml",
-  "res/values-pt/values-pt.xml",
   "res/values-pt-rBR/values-pt-rBR.xml",
   "res/values-pt-rPT/values-pt-rPT.xml",
+  "res/values-pt/values-pt.xml",
   "res/values-ro/values-ro.xml",
   "res/values-ru/values-ru.xml",
   "res/values-si/values-si.xml",
@@ -95,7 +94,8 @@
   "res/values-zh-rCN/values-zh-rCN.xml",
   "res/values-zh-rHK/values-zh-rHK.xml",
   "res/values-zh-rTW/values-zh-rTW.xml",
-  "res/values-zu/values-zu.xml"
+  "res/values-zu/values-zu.xml",
+  "res/values/values.xml"
 ]
 subjar_tuples = []
 subjars = []
diff --git a/third_party/angle b/third_party/angle
index 619e8bf..a035c7b 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 619e8bf8a8b656be5e2c786584b2a7910d1e1ebc
+Subproject commit a035c7b6c3db68994cffeff5a2f39acb8fb460c9
diff --git a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom
index 917d5ec7..e8c017c 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom
@@ -873,6 +873,7 @@
     kMasonryFlow = 814,
     // kMasonryAutoTracks = 815,
     kResult = 816,
+    // TODO(crbug.com/429392773): kAnimationTrigger* entries are deprecated.
     // renamed to kAnimationTriggerBehavior
     //kAnimationTriggerType = 817,
     kAnimationTriggerTimeline = 818,
@@ -927,7 +928,15 @@
     kCornerInlineEndShape = 867,
     kCorners = 868,
     kBorderShape = 869,
+    // TODO(crbug.com/429392773): This is deprecated.
     kAnimationTriggerBehavior = 870,
+    kTimelineTriggerName = 871,
+    kTimelineTriggerBehavior = 872,
+    kTimelineTriggerRangeStart = 873,
+    kTimelineTriggerRangeEnd = 874,
+    kTimelineTriggerExitRangeStart = 875,
+    kTimelineTriggerExitRangeEnd = 876,
+    kTimelineTriggerTimeline = 877,
 
     // 1. Add new features above this line (don't change the assigned numbers of
     //    the existing items).
diff --git a/third_party/blink/renderer/bindings/core/v8/binding_security.cc b/third_party/blink/renderer/bindings/core/v8/binding_security.cc
index 900b9cdf7..4a0ad27 100644
--- a/third_party/blink/renderer/bindings/core/v8/binding_security.cc
+++ b/third_party/blink/renderer/bindings/core/v8/binding_security.cc
@@ -233,14 +233,14 @@
     return true;
   }
 
-  v8::Isolate* isolate = accessing_context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   return ShouldAllowAccessToV8ContextInternal(
       ScriptState::From(isolate, accessing_context),
       ScriptState::From(isolate, target_context));
 }
 
 void BindingSecurity::FailedAccessCheckFor(v8::Local<v8::Object> holder) {
-  v8::Isolate* isolate = holder->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   DOMWindow* target = FindWindow(isolate, holder);
   // Failing to find a target means something is wrong. Failing to throw an
   // exception could be a security issue, so just crash.
diff --git a/third_party/blink/renderer/bindings/core/v8/module_record.cc b/third_party/blink/renderer/bindings/core/v8/module_record.cc
index 4958554..9939ad41 100644
--- a/third_party/blink/renderer/bindings/core/v8/module_record.cc
+++ b/third_party/blink/renderer/bindings/core/v8/module_record.cc
@@ -200,7 +200,7 @@
     v8::Local<v8::String> specifier,
     v8::Local<v8::FixedArray> import_attributes,
     v8::Local<v8::Module> referrer) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   Modulator* modulator = Modulator::From(ScriptState::From(isolate, context));
   DCHECK(modulator);
 
@@ -222,7 +222,7 @@
     v8::Local<v8::String> specifier,
     v8::Local<v8::FixedArray> import_attributes,
     v8::Local<v8::Module> referrer) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   ScriptState* script_state = ScriptState::From(isolate, context);
   Modulator* modulator = Modulator::From(script_state);
   DCHECK(modulator);
@@ -253,7 +253,7 @@
   // in the form [key1, value1, key2, value2, ...].
   const int kV8AttributeEntrySize = v8_import_attributes_has_positions ? 3 : 2;
 
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   Vector<ImportAttribute> import_attributes;
   int number_of_import_attributes =
       v8_import_attributes->Length() / kV8AttributeEntrySize;
diff --git a/third_party/blink/renderer/bindings/core/v8/referrer_script_info.cc b/third_party/blink/renderer/bindings/core/v8/referrer_script_info.cc
index 7eed34e..1b797783 100644
--- a/third_party/blink/renderer/bindings/core/v8/referrer_script_info.cc
+++ b/third_party/blink/renderer/bindings/core/v8/referrer_script_info.cc
@@ -73,7 +73,7 @@
     return Default(script_origin_resource_name);
   }
 
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   v8::Local<v8::Primitive> base_url_value =
       host_defined_options->Get(isolate, kBaseURL);
   SECURITY_CHECK(base_url_value->IsString());
diff --git a/third_party/blink/renderer/bindings/core/v8/shadow_realm_context.cc b/third_party/blink/renderer/bindings/core/v8/shadow_realm_context.cc
index 4e56d423..e00b350 100644
--- a/third_party/blink/renderer/bindings/core/v8/shadow_realm_context.cc
+++ b/third_party/blink/renderer/bindings/core/v8/shadow_realm_context.cc
@@ -65,7 +65,7 @@
   ExecutionContext* initiator_execution_context =
       ExecutionContext::From(initiator_context);
   DCHECK(initiator_execution_context);
-  v8::Isolate* isolate = initiator_context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   DOMWrapperWorld* world = DOMWrapperWorld::Create(
       isolate, DOMWrapperWorld::WorldType::kShadowRealm);
   CHECK(world);  // Not yet run out of the world id.
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.cc b/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.cc
index 2b9a63c..4cbb2da 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.cc
@@ -661,7 +661,7 @@
 
 ExecutionContext* ToExecutionContext(v8::Local<v8::Context> context) {
   DCHECK(!context.IsEmpty());
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   ScriptState* script_state = ScriptState::MaybeFrom(isolate, context);
   return script_state ? ToExecutionContext(script_state) : nullptr;
 }
@@ -687,7 +687,7 @@
   v8::Local<v8::Context> context = ToV8ContextEvenIfDetached(frame, world);
   if (context.IsEmpty())
     return nullptr;
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   ScriptState* script_state = ScriptState::From(isolate, context);
   if (!script_state->ContextIsValid())
     return nullptr;
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
index 9e484f5b..5f3ef90 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
@@ -148,7 +148,7 @@
 
 String ToBlinkString(v8::Local<v8::Context> context,
                      v8::Local<v8::String> source) {
-  v8::String::Value source_str(context->GetIsolate(), source);
+  v8::String::Value source_str(v8::Isolate::GetCurrent(), source);
   size_t len = std::min(ContentSecurityPolicy::kMaxSampleLength,
                         static_cast<size_t>(source_str.length()));
   // SAFETY: v8::String::Value guarantees *source_str has source_str.length()
@@ -168,7 +168,7 @@
 void V8Initializer::MessageHandlerInMainThread(v8::Local<v8::Message> message,
                                                v8::Local<v8::Value> data) {
   DCHECK(IsMainThread());
-  v8::Isolate* isolate = message->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
 
   if (isolate->GetEnteredOrMicrotaskContext().IsEmpty())
     return;
@@ -213,7 +213,7 @@
 
 void V8Initializer::MessageHandlerInWorker(v8::Local<v8::Message> message,
                                            v8::Local<v8::Value> data) {
-  v8::Isolate* isolate = message->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   // During the frame teardown, there may not be a valid context.
   ScriptState* script_state = ScriptState::ForCurrentRealm(isolate);
   if (!script_state->ContextIsValid())
@@ -301,9 +301,7 @@
     v8::PromiseRejectMessage data) {
   DCHECK(IsMainThread());
 
-  v8::Local<v8::Promise> promise = data.GetPromise();
-
-  v8::Isolate* isolate = promise->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
 
   // TODO(ikilpatrick): Remove this check, extensions tests that use
   // extensions::ModuleSystemTest incorrectly don't have a valid script state.
@@ -368,10 +366,8 @@
 }
 
 static void PromiseRejectHandlerInWorker(v8::PromiseRejectMessage data) {
-  v8::Local<v8::Promise> promise = data.GetPromise();
-
   // Bail out if called during context initialization.
-  v8::Isolate* isolate = promise->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   ScriptState* script_state = ScriptState::ForCurrentRealm(isolate);
   if (!script_state->ContextIsValid())
     return;
@@ -424,11 +420,11 @@
   return false;
 }
 
-static std::pair<bool, v8::MaybeLocal<v8::String>>
-TrustedTypesCodeGenerationCheck(v8::Local<v8::Context> context,
-                                v8::Local<v8::Value> source,
-                                bool is_code_like) {
-  v8::Isolate* isolate = context->GetIsolate();
+std::pair<bool, v8::MaybeLocal<v8::String>> TrustedTypesCodeGenerationCheck(
+    v8::Local<v8::Context> context,
+    v8::Local<v8::Value> source,
+    bool is_code_like) {
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   // If the input is not a string or TrustedScript, pass it through.
   if (!source->IsString() && !is_code_like &&
       !V8TrustedScript::HasInstance(isolate, source)) {
@@ -457,7 +453,7 @@
     return {false, v8::MaybeLocal<v8::String>()};
   }
 
-  return {true, V8String(context->GetIsolate(), stringified_source)};
+  return {true, V8String(isolate, stringified_source)};
 }
 
 // static
@@ -640,7 +636,7 @@
     v8::Local<v8::String> v8_specifier,
     v8::ModuleImportPhase import_phase,
     v8::Local<v8::FixedArray> v8_import_attributes) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   ScriptState* script_state = ScriptState::From(isolate, context);
 
   Modulator* modulator = Modulator::From(script_state);
@@ -727,7 +723,7 @@
 void HostGetImportMetaProperties(v8::Local<v8::Context> context,
                                  v8::Local<v8::Module> module,
                                  v8::Local<v8::Object> meta) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   ScriptState* script_state = ScriptState::From(isolate, context);
   v8::HandleScope handle_scope(isolate);
 
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_object_parser.cc b/third_party/blink/renderer/bindings/core/v8/v8_object_parser.cc
index 9e2bc56..7288815 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_object_parser.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_object_parser.cc
@@ -19,7 +19,7 @@
     Vector<CSSPropertyID>* native_properties,
     Vector<AtomicString>* custom_properties,
     ExceptionState& exception_state) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   TryRethrowScope rethrow_scope(isolate, exception_state);
 
   v8::Local<v8::Value> list_value;
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_page_popup_controller_binding.cc b/third_party/blink/renderer/bindings/core/v8/v8_page_popup_controller_binding.cc
index 1dec2037..62748f8 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_page_popup_controller_binding.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_page_popup_controller_binding.cc
@@ -51,7 +51,8 @@
 
   window_wrapper
       ->SetNativeDataProperty(
-          context, V8AtomicString(context->GetIsolate(), "pagePopupController"),
+          context,
+          V8AtomicString(v8::Isolate::GetCurrent(), "pagePopupController"),
           PagePopupControllerAttributeGetterCallback, nullptr,
           v8::Local<v8::Value>(), v8::ReadOnly)
       .ToChecked();
diff --git a/third_party/blink/renderer/bindings/modules/v8/v8_context_snapshot_impl.cc b/third_party/blink/renderer/bindings/modules/v8/v8_context_snapshot_impl.cc
index f0e7b9d..a1e489e1 100644
--- a/third_party/blink/renderer/bindings/modules/v8/v8_context_snapshot_impl.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/v8_context_snapshot_impl.cc
@@ -212,7 +212,7 @@
     return {nullptr, 0};
   }
   const WrapperTypeInfo* wrapper_type_info = ToWrapperTypeInfo(wrappable);
-  CHECK_EQ(wrappable, ToAnyScriptWrappable(holder->GetIsolate(), holder));
+  CHECK_EQ(wrappable, ToAnyScriptWrappable(v8::Isolate::GetCurrent(), holder));
   constexpr size_t kSize = 1;
   static_assert(sizeof (InternalFieldSerializedValue) == kSize);
   auto* serialized_value = new InternalFieldSerializedValue();
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
index 870aeeea..a67710c 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -3826,7 +3826,7 @@
             _format(
                 "{blink_class}* blink_accessed_object = "
                 "${class_name}::ToWrappableUnsafe("
-                "accessing_context->GetIsolate(),"
+                "v8::Isolate::GetCurrent(),"
                 "${accessed_object});",
                 blink_class=blink_class)),
         TextNode("return BindingSecurity::ShouldAllowAccessTo("
@@ -4329,7 +4329,7 @@
         S("is_in_secure_context",
           ("const bool ${is_in_secure_context} = "
            "${execution_context}->IsSecureContext();")),
-        S("isolate", "v8::Isolate* ${isolate} = ${v8_context}->GetIsolate();"),
+        S("isolate", "v8::Isolate* ${isolate} = v8::Isolate::GetCurrent();"),
         S("script_state", ("ScriptState* ${script_state} = "
                            "ScriptState::From(${isolate}, ${v8_context});")),
         S("wrapper_type_info",
diff --git a/third_party/blink/renderer/controller/performance_manager/v8_detailed_memory_reporter_impl.cc b/third_party/blink/renderer/controller/performance_manager/v8_detailed_memory_reporter_impl.cc
index 266dfb6d..6266ece5 100644
--- a/third_party/blink/renderer/controller/performance_manager/v8_detailed_memory_reporter_impl.cc
+++ b/third_party/blink/renderer/controller/performance_manager/v8_detailed_memory_reporter_impl.cc
@@ -70,7 +70,7 @@
         isolate_memory_usage->detached_bytes_used += size;
         continue;
       }
-      v8::Isolate* isolate = context->GetIsolate();
+      v8::Isolate* isolate = v8::Isolate::GetCurrent();
       if (DOMWrapperWorld::World(isolate, context).GetWorldId() !=
           DOMWrapperWorld::kMainWorldId) {
         // TODO(crbug.com/1085129): Handle extension contexts once they get
diff --git a/third_party/blink/renderer/core/animation/css/css_animation_data.cc b/third_party/blink/renderer/core/animation/css/css_animation_data.cc
index cf2fc6c..9f9f35b 100644
--- a/third_party/blink/renderer/core/animation/css/css_animation_data.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animation_data.cc
@@ -48,6 +48,12 @@
   return trigger_timeline;
 }
 
+const StyleTimeline& CSSAnimationData::InitialTimelineTriggerTimeline() {
+  DEFINE_STATIC_LOCAL(const StyleTimeline, timeline_trigger_timeline,
+                      (CSSValueID::kAuto));
+  return timeline_trigger_timeline;
+}
+
 bool CSSAnimationData::AnimationsMatchForStyleRecalc(
     const CSSAnimationData& other) const {
   return name_list_ == other.name_list_ &&
diff --git a/third_party/blink/renderer/core/animation/css/css_animation_data.h b/third_party/blink/renderer/core/animation/css/css_animation_data.h
index 70de784..f82a1e2 100644
--- a/third_party/blink/renderer/core/animation/css/css_animation_data.h
+++ b/third_party/blink/renderer/core/animation/css/css_animation_data.h
@@ -78,6 +78,31 @@
   const Vector<TimelineOffsetOrAuto>& TriggerExitRangeEndList() const {
     return trigger_exit_range_end_list_;
   }
+  const Vector<Persistent<const ScopedCSSName>>& TimelineTriggerNameList()
+      const {
+    return timeline_trigger_name_list_;
+  }
+  const Vector<EAnimationTriggerBehavior>& TimelineTriggerBehaviorList() const {
+    return timeline_trigger_behavior_list_;
+  }
+  const Vector<std::optional<TimelineOffset>>& TimelineTriggerRangeStartList()
+      const {
+    return timeline_trigger_range_start_list_;
+  }
+  const Vector<std::optional<TimelineOffset>>& TimelineTriggerRangeEndList()
+      const {
+    return timeline_trigger_range_end_list_;
+  }
+  const Vector<TimelineOffsetOrAuto>& TimelineTriggerExitRangeStartList()
+      const {
+    return timeline_trigger_exit_range_start_list_;
+  }
+  const Vector<TimelineOffsetOrAuto>& TimelineTriggerExitRangeEndList() const {
+    return timeline_trigger_exit_range_end_list_;
+  }
+  const Vector<StyleTimeline>& TimelineTriggerTimelineList() const {
+    return timeline_trigger_timeline_list_;
+  }
 
   EffectModel::CompositeOperation GetComposition(size_t animation_index) const {
     if (!composition_list_.size()) {
@@ -122,6 +147,27 @@
   Vector<TimelineOffsetOrAuto>& TriggerExitRangeEndList() {
     return trigger_exit_range_end_list_;
   }
+  Vector<Persistent<const ScopedCSSName>>& TimelineTriggerNameList() {
+    return timeline_trigger_name_list_;
+  }
+  Vector<EAnimationTriggerBehavior>& TimelineTriggerBehaviorList() {
+    return timeline_trigger_behavior_list_;
+  }
+  Vector<std::optional<TimelineOffset>>& TimelineTriggerRangeStartList() {
+    return timeline_trigger_range_start_list_;
+  }
+  Vector<std::optional<TimelineOffset>>& TimelineTriggerRangeEndList() {
+    return timeline_trigger_range_end_list_;
+  }
+  Vector<TimelineOffsetOrAuto>& TimelineTriggerExitRangeStartList() {
+    return timeline_trigger_exit_range_start_list_;
+  }
+  Vector<TimelineOffsetOrAuto>& TimelineTriggerExitRangeEndList() {
+    return timeline_trigger_exit_range_end_list_;
+  }
+  Vector<StyleTimeline>& TimelineTriggerTimelineList() {
+    return timeline_trigger_timeline_list_;
+  }
 
   bool HasSingleInitialTimeline() const {
     return timeline_list_.size() == 1u &&
@@ -170,6 +216,25 @@
   static TimelineOffsetOrAuto InitialTriggerExitRangeEnd() {
     return TimelineOffsetOrAuto();
   }
+  static Persistent<const ScopedCSSName> InitialTimelineTriggerName() {
+    return nullptr;
+  }
+  static EAnimationTriggerBehavior InitialTimelineTriggerBehavior() {
+    return EAnimationTriggerBehavior::kOnce;
+  }
+  static std::optional<TimelineOffset> InitialTimelineTriggerRangeStart() {
+    return std::nullopt;
+  }
+  static std::optional<TimelineOffset> InitialTimelineTriggerRangeEnd() {
+    return std::nullopt;
+  }
+  static TimelineOffsetOrAuto InitialTimelineTriggerExitRangeStart() {
+    return TimelineOffsetOrAuto();
+  }
+  static TimelineOffsetOrAuto InitialTimelineTriggerExitRangeEnd() {
+    return TimelineOffsetOrAuto();
+  }
+  static const StyleTimeline& InitialTimelineTriggerTimeline();
 
  private:
   Vector<AtomicString> name_list_;
@@ -181,12 +246,22 @@
   Vector<Timing::FillMode> fill_mode_list_;
   Vector<EAnimPlayState> play_state_list_;
   Vector<EffectModel::CompositeOperation> composition_list_;
+  // TODO(crbug.com/429392773): trigger_* properties are deprecated in favor of
+  // timeline_trigger_* properties.
   Vector<EAnimationTriggerBehavior> trigger_behavior_list_;
   Vector<StyleTimeline> trigger_timeline_list_;
   Vector<std::optional<TimelineOffset>> trigger_range_start_list_;
   Vector<std::optional<TimelineOffset>> trigger_range_end_list_;
   Vector<TimelineOffsetOrAuto> trigger_exit_range_start_list_;
   Vector<TimelineOffsetOrAuto> trigger_exit_range_end_list_;
+
+  Vector<Persistent<const ScopedCSSName>> timeline_trigger_name_list_;
+  Vector<EAnimationTriggerBehavior> timeline_trigger_behavior_list_;
+  Vector<std::optional<TimelineOffset>> timeline_trigger_range_start_list_;
+  Vector<std::optional<TimelineOffset>> timeline_trigger_range_end_list_;
+  Vector<TimelineOffsetOrAuto> timeline_trigger_exit_range_start_list_;
+  Vector<TimelineOffsetOrAuto> timeline_trigger_exit_range_end_list_;
+  Vector<StyleTimeline> timeline_trigger_timeline_list_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc
index ad3fe8a..5bca949 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -3528,6 +3528,13 @@
     case CSSPropertyID::kTextCombineUpright:
     case CSSPropertyID::kTextOrientation:
     case CSSPropertyID::kTimelineScope:
+    case CSSPropertyID::kTimelineTriggerName:
+    case CSSPropertyID::kTimelineTriggerBehavior:
+    case CSSPropertyID::kTimelineTriggerRangeStart:
+    case CSSPropertyID::kTimelineTriggerRangeEnd:
+    case CSSPropertyID::kTimelineTriggerExitRangeStart:
+    case CSSPropertyID::kTimelineTriggerExitRangeEnd:
+    case CSSPropertyID::kTimelineTriggerTimeline:
     case CSSPropertyID::kTransition:
     case CSSPropertyID::kTransitionBehavior:
     case CSSPropertyID::kTransitionDelay:
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index b3b98baf..9ef592d0 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -1088,6 +1088,7 @@
       runtime_flag: "AnimationTrigger",
     },
     {
+      // TODO(crbug.com/429392773): This is deprecated and should be deleted.
       name: "animation-trigger-behavior",
       property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "InitialValue"],
       style_builder_template: "animation",
@@ -1101,6 +1102,94 @@
       runtime_flag: "AnimationTrigger",
     },
     {
+      name: "timeline-trigger-behavior",
+      property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "InitialValue"],
+      style_builder_template: "animation",
+      style_builder_template_args: {
+        attribute: "TimelineTriggerBehavior",
+      },
+      keywords: ["once", "repeat", "alternate", "state"],
+      typedom_types: ["Keyword"],
+      separator: ",",
+      valid_for_marker: true,
+      runtime_flag: "AnimationTrigger",
+    },
+    {
+      name: "timeline-trigger-name",
+      property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "InitialValue"],
+      field_group: "*",
+      field_template: "external",
+      // TODO: maybe define an animation_trigger template?
+      style_builder_template: "animation",
+      style_builder_template_args: {
+        attribute: "TimelineTriggerName",
+      },
+      default_value: "nullptr",
+      type_name: "ScopedCSSNameList",
+      wrapper_pointer_name: "Member",
+      converter: "ConvertTimelineTriggerName",
+      separator: ",",
+      valid_for_marker: true,
+      runtime_flag: "AnimationTrigger",
+    },
+    {
+      name: "timeline-trigger-range-start",
+      property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "InitialValue"],
+      style_builder_template: "animation",
+      style_builder_template_args: {
+        attribute: "TimelineTriggerRangeStart",
+      },
+      separator: ",",
+      valid_for_marker: true,
+      runtime_flag: "AnimationTrigger",
+    },
+    {
+      name: "timeline-trigger-range-end",
+      property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "InitialValue"],
+      style_builder_template: "animation",
+      style_builder_template_args: {
+        attribute: "TimelineTriggerRangeEnd",
+      },
+      separator: ",",
+      valid_for_marker: true,
+      runtime_flag: "AnimationTrigger",
+    },
+    {
+      name: "timeline-trigger-exit-range-start",
+      property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "InitialValue"],
+      style_builder_template: "animation",
+      style_builder_template_args: {
+        attribute: "TimelineTriggerExitRangeStart",
+      },
+      separator: ",",
+      valid_for_marker: true,
+      runtime_flag: "AnimationTrigger",
+    },
+    {
+      name: "timeline-trigger-exit-range-end",
+      property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "InitialValue"],
+      style_builder_template: "animation",
+      style_builder_template_args: {
+        attribute: "TimelineTriggerExitRangeEnd",
+      },
+      separator: ",",
+      valid_for_marker: true,
+      runtime_flag: "AnimationTrigger",
+    },
+    {
+      name: "timeline-trigger-timeline",
+      property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "InitialValue"],
+      style_builder_template: "animation",
+      style_builder_template_args: {
+        attribute: "TimelineTriggerTimeline",
+      },
+      keywords: ["none", "auto"],
+      typedom_types: ["Keyword"],
+      separator: ",",
+      valid_for_marker: true,
+      runtime_flag: "AnimationTrigger",
+    },
+    {
       name: "transition-delay",
       property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "InitialValue"],
       style_builder_template: "transition",
diff --git a/third_party/blink/renderer/core/css/css_property_equality.cc b/third_party/blink/renderer/core/css/css_property_equality.cc
index a84a3aa2..4f133547 100644
--- a/third_party/blink/renderer/core/css/css_property_equality.cc
+++ b/third_party/blink/renderer/core/css/css_property_equality.cc
@@ -971,6 +971,13 @@
     // be defined for them.
     case CSSPropertyID::kScrollTimelineAxis:
     case CSSPropertyID::kScrollTimelineName:
+    case CSSPropertyID::kTimelineTriggerBehavior:
+    case CSSPropertyID::kTimelineTriggerName:
+    case CSSPropertyID::kTimelineTriggerRangeStart:
+    case CSSPropertyID::kTimelineTriggerRangeEnd:
+    case CSSPropertyID::kTimelineTriggerExitRangeStart:
+    case CSSPropertyID::kTimelineTriggerExitRangeEnd:
+    case CSSPropertyID::kTimelineTriggerTimeline:
     case CSSPropertyID::kViewTimelineAxis:
     case CSSPropertyID::kViewTimelineInset:
     case CSSPropertyID::kViewTimelineName:
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index 22e1195..3b27519 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -2524,7 +2524,6 @@
 
 CSSValue* ComputedStyleUtils::ValueForAnimationRangeList(
     const Vector<std::optional<TimelineOffset>>& range_list,
-    const CSSAnimationData* animation_data,
     const ComputedStyle& style,
     const Length& default_offset) {
   return CreateAnimationValueList(range_list, &ValueForAnimationRange, style,
@@ -2539,7 +2538,7 @@
           ? animation_data->RangeStartList()
           : Vector<std::optional<TimelineOffset>>{CSSAnimationData::
                                                       InitialRangeStart()},
-      animation_data, style, Length::Percent(0.0));
+      style, Length::Percent(0.0));
 }
 
 CSSValue* ComputedStyleUtils::ValueForAnimationRangeEndList(
@@ -2550,7 +2549,7 @@
           ? animation_data->RangeEndList()
           : Vector<std::optional<TimelineOffset>>{CSSAnimationData::
                                                       InitialRangeEnd()},
-      animation_data, style, Length::Percent(100.0));
+      style, Length::Percent(100.0));
 }
 
 CSSValue* ComputedStyleUtils::ValueForAnimationTriggerRangeStartList(
@@ -2561,7 +2560,7 @@
           ? animation_data->TriggerRangeStartList()
           : Vector<std::optional<
                 TimelineOffset>>{CSSAnimationData::InitialTriggerRangeStart()},
-      animation_data, style, Length::Percent(0.0));
+      style, Length::Percent(0.0));
 }
 
 CSSValue* ComputedStyleUtils::ValueForAnimationTriggerRangeEndList(
@@ -2572,7 +2571,7 @@
           ? animation_data->TriggerRangeEndList()
           : Vector<std::optional<TimelineOffset>>{CSSAnimationData::
                                                       InitialTriggerRangeEnd()},
-      animation_data, style, Length::Percent(100.0));
+      style, Length::Percent(100.0));
 }
 
 CSSValue* ComputedStyleUtils::ValueForAnimationRangeOrAuto(
@@ -2588,7 +2587,6 @@
 
 CSSValue* ComputedStyleUtils::ValueForAnimationTriggerExitRangeList(
     const Vector<TimelineOffsetOrAuto>& range_list,
-    const CSSAnimationData* animation_data,
     const ComputedStyle& style,
     const Length& default_offset) {
   return CreateAnimationValueList(range_list, &ValueForAnimationRangeOrAuto,
@@ -2603,7 +2601,7 @@
           ? animation_data->TriggerExitRangeStartList()
           : Vector<TimelineOffsetOrAuto>{CSSAnimationData::
                                              InitialTriggerExitRangeStart()},
-      animation_data, style, Length::Percent(0.0));
+      style, Length::Percent(0.0));
 }
 
 CSSValue* ComputedStyleUtils::ValueForAnimationTriggerExitRangeEndList(
@@ -2614,7 +2612,7 @@
           ? animation_data->TriggerExitRangeEndList()
           : Vector<TimelineOffsetOrAuto>{CSSAnimationData::
                                              InitialTriggerExitRangeEnd()},
-      animation_data, style, Length::Percent(100.0));
+      style, Length::Percent(100.0));
 }
 
 CSSValue* ComputedStyleUtils::ValueForAnimationTimingFunction(
@@ -2783,6 +2781,76 @@
       &ValueForAnimationTriggerBehavior);
 }
 
+CSSValue* ComputedStyleUtils::ValueForTimelineTriggerBehaviorList(
+    const CSSAnimationData* animation_data) {
+  return CreateAnimationValueList(
+      animation_data
+          ? animation_data->TimelineTriggerBehaviorList()
+          : Vector<
+                EAnimationTriggerBehavior>{CSSAnimationData::
+                                               InitialTimelineTriggerBehavior()},
+      &ValueForAnimationTriggerBehavior);
+}
+
+CSSValue* ComputedStyleUtils::ValueForTimelineTriggerRangeStartList(
+    const CSSAnimationData* animation_data,
+    const ComputedStyle& style) {
+  return ValueForAnimationRangeList(
+      animation_data
+          ? animation_data->TimelineTriggerRangeStartList()
+          : Vector<std::optional<
+                TimelineOffset>>{CSSAnimationData::
+                                     InitialTimelineTriggerRangeStart()},
+      style, Length::Percent(0.0));
+}
+
+CSSValue* ComputedStyleUtils::ValueForTimelineTriggerRangeEndList(
+    const CSSAnimationData* animation_data,
+    const ComputedStyle& style) {
+  return ValueForAnimationRangeList(
+      animation_data
+          ? animation_data->TimelineTriggerRangeEndList()
+          : Vector<std::optional<
+                TimelineOffset>>{CSSAnimationData::
+                                     InitialTimelineTriggerRangeEnd()},
+      style, Length::Percent(100.0));
+}
+
+CSSValue* ComputedStyleUtils::ValueForTimelineTriggerExitRangeStartList(
+    const CSSAnimationData* animation_data,
+    const ComputedStyle& style) {
+  return ValueForAnimationTriggerExitRangeList(
+      animation_data
+          ? animation_data->TimelineTriggerExitRangeStartList()
+          : Vector<
+                TimelineOffsetOrAuto>{CSSAnimationData::
+                                          InitialTimelineTriggerExitRangeStart()},
+      style, Length::Percent(0.0));
+}
+
+CSSValue* ComputedStyleUtils::ValueForTimelineTriggerExitRangeEndList(
+    const CSSAnimationData* animation_data,
+    const ComputedStyle& style) {
+  return ValueForAnimationTriggerExitRangeList(
+      animation_data
+          ? animation_data->TimelineTriggerExitRangeEndList()
+          : Vector<
+                TimelineOffsetOrAuto>{CSSAnimationData::
+                                          InitialTimelineTriggerExitRangeEnd()},
+      style, Length::Percent(100.0));
+}
+
+CSSValue* ComputedStyleUtils::ValueForTimelineTriggerTimelineList(
+    const CSSAnimationData* animation_data,
+    const ComputedStyle& style) {
+  return CreateAnimationValueList(
+      animation_data
+          ? animation_data->TimelineTriggerTimelineList()
+          : Vector<StyleTimeline>{CSSAnimationData::
+                                      InitialTimelineTriggerTimeline()},
+      &ValueForAnimationTimeline, style);
+}
+
 CSSValue* ComputedStyleUtils::ValueForAnimationTriggerTimelineList(
     const CSSAnimationData* animation_data,
     const ComputedStyle& style) {
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.h b/third_party/blink/renderer/core/css/properties/computed_style_utils.h
index e7f2b8c..5d4f355 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.h
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.h
@@ -183,7 +183,6 @@
   static CSSValue* ValueForAnimationPlayState(EAnimPlayState);
   static CSSValue* ValueForAnimationRangeList(
       const Vector<std::optional<TimelineOffset>>& range_list,
-      const CSSAnimationData* animation_data,
       const ComputedStyle& style,
       const Length& default_offset);
   static CSSValue* ValueForAnimationTimingFunction(
@@ -229,7 +228,6 @@
       const Length& default_offset);
   static CSSValue* ValueForAnimationTriggerExitRangeList(
       const Vector<TimelineOffsetOrAuto>& range_list,
-      const CSSAnimationData* animation_data,
       const ComputedStyle& style,
       const Length& default_offset);
   static CSSValue* ValueForAnimationTriggerExitRangeStartList(
@@ -242,6 +240,21 @@
       const EAnimationTriggerBehavior);
   static CSSValue* ValueForAnimationTriggerBehaviorList(
       const CSSAnimationData*);
+  static CSSValue* ValueForTimelineTriggerBehaviorList(const CSSAnimationData*);
+  static CSSValue* ValueForTimelineTriggerRangeStartList(
+      const CSSAnimationData* animation_data,
+      const ComputedStyle& style);
+  static CSSValue* ValueForTimelineTriggerRangeEndList(
+      const CSSAnimationData* animation_data,
+      const ComputedStyle& style);
+  static CSSValue* ValueForTimelineTriggerExitRangeStartList(
+      const CSSAnimationData* animation_data,
+      const ComputedStyle& style);
+  static CSSValue* ValueForTimelineTriggerExitRangeEndList(
+      const CSSAnimationData* animation_data,
+      const ComputedStyle& style);
+  static CSSValue* ValueForTimelineTriggerTimelineList(const CSSAnimationData*,
+                                                       const ComputedStyle&);
   static CSSValue* ValueForAnimationTriggerTimelineList(const CSSAnimationData*,
                                                         const ComputedStyle&);
   static CSSValueList* ValuesForBorderRadiusCorner(const LengthSize&,
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index b21ca81..8cb8348 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -4528,14 +4528,24 @@
                       CSSValueID::kY>(stream);
 }
 
-CSSValue* ConsumeSingleTimelineName(CSSParserTokenStream& stream,
-                                    const CSSParserContext& context) {
+CSSValue* ConsumeNoneOrDashIdent(CSSParserTokenStream& stream,
+                                 const CSSParserContext& context) {
   if (CSSValue* value = ConsumeIdent<CSSValueID::kNone>(stream)) {
     return value;
   }
   return ConsumeDashedIdent(stream, context);
 }
 
+CSSValue* ConsumeSingleTimelineName(CSSParserTokenStream& stream,
+                                    const CSSParserContext& context) {
+  return ConsumeNoneOrDashIdent(stream, context);
+}
+
+CSSValue* ConsumeSingleTimelineTriggerName(CSSParserTokenStream& stream,
+                                           const CSSParserContext& context) {
+  return ConsumeNoneOrDashIdent(stream, context);
+}
+
 namespace {
 
 CSSValue* ConsumeSingleTimelineInsetSide(CSSParserTokenStream& stream,
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
index 91a32e22..16bf274 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
@@ -681,6 +681,10 @@
 // the omitted value should be the first one repeated.
 bool IsRepeatedPositionAreaValue(CSSValueID value_id);
 
+// https://drafts.csswg.org/css-animations-2/#animation-trigger
+CSSValue* ConsumeSingleTimelineTriggerName(CSSParserTokenStream& stream,
+                                           const CSSParserContext& context);
+
 // Template implementations are at the bottom of the file for readability.
 
 template <typename... emptyBaseCase>
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index fac76159..dc328e04 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -768,6 +768,177 @@
   return CSSIdentifierValue::Create(CSSValueID::kAuto);
 }
 
+const CSSValue* TimelineTriggerBehavior::InitialValue() const {
+  return CSSIdentifierValue::Create(CSSValueID::kOnce);
+}
+
+const CSSValue* TimelineTriggerBehavior::CSSValueFromComputedStyleInternal(
+    const ComputedStyle& style,
+    const LayoutObject*,
+    bool allow_visited_style,
+    CSSValuePhase value_phase) const {
+  return ComputedStyleUtils::ValueForTimelineTriggerBehaviorList(
+      style.Animations());
+}
+
+const CSSValue* TimelineTriggerBehavior::ParseSingleValue(
+    CSSParserTokenStream& stream,
+    const CSSParserContext&,
+    const CSSParserLocalContext&) const {
+  return css_parsing_utils::ConsumeCommaSeparatedList<CSSIdentifierValue*(
+      CSSParserTokenStream&)>(
+      css_parsing_utils::ConsumeIdent<CSSValueID::kOnce, CSSValueID::kRepeat,
+                                      CSSValueID::kAlternate,
+                                      CSSValueID::kState>,
+      stream);
+}
+
+const CSSValue* TimelineTriggerName::ParseSingleValue(
+    CSSParserTokenStream& stream,
+    const CSSParserContext& context,
+    const CSSParserLocalContext&) const {
+  using css_parsing_utils::ConsumeCommaSeparatedList;
+  using css_parsing_utils::ConsumeSingleTimelineTriggerName;
+  return ConsumeCommaSeparatedList(ConsumeSingleTimelineTriggerName, stream,
+                                   context);
+}
+
+const CSSValue* TimelineTriggerName::CSSValueFromComputedStyleInternal(
+    const ComputedStyle& style,
+    const LayoutObject* layout_object,
+    bool allow_visited_style,
+    CSSValuePhase value_phase) const {
+  if (const CSSAnimationData* data = style.Animations()) {
+    CSSValueList* list = CSSValueList::CreateCommaSeparated();
+    if (data->TimelineTriggerNameList().empty()) {
+      return InitialValue();
+    }
+    for (const Persistent<const ScopedCSSName>& name :
+         data->TimelineTriggerNameList()) {
+      list->Append(*ComputedStyleUtils::ValueForCustomIdentOrNone(name.Get()));
+    }
+    return list;
+  }
+
+  return InitialValue();
+}
+
+const CSSValue* TimelineTriggerName::InitialValue() const {
+  CSSValueList* list = CSSValueList::CreateCommaSeparated();
+  list->Append(*CSSIdentifierValue::Create(CSSValueID::kNone));
+  return list;
+}
+
+const CSSValue* TimelineTriggerRangeStart::ParseSingleValue(
+    CSSParserTokenStream& stream,
+    const CSSParserContext& context,
+    const CSSParserLocalContext&) const {
+  return css_parsing_utils::ConsumeCommaSeparatedList(
+      css_parsing_utils::ConsumeAnimationRange, stream, context,
+      /* default_offset_percent */ 0.0, /*allow_auto=*/false);
+}
+
+const CSSValue* TimelineTriggerRangeStart::CSSValueFromComputedStyleInternal(
+    const ComputedStyle& style,
+    const LayoutObject*,
+    bool allow_visited_style,
+    CSSValuePhase value_phase) const {
+  return ComputedStyleUtils::ValueForTimelineTriggerRangeStartList(
+      style.Animations(), style);
+}
+
+const CSSValue* TimelineTriggerRangeStart::InitialValue() const {
+  return CSSIdentifierValue::Create(CSSValueID::kNormal);
+}
+
+const CSSValue* TimelineTriggerRangeEnd::ParseSingleValue(
+    CSSParserTokenStream& stream,
+    const CSSParserContext& context,
+    const CSSParserLocalContext&) const {
+  return css_parsing_utils::ConsumeCommaSeparatedList(
+      css_parsing_utils::ConsumeAnimationRange, stream, context,
+      /* default_offset_percent */ 100.0, /*allow_auto=*/false);
+}
+
+const CSSValue* TimelineTriggerRangeEnd::CSSValueFromComputedStyleInternal(
+    const ComputedStyle& style,
+    const LayoutObject*,
+    bool allow_visited_style,
+    CSSValuePhase value_phase) const {
+  return ComputedStyleUtils::ValueForTimelineTriggerRangeEndList(
+      style.Animations(), style);
+}
+
+const CSSValue* TimelineTriggerRangeEnd::InitialValue() const {
+  return CSSIdentifierValue::Create(CSSValueID::kNormal);
+}
+
+const CSSValue* TimelineTriggerExitRangeStart::ParseSingleValue(
+    CSSParserTokenStream& stream,
+    const CSSParserContext& context,
+    const CSSParserLocalContext&) const {
+  return css_parsing_utils::ConsumeCommaSeparatedList(
+      css_parsing_utils::ConsumeAnimationRange, stream, context,
+      /* default_offset_percent */ 0.0, /*allow_auto=*/true);
+}
+
+const CSSValue*
+TimelineTriggerExitRangeStart::CSSValueFromComputedStyleInternal(
+    const ComputedStyle& style,
+    const LayoutObject*,
+    bool allow_visited_style,
+    CSSValuePhase value_phase) const {
+  return ComputedStyleUtils::ValueForTimelineTriggerExitRangeStartList(
+      style.Animations(), style);
+}
+
+const CSSValue* TimelineTriggerExitRangeStart::InitialValue() const {
+  return CSSIdentifierValue::Create(CSSValueID::kAuto);
+}
+
+const CSSValue* TimelineTriggerExitRangeEnd::ParseSingleValue(
+    CSSParserTokenStream& stream,
+    const CSSParserContext& context,
+    const CSSParserLocalContext&) const {
+  return css_parsing_utils::ConsumeCommaSeparatedList(
+      css_parsing_utils::ConsumeAnimationRange, stream, context,
+      /* default_offset_percent */ 100.0, /*allow_auto=*/true);
+}
+
+const CSSValue* TimelineTriggerExitRangeEnd::CSSValueFromComputedStyleInternal(
+    const ComputedStyle& style,
+    const LayoutObject*,
+    bool allow_visited_style,
+    CSSValuePhase value_phase) const {
+  return ComputedStyleUtils::ValueForTimelineTriggerExitRangeEndList(
+      style.Animations(), style);
+}
+
+const CSSValue* TimelineTriggerExitRangeEnd::InitialValue() const {
+  return CSSIdentifierValue::Create(CSSValueID::kAuto);
+}
+
+const CSSValue* TimelineTriggerTimeline::ParseSingleValue(
+    CSSParserTokenStream& stream,
+    const CSSParserContext& context,
+    const CSSParserLocalContext& local_context) const {
+  return css_parsing_utils::ConsumeCommaSeparatedList(
+      css_parsing_utils::ConsumeAnimationTimeline, stream, context);
+}
+
+const CSSValue* TimelineTriggerTimeline::CSSValueFromComputedStyleInternal(
+    const ComputedStyle& style,
+    const LayoutObject*,
+    bool allow_visited_style,
+    CSSValuePhase value_phase) const {
+  return ComputedStyleUtils::ValueForTimelineTriggerTimelineList(
+      style.Animations(), style);
+}
+
+const CSSValue* TimelineTriggerTimeline::InitialValue() const {
+  return CSSIdentifierValue::Create(CSSValueID::kAuto);
+}
+
 const CSSValue* AspectRatio::ParseSingleValue(
     CSSParserTokenStream& stream,
     const CSSParserContext& context,
diff --git a/third_party/blink/renderer/core/css/resolver/css_to_style_map.cc b/third_party/blink/renderer/core/css/resolver/css_to_style_map.cc
index ece8397..cea434c 100644
--- a/third_party/blink/renderer/core/css/resolver/css_to_style_map.cc
+++ b/third_party/blink/renderer/core/css/resolver/css_to_style_map.cc
@@ -839,4 +839,64 @@
   return TimelineOffsetOrAuto(MapAnimationRange(state, value, 100));
 }
 
+Persistent<const ScopedCSSName> CSSToStyleMap::MapAnimationTimelineTriggerName(
+    StyleResolverState& state,
+    const CSSValue& value) {
+  DCHECK(value.IsScopedValue());
+  if (auto* ident = DynamicTo<CSSIdentifierValue>(value)) {
+    DCHECK(ident->GetValueID() == CSSValueID::kNone);
+    return nullptr;
+  }
+  if (auto* custom_ident = DynamicTo<CSSCustomIdentValue>(value)) {
+    return MakeGarbageCollected<ScopedCSSName>(
+        custom_ident->ComputeIdent(state.CssToLengthConversionData()),
+        custom_ident->GetTreeScope());
+  }
+  return nullptr;
+}
+
+EAnimationTriggerBehavior CSSToStyleMap::MapAnimationTimelineTriggerBehavior(
+    StyleResolverState& state,
+    const CSSValue& value) {
+  return MapAnimationTriggerBehavior(state, value);
+}
+
+std::optional<TimelineOffset>
+CSSToStyleMap::MapAnimationTimelineTriggerRangeStart(StyleResolverState& state,
+                                                     const CSSValue& value) {
+  return MapAnimationRange(state, value, 0);
+}
+
+std::optional<TimelineOffset>
+CSSToStyleMap::MapAnimationTimelineTriggerRangeEnd(StyleResolverState& state,
+                                                   const CSSValue& value) {
+  return MapAnimationRange(state, value, 100);
+}
+
+TimelineOffsetOrAuto CSSToStyleMap::MapAnimationTimelineTriggerExitRangeStart(
+    StyleResolverState& state,
+    const CSSValue& value) {
+  if (auto* ident = DynamicTo<CSSIdentifierValue>(value);
+      ident && ident->GetValueID() == CSSValueID::kAuto) {
+    return TimelineOffsetOrAuto();
+  }
+  return TimelineOffsetOrAuto(MapAnimationRange(state, value, 0));
+}
+
+TimelineOffsetOrAuto CSSToStyleMap::MapAnimationTimelineTriggerExitRangeEnd(
+    StyleResolverState& state,
+    const CSSValue& value) {
+  if (auto* ident = DynamicTo<CSSIdentifierValue>(value);
+      ident && ident->GetValueID() == CSSValueID::kAuto) {
+    return TimelineOffsetOrAuto();
+  }
+  return TimelineOffsetOrAuto(MapAnimationRange(state, value, 100));
+}
+
+StyleTimeline CSSToStyleMap::MapAnimationTimelineTriggerTimeline(
+    StyleResolverState& state,
+    const CSSValue& value) {
+  return MapAnimationTimeline(state, value);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/resolver/css_to_style_map.h b/third_party/blink/renderer/core/css/resolver/css_to_style_map.h
index bda74713..2d17928d 100644
--- a/third_party/blink/renderer/core/css/resolver/css_to_style_map.h
+++ b/third_party/blink/renderer/core/css/resolver/css_to_style_map.h
@@ -134,6 +134,26 @@
   static TimelineOffsetOrAuto MapAnimationTriggerExitRangeEnd(
       StyleResolverState&,
       const CSSValue&);
+  static Persistent<const ScopedCSSName> MapAnimationTimelineTriggerName(
+      StyleResolverState&,
+      const CSSValue&);
+  static EAnimationTriggerBehavior MapAnimationTimelineTriggerBehavior(
+      StyleResolverState&,
+      const CSSValue&);
+  static std::optional<TimelineOffset> MapAnimationTimelineTriggerRangeStart(
+      StyleResolverState&,
+      const CSSValue&);
+  static std::optional<TimelineOffset> MapAnimationTimelineTriggerRangeEnd(
+      StyleResolverState&,
+      const CSSValue&);
+  static TimelineOffsetOrAuto MapAnimationTimelineTriggerExitRangeStart(
+      StyleResolverState&,
+      const CSSValue&);
+  static TimelineOffsetOrAuto MapAnimationTimelineTriggerExitRangeEnd(
+      StyleResolverState&,
+      const CSSValue&);
+  static StyleTimeline MapAnimationTimelineTriggerTimeline(StyleResolverState&,
+                                                           const CSSValue&);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
index 265be1e..c63e58ac 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
@@ -176,6 +176,18 @@
   }
 }
 
+ScopedCSSNameList* ConvertNoneOrCustomIdentList(StyleResolverState& state,
+                                                const CSSValue& value) {
+  DCHECK(value.IsScopedValue());
+  DCHECK(value.IsBaseValueList());
+  HeapVector<Member<const ScopedCSSName>> names;
+  for (const Member<const CSSValue>& item : To<CSSValueList>(value)) {
+    names.push_back(
+        StyleBuilderConverter::ConvertNoneOrCustomIdent(state, *item));
+  }
+  return MakeGarbageCollected<ScopedCSSNameList>(std::move(names));
+}
+
 }  // namespace
 
 StyleReflection* StyleBuilderConverter::ConvertBoxReflect(
@@ -3721,13 +3733,7 @@
 ScopedCSSNameList* StyleBuilderConverter::ConvertViewTimelineName(
     StyleResolverState& state,
     const CSSValue& value) {
-  DCHECK(value.IsScopedValue());
-  DCHECK(value.IsBaseValueList());
-  HeapVector<Member<const ScopedCSSName>> names;
-  for (const Member<const CSSValue>& item : To<CSSValueList>(value)) {
-    names.push_back(ConvertNoneOrCustomIdent(state, *item));
-  }
-  return MakeGarbageCollected<ScopedCSSNameList>(std::move(names));
+  return ConvertNoneOrCustomIdentList(state, value);
 }
 
 ScopedCSSNameList* StyleBuilderConverter::ConvertTimelineScope(
@@ -4024,4 +4030,10 @@
   return FitText(target, method, size_limit);
 }
 
+ScopedCSSNameList* StyleBuilderConverter::ConvertTimelineTriggerName(
+    StyleResolverState& state,
+    const CSSValue& value) {
+  return ConvertNoneOrCustomIdentList(state, value);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
index 9381922..d1430dc2 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h
@@ -429,6 +429,9 @@
       StyleResolverState&,
       const CSSValue&);
   static FitText ConvertFitText(StyleResolverState&, const CSSValue&);
+
+  static ScopedCSSNameList* ConvertTimelineTriggerName(StyleResolverState&,
+                                                       const CSSValue&);
 };
 
 template <typename T>
diff --git a/third_party/blink/renderer/core/exported/web_isolated_world_info.cc b/third_party/blink/renderer/core/exported/web_isolated_world_info.cc
index 5c4831a2..afe370e9 100644
--- a/third_party/blink/renderer/core/exported/web_isolated_world_info.cc
+++ b/third_party/blink/renderer/core/exported/web_isolated_world_info.cc
@@ -34,14 +34,14 @@
 }
 
 WebString GetIsolatedWorldStableId(v8::Local<v8::Context> context) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   const DOMWrapperWorld& world = DOMWrapperWorld::World(isolate, context);
   DCHECK(!world.IsMainWorld());
   return world.NonMainWorldStableId();
 }
 
 WebString GetIsolatedWorldHumanReadableName(v8::Local<v8::Context> context) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   const DOMWrapperWorld& world = DOMWrapperWorld::World(isolate, context);
   DCHECK(!world.IsMainWorld());
   return world.NonMainWorldHumanReadableName();
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 1ce89f4..2c8c8ee 100644
--- a/third_party/blink/renderer/core/exported/web_v8_features.cc
+++ b/third_party/blink/renderer/core/exported/web_v8_features.cc
@@ -41,7 +41,7 @@
     // (crbug.com/976506)
     ContextFeatureSettings::CrashIfMojoJSNotAllowed();
   }
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   ScriptState* script_state = ScriptState::From(isolate, context);
   DCHECK(script_state->World().IsMainWorld() ||
          script_state->World().IsWorkerOrWorkletWorld());
@@ -78,7 +78,7 @@
     // (crbug.com/976506)
     ContextFeatureSettings::CrashIfMojoJSNotAllowed();
   }
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   ScriptState* script_state = ScriptState::From(isolate, context);
   DCHECK(script_state->World().IsMainWorld());
 
@@ -104,7 +104,7 @@
 
 // static
 bool WebV8Features::IsMojoJSEnabledForTesting(v8::Local<v8::Context> context) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   ScriptState* script_state = ScriptState::From(isolate, context);
   DCHECK(script_state->World().IsMainWorld());
   ContextFeatureSettings* settings = ContextFeatureSettings::From(
@@ -116,7 +116,7 @@
 // static
 void WebV8Features::EnableMojoJSWithoutSecurityChecksForTesting(
     v8::Local<v8::Context> context) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   ScriptState* script_state = ScriptState::From(isolate, context);
   DCHECK(script_state->World().IsMainWorld());
   ContextFeatureSettings::From(
diff --git a/third_party/blink/renderer/core/frame/ad_tracker.cc b/third_party/blink/renderer/core/frame/ad_tracker.cc
index c6c3cbf..673a1f8 100644
--- a/third_party/blink/renderer/core/frame/ad_tracker.cc
+++ b/third_party/blink/renderer/core/frame/ad_tracker.cc
@@ -58,7 +58,7 @@
   }
   int contextId = v8_inspector::V8ContextInfo::executionContextId(v8_context);
   ThreadDebugger* thread_debugger =
-      ThreadDebugger::From(v8_context->GetIsolate());
+      ThreadDebugger::From(v8::Isolate::GetCurrent());
   DCHECK(thread_debugger);
   v8_inspector::V8Inspector* inspector = thread_debugger->GetV8Inspector();
   DCHECK(inspector);
diff --git a/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc b/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc
index a40102a..b104613 100644
--- a/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc
+++ b/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc
@@ -799,7 +799,7 @@
   user_entry_point.invoker_type =
       ScriptTimingInfo::InvokerType::kUserEntryPoint;
   user_entry_point.source_location = CaptureScriptSourceLocation(
-      probe_data.callback_object->GetIsolate(), probe_data.callback_object);
+      v8::Isolate::GetCurrent(), probe_data.callback_object);
   PopScriptEntryPointInternal(probe_data.context, probe_data.CaptureEndTime(),
                               user_entry_point);
   user_entry_points_.erase(it);
diff --git a/third_party/blink/renderer/core/frame/deprecation/deprecation.json5 b/third_party/blink/renderer/core/frame/deprecation/deprecation.json5
index 832631e..9c14861 100644
--- a/third_party/blink/renderer/core/frame/deprecation/deprecation.json5
+++ b/third_party/blink/renderer/core/frame/deprecation/deprecation.json5
@@ -387,6 +387,15 @@
       milestone: 108,
     },
     {
+      name: "OverrideFlashEmbedwithHTML",
+      message: "Legacy flash video embed has been rewritten to HTML iframe. Flash is long gone, this rewriting hack is deprecated and may be removed in the future.",
+      translation_note: "Warning displayed to developers when they use a Flash Embed URLS to let them know that the browser will not automatically link to their equivalent HTML5 link.",
+      web_features: [
+        "kOverrideFlashEmbedwithHTML",
+      ],
+      milestone: 140,
+    },
+    {
       name: "PaymentInstruments",
       message: "`paymentManager.instruments` is deprecated. Please use just-in-time install for payment handlers instead.",
       translation_note: "Warning displayed to developers when they use the PaymentInstruments API to let them know this API is deprecated.",
diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
index 7a17873..619567e 100644
--- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
@@ -126,7 +126,7 @@
 v8::MaybeLocal<v8::Value> GetProperty(v8::Local<v8::Context> context,
                                       v8::Local<v8::Value> object,
                                       const String& name) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   v8::Local<v8::String> name_str = V8String(isolate, name);
   v8::Local<v8::Object> object_obj;
   if (!object->ToObject(context).ToLocal(&object_obj)) {
@@ -143,7 +143,7 @@
   v8::Local<v8::Context> context = MainWorldScriptContext(local_frame);
 
   v8::Context::Scope context_scope(context);
-  v8::LocalVector<v8::Value> args(context->GetIsolate());
+  v8::LocalVector<v8::Value> args(v8::Isolate::GetCurrent());
   for (const auto& argument : arguments) {
     args.push_back(converter->ToV8Value(argument, context));
   }
diff --git a/third_party/blink/renderer/core/frame/pausable_script_executor.cc b/third_party/blink/renderer/core/frame/pausable_script_executor.cc
index 2d674ce..19e5c838 100644
--- a/third_party/blink/renderer/core/frame/pausable_script_executor.cc
+++ b/third_party/blink/renderer/core/frame/pausable_script_executor.cc
@@ -231,7 +231,7 @@
     v8::Local<v8::Value> argv[],
     mojom::blink::WantResultOption want_result_option,
     WebScriptExecutionCallback callback) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   ScriptState* script_state = ScriptState::From(isolate, context);
   if (!script_state->ContextIsValid()) {
     if (callback)
@@ -244,8 +244,8 @@
           mojom::blink::LoadEventBlockingOption::kDoNotBlock,
           want_result_option, mojom::blink::PromiseResultOption::kDoNotWait,
           std::move(callback),
-          MakeGarbageCollected<V8FunctionExecutor>(
-              script_state->GetIsolate(), function, receiver, argc, argv));
+          MakeGarbageCollected<V8FunctionExecutor>(isolate, function, receiver,
+                                                   argc, argv));
   executor->Run();
 }
 
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 470cb0a..fa117f2d 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -1151,7 +1151,7 @@
 int32_t WebLocalFrameImpl::GetScriptContextWorldId(
     v8::Local<v8::Context> script_context) const {
   DCHECK_EQ(this, FrameForContext(script_context));
-  v8::Isolate* isolate = script_context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   return DOMWrapperWorld::World(isolate, script_context).GetWorldId();
 }
 
diff --git a/third_party/blink/renderer/core/html/html_embed_element.cc b/third_party/blink/renderer/core/html/html_embed_element.cc
index d1c8f22..dc14d55 100644
--- a/third_party/blink/renderer/core/html/html_embed_element.cc
+++ b/third_party/blink/renderer/core/html/html_embed_element.cc
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/dom/attribute.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
+#include "third_party/blink/renderer/core/frame/deprecation/deprecation.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/html/html_image_loader.h"
@@ -167,7 +168,8 @@
       GetDocument().GetFrame()->Client()->OverrideFlashEmbedWithHTML(
           GetDocument().CompleteURL(url_));
   if (!overriden_url.IsEmpty()) {
-    UseCounter::Count(GetDocument(), WebFeature::kOverrideFlashEmbedwithHTML);
+    Deprecation::CountDeprecation(GetDocument().GetExecutionContext(),
+                                      WebFeature::kOverrideFlashEmbedwithHTML);
     url_ = overriden_url.GetString();
     SetServiceType("text/html");
   }
diff --git a/third_party/blink/renderer/core/html/html_object_element.cc b/third_party/blink/renderer/core/html/html_object_element.cc
index c3ea63d..d7613da0 100644
--- a/third_party/blink/renderer/core/html/html_object_element.cc
+++ b/third_party/blink/renderer/core/html/html_object_element.cc
@@ -33,6 +33,7 @@
 #include "third_party/blink/renderer/core/dom/tag_collection.h"
 #include "third_party/blink/renderer/core/dom/text.h"
 #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
+#include "third_party/blink/renderer/core/frame/deprecation/deprecation.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
@@ -221,7 +222,8 @@
       GetDocument().GetFrame()->Client()->OverrideFlashEmbedWithHTML(
           GetDocument().CompleteURL(url_));
   if (!overriden_url.IsEmpty()) {
-    UseCounter::Count(GetDocument(), WebFeature::kOverrideFlashEmbedwithHTML);
+    Deprecation::CountDeprecation(GetDocument().GetExecutionContext(),
+                                  WebFeature::kOverrideFlashEmbedwithHTML);
     url_ = overriden_url.GetString();
     SetServiceType("text/html");
   }
diff --git a/third_party/blink/renderer/core/inspector/exception_metadata.cc b/third_party/blink/renderer/core/inspector/exception_metadata.cc
index 6ef8a711..56e9e79d2 100644
--- a/third_party/blink/renderer/core/inspector/exception_metadata.cc
+++ b/third_party/blink/renderer/core/inspector/exception_metadata.cc
@@ -26,8 +26,8 @@
   if (!exception->IsObject()) {
     return;
   }
-  v8::Local<v8::Object> object = exception.As<v8::Object>();
-  v8::Isolate* isolate = object->GetIsolate();
+
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   ThreadDebugger* debugger = ThreadDebugger::From(isolate);
   debugger->GetV8Inspector()->associateExceptionData(
       v8::Local<v8::Context>(), exception, V8String(isolate, key),
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
index b876087..32c0f30 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
@@ -445,7 +445,7 @@
   v8::Context::Scope scope(context);
   V8EventListenerInfoList event_information;
   InspectorDOMDebuggerAgent::EventListenersInfoForTarget(
-      context->GetIsolate(), object, depth.value_or(1), pierce.value_or(false),
+      isolate_, object, depth.value_or(1), pierce.value_or(false),
       dom_agent_->IncludeWhitespace(), &event_information);
   *listeners_array = BuildObjectsForEventListeners(event_information, context,
                                                    object_group->string());
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
index 011d019..ef665e1 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -115,7 +115,7 @@
 v8::MaybeLocal<v8::Value> GetV8Property(v8::Local<v8::Context> context,
                                         v8::Local<v8::Value> object,
                                         const String& name) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   v8::Local<v8::String> name_str = V8String(isolate, name);
   v8::Local<v8::Object> object_obj;
   if (!object->ToObject(context).ToLocal(&object_obj)) {
@@ -1450,13 +1450,12 @@
   v8::Local<v8::Context> context = script_state->GetContext();
   v8::Context::Scope context_scope(context);
 
-  v8::LocalVector<v8::Value> args(context->GetIsolate());
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::LocalVector<v8::Value> args(isolate);
   int args_length = 2;
-  v8::Local<v8::Array> params(
-      v8::Array::New(context->GetIsolate(), args_length));
-  v8::Local<v8::Value> local_method(V8String(context->GetIsolate(), method));
-  v8::Local<v8::Value> local_argument(
-      V8String(context->GetIsolate(), argument));
+  v8::Local<v8::Array> params(v8::Array::New(isolate, args_length));
+  v8::Local<v8::Value> local_method(V8String(isolate, method));
+  v8::Local<v8::Value> local_argument(V8String(isolate, argument));
   params->CreateDataProperty(context, 0, local_method).Check();
   params->CreateDataProperty(context, 1, local_argument).Check();
   args.push_back(params);
diff --git a/third_party/blink/renderer/core/inspector/resolve_node.cc b/third_party/blink/renderer/core/inspector/resolve_node.cc
index 3cce375b..a41570a1 100644
--- a/third_party/blink/renderer/core/inspector/resolve_node.cc
+++ b/third_party/blink/renderer/core/inspector/resolve_node.cc
@@ -17,7 +17,7 @@
 namespace blink {
 
 v8::Local<v8::Value> NodeV8Value(v8::Local<v8::Context> context, Node* node) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   if (!node ||
       !BindingSecurity::ShouldAllowAccessTo(CurrentDOMWindow(isolate), node)) {
     return v8::Null(isolate);
diff --git a/third_party/blink/renderer/core/inspector/thread_debugger_common_impl.cc b/third_party/blink/renderer/core/inspector/thread_debugger_common_impl.cc
index cdd9370a..0b9616a 100644
--- a/third_party/blink/renderer/core/inspector/thread_debugger_common_impl.cc
+++ b/third_party/blink/renderer/core/inspector/thread_debugger_common_impl.cc
@@ -485,7 +485,7 @@
   v8::MaybeLocal<v8::Value> include_shadow_tree_parameter =
       additional_parameters->Get(
           context,
-          V8String(context->GetIsolate(), kIncludeShadowTreeParameterName));
+          V8String(v8::Isolate::GetCurrent(), kIncludeShadowTreeParameterName));
   if (!include_shadow_tree_parameter.IsEmpty()) {
     v8::Local<v8::Value> include_shadow_tree_value =
         include_shadow_tree_parameter.ToLocalChecked();
@@ -496,8 +496,9 @@
                     " should be of type string."}));
         return false;
       }
-      String include_shadow_tree_string = ToCoreString(
-          context->GetIsolate(), include_shadow_tree_value.As<v8::String>());
+      String include_shadow_tree_string =
+          ToCoreString(v8::Isolate::GetCurrent(),
+                       include_shadow_tree_value.As<v8::String>());
 
       if (include_shadow_tree_string == kIncludeShadowTreeValueNone) {
         include_shadow_tree = ShadowTreeSerialization::kNone;
@@ -515,8 +516,8 @@
   }
 
   v8::MaybeLocal<v8::Value> max_node_depth_parameter =
-      additional_parameters->Get(
-          context, V8String(context->GetIsolate(), kMaxNodeDepthParameterName));
+      additional_parameters->Get(context, V8String(v8::Isolate::GetCurrent(),
+                                                   kMaxNodeDepthParameterName));
   if (!max_node_depth_parameter.IsEmpty()) {
     v8::Local<v8::Value> max_node_depth_value =
         max_node_depth_parameter.ToLocalChecked();
@@ -695,9 +696,9 @@
                                           v8::Local<v8::Object> object,
                                           v8::Local<v8::Name> key,
                                           v8::Local<v8::Value> value) {
-  v8::TryCatch try_catch(context->GetIsolate());
+  v8::TryCatch try_catch(v8::Isolate::GetCurrent());
   v8::Isolate::DisallowJavascriptExecutionScope throw_js(
-      context->GetIsolate(),
+      v8::Isolate::GetCurrent(),
       v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
   return object->CreateDataProperty(context, key, value);
 }
@@ -710,7 +711,7 @@
     v8::Local<v8::Value> data,
     const char* description,
     v8::SideEffectType side_effect_type) {
-  v8::Local<v8::String> func_name = V8String(context->GetIsolate(), name);
+  v8::Local<v8::String> func_name = V8String(v8::Isolate::GetCurrent(), name);
   v8::Local<v8::Function> func;
   if (!v8::Function::New(context, callback, data, 0,
                          v8::ConstructorBehavior::kThrow, side_effect_type)
@@ -718,14 +719,14 @@
     return;
   func->SetName(func_name);
   v8::Local<v8::String> return_value =
-      V8String(context->GetIsolate(), description);
+      V8String(v8::Isolate::GetCurrent(), description);
   v8::Local<v8::Function> to_string_function;
   if (v8::Function::New(context, ReturnDataCallback, return_value, 0,
                         v8::ConstructorBehavior::kThrow,
                         v8::SideEffectType::kHasNoSideEffect)
           .ToLocal(&to_string_function))
     CreateDataProperty(context, func,
-                       V8AtomicString(context->GetIsolate(), "toString"),
+                       V8AtomicString(v8::Isolate::GetCurrent(), "toString"),
                        to_string_function);
   CreateDataProperty(context, object, func_name, func);
 }
@@ -735,9 +736,9 @@
     v8::Local<v8::Array> array,
     int index,
     v8::Local<v8::Value> value) {
-  v8::TryCatch try_catch(context->GetIsolate());
+  v8::TryCatch try_catch(v8::Isolate::GetCurrent());
   v8::Isolate::DisallowJavascriptExecutionScope throw_js(
-      context->GetIsolate(),
+      v8::Isolate::GetCurrent(),
       v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
   return array->CreateDataProperty(context, index, value);
 }
@@ -749,9 +750,10 @@
     v8::FunctionCallback callback,
     const char* description,
     v8::SideEffectType side_effect_type) {
-  CreateFunctionPropertyWithData(context, object, name, callback,
-                                 v8::External::New(context->GetIsolate(), this),
-                                 description, side_effect_type);
+  CreateFunctionPropertyWithData(
+      context, object, name, callback,
+      v8::External::New(v8::Isolate::GetCurrent(), this), description,
+      side_effect_type);
 }
 
 void ThreadDebuggerCommonImpl::installAdditionalCommandLineAPI(
@@ -775,7 +777,7 @@
       "function getAccessibleRole(node) { [Command Line API] }",
       v8::SideEffectType::kHasNoSideEffect);
 
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   ScriptEvaluationResult result =
       ClassicScript::CreateUnspecifiedScript(
           "(function(e) { console.log(e.type, e); })",
diff --git a/third_party/blink/renderer/core/script/detect_javascript_frameworks.cc b/third_party/blink/renderer/core/script/detect_javascript_frameworks.cc
index 750ad043..2615abfde 100644
--- a/third_party/blink/renderer/core/script/detect_javascript_frameworks.cc
+++ b/third_party/blink/renderer/core/script/detect_javascript_frameworks.cc
@@ -37,7 +37,7 @@
 
 bool IsFrameworkVariableUsed(v8::Local<v8::Context> context,
                              const String& framework_variable_name) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   v8::Local<v8::Object> global = context->Global();
   v8::TryCatch try_catch(isolate);
   bool has_property;
diff --git a/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.cc b/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.cc
index 7d2b2b3..62f1dfb 100644
--- a/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.cc
+++ b/third_party/blink/renderer/core/script/value_wrapper_synthetic_module_script.cc
@@ -180,7 +180,7 @@
 v8::MaybeLocal<v8::Value> ValueWrapperSyntheticModuleScript::EvaluationSteps(
     v8::Local<v8::Context> context,
     v8::Local<v8::Module> module) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   ScriptState* script_state = ScriptState::From(isolate, context);
   Modulator* modulator = Modulator::From(script_state);
   ModuleRecordResolver* module_record_resolver =
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
index f082730..d9afc57 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
@@ -39,7 +39,7 @@
                          v8::Local<v8::Object> constructor,
                          Vector<CSSSyntaxDefinition>* input_argument_types,
                          ExceptionState& exception_state) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   TryRethrowScope rethrow_scope(isolate, exception_state);
 
   if (RuntimeEnabledFeatures::CSSPaintAPIArgumentsEnabled()) {
@@ -76,7 +76,7 @@
     v8::Local<v8::Context> context,
     v8::Local<v8::Object> constructor,
     ExceptionState& exception_state) {
-  v8::Isolate* isolate = context->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   TryRethrowScope rethrow_scope(isolate, exception_state);
 
   v8::Local<v8::Value> context_settings_value;
diff --git a/third_party/blink/renderer/platform/bindings/callback_function_base.cc b/third_party/blink/renderer/platform/bindings/callback_function_base.cc
index 28f8aebd..c45eb73 100644
--- a/third_party/blink/renderer/platform/bindings/callback_function_base.cc
+++ b/third_party/blink/renderer/platform/bindings/callback_function_base.cc
@@ -16,7 +16,7 @@
     v8::Local<v8::Object> callback_function) {
   DCHECK(!callback_function.IsEmpty());
 
-  v8::Isolate* isolate = callback_function->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   callback_function_.Reset(isolate, callback_function);
 
   incumbent_script_state_ =
diff --git a/third_party/blink/renderer/platform/bindings/callback_interface_base.cc b/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
index 17e4d6ad..c492784f 100644
--- a/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
+++ b/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
@@ -16,7 +16,7 @@
     SingleOperationOrNot single_op_or_not) {
   DCHECK(!callback_object.IsEmpty());
 
-  v8::Isolate* isolate = callback_object->GetIsolate();
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
   callback_object_.Reset(isolate, callback_object);
 
   incumbent_script_state_ =
diff --git a/third_party/blink/renderer/platform/bindings/script_state.cc b/third_party/blink/renderer/platform/bindings/script_state.cc
index 58aa3d5b..2d5df76 100644
--- a/third_party/blink/renderer/platform/bindings/script_state.cc
+++ b/third_party/blink/renderer/platform/bindings/script_state.cc
@@ -30,7 +30,7 @@
 ScriptState::ScriptState(v8::Local<v8::Context> context,
                          DOMWrapperWorld* world,
                          ExecutionContext* execution_context)
-    : isolate_(context->GetIsolate()),
+    : isolate_(v8::Isolate::GetCurrent()),
       context_(isolate_, context),
       world_(world),
       per_context_data_(MakeGarbageCollected<V8PerContextData>(context)) {
diff --git a/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc b/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc
index fe76a2f..34e0443 100644
--- a/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc
+++ b/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc
@@ -52,7 +52,7 @@
 }  // namespace
 
 V8PerContextData::V8PerContextData(v8::Local<v8::Context> context)
-    : isolate_(context->GetIsolate()),
+    : isolate_(v8::Isolate::GetCurrent()),
       context_holder_(std::make_unique<gin::ContextHolder>(isolate_)),
       context_(isolate_, context),
       activity_logger_(nullptr) {
diff --git a/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc b/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc
index b7e24c4..a12a0d67 100644
--- a/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc
+++ b/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc
@@ -52,7 +52,7 @@
 
 const WrapperTypeInfo* ToWrapperTypeInfo(v8::Local<v8::Object> wrapper) {
   const v8::Object::Wrappable* wrappable =
-      ToAnyWrappable(wrapper->GetIsolate(), wrapper);
+      ToAnyWrappable(v8::Isolate::GetCurrent(), wrapper);
   // It's either us or legacy embedders
   DCHECK(!wrappable || !WrapperTypeInfo::HasLegacyInternalFieldsSet(wrapper));
   if (!wrappable) {
diff --git a/third_party/blink/renderer/platform/heap_observer_list.h b/third_party/blink/renderer/platform/heap_observer_list.h
index 89a78c9d..084201e3 100644
--- a/third_party/blink/renderer/platform/heap_observer_list.h
+++ b/third_party/blink/renderer/platform/heap_observer_list.h
@@ -5,50 +5,76 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_OBSERVER_LIST_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_OBSERVER_LIST_H_
 
+#include <algorithm>
+
 #include "base/auto_reset.h"
+#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
 
 namespace blink {
 
-// A list of observers. Ensures list is not mutated while iterating. Observers
-// are not retained by the list. The implementation favors performance of
-// iteration instead of modifications.
+// A list of observers. Ensures that the list is not mutated while iterating.
+// Observers are not retained by the list. The implementation favors performance
+// of iteration over modifications via add and remove.
 template <class ObserverType>
 class PLATFORM_EXPORT HeapObserverList final {
   DISALLOW_NEW();
 
  public:
-  // Add an observer to this list. An observer must not be added to the same
-  // list more than once. Adding an observer is O(n).
+  // Adds an observer to this list. An observer must not be added to the same
+  // list more than once. Adding an observer is O(n) but amortized constant.
   void AddObserver(ObserverType* observer) {
     CHECK(mutation_state_ & kAllowAddition);
     DCHECK(!HasObserver(observer));
+    DCHECK_EQ(observers_to_indices_.size(), observers_.size());
+    // `push_back()` may trigger GC which results in `observers_to_indices_`
+    // missing the to-be-added observer.
     observers_.push_back(observer);
+    observers_to_indices_.insert(observer, observers_.size() - 1);
     if (check_capacity_) [[unlikely]] {
       // On first addition after GC, check whether we need to shrink to a
       // reasoanble capacity.
       observers_.ShrinkToReasonableCapacity();
+      // observers_to_indices_ did already shrink if necessary due to insert().
       check_capacity_ = false;
     }
   }
 
   // Removes the given observer from this list. Does nothing if this observer is
-  // not in this list. Removing an observer is O(n).
+  // not in this list. Removing an observer is O(n) but amortized constant.
   void RemoveObserver(ObserverType* observer) {
     CHECK(mutation_state_ & kAllowRemoval);
-    const auto it = std::find(observers_.begin(), observers_.end(), observer);
-    if (it != observers_.end()) {
-      observers_.erase(it);
+    DCHECK_EQ(observers_to_indices_.size(), observers_.size());
+
+    const auto it = observers_to_indices_.find(observer);
+    if (it == observers_to_indices_.end()) [[unlikely]] {
+      return;
     }
+    const wtf_size_t index = it->value;
+    // `erase()` may trigger GC which results in `observers_to_indices_` already
+    // missing the to-be-deleted observer.
+    observers_to_indices_.erase(it);
+
+    const wtf_size_t last_observer_index = observers_.size() - 1;
+    if (index != last_observer_index) {
+      auto* last_observer = observers_[last_observer_index].Get();
+      observers_[index] = last_observer;
+      observers_to_indices_.find(last_observer)->value = index;
+    }
+    observers_.pop_back();
+    observers_.ShrinkToReasonableCapacity();
+    // observers_to_indices_ did already shrink if necessary due to erase().
+    check_capacity_ = false;
   }
 
   // Determine whether a particular observer is in the list. Checking
-  // containment is O(n).
+  // containment is O(1).
   bool HasObserver(ObserverType* observer) const {
     DCHECK(!IsIteratingOverObservers());
-    return observers_.Contains(observer);
+    return observers_to_indices_.Contains(observer);
   }
 
   // Returns true if the list is being iterated over.
@@ -58,6 +84,7 @@
   void Clear() {
     CHECK(mutation_state_ & kAllowRemoval);
     observers_.clear();
+    observers_to_indices_.clear();
   }
 
   // Iterate over the registered lifecycle observers in an unpredictable order.
@@ -66,9 +93,11 @@
   // will be called synchronously inside `ForEachObserver()`.
   //
   // Sample usage:
-  //     ForEachObserver([](ObserverType* observer) {
-  //       observer->SomeMethod();
-  //     });
+  // ```
+  // ForEachObserver([](ObserverType* observer) {
+  //   observer->SomeMethod();
+  // });
+  // ```
   template <typename ForEachCallable>
   void ForEachObserver(const ForEachCallable& callable) const {
     base::AutoReset<MutationState> scope(&mutation_state_, kNoMutationAllowed);
@@ -84,13 +113,10 @@
     visitor->Trace(observers_);
     visitor->RegisterWeakCallbackMethod<
         HeapObserverList, &HeapObserverList::CleanupDeadObservers>(this);
+    visitor->Trace(observers_to_indices_);
   }
 
  private:
-  // HeapVector doesn't allow WeakMember at this point. Instead, use a
-  // UntracedMember with a custom weak callback.
-  using ObserverList = HeapVector<UntracedMember<ObserverType>>;
-
   void CleanupDeadObservers(const LivenessBroker& broker) {
     // This method must not allocate.
 
@@ -111,9 +137,25 @@
       observers_.erase(
           std::remove_if(observers_.begin(), observers_.end(),
                          [broker](auto& observer) {
-                           return !broker.IsHeapObjectAlive(observer);
+                           // If there's no iteration allowed we just `Clear()`
+                           // the entry which writes nullptr.
+                           // `IsHeapObjectAlive()` then treats nullptr as live
+                           // object for other reasons. Consequently, we require
+                           // an explicit nullptr check here.
+                           return !observer ||
+                                  !broker.IsHeapObjectAlive(observer);
                          }),
           observers_.end());
+      // We also need to fix up the indices map. This works because we only
+      // query and update live indices.
+      for (wtf_size_t i = 0; i < observers_.size(); ++i) {
+        // The callback here may be executed while `observers_to_indices_`
+        // misses out on the observer that is either added or removed.
+        const auto it = observers_to_indices_.find(observers_[i]);
+        if (it != observers_to_indices_.end()) [[likely]] {
+          it->value = i;
+        }
+      }
       check_capacity_ = true;
     }
   }
@@ -131,7 +173,13 @@
   // observers. Iteration e.g. prohibits any mutations.
   mutable MutationState mutation_state_ = kAllowAll;
   bool check_capacity_ = false;
-  ObserverList observers_;
+  // Compact list of observers used for iteration. HeapVector doesn't allow
+  // WeakMember at this point. Instead, use UntracedMember with a custom weak
+  // callback.
+  HeapVector<UntracedMember<ObserverType>> observers_;
+  // Mapping observers to indices in `observers_`. Observers may be missing in
+  // here temporarily as modification treats `observers_` as source of truth.
+  HeapHashMap<WeakMember<ObserverType>, wtf_size_t> observers_to_indices_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/peerconnection/DEPS b/third_party/blink/renderer/platform/peerconnection/DEPS
index ec6d916..0ce8306 100644
--- a/third_party/blink/renderer/platform/peerconnection/DEPS
+++ b/third_party/blink/renderer/platform/peerconnection/DEPS
@@ -61,7 +61,4 @@
         "+third_party/blink/renderer/platform/testing/task_environment.h",
         "+third_party/blink/renderer/platform/testing/fuzzed_data_provider.h",
     ],
-    "webrtc_video_track_source_test.cc" : [
-        "+gpu/command_buffer/client/fake_gpu_memory_buffer.h"
-    ]
 }
diff --git a/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc b/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc
index 1191bfe..3a746fb 100644
--- a/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc
+++ b/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source_test.cc
@@ -13,8 +13,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "components/viz/common/resources/shared_image_format_utils.h"
-// TODO(crbug.com/421746653): Remove.
-#include "gpu/command_buffer/client/fake_gpu_memory_buffer.h"
+#include "gpu/command_buffer/client/client_shared_image.h"
 #include "gpu/command_buffer/client/test_shared_image_interface.h"
 #include "media/base/format_utils.h"
 #include "media/base/media_switches.h"
@@ -62,7 +61,7 @@
 class WebRtcVideoTrackSourceTest
     : public ::testing::TestWithParam<
           std::tuple<media::VideoFrame::StorageType, media::VideoPixelFormat>>,
-      public gpu::FakeGpuMemoryBuffer::MapCallbackController {
+      public gpu::ClientSharedImage::MapCallbackControllerForTesting {
  public:
   WebRtcVideoTrackSourceTest()
       : shared_resources_(
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index df15eb7..382c9749 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -4211,7 +4211,7 @@
     },
     {
       name: "SecurePaymentConfirmationBrowserBoundKeys",
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "SecurePaymentConfirmationDebug",
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/animation-trigger-name-parsing.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/animation-trigger-name-parsing.html
new file mode 100644
index 0000000..c2a746b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/animation-trigger-name-parsing.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<div id="target"></div>
+<script>
+test_valid_value('timeline-trigger-name', 'initial');
+test_valid_value('timeline-trigger-name', 'inherit');
+test_valid_value('timeline-trigger-name', 'unset');
+test_valid_value('timeline-trigger-name', 'revert');
+
+test_valid_value('timeline-trigger-name', 'none');
+test_valid_value('timeline-trigger-name', '--abc');
+test_valid_value('timeline-trigger-name', '  --abc', '--abc');
+test_valid_value('timeline-trigger-name', '--aBc');
+test_valid_value('timeline-trigger-name', '--foo, --bar');
+test_valid_value('timeline-trigger-name', '--bar, --foo');
+test_valid_value('timeline-trigger-name', 'none, none');
+test_valid_value('timeline-trigger-name', '--a, none, --b');
+
+test_computed_value('timeline-trigger-name', 'none');
+test_computed_value('timeline-trigger-name', '--abc');
+test_computed_value('timeline-trigger-name', '  --abc', '--abc');
+test_computed_value('timeline-trigger-name', '--aBc');
+test_computed_value('timeline-trigger-name', '--foo, --bar');
+test_computed_value('timeline-trigger-name', '--bar, --foo');
+test_computed_value('timeline-trigger-name', 'none, none');
+test_computed_value('timeline-trigger-name', '--a, none, --b');
+
+test_invalid_value('timeline-trigger-name', 'auto');
+test_invalid_value('timeline-trigger-name', 'abc');
+test_invalid_value('timeline-trigger-name', 'default');
+test_invalid_value('timeline-trigger-name', '10px');
+test_invalid_value('timeline-trigger-name', 'foo bar');
+test_invalid_value('timeline-trigger-name', '"foo" "bar"');
+test_invalid_value('timeline-trigger-name', 'rgb(1, 2, 3)');
+test_invalid_value('timeline-trigger-name', '#fefefe');
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-behavior-computed.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-behavior-computed.tentative.html
new file mode 100644
index 0000000..618ac062
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-behavior-computed.tentative.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Animations: getComputedStyle().animationTriggerBehavior</title>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<meta name="assert" content="timeline-trigger-type computed value is as specified.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("timeline-trigger-behavior", "initial", "once");
+test_computed_value("timeline-trigger-behavior", "once, repeat, alternate, state");
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-behavior-valid.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-behavior-valid.tentative.html
new file mode 100644
index 0000000..7d710049
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-behavior-valid.tentative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Animations: parsing timeline-trigger-behavior with valid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<meta name="assert" content="animation-composition supports the full grammar '<single-timeline-trigger-behavior> #'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value("timeline-trigger-behavior", "once");
+test_valid_value("timeline-trigger-behavior", "repeat");
+test_valid_value("timeline-trigger-behavior", "alternate");
+test_valid_value("timeline-trigger-behavior", "state");
+test_valid_value("timeline-trigger-behavior", "once, repeat");
+test_valid_value("timeline-trigger-behavior", "once, repeat, alternate");
+test_valid_value("timeline-trigger-behavior", "once, repeat, alternate, state");
+
+test_invalid_value("timeline-trigger-behavior", "oncerepeat");
+test_invalid_value("timeline-trigger-behavior", "repeatalternate");
+test_invalid_value("timeline-trigger-behavior", "once, repeatalternate");
+test_invalid_value("timeline-trigger-behavior", "junk");
+test_invalid_value("timeline-trigger-behavior", "enco, taeper, etanretla, etats");
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-end-invalid.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-end-invalid.tentative.html
new file mode 100644
index 0000000..dd5fcfa2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-end-invalid.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script>
+test_invalid_value("timeline-trigger-exit-range-end", "infinite");
+test_invalid_value("timeline-trigger-exit-range-end", "1s 2s");
+test_invalid_value("timeline-trigger-exit-range-end", "1s / 2s");
+
+test_invalid_value("timeline-trigger-exit-range-end", "peek 50%");
+test_invalid_value("timeline-trigger-exit-range-end", "50% contain");
+test_invalid_value("timeline-trigger-exit-range-end", "50% cover");
+test_invalid_value("timeline-trigger-exit-range-end", "50% entry");
+test_invalid_value("timeline-trigger-exit-range-end", "50% enter");
+test_invalid_value("timeline-trigger-exit-range-end", "50% exit");
+test_invalid_value("timeline-trigger-exit-range-end", "contain contain");
+test_invalid_value("timeline-trigger-exit-range-end", "none");
+test_invalid_value("timeline-trigger-exit-range-end", "cover 50% enter 50%");
+test_invalid_value("timeline-trigger-exit-range-end", "auto contain");
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-end-valid.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-end-valid.tentative.html
new file mode 100644
index 0000000..7ee8c9f2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-end-valid.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script>
+// https://drafts.csswg.org/scroll-animations-1/#view-timelines-ranges
+test_valid_value("timeline-trigger-exit-range-end", "auto");
+test_valid_value("timeline-trigger-exit-range-end", "normal");
+test_valid_value("timeline-trigger-exit-range-end", "cover 0%");
+test_valid_value("timeline-trigger-exit-range-end", "cover 100%", "cover");
+test_valid_value("timeline-trigger-exit-range-end", "cover 120%");
+test_valid_value("timeline-trigger-exit-range-end", "cover 42%");
+test_valid_value("timeline-trigger-exit-range-end", "0", "0px");
+test_valid_value("timeline-trigger-exit-range-end", "120%");
+test_valid_value("timeline-trigger-exit-range-end", "120px");
+test_valid_value("timeline-trigger-exit-range-end", "cover -42%");
+test_valid_value("timeline-trigger-exit-range-end", "contain 42%");
+test_valid_value("timeline-trigger-exit-range-end", "exit 42%");
+test_valid_value("timeline-trigger-exit-range-end", "exit 1%, cover 2%, contain 0%");
+test_valid_value("timeline-trigger-exit-range-end", "exit 1%, cover 2%, contain 100%", "exit 1%, cover 2%, contain");
+test_valid_value("timeline-trigger-exit-range-end", "exit-crossing 42%");
+test_valid_value("timeline-trigger-exit-range-end", "entry 42px");
+test_valid_value("timeline-trigger-exit-range-end", "entry-crossing 42px");
+test_valid_value("timeline-trigger-exit-range-end", "contain calc(10px + 10%)", "contain calc(10% + 10px)");
+test_valid_value("timeline-trigger-exit-range-end", "entry 1em");
+test_valid_value("timeline-trigger-exit-range-end", "exit calc(1em + 10px)");
+test_valid_value("timeline-trigger-exit-range-end", "entry 42%");
+test_valid_value("timeline-trigger-exit-range-end", "cover");
+test_valid_value("timeline-trigger-exit-range-end", "contain");
+test_valid_value("timeline-trigger-exit-range-end", "entry");
+test_valid_value("timeline-trigger-exit-range-end", "exit");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-end.computed.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-end.computed.tentative.html
new file mode 100644
index 0000000..9033304
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-end.computed.tentative.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<div id="target" style="font-size:10px;"></div>
+<script>
+test_computed_value("timeline-trigger-exit-range-end", "initial", "auto");
+test_computed_value("timeline-trigger-exit-range-end", "auto");
+test_computed_value("timeline-trigger-exit-range-end", "normal");
+test_computed_value("timeline-trigger-exit-range-end", "cover 0%");
+test_computed_value("timeline-trigger-exit-range-end", "cover 100%", "cover");
+test_computed_value("timeline-trigger-exit-range-end", "COVER 0%", "cover 0%");
+test_computed_value("timeline-trigger-exit-range-end", "COVER 100%", "cover");
+test_computed_value("timeline-trigger-exit-range-end", "cover 120%");
+test_computed_value("timeline-trigger-exit-range-end", "0", "0px");
+test_computed_value("timeline-trigger-exit-range-end", "120%");
+test_computed_value("timeline-trigger-exit-range-end", "120px");
+test_computed_value("timeline-trigger-exit-range-end", "cover 42%");
+test_computed_value("timeline-trigger-exit-range-end", "cover -42%");
+test_computed_value("timeline-trigger-exit-range-end", "contain 42%");
+test_computed_value("timeline-trigger-exit-range-end", "exit 42%");
+test_computed_value("timeline-trigger-exit-range-end", "exit calc(41% + 1%)", "exit 42%");
+test_computed_value("timeline-trigger-exit-range-end", "exit-crossing 42%");
+test_computed_value("timeline-trigger-exit-range-end", "exit 1%, cover 2%, contain 0%");
+test_computed_value("timeline-trigger-exit-range-end", "exit 1%, cover 2%, contain 100%", "exit 1%, cover 2%, contain");
+test_computed_value("timeline-trigger-exit-range-end", "entry 42px");
+test_computed_value("timeline-trigger-exit-range-end", "entry-crossing 42px");
+
+test_computed_value("timeline-trigger-exit-range-end", "contain calc(10% + 10px)");
+test_computed_value("timeline-trigger-exit-range-end", "entry 1em", "entry 10px");
+test_computed_value("timeline-trigger-exit-range-end", "exit calc(1em + 10px)", "exit 20px");
+test_computed_value("timeline-trigger-exit-range-end", "cover");
+test_computed_value("timeline-trigger-exit-range-end", "contain");
+test_computed_value("timeline-trigger-exit-range-end", "entry");
+test_computed_value("timeline-trigger-exit-range-end", "exit");
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-start-computed.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-start-computed.tentative.html
new file mode 100644
index 0000000..fd6878a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-start-computed.tentative.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<div id="target" style="font-size:10px;"></div>
+<script>
+test_computed_value("timeline-trigger-exit-range-start", "initial", "auto");
+test_computed_value("timeline-trigger-exit-range-start", "auto");
+test_computed_value("timeline-trigger-exit-range-start", "normal");
+test_computed_value("timeline-trigger-exit-range-start", "cover 0%", "cover");
+test_computed_value("timeline-trigger-exit-range-start", "cover 100%");
+test_computed_value("timeline-trigger-exit-range-start", "COVER 0%", "cover");
+test_computed_value("timeline-trigger-exit-range-start", "COVER 100%", "cover 100%");
+test_computed_value("timeline-trigger-exit-range-start", "cover 120%");
+test_computed_value("timeline-trigger-exit-range-start", "cover 42%");
+test_computed_value("timeline-trigger-exit-range-start", "0", "0px");
+test_computed_value("timeline-trigger-exit-range-start", "120%");
+test_computed_value("timeline-trigger-exit-range-start", "120px");
+test_computed_value("timeline-trigger-exit-range-start", "cover -42%");
+test_computed_value("timeline-trigger-exit-range-start", "contain 42%");
+test_computed_value("timeline-trigger-exit-range-start", "exit 42%");
+test_computed_value("timeline-trigger-exit-range-start", "exit calc(41% + 1%)", "exit 42%");
+test_computed_value("timeline-trigger-exit-range-start", "exit 1%, cover 2%, contain 0%", "exit 1%, cover 2%, contain");
+test_computed_value("timeline-trigger-exit-range-start", "exit 1%, cover 2%, contain 100%");
+test_computed_value("timeline-trigger-exit-range-start", "exit-crossing 42%");
+test_computed_value("timeline-trigger-exit-range-start", "entry 42px");
+test_computed_value("timeline-trigger-exit-range-start", "entry-crossing 42px");
+test_computed_value("timeline-trigger-exit-range-start", "contain calc(10% + 10px)");
+test_computed_value("timeline-trigger-exit-range-start", "entry 1em", "entry 10px");
+test_computed_value("timeline-trigger-exit-range-start", "exit calc(1em + 10px)", "exit 20px");
+test_computed_value("timeline-trigger-exit-range-start", "cover");
+test_computed_value("timeline-trigger-exit-range-start", "contain");
+test_computed_value("timeline-trigger-exit-range-start", "entry");
+test_computed_value("timeline-trigger-exit-range-start", "exit");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-start-invalid.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-start-invalid.tentative.html
new file mode 100644
index 0000000..931e1ac
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-start-invalid.tentative.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script>
+test_invalid_value("timeline-trigger-exit-range-start", "peek 50%");
+test_invalid_value("timeline-trigger-exit-range-start", "50% contain");
+test_invalid_value("timeline-trigger-exit-range-start", "50% cover");
+test_invalid_value("timeline-trigger-exit-range-start", "50% entry");
+test_invalid_value("timeline-trigger-exit-range-start", "50% enter");
+test_invalid_value("timeline-trigger-exit-range-start", "50% exit");
+test_invalid_value("timeline-trigger-exit-range-start", "contain contain");
+test_invalid_value("timeline-trigger-exit-range-start", "none");
+test_invalid_value("timeline-trigger-exit-range-start", "cover 50% enter 50%");
+test_invalid_value("timeline-trigger-exit-range-start", "auto contain");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-start-valid.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-start-valid.tentative.html
new file mode 100644
index 0000000..23700ca
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-exit-range-start-valid.tentative.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script>
+// https://drafts.csswg.org/scroll-animations-1/#view-timelines-ranges
+test_valid_value("timeline-trigger-exit-range-start", "auto");
+test_valid_value("timeline-trigger-exit-range-start", "normal");
+test_valid_value("timeline-trigger-exit-range-start", "cover 0%", "cover");
+test_valid_value("timeline-trigger-exit-range-start", "cover 100%");
+test_valid_value("timeline-trigger-exit-range-start", "cover 120%");
+test_valid_value("timeline-trigger-exit-range-start", "cover 42%");
+test_valid_value("timeline-trigger-exit-range-start", "0", "0px");
+test_valid_value("timeline-trigger-exit-range-start", "120%");
+test_valid_value("timeline-trigger-exit-range-start", "120px");
+test_valid_value("timeline-trigger-exit-range-start", "cover -42%");
+test_valid_value("timeline-trigger-exit-range-start", "contain 42%");
+test_valid_value("timeline-trigger-exit-range-start", "exit 42%");
+test_valid_value("timeline-trigger-exit-range-start", "exit 1%, cover 2%, contain 0%", "exit 1%, cover 2%, contain");
+test_valid_value("timeline-trigger-exit-range-start", "exit 1%, cover 2%, contain 100%");
+test_valid_value("timeline-trigger-exit-range-start", "exit-crossing 42%");
+test_valid_value("timeline-trigger-exit-range-start", "entry 42px");
+test_valid_value("timeline-trigger-exit-range-start", "entry-crossing 42px");
+test_valid_value("timeline-trigger-exit-range-start", "contain calc(10px + 10%)", "contain calc(10% + 10px)");
+test_valid_value("timeline-trigger-exit-range-start", "entry 1em");
+test_valid_value("timeline-trigger-exit-range-start", "exit calc(1em + 10px)");
+test_valid_value("timeline-trigger-exit-range-start", "entry 42%");
+test_valid_value("timeline-trigger-exit-range-start", "cover");
+test_valid_value("timeline-trigger-exit-range-start", "contain");
+test_valid_value("timeline-trigger-exit-range-start", "entry");
+test_valid_value("timeline-trigger-exit-range-start", "exit");
+test_valid_value("timeline-trigger-exit-range-start", "cover calc(sign(100em - 1px) * 1%)", "cover calc(1% * sign(100em - 1px))");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-end-computed.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-end-computed.tentative.html
new file mode 100644
index 0000000..daa9616
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-end-computed.tentative.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<div id="target" style="font-size:10px;"></div>
+<script>
+test_computed_value("timeline-trigger-range-end", "initial", "normal");
+test_computed_value("timeline-trigger-range-end", "normal");
+test_computed_value("timeline-trigger-range-end", "cover 0%");
+test_computed_value("timeline-trigger-range-end", "cover 100%", "cover");
+test_computed_value("timeline-trigger-range-end", "COVER 0%", "cover 0%");
+test_computed_value("timeline-trigger-range-end", "COVER 100%", "cover");
+test_computed_value("timeline-trigger-range-end", "cover 120%");
+test_computed_value("timeline-trigger-range-end", "0", "0px");
+test_computed_value("timeline-trigger-range-end", "120%");
+test_computed_value("timeline-trigger-range-end", "120px");
+test_computed_value("timeline-trigger-range-end", "cover 42%");
+test_computed_value("timeline-trigger-range-end", "cover -42%");
+test_computed_value("timeline-trigger-range-end", "contain 42%");
+test_computed_value("timeline-trigger-range-end", "exit 42%");
+test_computed_value("timeline-trigger-range-end", "exit calc(41% + 1%)", "exit 42%");
+test_computed_value("timeline-trigger-range-end", "exit-crossing 42%");
+test_computed_value("timeline-trigger-range-end", "exit 1%, cover 2%, contain 0%");
+test_computed_value("timeline-trigger-range-end", "exit 1%, cover 2%, contain 100%", "exit 1%, cover 2%, contain");
+test_computed_value("timeline-trigger-range-end", "entry 42px");
+test_computed_value("timeline-trigger-range-end", "entry-crossing 42px");
+
+test_computed_value("timeline-trigger-range-end", "contain calc(10% + 10px)");
+test_computed_value("timeline-trigger-range-end", "entry 1em", "entry 10px");
+test_computed_value("timeline-trigger-range-end", "exit calc(1em + 10px)", "exit 20px");
+test_computed_value("timeline-trigger-range-end", "cover");
+test_computed_value("timeline-trigger-range-end", "contain");
+test_computed_value("timeline-trigger-range-end", "entry");
+test_computed_value("timeline-trigger-range-end", "exit");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-end-invalid.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-end-invalid.tentative.html
new file mode 100644
index 0000000..306112d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-end-invalid.tentative.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script>
+test_invalid_value("timeline-trigger-range-end", "infinite");
+test_invalid_value("timeline-trigger-range-end", "1s 2s");
+test_invalid_value("timeline-trigger-range-end", "1s / 2s");
+
+test_invalid_value("timeline-trigger-range-end", "peek 50%");
+test_invalid_value("timeline-trigger-range-end", "50% contain");
+test_invalid_value("timeline-trigger-range-end", "50% cover");
+test_invalid_value("timeline-trigger-range-end", "50% entry");
+test_invalid_value("timeline-trigger-range-end", "50% enter");
+test_invalid_value("timeline-trigger-range-end", "50% exit");
+test_invalid_value("timeline-trigger-range-end", "contain contain");
+test_invalid_value("timeline-trigger-range-end", "none");
+test_invalid_value("timeline-trigger-range-end", "cover 50% enter 50%");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-end-valid.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-end-valid.tentative.html
new file mode 100644
index 0000000..71d8b7f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-end-valid.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script>
+// https://drafts.csswg.org/scroll-animations-1/#view-timelines-ranges
+test_valid_value("timeline-trigger-range-end", "normal");
+test_valid_value("timeline-trigger-range-end", "cover 0%");
+test_valid_value("timeline-trigger-range-end", "cover 100%", "cover");
+test_valid_value("timeline-trigger-range-end", "cover 120%");
+test_valid_value("timeline-trigger-range-end", "cover 42%");
+test_valid_value("timeline-trigger-range-end", "0", "0px");
+test_valid_value("timeline-trigger-range-end", "120%");
+test_valid_value("timeline-trigger-range-end", "120px");
+test_valid_value("timeline-trigger-range-end", "cover -42%");
+test_valid_value("timeline-trigger-range-end", "contain 42%");
+test_valid_value("timeline-trigger-range-end", "exit 42%");
+test_valid_value("timeline-trigger-range-end", "exit 1%, cover 2%, contain 0%");
+test_valid_value("timeline-trigger-range-end", "exit 1%, cover 2%, contain 100%", "exit 1%, cover 2%, contain");
+test_valid_value("timeline-trigger-range-end", "exit-crossing 42%");
+test_valid_value("timeline-trigger-range-end", "entry 42px");
+test_valid_value("timeline-trigger-range-end", "entry-crossing 42px");
+test_valid_value("timeline-trigger-range-end", "contain calc(10px + 10%)", "contain calc(10% + 10px)");
+test_valid_value("timeline-trigger-range-end", "entry 1em");
+test_valid_value("timeline-trigger-range-end", "exit calc(1em + 10px)");
+test_valid_value("timeline-trigger-range-end", "entry 42%");
+test_valid_value("timeline-trigger-range-end", "cover");
+test_valid_value("timeline-trigger-range-end", "contain");
+test_valid_value("timeline-trigger-range-end", "entry");
+test_valid_value("timeline-trigger-range-end", "exit");
+</script>trigger-
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-start-computed.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-start-computed.tentative.html
new file mode 100644
index 0000000..7727f0f0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-start-computed.tentative.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<div id="target" style="font-size:10px;"></div>
+<script>
+test_computed_value("timeline-trigger-range-start", "initial", "normal");
+test_computed_value("timeline-trigger-range-start", "normal");
+test_computed_value("timeline-trigger-range-start", "cover 0%", "cover");
+test_computed_value("timeline-trigger-range-start", "cover 100%");
+test_computed_value("timeline-trigger-range-start", "COVER 0%", "cover");
+test_computed_value("timeline-trigger-range-start", "COVER 100%", "cover 100%");
+test_computed_value("timeline-trigger-range-start", "cover 120%");
+test_computed_value("timeline-trigger-range-start", "cover 42%");
+test_computed_value("timeline-trigger-range-start", "0", "0px");
+test_computed_value("timeline-trigger-range-start", "120%");
+test_computed_value("timeline-trigger-range-start", "120px");
+test_computed_value("timeline-trigger-range-start", "cover -42%");
+test_computed_value("timeline-trigger-range-start", "contain 42%");
+test_computed_value("timeline-trigger-range-start", "exit 42%");
+test_computed_value("timeline-trigger-range-start", "exit calc(41% + 1%)", "exit 42%");
+test_computed_value("timeline-trigger-range-start", "exit 1%, cover 2%, contain 0%", "exit 1%, cover 2%, contain");
+test_computed_value("timeline-trigger-range-start", "exit 1%, cover 2%, contain 100%");
+test_computed_value("timeline-trigger-range-start", "exit-crossing 42%");
+test_computed_value("timeline-trigger-range-start", "entry 42px");
+test_computed_value("timeline-trigger-range-start", "entry-crossing 42px");
+test_computed_value("timeline-trigger-range-start", "contain calc(10% + 10px)");
+test_computed_value("timeline-trigger-range-start", "entry 1em", "entry 10px");
+test_computed_value("timeline-trigger-range-start", "exit calc(1em + 10px)", "exit 20px");
+test_computed_value("timeline-trigger-range-start", "cover");
+test_computed_value("timeline-trigger-range-start", "contain");
+test_computed_value("timeline-trigger-range-start", "entry");
+test_computed_value("timeline-trigger-range-start", "exit");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-start-invalid.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-start-invalid.tentative.html
new file mode 100644
index 0000000..975b208f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-start-invalid.tentative.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script>
+test_invalid_value("timeline-trigger-range-start", "peek 50%");
+test_invalid_value("timeline-trigger-range-start", "50% contain");
+test_invalid_value("timeline-trigger-range-start", "50% cover");
+test_invalid_value("timeline-trigger-range-start", "50% entry");
+test_invalid_value("timeline-trigger-range-start", "50% enter");
+test_invalid_value("timeline-trigger-range-start", "50% exit");
+test_invalid_value("timeline-trigger-range-start", "contain contain");
+test_invalid_value("timeline-trigger-range-start", "none");
+test_invalid_value("timeline-trigger-range-start", "cover 50% enter 50%");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-start-valid.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-start-valid.tentative.html
new file mode 100644
index 0000000..9f391d1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-range-start-valid.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script>
+// https://drafts.csswg.org/scroll-animations-1/#view-timelines-ranges
+test_valid_value("timeline-trigger-range-start", "normal");
+test_valid_value("timeline-trigger-range-start", "cover 0%", "cover");
+test_valid_value("timeline-trigger-range-start", "cover 100%");
+test_valid_value("timeline-trigger-range-start", "cover 120%");
+test_valid_value("timeline-trigger-range-start", "cover 42%");
+test_valid_value("timeline-trigger-range-start", "0", "0px");
+test_valid_value("timeline-trigger-range-start", "120%");
+test_valid_value("timeline-trigger-range-start", "120px");
+test_valid_value("timeline-trigger-range-start", "cover -42%");
+test_valid_value("timeline-trigger-range-start", "contain 42%");
+test_valid_value("timeline-trigger-range-start", "exit 42%");
+test_valid_value("timeline-trigger-range-start", "exit 1%, cover 2%, contain 0%", "exit 1%, cover 2%, contain");
+test_valid_value("timeline-trigger-range-start", "exit 1%, cover 2%, contain 100%");
+test_valid_value("timeline-trigger-range-start", "exit-crossing 42%");
+test_valid_value("timeline-trigger-range-start", "entry 42px");
+test_valid_value("timeline-trigger-range-start", "entry-crossing 42px");
+test_valid_value("timeline-trigger-range-start", "contain calc(10px + 10%)", "contain calc(10% + 10px)");
+test_valid_value("timeline-trigger-range-start", "entry 1em");
+test_valid_value("timeline-trigger-range-start", "exit calc(1em + 10px)");
+test_valid_value("timeline-trigger-range-start", "entry 42%");
+test_valid_value("timeline-trigger-range-start", "cover");
+test_valid_value("timeline-trigger-range-start", "contain");
+test_valid_value("timeline-trigger-range-start", "entry");
+test_valid_value("timeline-trigger-range-start", "exit");
+test_valid_value("timeline-trigger-range-start", "cover calc(sign(100em - 1px) * 1%)", "cover calc(1% * sign(100em - 1px))");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-timeline-parsing.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-timeline-parsing.tentative.html
new file mode 100644
index 0000000..61bef268
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/animation-trigger/parsing/timeline-trigger-timeline-parsing.tentative.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-trigger">
+<meta name="assert" content="timeline-trigger-type supports only the grammar '<single-timeline-trigger-timeline> #'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+
+test_valid_value('timeline-trigger-timeline', 'initial');
+test_valid_value('timeline-trigger-timeline', 'inherit');
+test_valid_value('timeline-trigger-timeline', 'unset');
+test_valid_value('timeline-trigger-timeline', 'revert');
+test_valid_value('timeline-trigger-timeline', 'auto');
+test_valid_value('timeline-trigger-timeline', 'none');
+test_valid_value('timeline-trigger-timeline', 'auto, auto');
+test_valid_value('timeline-trigger-timeline', 'none, none');
+test_valid_value('timeline-trigger-timeline', 'auto, none');
+test_valid_value('timeline-trigger-timeline', 'none, auto');
+test_valid_value('timeline-trigger-timeline', '--test');
+test_valid_value('timeline-trigger-timeline', '--test1, --test2');
+test_valid_value('timeline-trigger-timeline', '--test1, --test2, none, --test3, auto');
+
+test_invalid_value('timeline-trigger-timeline', 'test1');
+test_invalid_value('timeline-trigger-timeline', '10px');
+test_invalid_value('timeline-trigger-timeline', 'auto auto');
+test_invalid_value('timeline-trigger-timeline', 'none none');
+test_invalid_value('timeline-trigger-timeline', 'foo bar');
+test_invalid_value('timeline-trigger-timeline', '"foo" "bar"');
+test_invalid_value('timeline-trigger-timeline', 'rgb(1, 2, 3)');
+test_invalid_value('timeline-trigger-timeline', '#fefefe');
+test_invalid_value('timeline-trigger-timeline', '"test"');
+
+// https://drafts.csswg.org/scroll-animations-1/#scroll-notation
+//
+// timeline-trigger-timeline: scroll(<axis>? <scroller>?);
+// <axis> = block | inline | x | y
+// <scroller> = root | nearest | self
+test_valid_value('timeline-trigger-timeline', 'scroll()');
+test_valid_value('timeline-trigger-timeline', ' scroll() ', 'scroll()');
+test_valid_value('timeline-trigger-timeline', 'scroll(block)', 'scroll()');
+test_valid_value('timeline-trigger-timeline', 'scroll(inline)');
+test_valid_value('timeline-trigger-timeline', 'scroll(x)');
+test_valid_value('timeline-trigger-timeline', 'scroll(y)');
+test_valid_value('timeline-trigger-timeline', 'scroll(root)');
+test_valid_value('timeline-trigger-timeline', 'scroll(nearest)', 'scroll()');
+test_valid_value('timeline-trigger-timeline', 'scroll(self)');
+test_valid_value('timeline-trigger-timeline', 'scroll(inline nearest)', 'scroll(inline)');
+test_valid_value('timeline-trigger-timeline', 'scroll(nearest inline)', 'scroll(inline)');
+test_valid_value('timeline-trigger-timeline', 'scroll(block self)', 'scroll(self)');
+test_valid_value('timeline-trigger-timeline', 'scroll(self block)', 'scroll(self)');
+test_valid_value('timeline-trigger-timeline', 'scroll(y root)', 'scroll(root y)');
+
+test_invalid_value('timeline-trigger-timeline', 'scroll(abc root)');
+test_invalid_value('timeline-trigger-timeline', 'scroll(abc)');
+test_invalid_value('timeline-trigger-timeline', 'scroll(y abc)');
+test_invalid_value('timeline-trigger-timeline', 'scroll("string")');
+
+// https://drafts.csswg.org/scroll-animations-1/#view-notation
+test_valid_value('timeline-trigger-timeline', 'view()');
+test_valid_value('timeline-trigger-timeline', ' view() ', 'view()');
+test_valid_value('timeline-trigger-timeline', 'view(block)', 'view()');
+test_valid_value('timeline-trigger-timeline', 'view(inline)');
+test_valid_value('timeline-trigger-timeline', 'view(x)');
+test_valid_value('timeline-trigger-timeline', 'view(y)');
+test_valid_value('timeline-trigger-timeline', 'view(y 1px 2px)');
+test_valid_value('timeline-trigger-timeline', 'view(y 1px)');
+test_valid_value('timeline-trigger-timeline', 'view(y auto)', 'view(y)');
+test_valid_value('timeline-trigger-timeline', 'view(y auto auto)', 'view(y)');
+test_valid_value('timeline-trigger-timeline', 'view(y auto 1px)');
+test_valid_value('timeline-trigger-timeline', 'view(1px 2px y)', 'view(y 1px 2px)');
+test_valid_value('timeline-trigger-timeline', 'view(1px y)', 'view(y 1px)');
+test_valid_value('timeline-trigger-timeline', 'view(auto x)', 'view(x)');
+test_valid_value('timeline-trigger-timeline', 'view(1px 2px)');
+test_valid_value('timeline-trigger-timeline', 'view(1px)');
+test_valid_value('timeline-trigger-timeline', 'view(1px 1px)', 'view(1px)');
+test_valid_value('timeline-trigger-timeline', 'view(1px auto)');
+test_valid_value('timeline-trigger-timeline', 'view(auto calc(1% + 1px))');
+test_valid_value('timeline-trigger-timeline', 'view(2em calc(1% + 1em))');
+test_valid_value('timeline-trigger-timeline', 'view(auto)', 'view()');
+test_valid_value('timeline-trigger-timeline', 'view(auto auto)', 'view()');
+
+test_invalid_value('timeline-trigger-timeline', 'view(y 1px 2px 3px)');
+test_invalid_value('timeline-trigger-timeline', 'view(1px y 3px)');
+test_invalid_value('timeline-trigger-timeline', 'view(1px 2px 3px)');
+test_invalid_value('timeline-trigger-timeline', 'view(abc block)');
+test_invalid_value('timeline-trigger-timeline', 'view(abc)');
+test_invalid_value('timeline-trigger-timeline', 'view(y abc)');
+test_invalid_value('timeline-trigger-timeline', 'view("string")');
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
index cc0c296..2584700b6 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
@@ -409,6 +409,13 @@
 text-wrap-mode: wrap
 text-wrap-style: auto
 timeline-scope: none
+timeline-trigger-behavior: once
+timeline-trigger-exit-range-end: auto
+timeline-trigger-exit-range-start: auto
+timeline-trigger-name: none
+timeline-trigger-range-end: normal
+timeline-trigger-range-start: normal
+timeline-trigger-timeline: auto
 top: auto
 touch-action: auto
 transform: none
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
index 98f6045e..014249d 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
@@ -409,6 +409,13 @@
 text-wrap-mode: wrap
 text-wrap-style: auto
 timeline-scope: none
+timeline-trigger-behavior: once
+timeline-trigger-exit-range-end: auto
+timeline-trigger-exit-range-start: auto
+timeline-trigger-name: none
+timeline-trigger-range-end: normal
+timeline-trigger-range-start: normal
+timeline-trigger-timeline: auto
 top: auto
 touch-action: auto
 transform: none
diff --git a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
index 390443d..93a05417 100644
--- a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
+++ b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
@@ -409,6 +409,13 @@
 text-wrap-mode: wrap
 text-wrap-style: auto
 timeline-scope: none
+timeline-trigger-behavior: once
+timeline-trigger-exit-range-end: auto
+timeline-trigger-exit-range-start: auto
+timeline-trigger-name: none
+timeline-trigger-range-end: normal
+timeline-trigger-range-start: normal
+timeline-trigger-timeline: auto
 top: auto
 touch-action: auto
 transform: none
diff --git a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
index 826260f..144e0d68 100644
--- a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
+++ b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
@@ -535,6 +535,13 @@
 textWrapMode
 textWrapStyle
 timelineScope
+timelineTriggerBehavior
+timelineTriggerExitRangeEnd
+timelineTriggerExitRangeStart
+timelineTriggerName
+timelineTriggerRangeEnd
+timelineTriggerRangeStart
+timelineTriggerTimeline
 top
 touchAction
 transform
diff --git a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
index 7f0c750..e8e14cfd 100644
--- a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
@@ -442,6 +442,13 @@
     text-wrap-mode
     text-wrap-style
     timeline-scope
+    timeline-trigger-behavior
+    timeline-trigger-exit-range-end
+    timeline-trigger-exit-range-start
+    timeline-trigger-name
+    timeline-trigger-range-end
+    timeline-trigger-range-start
+    timeline-trigger-timeline
     top
     touch-action
     transform
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index e1e514d..72872b9f 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit e1e514dbf063209c9e2c71852ff41dad7c94b919
+Subproject commit 72872b9f96f826156f50b8416decbf0a437e4427
diff --git a/third_party/perfetto b/third_party/perfetto
index 6e2232a6..b771e98 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 6e2232a6e0465a68bb8610a2b0d45e3fd509993d
+Subproject commit b771e98cb55a18bb45cc5ca6feb62483d863f1c3
diff --git a/third_party/skia b/third_party/skia
index 53cfcdd6..891566f 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 53cfcdd684fe8e27721245e047abc0a6e5f66ef6
+Subproject commit 891566fc6c86826a2999d040b3cfca46bd48967a
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index 08147a1..61576f7 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit 08147a1cf942d635a8324ad45d8ac8991d90994c
+Subproject commit 61576f7bb0ed2d1560bf03ea018207eda497544a
diff --git a/third_party/vulkan-loader/src b/third_party/vulkan-loader/src
index 8beef6c..da8d2ca 160000
--- a/third_party/vulkan-loader/src
+++ b/third_party/vulkan-loader/src
@@ -1 +1 @@
-Subproject commit 8beef6cb63ffadb02300bf6321b4d3af85ea7417
+Subproject commit da8d2caad9341ca8c5a7c3deba217d7da50a7c24
diff --git a/third_party/vulkan-utility-libraries/src b/third_party/vulkan-utility-libraries/src
index f3cfb7f..ec329e2 160000
--- a/third_party/vulkan-utility-libraries/src
+++ b/third_party/vulkan-utility-libraries/src
@@ -1 +1 @@
-Subproject commit f3cfb7fa8994e37c7c0568e33a785591af2ca696
+Subproject commit ec329e2721921f79743b90307ee047d08e057788
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src
index 5f2be4c2..42094612 160000
--- a/third_party/vulkan-validation-layers/src
+++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@
-Subproject commit 5f2be4c2611e6b1dfdc224c06b322068ab52e09d
+Subproject commit 42094612240193e0748bb7c19294ca144da4db97
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 68777345..6c7a8618b 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -4681,6 +4681,14 @@
   </description>
 </action>
 
+<action name="Autofill_PolledLoyaltyCardSuggestions">
+  <owner>vizcay@google.com</owner>
+  <description>
+    This user action is logged when the user interacts with an autofillable
+    loyalty card field field, either by clicking or by typing.
+  </description>
+</action>
+
 <action name="Autofill_PolledProfileSuggestions">
   <owner>battre@chromium.org</owner>
   <description>
@@ -6649,7 +6657,7 @@
 
 <action name="ChromeOS.AppInstallDialog.AppLaunched">
   <owner>brettw@chromium.org</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <description>
     Recorded when the user requested a previously installed app to be launched
     in the CrOS App Install dialog.
@@ -6658,7 +6666,7 @@
 
 <action name="ChromeOS.AppInstallDialog.Cancelled">
   <owner>brettw@chromium.org</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <description>
     Recorded when the user closed an installation dialog without installing.
   </description>
@@ -6666,7 +6674,7 @@
 
 <action name="ChromeOS.AppInstallDialog.Installed">
   <owner>brettw@chromium.org</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <description>
     Recorded when the user requested an app to be installed in the CrOS App
     Install dialog.
@@ -6675,7 +6683,7 @@
 
 <action name="ChromeOS.AppInstallDialog.Shown">
   <owner>brettw@chromium.org</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <description>
     Recorded when the CrOS App Install dialog was opened by the user.
   </description>
@@ -36309,6 +36317,77 @@
   </description>
 </action>
 
+<action name="Settings.DeleteBrowsingData.CheckboxesShowMoreClick">
+  <owner>hkamila@google.com</owner>
+  <owner>zalmashni@chromium.org</owner>
+  <owner>chrome-browser-privacy-team@google.com</owner>
+  <description>
+    User clicks on the &quot;Show more&quot; button in the Clear Browsing Data
+    dialog to reveal more options.
+  </description>
+</action>
+
+<action name="Settings.DeleteBrowsingData.CookiesSignOutLinkClick">
+  <owner>hkamila@google.com</owner>
+  <owner>zalmashni@chromium.org</owner>
+  <owner>chrome-browser-privacy-team@google.com</owner>
+  <description>
+    User clicks on the &quot;Sign out&quot; link within the sub-label of the
+    &quot;Cookies and other site data&quot; checkbox in the Clear Browsing Data
+    dialog.
+  </description>
+</action>
+
+<action name="Settings.DeleteBrowsingData.GoogleSearchHistoryLinkClick">
+  <owner>hkamila@google.com</owner>
+  <owner>zalmashni@chromium.org</owner>
+  <owner>chrome-browser-privacy-team@google.com</owner>
+  <description>
+    User clicks on the &quot;Search History&quot; link in the &quot;Other Google
+    data&quot; dialog.
+  </description>
+</action>
+
+<action name="Settings.DeleteBrowsingData.MyActivityLinkClick">
+  <owner>hkamila@google.com</owner>
+  <owner>zalmashni@chromium.org</owner>
+  <owner>chrome-browser-privacy-team@google.com</owner>
+  <description>
+    User clicks on the &quot;My Activity&quot; link in the &quot;Other Google
+    data&quot; dialog.
+  </description>
+</action>
+
+<action name="Settings.DeleteBrowsingData.OtherDataEntryPointClick">
+  <owner>hkamila@google.com</owner>
+  <owner>zalmashni@chromium.org</owner>
+  <owner>chrome-browser-privacy-team@google.com</owner>
+  <description>
+    User clicks on the &quot;Manage other Google data&quot; or &quot;Manage
+    other data&quot; link row in the Clear Browsing Data dialog.
+  </description>
+</action>
+
+<action name="Settings.DeleteBrowsingData.PasswordManagerLinkClick">
+  <owner>hkamila@google.com</owner>
+  <owner>zalmashni@chromium.org</owner>
+  <owner>chrome-browser-privacy-team@google.com</owner>
+  <description>
+    User clicks on the &quot;Password Manager&quot; link in the &quot;Other
+    Google data&quot; dialog.
+  </description>
+</action>
+
+<action name="Settings.DeleteBrowsingData.TimePickerMoreClick">
+  <owner>hkamila@google.com</owner>
+  <owner>zalmashni@chromium.org</owner>
+  <owner>chrome-browser-privacy-team@google.com</owner>
+  <description>
+    User clicks on the &quot;More&quot; button in the time period picker of the
+    Clear Browsing Data dialog to reveal more time range options.
+  </description>
+</action>
+
 <action name="Settings.DownloadsSettings">
   <owner>qpubert@google.com.org</owner>
   <owner>olivierrobin@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml
index 0c1429df..6d4fcba 100644
--- a/tools/metrics/histograms/metadata/apps/histograms.xml
+++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -186,7 +186,7 @@
     enum="AppManagementUserAction" expires_after="2025-12-14">
   <owner>sharminzaman@google.com</owner>
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <owner>pwa-team@google.com</owner>
   <summary>
     The actions taken by the user when they are viewing the app details of an
@@ -207,14 +207,14 @@
     expires_after="2024-05-05">
   <owner>sharminzaman@google.com</owner>
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>The ways the user opens up the App Management interface.</summary>
 </histogram>
 
 <histogram name="AppPreloadService.FirstLoginFlowTime.{Status}" units="ms"
     expires_after="2026-03-02">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     The time it takes for the first login flow of the App Preload service to
     complete with the final status of {Status}.
@@ -231,7 +231,7 @@
 <histogram name="AppPreloadService.ServerResponseCodes"
     enum="CombinedHttpResponseAndNetErrorCode" expires_after="2025-11-09">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     The combined http and net error codes the App Provisioning Server responds
     with after a request is made.
@@ -241,7 +241,7 @@
 <histogram name="Apps.ActivatedCount.{AppType}" units="units"
     expires_after="2022-07-01">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Recorded the app window activated times for {AppType} in Chrome OS. This is
     logged when more than 24 hours pass after last log, or during the system
@@ -360,7 +360,7 @@
 <histogram name="Apps.AppLaunchPerAppType" enum="AppType"
     expires_after="2025-11-16">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records an app launch grouped by app type. This is recorded when an app is
     launched.
@@ -370,7 +370,7 @@
 <histogram name="Apps.AppLaunchPerAppTypeV2" enum="AppTypeV2"
     expires_after="2025-11-16">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records an app launch grouped by app type V2. This is recorded when an app
     is launched.
@@ -380,7 +380,7 @@
 <histogram name="Apps.AppLaunchSource" enum="LaunchSource"
     expires_after="2025-11-16">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>Records an app launch grouped by launch source.</summary>
 </histogram>
 
@@ -2169,7 +2169,7 @@
 <histogram name="Apps.AppsCount.{AppType}" units="Apps"
     expires_after="2025-12-28">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <component>1389907</component>
   <summary>
     The total number of installed {AppType} for each app type in Chrome OS. This
@@ -2183,7 +2183,7 @@
 <histogram name="Apps.AppsCountPerInstallReason.{AppType}.{InstallReason}"
     units="Apps" expires_after="2025-12-21">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <component>1389907</component>
   <summary>
     The total number of installed {AppType} for each app type {InstallReason} in
@@ -2326,7 +2326,7 @@
 <histogram name="Apps.DefaultAppLaunch{DefaultAppLaunchSource}"
     enum="DefaultAppName" expires_after="2025-12-21">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <owner>cros-device-enablement@google.com</owner>
   <component>1389907</component>
   <summary>
@@ -2476,7 +2476,7 @@
 <histogram name="Apps.IconLoadingMethod" enum="IconLoadingMethod"
     expires_after="2024-10-22">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>Records the app icon loading method when loading app icons.</summary>
 </histogram>
 
@@ -2556,7 +2556,7 @@
 <histogram name="Apps.NoteTakingApp.DefaultLaunchResult"
     enum="NoteTakingAppLaunchResult" expires_after="2024-10-28">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     The result of attempting to launch a default note-taking app on Chrome OS.
     Only reported if the preferred app was unspecified or failed to launch.
@@ -2566,7 +2566,7 @@
 <histogram name="Apps.NoteTakingApp.PreferredLaunchResult"
     enum="NoteTakingAppLaunchResult" expires_after="2024-08-29">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     The result of attempting to launch the user-specified preferred note-taking
     app, if any, on Chrome OS.
@@ -2660,7 +2660,7 @@
 <histogram name="Apps.PreferredApps.EntryCount" units="entries"
     expires_after="2023-07-09">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records the number of entries in the preferred apps list that was stored on
     the disk. The preferred apps list is a vector that identifies the preferred
@@ -2672,7 +2672,7 @@
 <histogram name="Apps.PromiseApp.LifecycleEvent"
     enum="PromiseAppLifecycleEvent" expires_after="2026-02-23">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records how often each key lifecycle event occurs across all promise apps. A
     promise app is a temporary item in the Launcher/ Shelf which represents an
@@ -2684,7 +2684,7 @@
 <histogram name="Apps.PromiseApp.PromiseAppIconType" enum="PromiseAppIconType"
     expires_after="2026-02-23">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records how often placeholder icons (as opposed to real app icons) are used
     across all promise apps. A promise app is a temporary item in the Launcher/
@@ -2697,7 +2697,7 @@
 <histogram name="Apps.PromiseApp.PromiseAppType" enum="PromiseAppType"
     expires_after="2026-02-23">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records what app type a promise app is when the app installation
     successfully finishes. A promise app is a temporary item in the Launcher/
@@ -2777,7 +2777,7 @@
 <histogram name="Apps.RunningDuration.{AppType}" units="ms"
     expires_after="2025-12-28">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <component>1389907</component>
   <summary>
     Recorded the app running duration for {AppType} in Chrome OS. This is logged
@@ -2790,7 +2790,7 @@
 <histogram name="Apps.RunningPercentage.{AppType}" units="%"
     expires_after="2022-07-01">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Recorded the app running duration percentage for {AppType} in Chrome OS.
     This is logged when more than 24 hours pass after last log, or during the
@@ -2948,7 +2948,7 @@
 <histogram name="Apps.UsageTime.{AppType}" units="ms"
     expires_after="2025-12-28">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <component>1389907</component>
   <summary>
     Records the amount of time that {AppType} were used in the last five minutes
@@ -2960,7 +2960,7 @@
 <histogram name="Apps.UsageTimeV2.{AppTypeV2}" units="ms"
     expires_after="2025-12-28">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <component>1389907</component>
   <summary>
     Records the amount of time that {AppTypeV2} were used in the last five
diff --git a/tools/metrics/histograms/metadata/blink/enums.xml b/tools/metrics/histograms/metadata/blink/enums.xml
index cc61cc53..95468c5a9 100644
--- a/tools/metrics/histograms/metadata/blink/enums.xml
+++ b/tools/metrics/histograms/metadata/blink/enums.xml
@@ -7839,6 +7839,13 @@
   <int value="868" label="corners"/>
   <int value="869" label="border-shape"/>
   <int value="870" label="animation-trigger-behavior"/>
+  <int value="871" label="timeline-trigger-name"/>
+  <int value="872" label="timeline-trigger-behavior"/>
+  <int value="873" label="timeline-trigger-range-start"/>
+  <int value="874" label="timeline-trigger-range-end"/>
+  <int value="875" label="timeline-trigger-exit-range-start"/>
+  <int value="876" label="timeline-trigger-exit-range-end"/>
+  <int value="877" label="timeline-trigger-timeline"/>
 </enum>
 
 <!-- LINT.ThenChange(//third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom:CSSSampleId) -->
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index ae27a95..0e280022 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -284,7 +284,7 @@
 <histogram name="ChromeOS.Apps.IntentPickerDestinationPlatform"
     enum="ArcIntentHandlerDestinationPlatform" expires_after="2025-09-14">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records the app platform shown as the result of link intent handling.
     Recorded when the user makes a choice in the Intent Picker dialog, and when
@@ -2407,7 +2407,7 @@
 <histogram name="ChromeOS.Intents.IntentPickerAction"
     enum="IntentPickerDialogAction" expires_after="2025-06-01">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     The intent picker dialog is shown (either automatically, or though a page
     action button) when the user navigates to a page whose URL can be handled by
@@ -2419,7 +2419,7 @@
 <histogram name="ChromeOS.Intents.IntentPickerIconEvent"
     enum="IntentPickerIconEvent" expires_after="2025-06-01">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records events associated with the intent picker icon in the omnibox.
     Recorded when the icon is shown or interacted with.
@@ -4407,7 +4407,7 @@
 <histogram name="ChromeOS.Sharesheet.AppCount2.{AppType}" units="apps"
     expires_after="2025-06-01">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records number of {AppType} apps found for a given intent in the Sharesheet
     when the sharesheet is invoked. Has 100 exponential buckets. (The original
@@ -4425,7 +4425,7 @@
 <histogram name="ChromeOS.Sharesheet.FileCount" units="files"
     expires_after="2025-06-01">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records the number of files a user is trying to share from the Sharesheet
     when the sharesheet is invoked.
@@ -4435,7 +4435,7 @@
 <histogram name="ChromeOS.Sharesheet.LaunchSource"
     enum="ChromeOSSharesheetLaunchSource" expires_after="2025-06-01">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records the source from which the Sharesheet is invoked.
 
@@ -4446,14 +4446,14 @@
 <histogram name="ChromeOS.Sharesheet.UserAction"
     enum="ChromeOSSharesheetAction" expires_after="2025-11-09">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>Records user action on sharesheet invocations.</summary>
 </histogram>
 
 <histogram name="ChromeOS.Sharesheet.{Location}.MimeType"
     enum="ChromeOSSharesheetMimeType" expires_after="2024-09-22">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records the mime type of the content being copied at the {Location} point in
     the Sharesheet flow.
@@ -4892,7 +4892,7 @@
 <histogram name="ChromeOS.WebAPK.{InstallType}.Result"
     enum="WebApkInstallResultChromeOS" expires_after="2024-04-28">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records the overall final state of a WebAPK {InstallType} operation on
     ChromeOS. A WebAPK is generated and installed when a PWA which supports Web
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
index 3cbd90ff..bf1b67d 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -1047,7 +1047,7 @@
     enum="TwaLaunchHandlerClientMode" expires_after="2026-05-01">
   <owner>tkachenkoo@google.com</owner>
   <owner>sselyvon@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records the client mode of a web app launched through Trusted Web Activity.
     Client mode is a part of Launch Handler API. Recorded on Trusted Web
@@ -1059,7 +1059,7 @@
     enum="TwaLaunchHandlerFailureReason" expires_after="2026-05-01">
   <owner>tkachenkoo@google.com</owner>
   <owner>sselyvon@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records the verification failure that caused launch queue to not be notified
     in Trusted Web Activity, if any. Recorded on Trusted Web Activity startup if
@@ -1071,7 +1071,7 @@
     enum="TwaLaunchHandlerFileHandling" expires_after="2026-05-01">
   <owner>tkachenkoo@google.com</owner>
   <owner>sselyvon@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records the usage of file handling API in Trusted Web Activities, as part of
     Launch Handler API. Recorded on Trusted Web Activity startup if the Launch
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index bdc18909..1810322 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -2904,7 +2904,7 @@
 </histogram>
 
 <histogram name="Memory.Total.PrivateSwapFootprint" units="MB"
-    expires_after="2025-11-16">
+    expires_after="2026-07-13">
   <owner>thiabaud@google.com</owner>
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -2999,7 +2999,7 @@
 </histogram>
 
 <histogram name="Memory.Total.RendererPrivateSwapFootprint" units="MB"
-    expires_after="2025-11-16">
+    expires_after="2026-07-13">
   <owner>thiabaud@google.com</owner>
   <owner>lizeb@chromium.org</owner>
   <summary>
@@ -3489,7 +3489,7 @@
 </histogram>
 
 <histogram name="Memory{ProfiledProcess}.Pss" units="MiB"
-    expires_after="2025-07-13">
+    expires_after="2026-07-13">
   <owner>thiabaud@google.com</owner>
   <summary>
     PSS metric for the process. Recorded on Android, Linux, and ChromeOS only.
@@ -3498,7 +3498,7 @@
 </histogram>
 
 <histogram name="Memory{ProfiledProcess}.SwapPss" units="MiB"
-    expires_after="2025-07-13">
+    expires_after="2026-07-13">
   <owner>thiabaud@google.com</owner>
   <summary>
     Swap PSS metric for the process. Recorded on Android, Linux, and ChromeOS
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 6835d75..d7e2e62 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -10345,7 +10345,7 @@
 <histogram name="WebUsb.DetachKernelDriverOutcome"
     enum="DetachKernelDriverOutcome" expires_after="2026-06-15">
   <owner>ovn@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records the outcome of detaching a kernel driver during ClaimInterface (for
     Android and Linux only).
diff --git a/tools/metrics/histograms/metadata/search/histograms.xml b/tools/metrics/histograms/metadata/search/histograms.xml
index aff3464..fd06e00 100644
--- a/tools/metrics/histograms/metadata/search/histograms.xml
+++ b/tools/metrics/histograms/metadata/search/histograms.xml
@@ -361,6 +361,22 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Search.ChoiceDebug.UnexpectedRecordDisplayStateReentryHasCompletion"
+    enum="Boolean" expires_after="2025-11-18">
+  <owner>dgn@chromium.org</owner>
+  <owner>chrome-waffle-eng@google.com</owner>
+  <summary>
+    At the moment re-entry into MaybeRecordChoiceScreenDisplayState from user
+    choice moment is detected, records whether we already have valid completion
+    metadata written stored in prefs.
+
+    This is intended to monitor the ongoing prevalence of
+    https://crbug.com/390272573, to allow detection if the rate increases
+    significantly.
+  </summary>
+</histogram>
+
 <histogram name="Search.ChoiceDebug.UnknownCountryIdStored"
     enum="UnknownCountryIdStored" expires_after="2025-11-18">
   <owner>cschlosser@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/sync/enums.xml b/tools/metrics/histograms/metadata/sync/enums.xml
index 58b0bb4..f2cf9bb 100644
--- a/tools/metrics/histograms/metadata/sync/enums.xml
+++ b/tools/metrics/histograms/metadata/sync/enums.xml
@@ -109,33 +109,6 @@
 
 <!-- LINT.ThenChange(/components/sync/engine/bookmark_update_preprocessing.cc:BookmarkGUIDSource) -->
 
-<!-- LINT.IfChange(BookmarkSetComparisonOutcome) -->
-
-<enum name="BookmarkSetComparisonOutcome">
-  <int value="0" label="Both datasets empty"/>
-  <int value="1" label="Local data empty (account data non-empty)"/>
-  <int value="2" label="Account data empty (local data non-empty)"/>
-  <int value="3" label="Both datasets equal and non-empty"/>
-  <int value="4"
-      label="Local data non-empty and a strict subset of account data"/>
-  <int value="5"
-      label="Intersection of local and account data [99%, 100%) of the total"/>
-  <int value="6"
-      label="Intersection of local and account data [95%, 99%) of the total"/>
-  <int value="7"
-      label="Intersection of local and account data [90%, 95%) of the total"/>
-  <int value="8"
-      label="Intersection of local and account data [50%, 90%) of the total"/>
-  <int value="9"
-      label="Intersection of local and account data [10%, 50%) of the total"/>
-  <int value="10"
-      label="Intersection of local and account data (0%, 10%) of the total"/>
-  <int value="11"
-      label="Local and account datasets are non-empty but do not intersect"/>
-</enum>
-
-<!-- LINT.ThenChange(/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.h:BookmarkSetComparisonOutcome) -->
-
 <!-- LINT.IfChange(BookmarksGUIDDuplicates) -->
 
 <enum name="BookmarksGUIDDuplicates">
@@ -203,33 +176,6 @@
 
 <!-- LINT.ThenChange(/components/sync_bookmarks/bookmark_specifics_conversions.cc:InvalidBookmarkSpecificsError) -->
 
-<!-- LINT.IfChange(LegacyBookmarkSetComparisonOutcome) -->
-
-<enum name="LegacyBookmarkSetComparisonOutcome">
-  <int value="0" label="Both datasets empty"/>
-  <int value="1" label="Account data empty (local data non-empty)"/>
-  <int value="2" label="Local data empty (account data non-empty)"/>
-  <int value="3" label="Both datasets equal and non-empty"/>
-  <int value="4"
-      label="Account data non-empty and a strict subset of local data"/>
-  <int value="5"
-      label="Intersection of local and account data [99%, 100%) of the total"/>
-  <int value="6"
-      label="Intersection of local and account data [95%, 99%) of the total"/>
-  <int value="7"
-      label="Intersection of local and account data [90%, 95%) of the total"/>
-  <int value="8"
-      label="Intersection of local and account data [50%, 90%) of the total"/>
-  <int value="9"
-      label="Intersection of local and account data [10%, 50%) of the total"/>
-  <int value="10"
-      label="Intersection of local and account data (0%, 10%) of the total"/>
-  <int value="11"
-      label="Local and account datasets are non-empty but do not intersect"/>
-</enum>
-
-<!-- LINT.ThenChange(//components/sync_bookmarks/bookmark_model_merger_comparison_metrics_unittests.cc:LegacyBookmarkSetComparisonOutcome) -->
-
 <!-- LINT.IfChange(NudgedUpdateResult) -->
 
 <enum name="NudgedUpdateResult">
@@ -280,18 +226,6 @@
 
 <!-- LINT.ThenChange(/components/sync/engine/data_type_worker.h:PendingInvalidationStatus) -->
 
-<!-- LINT.IfChange(PreviouslySyncingGaiaIdInfoForMetrics) -->
-
-<enum name="PreviouslySyncingGaiaIdInfoForMetrics">
-  <int value="0" label="Unspecified"/>
-  <int value="1" label="(deprecated) Not enough information"/>
-  <int value="2" label="Sync feature never previously turned on"/>
-  <int value="3" label="Current Gaia ID matches previous with Sync on"/>
-  <int value="4" label="Current Gaia ID differs previous with Sync on"/>
-</enum>
-
-<!-- LINT.ThenChange(/components/sync/base/previously_syncing_gaia_id_info_for_metrics.h:PreviouslySyncingGaiaIdInfoForMetrics) -->
-
 <!-- LINT.IfChange(RemoteBookmarkUpdateError) -->
 
 <enum name="RemoteBookmarkUpdateError">
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index fbfcce7..239d91c4 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -363,173 +363,6 @@
   </summary>
 </histogram>
 
-<histogram
-    name="Sync.BookmarkModelMerger.Comparison2{OptionalPreviouslySyncingGaiaId}.{SubtreeSelection}.{GroupingKey}{OptionalBookmarkCount}"
-    enum="BookmarkSetComparisonOutcome" expires_after="2025-11-23">
-  <owner>mastiz@chromium.org</owner>
-  <owner>droger@chromium.org</owner>
-  <summary>
-    Records the result of comparing URL bookmarks stored locally with those
-    downloaded from the Sync server. This particular metric variant considers
-    {SubtreeSelection} using {GroupingKey} as uniqueness key when finding
-    matches across local and account bookmarks.
-
-    Recorded on desktop platforms (excluding ChromeOS) only when bookmark sync
-    (legacy Full Sync) is turned on and after account bookmarks are downloaded
-    from the Sync
-    server{OptionalPreviouslySyncingGaiaId}{OptionalBookmarkCount}.
-  </summary>
-<!-- LINT.IfChange(BookmarkComparisonPreviouslySyncingGaiaId) -->
-
-  <token key="OptionalPreviouslySyncingGaiaId">
-    <variant name="" summary=""/>
-    <variant name=".DiffersPreviousGaiaId"
-        summary=", for the case where the user (gaia ID) currently turning on
-                 bookmark sync is different to the one that had previously
-                 turned sync on"/>
-    <variant name=".MatchesPreviousGaiaId"
-        summary=", for the case where the user (gaia ID) currently turning on
-                 bookmark sync is the same as the one that had previously
-                 turned sync on"/>
-    <variant name=".NoPreviousGaiaId"
-        summary=", for the case where sync was never turned on before in the
-                 current browser profile"/>
-  </token>
-<!-- LINT.ThenChange(//components/sync_bookmarks/bookmark_model_merger_comparison_metrics.cc:BookmarkComparisonPreviouslySyncingGaiaId) -->
-
-<!-- LINT.IfChange(BookmarkComparisonSubtreeSelection) -->
-
-  <token key="SubtreeSelection">
-    <variant name="ConsideringAllBookmarks" summary="the entire bookmark tree"/>
-    <variant name="UnderBookmarksBar"
-        summary="desdendants of the Bookmarks Bar"/>
-  </token>
-<!-- LINT.ThenChange(//components/sync_bookmarks/bookmark_model_merger_comparison_metrics.cc:BookmarkComparisonSubtreeSelection) -->
-
-<!-- LINT.IfChange(BookmarkComparisonGroupingKey) -->
-
-  <token key="GroupingKey">
-    <variant name="ByUrl" summary="the bookmark's URL"/>
-    <variant name="ByUrlAndTitle" summary="&lt;URL, title&gt;"/>
-    <variant name="ByUrlAndTitleAndPath"
-        summary="&lt;URL, title, path&gt; (where the path contains ancestor's
-                 titles)"/>
-    <variant name="ByUrlAndTitleAndPathAndUuid"
-        summary="&lt;URL, title, path, UUID&gt; (where the path contains
-                 ancestor's titles)"/>
-    <variant name="ByUrlAndUuid" summary="&lt;URL, UUID&gt;"/>
-  </token>
-<!-- LINT.ThenChange(//components/sync_bookmarks/bookmark_model_merger_comparison_metrics.cc:BookmarkComparisonGroupingKey) -->
-
-<!-- LINT.IfChange(BookmarkComparisonBookmarkCount) -->
-
-  <token key="OptionalBookmarkCount">
-    <variant name="" summary=""/>
-    <variant name=".1000OrMoreLocalUrlBookmarks"
-        summary=", only if there were 1,000 or more URL bookmarks stored
-                 locally and relevant in this metric variant (prior to any
-                 grouping based on uniqueness keys)"/>
-    <variant name=".Between1And19LocalUrlBookmarks"
-        summary=", only if there were between 1 and 19 (both inclusive) URL
-                 bookmarks stored locally and relevant in this metric variant
-                 (prior to any grouping based on uniqueness keys)"/>
-    <variant name=".Between20and999LocalUrlBookmarks"
-        summary=", only if there were between 20 and 999 (both inclusive) URL
-                 bookmarks stored locally and relevant in this metric variant
-                 (prior to any grouping based on uniqueness keys)"/>
-    <variant name=".ZeroLocalUrlBookmarks"
-        summary=", only if there were no URL bookmarks stored locally that
-                 are relevant in this metric variant"/>
-  </token>
-<!-- LINT.ThenChange(//components/sync_bookmarks/bookmark_model_merger_comparison_metrics.cc:BookmarkComparisonBookmarkCount) -->
-
-</histogram>
-
-<histogram
-    name="Sync.BookmarkModelMerger.Comparison{OptionalPreviouslySyncingGaiaId}.{SubtreeSelection}.{GroupingKey}{OptionalBookmarkCount}"
-    enum="LegacyBookmarkSetComparisonOutcome" expires_after="2025-11-23">
-  <owner>mastiz@chromium.org</owner>
-  <owner>droger@chromium.org</owner>
-  <summary>
-    NOTE: Prefer analyzing analogous metrics whose name starts with
-    Sync.BookmarkModelMerger.Comparison2 for newer browser versions, starting
-    with M138. This histogram is analogous but the buckets are slightly
-    different, as a result of a bug in the original implementation.
-
-    Records the result of comparing URL bookmarks stored locally with those
-    downloaded from the Sync server. This particular metric variant considers
-    {SubtreeSelection} using {GroupingKey} as uniqueness key when finding
-    matches across local and account bookmarks.
-
-    Recorded on desktop platforms (excluding ChromeOS) only when bookmark sync
-    (legacy Full Sync) is turned on and after account bookmarks are downloaded
-    from the Sync
-    server{OptionalPreviouslySyncingGaiaId}{OptionalBookmarkCount}.
-  </summary>
-  <token key="OptionalPreviouslySyncingGaiaId">
-    <variant name="" summary=""/>
-    <variant name=".DiffersPreviousGaiaId"
-        summary=", for the case where the user (gaia ID) currently turning on
-                 bookmark sync is different to the one that had previously
-                 turned sync on"/>
-    <variant name=".MatchesPreviousGaiaId"
-        summary=", for the case where the user (gaia ID) currently turning on
-                 bookmark sync is the same as the one that had previously
-                 turned sync on"/>
-    <variant name=".NoPreviousGaiaId"
-        summary=", for the case where sync was never turned on before in the
-                 current browser profile"/>
-  </token>
-  <token key="SubtreeSelection">
-    <variant name="ConsideringAllBookmarks" summary="the entire bookmark tree"/>
-    <variant name="UnderBookmarksBar"
-        summary="desdendants of the Bookmarks Bar"/>
-  </token>
-  <token key="GroupingKey">
-    <variant name="ByUrl" summary="the bookmark's URL"/>
-    <variant name="ByUrlAndTitle" summary="&lt;URL, title&gt;"/>
-    <variant name="ByUrlAndTitleAndPath"
-        summary="&lt;URL, title, path&gt; (where the path contains ancestor's
-                 titles)"/>
-    <variant name="ByUrlAndTitleAndPathAndUuid"
-        summary="&lt;URL, title, path, UUID&gt; (where the path contains
-                 ancestor's titles)"/>
-    <variant name="ByUrlAndUuid" summary="&lt;URL, UUID&gt;"/>
-  </token>
-  <token key="OptionalBookmarkCount">
-    <variant name="" summary=""/>
-    <variant name=".1000OrMoreLocalUrlBookmarks"
-        summary=", only if there were 1,000 or more URL bookmarks stored
-                 locally and relevant in this metric variant (prior to any
-                 grouping based on uniqueness keys)"/>
-    <variant name=".Between1And19LocalUrlBookmarks"
-        summary=", only if there were between 1 and 19 (both inclusive) URL
-                 bookmarks stored locally and relevant in this metric variant
-                 (prior to any grouping based on uniqueness keys)"/>
-    <variant name=".Between20and999LocalUrlBookmarks"
-        summary=", only if there were between 20 and 999 (both inclusive) URL
-                 bookmarks stored locally and relevant in this metric variant
-                 (prior to any grouping based on uniqueness keys)"/>
-    <variant name=".ZeroLocalUrlBookmarks"
-        summary=", only if there were no URL bookmarks stored locally that
-                 are relevant in this metric variant"/>
-  </token>
-</histogram>
-
-<histogram name="Sync.BookmarkModelMerger.PreviouslySyncingGaiaId"
-    enum="PreviouslySyncingGaiaIdInfoForMetrics" expires_after="2025-12-14">
-  <owner>mastiz@chromium.org</owner>
-  <owner>droger@chromium.org</owner>
-  <summary>
-    Records how the current gaia ID (user) compares to the last gaia ID known to
-    have turned Sync the feature on within the same profile, if any.
-
-    Recorded on desktop platforms (excluding ChromeOS) only when bookmark sync
-    (legacy Full Sync) is turned on and after account bookmarks are downloaded
-    from the Sync server.
-  </summary>
-</histogram>
-
 <histogram name="Sync.BookmarkModelMerger.ReachableInputUpdates"
     units="bookmarks" expires_after="2025-09-19">
   <owner>rushans@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/webapps/histograms.xml b/tools/metrics/histograms/metadata/webapps/histograms.xml
index 121824f..1e2cc8cb 100644
--- a/tools/metrics/histograms/metadata/webapps/histograms.xml
+++ b/tools/metrics/histograms/metadata/webapps/histograms.xml
@@ -2371,7 +2371,7 @@
 <histogram name="Webapp.SystemApps.FreshInstallDuration" units="ms"
     expires_after="2026-01-05">
   <owner>renkens@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records the time taken to perform a fresh install of all system web apps. It
     measures the time from when we dispatch a call to install them, until we get
@@ -2384,7 +2384,7 @@
 <histogram name="Webapp.SystemApps.IconsAreHealthyInSession"
     enum="BooleanSuccess" expires_after="2025-12-08">
   <owner>renkens@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records whether system web app icons are healthy for each user session that
     has system web apps installed. Failure indicates that at least one app has
@@ -2395,7 +2395,7 @@
 <histogram name="Webapp.SystemApps.IconsFixedOnReinstall" enum="BooleanSuccess"
     expires_after="2025-12-08">
   <owner>renkens@google.com</owner>
-  <owner>cros-web-apps-team@google.com</owner>
+  <owner>lt-web-apps-team@google.com</owner>
   <summary>
     Records whether a reinstall fixes previously broken system web app icons.
     This is recorded during icon check after a reinstall (when icons were broken
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 06d8a99..aaad57f 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -19684,7 +19684,7 @@
   <metric name="PredictionsApiResponse.GrantLikelihood"
       enum="PredictionGrantLikelihood">
     <summary>
-      An enum of type PermissionUmaUtil::PredictionGrantLikelihood describing
+      An enum of type PermissionUiSelector::PredictionGrantLikelihood describing
       the likelihood returned by the Web Permission Predictions Service, if the
       service was successfully queried.
     </summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 92f9b79..508856d 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/v51.2/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "76dad151601c7e2f7b4041d91f36bb723dc0efab",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/17e2ace16a3429eca03fdbea1c3b5e80cf2a6b2f/trace_processor_shell.exe"
+            "hash": "e4b6ec7a6ae431bb69ac8c1baf9e13300dcdae38",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/9780c6601d4febb8f2b5845fa8568f5f40ce0957/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "e212779ceb1afab1bb5c71e6833fa615137ad852",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v51.2/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "000e5c277c311fef6539a229e4dfe2697b04a15b",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/d71a6c715511296ff6fca63b80b96437d9762dc0/trace_processor_shell"
+            "hash": "8287b3032c49a81ec6ed1a94bfa7c77710168b7f",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/b771e98cb55a18bb45cc5ca6feb62483d863f1c3/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 6c5bccc..39344c9 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -352,7 +352,7 @@
  <item id="network_diagnostics_stun" added_in_milestone="111" content_hash_code="059bf138" os_list="chromeos" file_path="chrome/browser/ash/net/network_diagnostics/network_diagnostics_util.cc" />
  <item id="network_diagnostics_tls" added_in_milestone="111" content_hash_code="0170b367" os_list="chromeos" file_path="chrome/browser/ash/net/network_diagnostics/tls_prober.cc" />
  <item id="gstatic_fast_checkout_funnels" added_in_milestone="111" content_hash_code="0531d118" os_list="android" file_path="chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_impl.cc" />
- <item id="webapk_minter_install_request" added_in_milestone="111" content_hash_code="04edcfed" os_list="chromeos" file_path="chrome/browser/apps/app_service/webapk/webapk_install_task.cc" />
+ <item id="webapk_minter_install_request" added_in_milestone="111" content_hash_code="06c1f369" os_list="chromeos" file_path="chrome/browser/apps/app_service/webapk/webapk_install_task.cc" />
  <item id="android_feedback_connectivity_checker" added_in_milestone="111" content_hash_code="0011753d" os_list="android" file_path="chrome/android/java/src/org/chromium/chrome/browser/feedback/ConnectivityChecker.java" />
  <item id="printer_config_fetch" added_in_milestone="111" content_hash_code="042f8883" os_list="chromeos" file_path="chromeos/printing/printer_config_cache.cc" />
  <item id="projector_xhr_loader_allow_cookie" added_in_milestone="112" content_hash_code="04a2140d" os_list="chromeos" file_path="ash/webui/projector_app/projector_xhr_sender.cc" />
diff --git a/ui/ozone/common/bitmap_cursor.h b/ui/ozone/common/bitmap_cursor.h
index 23b013b..fe4637f 100644
--- a/ui/ozone/common/bitmap_cursor.h
+++ b/ui/ozone/common/bitmap_cursor.h
@@ -60,6 +60,7 @@
 
   // For theme cursors.
   void* platform_data() { return platform_data_; }
+  void clear_platform_data() { platform_data_ = nullptr; }
 
   float cursor_image_scale_factor() const { return cursor_image_scale_factor_; }
 
@@ -74,7 +75,7 @@
 
   // Platform cursor data.  Having this non-nullptr means that this cursor
   // is supplied by the platform.
-  const raw_ptr<void> platform_data_ = nullptr;
+  raw_ptr<void> platform_data_ = nullptr;
 
   float cursor_image_scale_factor_ = 1.f;
 };
diff --git a/ui/ozone/platform/wayland/host/wayland_cursor_factory.cc b/ui/ozone/platform/wayland/host/wayland_cursor_factory.cc
index 572f40ec..9eec2f0 100644
--- a/ui/ozone/platform/wayland/host/wayland_cursor_factory.cc
+++ b/ui/ozone/platform/wayland/host/wayland_cursor_factory.cc
@@ -38,7 +38,12 @@
 
 WaylandCursorFactory::ThemeData::ThemeData() = default;
 
-WaylandCursorFactory::ThemeData::~ThemeData() = default;
+WaylandCursorFactory::ThemeData::~ThemeData() {
+  for (auto& [cursor_type, cursor] : cache()) {
+    // Avoids Dangling raw_ptr of wl_cursor*.
+    cursor->bitmap_cursor()->clear_platform_data();
+  }
+}
 
 void WaylandCursorFactory::ThemeData::AddThemeLoadedCallback(
     Callback callback) {
@@ -159,14 +164,14 @@
     float scale) {
   auto* const current_theme = GetThemeForScale(scale);
   DCHECK(current_theme);
-  if (current_theme->cache.count(type) == 0) {
+  if (current_theme->cache().count(type) == 0) {
     auto async_cursor = base::MakeRefCounted<WaylandAsyncCursor>();
-    current_theme->cache[type] = async_cursor;
+    current_theme->cache()[type] = async_cursor;
     current_theme->AddThemeLoadedCallback(
         base::BindOnce(&WaylandCursorFactory::FinishCursorLoad,
                        weak_factory_.GetWeakPtr(), async_cursor, type, scale));
   }
-  return current_theme->cache[type];
+  return current_theme->cache()[type];
 }
 
 wl_cursor* WaylandCursorFactory::GetCursorFromTheme(wl_cursor_theme* theme,
@@ -202,7 +207,7 @@
     return;
   }
   for (auto& item : *theme_cache_) {
-    for (auto& subitem : item.second->cache) {
+    for (auto& subitem : item.second->cache()) {
       auto bitmap_cursor = subitem.second->bitmap_cursor();
       if (bitmap_cursor && bitmap_cursor->platform_data() == cursor_data) {
         // The cursor that has been just attached is from the current theme.
@@ -247,7 +252,7 @@
 void WaylandCursorFactory::FlushThemeCache(bool force) {
   size_t num_cursor_objects = 0;
   for (auto& entry : *theme_cache_) {
-    num_cursor_objects += entry.second->cache.size();
+    num_cursor_objects += entry.second->cache().size();
   }
   if (force) {
     unloaded_theme_.reset();
@@ -275,6 +280,8 @@
   // have to be cautious when we actually load the shape.
   if (cache_entry) {
     cache_entry->SetLoadedTheme(loaded_theme);
+  } else {
+    wl_cursor_theme_destroy(loaded_theme);
   }
 }
 
diff --git a/ui/ozone/platform/wayland/host/wayland_cursor_factory.h b/ui/ozone/platform/wayland/host/wayland_cursor_factory.h
index 495d5684..8768b531 100644
--- a/ui/ozone/platform/wayland/host/wayland_cursor_factory.h
+++ b/ui/ozone/platform/wayland/host/wayland_cursor_factory.h
@@ -96,6 +96,8 @@
   class ThemeData {
    public:
     using Callback = base::OnceCallback<void(wl_cursor_theme*)>;
+    using TypeCursorCache =
+        base::flat_map<mojom::CursorType, scoped_refptr<WaylandAsyncCursor>>;
 
     ThemeData();
     ~ThemeData();
@@ -107,11 +109,12 @@
 
     wl_cursor_theme* theme() { return theme_.get(); }
 
-    base::flat_map<mojom::CursorType, scoped_refptr<WaylandAsyncCursor>> cache;
+    TypeCursorCache& cache() { return cache_; }
 
    private:
     bool loaded_ = false;
     wl::Object<wl_cursor_theme> theme_;
+    TypeCursorCache cache_;
     std::vector<Callback> callbacks_;
     base::WeakPtrFactory<ThemeData> weak_factory_{this};
   };
diff --git a/ui/ozone/platform/wayland/host/wayland_cursor_factory_unittest.cc b/ui/ozone/platform/wayland/host/wayland_cursor_factory_unittest.cc
index 42060e0f..dd8ee94 100644
--- a/ui/ozone/platform/wayland/host/wayland_cursor_factory_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_cursor_factory_unittest.cc
@@ -139,7 +139,7 @@
 
       auto* const new_current_theme = cursor_factory->theme_cache_.get();
       ASSERT_EQ(new_current_theme->size(), 1U);
-      EXPECT_EQ(new_current_theme->begin()->second->cache.size(), 0U);
+      EXPECT_EQ(new_current_theme->begin()->second->cache().size(), 0U);
       EXPECT_NE(new_current_theme, used_current_theme);
       EXPECT_EQ(cursor_factory->unloaded_theme_.get(), used_current_theme);
 
@@ -236,7 +236,7 @@
     ASSERT_EQ(cursor_factory->theme_cache_->size(), 1U);
     auto* const new_current_theme =
         cursor_factory->theme_cache_->begin()->second.get();
-    EXPECT_EQ(new_current_theme->cache.size(), 0U);
+    EXPECT_EQ(new_current_theme->cache().size(), 0U);
     EXPECT_NE(cursor_factory->unloaded_theme_.get(), nullptr);
 
     WaitForThemeLoaded();
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index f042df0..6a244d2 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -481,6 +481,7 @@
     // TODO(b/324294360): This will cause a lot of dangling pointers, which
     // breaks linux wayland bot. Fix them and enable on linux as well.
 #if BUILDFLAG(IS_CHROMEOS) || !PA_BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS)
+    cursor_factory_.reset();
     connection_.reset();
 #endif
   }
diff --git a/v8 b/v8
index 8c396d3..370446a 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 8c396d356a3034fa58739e764fba006d8b1e6eae
+Subproject commit 370446a348a888bf83d4324c2668fe174fb7cf74